Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

第 92 题:已知数据格式,实现一个函数 fn 找出链条中所有的父级 id #143

Open
yygmind opened this issue Jun 18, 2019 · 127 comments

Comments

@yygmind
Copy link
Contributor

yygmind commented Jun 18, 2019

const value = '112'
const fn = (value) => {
...
}
fn(value) // 输出 [1, 11, 112]

@Zephylaci
Copy link

Zephylaci commented Jun 19, 2019

const data = [{
    id: '1',
    name: 'test1',
    children: [
        {
            id: '11',
            name: 'test11',
            children: [
                {
                    id: '111',
                    name: 'test111'
                },
                {
                    id: '112',
                    name: 'test112'
                }
            ]

        },
        {
            id: '12',
            name: 'test12',
            children: [
                {
                    id: '121',
                    name: 'test121'
                },
                {
                    id: '122',
                    name: 'test122'
                }
            ]
        }
    ]
}];
const find = (value) => {
    let result = [];
    let findArr = data;
    let skey = '';
    for (let i = 0, l = value.length; i < l; i++) {
        skey += value[i]
        let item = findArr.find((item) => {
            return item.id == skey
        });

        if (!item) {
            return [];
        }

        result.push(item.id);
        if (item.children) {
            findArr = item.children;
        } else {
            if (i < l - 1) return []
            return result;
        }

    }

}
//调用看结果
function testFun() {
    console.log('1,11,111:', find('111'))
    console.log('1,11,112:', find('112'))
    console.log('1,12,121:', find('121'))
    console.log('1,12,122:', find('122'))
    console.log('[]:', find('113'))
    console.log('[]:', find('1114'))
}

@ZodiacSyndicate
Copy link

ZodiacSyndicate commented Jun 19, 2019

dfs

const fn = (data, value) => {
  let res = []
  const dfs = (arr, temp = []) => {
    for (const node of arr) {
      if (node.children) {
        dfs(node.children, temp.concat(node.id))
      } else {
        if (node.id === value) {
          res = temp
        }
        return
      }
    }
  }
  dfs(data)
  return res
}

@kungithub
Copy link

let list = [{
    id: '1',
    children: [{
        id: '11',
        children: [{
            id: '111'
        }, {
            id: '112'
        }]
    }]
}];

function fn(value) {
    // 回溯的标记
    let _p = Symbol('parent');
    // 找到子节点
    let result;
    function _fn(arr, p) {
        for (let i = 0; i < arr.length; i++) {
            arr[i][_p] = p;
            if (arr[i].id === value) {
                result = arr[i];
                return;
            }
            !result && arr[i].children && _fn(arr[i].children, arr[i])
        }
        if (result) return;
    }
    _fn(list, null);
    let tmp = [];
    if (!result) return null;
    while (result) {
        tmp.unshift(result.id);
        result = result[_p];
    }
    return tmp;
}

思路是找到子节点,再回溯找父节点
复杂度是O(n),循环n次子节点,但是需要额外空间记录父节点引用

@sgzhm4444
Copy link

const findItem = (value, list, graph) => {
    return list.some(item => {
        if (item.id === value) {
            graph.push(item.id)
            return true
        }
        if (item.children) {
            graph.push(item.id)
            return findItem(value, item.children, graph)
        }
    })
}

const fn = value => {
    let graph = []
    list.some(item => {
        graph.push(item.id)
        if (item.id === value) return true
        if (item.children) {
            let res = findItem(value, item.children, graph)
            if (!res) graph = []
            return res
        }
    })
    return graph
}

实现的思路是记录每次 dfs 遍历的路径,当找到元素时,返回记录的所有路径,找不到时清空路径遍历下个元素

希望有更好的解法

var list = [{
	id: '1',
	children: [{
		id: '11',
		children: [{
			id: '111'
		}, {
			id: '112'
		}]
	}, {
		id: '12',
		children: [{
			id: '121'
		}, {
			id: '122'
		}]
	}]
}]

const value = '122';

这个情景不太对吧

@yeyan1996
Copy link

const findItem = (value, list, graph) => {
    return list.some(item => {
        if (item.id === value) {
            graph.push(item.id)
            return true
        }
        if (item.children) {
            graph.push(item.id)
            return findItem(value, item.children, graph)
        }
    })
}

const fn = value => {
    let graph = []
    list.some(item => {
        graph.push(item.id)
        if (item.id === value) return true
        if (item.children) {
            let res = findItem(value, item.children, graph)
            if (!res) graph = []
            return res
        }
    })
    return graph
}

实现的思路是记录每次 dfs 遍历的路径,当找到元素时,返回记录的所有路径,找不到时清空路径遍历下个元素
希望有更好的解法

var list = [{
	id: '1',
	children: [{
		id: '11',
		children: [{
			id: '111'
		}, {
			id: '112'
		}]
	}, {
		id: '12',
		children: [{
			id: '121'
		}, {
			id: '122'
		}]
	}]
}]

const value = '122';

这个情景不太对吧

嗯我再研究一下~

@Zephylaci
Copy link

@ZodiacSyndicate
这个写法想查112的时候不就查不出来了..

@lhyt
Copy link

lhyt commented Jun 19, 2019

  • bfs利用队列实现,循环中做的是push => shift => push => shift
  • dfs利用栈实现,循环中做的是push => pop => push => pop

刚刚好,中间仅仅差了一个数组方法:

function bfs(target, id) {
  const quene = [...target]
  do {
    const current = quene.shift()
    if (current.children) {
      quene.push(...current.children.map(x => ({ ...x, path: (current.path || current.id) + '-' + x.id })))
    }
    if (current.id === id) {
      return current
    }
  } while(quene.length)
  return undefined
}

function dfs(target, id) {
  const stask = [...target]
  do {
    const current = stask.pop()
    if (current.children) {
      stask.push(...current.children.map(x => ({ ...x, path: (current.path || current.id) + '-' + x.id })))
    }
    if (current.id === id) {
      return current
    }
  } while(stask.length)
  return undefined
}

// 公共的搜索方法,默认bfs
function commonSearch(target, id, mode) {
  const staskOrQuene = [...target]
  do {
    const current = staskOrQuene[mode === 'dfs' ? 'pop' : 'shift']()
    if (current.children) {
      staskOrQuene.push(...current.children.map(x => ({ ...x, path: (current.path || current.id) + '-' + x.id })))
    }
    if (current.id === id) {
      return current
    }
  } while(staskOrQuene.length)
  return undefined
}

@Rashomon511
Copy link

        const fn = (value) => {
            let graph = []
            const mapData = new Map();
            function ParentMap(data, parentId) {
                parentId = parentId || 0;
                data.forEach(item => {
                    mapData[item.id] = { ...item, parentId }
                    if (item.children) {
                        ParentMap(item.children, item.id);
                    }
                })
            }
            ParentMap(data)
            function getId(data, value) {
                graph.unshift(data[value].id)
                if (data[value].parentId !== 0) {
                    getId(data, data[value].parentId)
                }
            }
            getId(mapData, value)
            return graph;
        }

@GuoYuFu123
Copy link

/*
* 已知数据格式,实现一个函数 fn 找出链条中所有的父级 id 
* 实现: 通过es6的class实现,思路:递归调用,下传当前的父辈的id
*/
class FindFather{
    constructor() {
        this.data = this.init(),
        this.target = null;
    }
    dfs(value,data,idArr) {
        var that = this;
        data.forEach((item, index) => {
            item.idArr = idArr.concat(item.id)
            if(item.id === value) {
                this.target = item.idArr;
            }
            if(item.children){
                return this.dfs(value, item.children, item.idArr)
            }
        })
    }
    result() {
        return this.target;
    }
    init() {
        return [{
            id: '1',
            name: 'test1',
            children: [
                {
                    id: '11',
                    name: 'test11',
                    children: [
                        {
                            id: '111',
                            name: 'test111'
                        },
                        {
                            id: '112',
                            name: 'test112'
                        }
                    ]

                },
                {
                    id: '12',
                    name: 'test12',
                    children: [
                        {
                            id: '121',
                            name: 'test121'
                        }
                    ]
                }
            ]
        }];
    }
    
}
var find = new FindFather();
find.dfs('112',find.data, [])
console.log(find.result())  //["1","12","121"]

@18692959234
Copy link

const data = [{
    id: '1',
    name: 'test1',
    children: [
        {
            id: '11',
            name: 'test11',
            children: [
                {
                    id: '111',
                    name: 'test111'
                },
                {
                    id: '112',
                    name: 'test112'
                }
            ]
        },
        {
            id: '12',
            name: 'test12',
            children: [
                {
                    id: '121',
                    name: 'test121',
                    children: [
                        {
                            id: '1221',
                            name: 'test121',
                            children:[
                                {
                                    id:'12345'
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
},
{
    id:'1234',
    children:[
        {
            id:'567'
        }
    ]
}
]
const value = '112'
const fn = (value) => {
    let err = 'return start'
    let interim = JSON.parse(JSON.stringify(data))
    let result = []
    try {
        while (interim.length > 0) {
            let point = interim.pop() 
            let child = point.children
            let queue = child
            let cresult = []
            result.push(point.id)
            while (queue && queue.length > 0) {
                let cpoint = queue.pop()
                cresult.push(cpoint.id)
                if (!cpoint.children || cpoint.id == value) {
                    if (cresult.indexOf(value) > -1) {
                        queue.length = 0
                    } else {
                        cresult = []
                    }
                    continue
                }
                queue.push(...cpoint.children)
            }
            if (result.concat(cresult).indexOf(value) > -1) {
                result = result.concat(cresult)
                throw new Error(err)
            } else {
                result = []
            }
        }
    } catch (e) {
        if (e.message === err) {
            console.log(result.map(v => parseInt(v)))
            return result.map(v => parseInt(v))
        } else {
            throw e
        }
    }

}
fn(value) // 输出 [1, 11, 112]
fn('1234') // 输出 [1234]
fn('12345') // 输出 [1, 12, 121, 1221, 12345]

写法是深度优先, 增加了数据结构的深度与广度,能可以正确输出

@lvwxx
Copy link

lvwxx commented Jun 19, 2019

function dfsFn(list, value) {
  let res = []

  function dfs(arr, ids=[]) {
    for (const item of arr) {
      if (item.id === value) {
        res = [...ids, ...[item.id]]
        return
      } else {
        if (item.children) {
          dfs(item.children, ids.concat([item.id]))
        }
      }
    }
  }

  dfs(list)

  return res
}

@B2D1
Copy link

B2D1 commented Jun 19, 2019

const data = [{
    id: '1',
    name: 'test1',
    children: [
        {
            id: '11',
            name: 'test11',
            children: [
                {
                    id: '111',
                    name: 'test111'
                },
                {
                    id: '112',
                    name: 'test112'
                }
            ]
        },
        {
            id: '12',
            name: 'test12',
            children: [
                {
                    id: '121',
                    name: 'test121'
                },
                {
                    id: '122',
                    name: 'test122'
                }
            ]
        }
    ]
}];

let res = [];

const findId = (list, value) => {
  let len = list.length;

  for (let i in list) {
    const item = list[i];

    if (item.id == value) {
      return res.push(item.id), [item.id];
    }

    if (item.children) {
      if (findId(item.children, value).length) {
        res.unshift(item.id);
        return res;
      }
    }

    if (i == len - 1) {
      return res;
    }
  }
};

// res =  []
findId(data, '123'); 

// res = [ '1' ]
findId(data, '1');   

// res = [ '1', '12' ]      
findId(data, '12');    

// res = [ '1', '12', '122' ]  
findId(data, '122');    

@nailfar
Copy link

nailfar commented Jun 20, 2019

function getIdChain(data, id, idkey = "id", childrenKey = "children") {
  if (check(data, id)) {
    return [];
  }

  loop.chain = [];
  loop(data, id);
  return loop.chain;

  function check(data, id) {
    return !Array.isArray(data) || !data.length || (!id && id !== 0);
  }

  function loop(arr, v) {
    if (check(arr, v)) {
      return false;
    }
    return arr.some(i => {
      return i[idkey] === v || (i[childrenKey] && loop(i[childrenKey], v))
        ? (loop.chain.unshift(i[idkey]), true)
        : false;
    });
  }
}


@yeyan1996
Copy link

评论好多直接复制黏贴都是错的,发之前先测试一下啊,另外如果按照示例中省市区id的规则,可以找到目标 id 然后直接推倒出所有父 id 的吧....

let res = []
let value = '112'
for(let i = 0;i<value.length;i++){
    res.push(value.slice(0,i+1))
}
console.log(res)

@LGwen
Copy link

LGwen commented Jun 20, 2019

const value = '112'
const fn = (value) => {
let vals = value.split('');
//1 11 112
for (let i = 0; i < vals.length; i++) {
let v = vals.slice(0,i+1).join('')
console.log(v)
}
}
fn(value)

@simon-woo
Copy link

simon-woo commented Jun 20, 2019

const fn = (str) => [...str].reduce((prev, next) => [...prev, prev.slice(-1) + next], [])

@xxyGoTop
Copy link

const bfs = data => {
  const queue = [...data]
  let res = []
  while(queue.length) {
    let node = queue.shift()
    if (node.children) {
      for (let child of node.children) {
        queue.push(child)
      }
      res.push(node.id)
    }
  }
  return res
}

@nailfar
Copy link

nailfar commented Jun 21, 2019

评论好多直接复制黏贴都是错的,发之前先测试一下啊,另外如果按照示例中省市区id的规则,可以找到目标 id 然后直接推倒出所有父 id 的吧....

let res = []
let value = '112'
for(let i = 0;i<value.length;i++){
    res.push(value.slice(0,i+1))
}
console.log(res)

仔细看了下题目意图应该是通过这个id值 解析出所有父级链路,但是根据数据截图,这样推演下去34个省 可能会出现 3411 34512 这就没法玩了 省应该至少占两位 01 - 34 ; 为了出题而出的题 ,没有实用价值

@benzhemin
Copy link

benzhemin commented Jun 21, 2019

const tree = {
  id: '1',
  name: 'test1',
  children: [
      {
          id: '11',
          name: 'test11',
          children: [
              {
                  id: '111',
                  name: 'test111'
              },
              {
                  id: '112',
                  name: 'test112'
              }
          ]
      },
      {
          id: '12',
          name: 'test12',
          children: [
              {
                  id: '121',
                  name: 'test121'
              },
              {
                  id: '122',
                  name: 'test122'
              }
          ]
      }
  ]
};

interface Area {
  id: string,
  name: string,
  children?: Array<Area>
};

function findAllParentId(tree: Area, id: string) {
  const stack = [tree];
  while(stack.length > 0) {
    const top = stack.slice().pop();
    if (top.id === id) break;

    if (top.children && top.children.length>0) {
      const cTop = top.children.pop();
      stack.push(cTop);
    } else {
      stack.pop();
    }
  }
  return stack.map(n => n.id);
}

const deepCopy = obj => JSON.parse(JSON.stringify(obj));
const idList = findAllParentId(deepCopy(tree), '122');
console.log(`${idList}`);

经典数据结构的深度遍历结构

@wangyuanyuan0521
Copy link

dfs

const fn = (data, value) => {
  let res = []
  const dfs = (arr, temp = []) => {
    for (const node of arr) {
      if (node.children) {
        dfs(node.children, temp.concat(node.id))
      } else {
        if (node.id === value) {
          res = temp
        }
        return
      }
    }
  }
  dfs(data)
  return res
}

为什么运行结果不对

@Marinerer
Copy link

@wangyuanyuan0521
@ZodiacSyndicate

function fn(data, value) {
  let res = []
  const dfs = (arr, temp = []) => {
    for (const node of arr) {
      if (node.id === value) {
        res = temp
        return
      } else {
        node.children && dfs(node.children, temp.concat(node.id))
      }
    }
  }
  dfs(data)
  return res
}

@k-0311
Copy link

k-0311 commented Jun 25, 2019

const cityData = [{
    id: '1',
    name: '广东省',
    children: [
      {
        id: '11',
        name: '深圳市',
        children: [
          {
            id: '111',
            name: '南山区'
          },
          {
            id: '112',
            name: '福田区',
            children: [{
              id: '1121',
              name: 'A街道'
            }]
          }
        ]
      },
      {
        id: '12',
        name: '东莞市',
        children: [
          {
            id: '121',
            name: 'A区'
          },
          {
            id: '122',
            name: 'B区',
          }
        ]
      }
    ]
  }];

  const tarId = '1121'
  const findId = (data, tarId, parentId = []) =>
    data.reduce((acc, item) =>
      acc.concat(item.id == tarId
        ? [...parentId, item.id]
        : item.children
          ? findId(item.children, tarId, [...parentId, item.id])
          : [])
      , [])

  let res = findId(cityData, tarId) // 输出 [1, 11, 112, 1121]
  console.log(res)

@lovelope
Copy link

lovelope commented Jul 3, 2019

源码

const fn = (value, radix = 1) =>
  Array.from(new Array(value.length / radix)).reduce(
    (al, _, idx) => [...al, value.slice(0, radix * (idx + 1))],
    []
  );

测试

fn('112') // ["1", "11", "112"]
fn('123456789', 3) // ["123", "123456", "123456789"]

解释

用途:用来把地址串转换为地址数组,常用于地址选择器,这里支持任何方式分割(常用于6 位地址每两位分割一次)
原理很简单,字符串是有规律的(每个更详细串包含父级串),就是简单的字符串分割(循环每次取前radix*(idx+1)范围的子串)

@Kntt
Copy link

Kntt commented Jul 9, 2019

评论好多直接复制黏贴都是错的,发之前先测试一下啊,另外如果按照示例中省市区id的规则,可以找到目标 id 然后直接推倒出所有父 id 的吧....

let res = []
let value = '112'
for(let i = 0;i<value.length;i++){
    res.push(value.slice(0,i+1))
}
console.log(res)

其实我也想问这个问题:

  • 如果id是有规律的,最容易处理,通过id解析出结果, 其他情况的重点是优先找到目标节点
  • 如果id的位数和层级有关系, 处理方法就要根据id,判断不同的层级, 做不同的处理(少很多递归)
  • 如果id和层级没有关系,就需要权衡是深度优先还是广度优先做处理

@mcfly001
Copy link

mcfly001 commented Jul 9, 2019

const arr = [
    {
        id: 1,
        name: '广东',
        children: [
            {
                id: 11,
                name: '深圳',
                children: [
                    {
                        id: 111,
                        name: '南山区'
                    },
                    {
                        id: 112,
                        name: '福田区'
                    }
                ]
            }
        ]
    }
]

const id = 112

const fn = (treeData, idsList = [], ids = []) => {
    treeData.forEach(item => {
        const { id, children } = item
        const tempIds = [].concat(ids)
        tempIds.push(id)

        idsList.push(tempIds);

        if (children && !!children.length) {
            fn(children, idsList, tempIds);
        }
    });

    return idsList;
};

const list = fn(arr)


const result = list.filter(item => {
    const lastValue = item[item.length - 1]

    return Number(lastValue) === Number(id)
})

思路:
1、将数组转换为多个一维数组,结果如下所示:
image
2、循环第一步所得的结果,判断最后一位是不是和要查询的id相同,如果是那么就返回结果

@azl397985856
Copy link

为了可读性,牺牲了部分复杂度。

代码:

function fn(id, list) {
  const match = list.find(item => item.id === id);
  if (match) return [id];
  const sub = list.find(item => id.startsWith(item.id));
  return [sub.id].concat(fn(id, sub.children));
}

@yanzhandong
Copy link

const data = [{
    id: '1',
    name: 'test1',
    children: [
        {
            id: '11',
            name: 'test11',
            children: [
                {
                    id: '111',
                    name: 'test111'
                },
                {
                    id: '112',
                    name: 'test112'
                }
            ]

        },
        {
            id: '12',
            name: 'test12',
            children: [
                {
                    id: '121',
                    name: 'test121'
                },
                {
                    id: '122',
					name: 'test122',
					children:[
						{
							id: '1221',
							name: 'test121'
						},
					]
                }
            ]
        }
    ]
}];

function findValue(data,valie){
	var key = [];
	function find (data,value){
		for(let i=0; i<data.length;i++){
			if(data[i].id === value){
				key.push(data[i].id)
				break
			}
			if(data[i].children && data[i].children.length > 0){
				if(find(data[i].children,value).length > 0){
					key.push(data[i].id)
					break;
				}
			}
		}
		return key
	}
	find(data,valie)
	return key
}
console.log(findValue(data,'1221'))

@jununx
Copy link

jununx commented Jul 16, 2019

刚好有类似需求,试完答案竟然基本都是错的,自己试试。

const data = [{
  id: 1,
  children: [{
    id: 2,
    children: [{
      id: 3
    }, {
      id: 4
    }]
  }]
}];

let find = (function () {
  let map = null;
  let findMap = (data, ids, map = {}) => {
    data.forEach(item => {
      map[item.id] = map[item.id] || [];
      if (ids) {
        map[item.id] = ids.concat(item.id);
      } else {
        map[item.id].push(item.id);
      }
      if (item.children) {
        return findMap(item.children, map[item.id], map);
      }
    });
    return map;
  }
  return function (value) {
    if (!map) {
      map = findMap(data);
    }
    return map[value];
  }
})();

console.log(find(1)) // [1]
console.log(find(2)) // [1, 2]
console.log(find(3)) // [1, 2, 3]
console.log(find(4)) // [1, 2, 4]

@Minzax
Copy link

Minzax commented Feb 24, 2021

const data = [{
  id: '1',
  name: 'test1',
  children: [
      {
          id: '11',
          name: 'test11',
          children: [
              {
                  id: '111',
                  name: 'test111'
              },
              {
                  id: '112',
                  name: 'test112'
              }
          ]

      },
      {
          id: '12',
          name: 'test12',
          children: [
              {
                  id: '121',
                  name: 'test121'
              },
              {
                  id: '122',
                  name: 'test122'
              }
          ]
      }
  ]
}];

// 通过递归来实现
function find(arr, value) {
  const result = [];
  for(let i = 0; i < arr.length; i++) {
    const item = arr[i];
    if(item.id === value) {
      return [value]
    }

    if(item.children) {
      const ids = find(item.children, value);
      if(ids.length > 0) {
        result.push(item.id, ...ids);
      }
    }
  }
  return result;
}

console.log(find(data, '11'));

@865077695
Copy link

const data = [
  {
    id: "1",
    name: "test1",
    children: [
      {
        id: "11",
        name: "test11",
        children: [
          {
            id: "111",
            name: "test111"
          },
          {
            id: "112",
            name: "test112"
          }
        ]
      },
      {
        id: "12",
        name: "test12",
        children: [
          {
            id: "121",
            name: "test121"
          },
          {
            id: "122",
            name:
              "test122"
          }
        ]
      }
    ]
  }
]

function find(arr, value) {
  let map = new Map()
  recursion(arr, [], map)
  console.log(map)
  return map.get(value);
}
function recursion(arr, parentArr, map) {
  arr.forEach(item => {
    let _parentArr = parentArr.slice()
    _parentArr.push(item.id)
    map.set(item.id, _parentArr);
    if (item.children && item.children.length > 0) {
      recursion(item.children, _parentArr, map)
    }
  })
}

console.log(find(data, '122'))

@chengduHe
Copy link

let list = [{
id: '1',
value: '四川省',
children: [{
id: '11',
value: '成都市',
children: [{
id: '111',
value: '高新区'
}, {
id: '112',
value: '天府新区'
}]
}]
}, {
id: '2',
value: '四川省',
children: [{
id: '21',
value: '成都市',
children: [{
id: '211',
value: '高新区'
}, {
id: '212',
value: '天府新区'
}]
}]
}];

function fn(list, id) {
let res = []

function search(list, res) {
let searched = false;
for (let node of list) {
if (node.id === id) {
res.push(node.id)
return true
} else {
if (node.children) {
res.push(node.id)
searched = search(node.children, res);
if (searched) {
return true
}
}
}
}
if (!searched) {
res.pop()
return false
}
}

search(list, res);
return res
}

console.log(fn(list, '212'))

@XW666
Copy link

XW666 commented Mar 25, 2021

 const find04 = (arr, val) => {
    let carr = {}
    let func = (list, label) => {
      list.forEach((item, index) => {
        if (carr[label]) {
          carr[item.id] = carr[label] + ',' + item.id
        } else {
          carr[item.id] = item.id
        }
        if (item.children && item.children.length > 0) {

          func(item.children, item.id)
        }
      })
    }
    func(arr)
    return carr[val].split(',')

  }

@ramseyfeng
Copy link

@wangyuanyuan0521
@ZodiacSyndicate

function fn(data, value) {
  let res = []
  const dfs = (arr, temp = []) => {
    for (const node of arr) {
      if (node.id === value) {
        res = temp
        return
      } else {
        node.children && dfs(node.children, temp.concat(node.id))
      }
    }
  }
  dfs(data)
  return res
}

这好像没提前返回吧?找到了不需要继续递归了

@xiangergou
Copy link

// 跟之前总结的树路径查找一个道理

const treePath = (tree, fn) => {
  const path = [], result = [];
  (function forNode(list = [...tree]){
  	list.forEach(node => {
    	path.push(node.id)
       if (fn(node)) result.push(...path)
      node.children && forNode(node.children)
      path.pop()
    })
  })()
  return result
}
console.log(treePath(tree, node => node.id === '212'))

@MrLeihe
Copy link

MrLeihe commented May 10, 2021

dfs + 回溯

const value = '1221'
const data = [
  {
    id: '1',
    name: '广东省',
    children: [
      {
        id: '11',
        name: '深圳市',
        children: [
          {
            id: '111',
            name: '南山区',
          },
          {
            id: '112',
            name: '福田区',
            children: [
              {
                id: '1121',
                name: 'test',
              },
            ],
          },
        ],
      },
      {
        id: '12',
        name: '广州市',
        children: [
          {
            id: '121',
            name: '天河区',
          },
          {
            id: '122',
            name: '番禺区',
            children: [
              {
                id: '1221',
                name: 'test',
              },
            ],
          },
        ],
      },
    ],
  },
]

function fn(value) {
  function dfs(cur, value, res) {
    if (!cur) {
      return null
    }

    res.push(cur.id)
    if (cur.id === value) {
      return res
    }
    if (cur.children && cur.children.length) {
      for (let node of cur.children) {
        var result = dfs(node, value, res)
        if (result) return result
      }
    }
    res.pop()
  }

  for (let item of data) {
    var result = dfs(item, value, [])
    if (result) {
      return result
    }
  }

  return []
}

console.log(fn(value)) // 输出 [ '1', '12', '122', '1221' ]

@13866368297
Copy link

13866368297 commented Jul 28, 2021

  const data = [{
      id: '1',
      children: [{
          id: '11',
          children: [{
              id: '111',
            },
            {
              id: '112',
            }
          ]

        },
        {
          id: '12',
          children: [{
              id: '121',
            },
            {
              id: '122',
            }
          ]
        }
      ]
    }, {
      id: '2',
      children: [{
          id: '21',
          children: [{
              id: '211',
            },
            {
              id: '212',
            }
          ]

        },
        {
          id: '22',
          children: [{
              id: '221',
            },
            {
              id: '222',
            }
          ]
        }
      ]
    }];

    function traverse(id, data, arr = []) {
      if (data.id === id) {
        arr.push(data)
        arr.done = true
        return arr
      }
      if (data.children) {
        for (const item of data.children) {
          const result = find(id, item, [...arr, data])
          if (result && result.done) {
            return result
          }
        }
      }
    }

    function traverseAll(id, data) {
      for (const item of data) {
        const result = traverse(id, item)
        if (result) {
          return result.reduce((res, cur) => [...res, cur.id], [])
        }
      }
    }

    const result = traverseAll("221", data)
    console.log(result);

@Soyn
Copy link

Soyn commented Aug 6, 2021

const solution = (data = [], id) => {
  const stack = [];
  for (let i = 0; i < data.length; i += 1) {
    stack.push(data[i]);
    while (stack.length) {
      const curr = stack.pop();
      if (curr.id === id) return curr.path;

      if (curr.children?.length) {
        const path = curr.path || [curr.id];
        stack.push(
          ...curr.children.map((c) => ({
            ...c,
            path: path.concat(c.id)
          }))
        );
      }
    }
  }
  return [];
};
const data = [
  {
    id: "1",
    name: "test1",
    children: [
      {
        id: "11",
        name: "test11",
        children: [
          {
            id: "111",
            name: "test111"
          },
          {
            id: "112",
            name: "test112"
          }
        ]
      },
      {
        id: "12",
        name: "test12",
        children: [
          {
            id: "121",
            name: "test121"
          },
          {
            id: "122",
            name: "test122"
          }
        ]
      }
    ]
  },
  {
    id: "2",
    name: "test1",
    children: [
      {
        id: "21",
        name: "test11",
        children: [
          {
            id: "211",
            name: "test111"
          },
          {
            id: "212",
            name: "test112"
          }
        ]
      },
      {
        id: "22",
        name: "test22",
        children: [
          {
            id: "221",
            name: "test221"
          },
          {
            id: "222",
            name: "test222"
          }
        ]
      }
    ]
  }
];

console.log(solution(data, "122"));
console.log(solution(data, "222"));

@yaoocheng
Copy link

 let list = [{
            id: '1',
            children: [{
                id: '11',
                children: [{
                    id: '111'
                }, {
                    id: '112'
                }]
            }]
        }];

        function _find(list) {
            let res = [];
            list.forEach(ele => {
                res.push(ele.id);
                if(ele.children && Array.isArray(ele.children)) {
                    res = res.concat(_find(ele.children)) 
                }
            });
            return res;
        }

@zhangtaolei
Copy link

dfs

  const find = (id)=>{
    let list = []
    const dfs = function(data){
      for(let i in data){
        let item = data[i]
        if(id === item.id){
          return list.unshift(item.id)
        }
        if(item.children && item.children.length > 0){
          let is = dfs(item.children)
          if(is){
            return list.unshift(item.id)
          }
        }
      }
    }
    dfs(data)
    return list
  }

@heart-to-the-sea
Copy link

heart-to-the-sea commented Dec 30, 2021

export default class FindChildParentIds {
      static data:Array<DataI> = [{
        id: '1',
        name: 'test1',
        children: [
            {
                id: '11',
                name: 'test11',
                children: [
                    {
                        id: '111',
                        name: 'test111'
                    },
                    {
                        id: '112',
                        name: 'test112'
                    }
                ]

            },
            {
                id: '12',
                name: 'test12',
                children: [
                    {
                        id: '121',
                        name: 'test121'
                    },
                    {
                        id: '122',
                        name: 'test122'
                    }
                ]
            },
            {
                id: '13',
                name: 'test13',
                children: [
                    {
                        id: '131',
                        name: 'test121'
                    },
                    {
                        id: '132',
                        name: 'test122'
                    }
                ]
            }
        ]
    }];
    test() {
        console.log('find child parent Ids')
        const parents = FindChildParentIds.dFCPIds('131')
        console.log(parents);
    }
    static dFCPIds(id: string):string{
      // 1 查找父节点
      const p = this.findChildRPid(id,this.data,'')

      // 3 返回结果
      return p
    }
    // 递归算法
    static findChildRPid(id:string,ch: Array<DataI>,pL: string): any {
      let str = ''
      for (const node of ch){
        if(node.id === id){
          return pL +' '+ id;
        } else if (node.children) {
          str = pL + ' ' + node.id
          str = this.findChildRPid( id, node.children, str)
        }
      }
      return str
    }
}

---> 1 13 131

@Joker-Qu
Copy link

回朔法

    const fn = (value) => {
    let result = [];
    const list = [
        {
            id: '1',
            name: '广东省',
            children: [
                {
                    id: '11',
                    name: '深圳市',
                    children: [
                        {
                            id: '111',
                            name: '南山区',
                        },
                        {
                            id: '112',
                            name: '福田区',
                        }
                    ]
                }
            ]
        },
        {
            id: '2',
            name: '福建省',
            children: [
                {
                    id: '21',
                    name: '厦门市',
                    children: [
                        {
                            id: '211',
                            name: '思明区',
                        },
                    ]
                }
            ]
        }
    ];
    for (let i = 0; i < list.length - 1; i++) {
        const trace = [list[i]];
        backtrace(trace, value);
        if (result) {
            return result.map(item => item.id);
        }
    }
    function backtrace(trace, target) {
        const node = trace[trace.length - 1];
        if (node.id === target) {
            result = [...trace];
            return;
        }
        if (!node.children) {
            return;
        }
        for (let i = 0; i < node.children.length; i++) {
            trace.push(node.children[i]);
            backtrace(trace, target);
            trace.pop(); 
        }
}   
 }
console.log(fn('112'));

@lijingjings
Copy link

lijingjings commented Feb 25, 2022

const data = [{
    id: '1',
    name: 'test1',
    children: [
        {
            id: '11',
            name: 'test11',
            children: [
                {
                    id: '111',
                    name: 'test111'
                },
                {
                    id: '112',
                    name: 'test112'
                }
            ]

        },
        {
            id: '12',
            name: 'test12',
            children: [
                {
                    id: '121',
                    name: 'test121'
                },
                {
                    id: '122',
                    name: 'test122'
                }
            ]
        }
    ]
}];

function flat(tree,value){
    const arr={};
    function flatTree(tree,parent){//先把tree打平借用之前88题思想
        tree.forEach(item=>{
            if(item.children){
                item.parentId = parent||'0'
                arr[item.id]=item;
                flatTree(item.children,item.id);
            }else{
                item.parentId = parent||'0'
                arr[item.id]=item;
            }
        })
        
    }
    flatTree(tree)
    const handle=[];
    function find(value){
        if(arr[value]){
            handle.unshift(value);
            let parentId = arr[value].parentId;
            if(parentId){
                find(parentId); 
            }
        }
        return handle;
    }
    arr[value];
    const result = find(value);
    return result;
}
const flatList = flat(data,'112');

@zhuiwen
Copy link

zhuiwen commented Apr 2, 2022

const data = [{
    id: 1,
    name: '广东',
    children: [{
        id: 11,
        name: '深圳市',
        children: [{
            id: 111,
            name: '南山区'
        }, {
            id: 112,
            name: '福田区'
        }]
    }]
}, {
    id: 2,
    name: '湖南',
    children: [{
        id: 22,
        name: '郴州市',
        children: [{
            id: 222,
            name: '桂阳'
        }, {
            id: 223,
            name: '燕塘'
        }]
    }]
}]

const getParents = (val, arr = []) => {
    for (let i in arr) {
        // 找到当前目标节点
        if (arr[i].id === val) {
            return [false]
        }
        if (arr[i].children) {
            const parentIdArr = getParents(val, arr[i].children)
            // parentIdArr 不为空,说明找到了目标节点,当前节点为目标节点的子节点
            if (parentIdArr.length) {

                return parentIdArr[0] ?  [arr[i].id, ...parentIdArr ] : [arr[i].id]
            }
        }
    }
    return []
}
console.log(getParents(112, data))

@waldonUB
Copy link

waldonUB commented Apr 8, 2022

深度优先

  • 直接把路径带过去,遍历到就直接返回
/**
 * dfs
 * @author waldon
 * @date 2022-04-08
 * @param {*} value - param
 * @param {*} arr - param
 */
function getArr(value, arr) {
  function getChildren(_arr, path = []) {
    for (const item of _arr) {
      if (item.id === value) {
        path.push(item.id)
        return path
      } else if (item.children) {
        path.push(item.id)
        return getChildren(item.children, [...path])
      }
    }
  }
  return getChildren(arr)
}
const data = [
  {
    id: '1',
    name: 'test1',
    children: [
      {
        id: '11',
        name: 'test11',
        children: [
          {
            id: '111',
            name: 'test111',
          },
          {
            id: '112',
            name: 'test112',
          },
        ],
      },
      {
        id: '12',
        name: 'test12',
        children: [
          {
            id: '121',
            name: 'test121',
          },
          {
            id: '122',
            name: 'test122',
          },
        ],
      },
    ],
  },
]
console.log(getArr('112', data))

@Margaux7
Copy link

简单的回溯,找到之后停止继续查找

function findParents(id, data) {
  let res;
  const path = [];
  
  backTrack(data);
  return res;
  
  function backTrack(arr) {
    if (res) return;
    
    for (let i = 0; i < arr.length; i++) {
      if (arr[i].id === id) {
        path.push(id);
        res = [...path];
        return;
      }
      
      if (arr[i].children) {
        path.push(arr[i].id);
        backTrack(arr[i].children);
        path.pop();        
      }
    }
  }
}

const data = [{
    id: '1',
    name: 'test1',
    children: [
        {
            id: '11',
            name: 'test11',
            children: [
                {
                    id: '111',
                    name: 'test111'
                },
                {
                    id: '112',
                    name: 'test112'
                }
            ]

        },
        {
            id: '12',
            name: 'test12',
            children: [
                {
                    id: '121',
                    name: 'test121'
                },
                {
                    id: '122',
                    name: 'test122'
                }
            ]
        }
    ]
}];

console.log(findParents('112', data));

@advanceCabbage
Copy link

利用将树转化为数组的方式

const data = [
  {
    id: "1",
    name: "test1",
    children: [
      {
        id: "11",
        name: "test11",
        children: [
          {
            id: "111",
            name: "test111",
          },
          {
            id: "112",
            name: "test112",
          },
        ],
      },
      {
        id: "12",
        name: "test12",
        children: [
          {
            id: "121",
            name: "test121",
          },
          {
            id: "122",
            name: "test122",
          },
        ],
      },
    ],
  },
];
function treeToArr(tree, id) {
  let map = new Map();
  function dfs(tree, path = "") {
    tree.forEach((element) => {
      element.path = path ? path + "-" + element.id : element.id;
      if (element.children) {
        dfs(element.children, element.path);
        delete element.children;
      } else {
        map.set(element.id, element);
      }
    });
  }
  dfs(tree);
  return map.get(id).path.split("-");
}
console.log(treeToArr(data, "112"));

@Wang-Tao1
Copy link

function findItem(item,arr = []){
for (let i = 0; i < item.length; i++) {
const v = item[i]
const newArr = [...arr]
newArr.push(v.id)
if(v.id == value){
return newArr;
}
if(v.children){
const returnRes = findItem(v.children,newArr)
if(returnRes){
return returnRes
}
}
}
}

这样可以吗

@showonne
Copy link

showonne commented Aug 9, 2022

看了一些评论的答案感觉 reduce 这种代码是最简洁的,有个问题就是会把所有数据都遍历一次,如果用 for 循环递归遍历的话就可以在找到 target 后终止查询。

@Inchill
Copy link

Inchill commented Sep 6, 2022

深度优先

  • 直接把路径带过去,遍历到就直接返回
/**
 * dfs
 * @author waldon
 * @date 2022-04-08
 * @param {*} value - param
 * @param {*} arr - param
 */
function getArr(value, arr) {
  function getChildren(_arr, path = []) {
    for (const item of _arr) {
      if (item.id === value) {
        path.push(item.id)
        return path
      } else if (item.children) {
        path.push(item.id)
        return getChildren(item.children, [...path])
      }
    }
  }
  return getChildren(arr)
}
const data = [
  {
    id: '1',
    name: 'test1',
    children: [
      {
        id: '11',
        name: 'test11',
        children: [
          {
            id: '111',
            name: 'test111',
          },
          {
            id: '112',
            name: 'test112',
          },
        ],
      },
      {
        id: '12',
        name: 'test12',
        children: [
          {
            id: '121',
            name: 'test121',
          },
          {
            id: '122',
            name: 'test122',
          },
        ],
      },
    ],
  },
]
console.log(getArr('112', data))

你这个当数组第一层有多个对象时就不正确了。

@waldonUB
Copy link

深度优先

  • 直接把路径带过去,遍历到就直接返回
/**
 * dfs
 * @author waldon
 * @date 2022-04-08
 * @param {*} value - param
 * @param {*} arr - param
 */
function getArr(value, arr) {
  function getChildren(_arr, path = []) {
    for (const item of _arr) {
      if (item.id === value) {
        path.push(item.id)
        return path
      } else if (item.children) {
        path.push(item.id)
        return getChildren(item.children, [...path])
      }
    }
  }
  return getChildren(arr)
}
const data = [
  {
    id: '1',
    name: 'test1',
    children: [
      {
        id: '11',
        name: 'test11',
        children: [
          {
            id: '111',
            name: 'test111',
          },
          {
            id: '112',
            name: 'test112',
          },
        ],
      },
      {
        id: '12',
        name: 'test12',
        children: [
          {
            id: '121',
            name: 'test121',
          },
          {
            id: '122',
            name: 'test122',
          },
        ],
      },
    ],
  },
]
console.log(getArr('112', data))

你这个当数组第一层有多个对象时就不正确了。

题目中就是这个数据格式,不会出现第一层有多个对象的情况,这种只适用于一个根节点的解法。

@PathFun
Copy link

PathFun commented Jan 6, 2023

const findItem = (value, list, graph) => {
    return list.some(item => {
        if (item.id === value) {
            graph.push(item.id)
            return true
        }
        if (item.children) {
            graph.push(item.id)
            return findItem(value, item.children, graph)
        }
    })
}

const fn = value => {
    let graph = []
    list.some(item => {
        graph.push(item.id)
        if (item.id === value) return true
        if (item.children) {
            let res = findItem(value, item.children, graph)
            if (!res) graph = []
            return res
        }
    })
    return graph
}

实现的思路是记录每次 dfs 遍历的路径,当找到元素时,返回记录的所有路径,找不到时清空路径遍历下个元素
希望有更好的解法

var list = [{
	id: '1',
	children: [{
		id: '11',
		children: [{
			id: '111'
		}, {
			id: '112'
		}]
	}, {
		id: '12',
		children: [{
			id: '121'
		}, {
			id: '122'
		}]
	}]
}]

const value = '122';

这个情景不太对吧

这个应该是不行的

@LoveSleepMouse
Copy link

function dfs(target, value) {
        const cloneList = [...target];
        let list = [];

        while (cloneList.length !== 0) {
          const element = cloneList.shift();
          list.push(element.id);
          if (element.id === value) {
            break;
          }

          if (
            cloneList.length !== 0 &&
            cloneList[0].id.length < element.id.length
          ) {
            list = list.filter((item) => item.length < cloneList[0].id.length);
          }

          if (!element.children) {
            list.pop();
            continue;
          }
          cloneList.unshift(...element.children);
        }
        return list;
      }

如果父子级的id长度规律是这样的,我这边测试没问题,但是我写的很慢,如果是面试的话,估计要挂

@aeolusheath
Copy link

 const initData = [{
      id: '1',
      name: 'test1',
      children: [
        {
          id: '11',
          name: 'test11',
          children: [
            {
              id: '111',
              name: 'test111'
            },
            {
              id: '112',
              name: 'test112'
            }
          ]

        },
        {
          id: '12',
          name: 'test12',
          children: [
            {
              id: '121',
              name: 'test121'
            },
            {
              id: '122',
              name: 'test122'
            }
          ]
        }
      ]
    }];


    var fn = (value) => {
      var a = []
      var help = (data, initStr)=>{
          for (let i =0; i < data.length; i++) {
            var s = initStr ? (initStr + ',' + data[i].id) : data[i].id
            if(value === data[i].id ) {
              a = s.split(',')   
            } 
            if (data[i].children) {
              help(data[i].children, s)
            } 
          }
      }
      help(initData, '')
      return a
    }
    fn('11')

@benzhemin
Copy link

const recordTraversePath = (nodeList, targetId, traverseList) => {
    for (const node of nodeList) {
        if (node.id === targetId) {
            traverseList.push(node.id);
            return traverseList;
        }

        const childList = node.children || [];
        const nodeList = recordTraversePath(childList, targetId, [...traverseList, node.id]);

        if (nodeList.length > 0) {
            return nodeList;
        }
    }

    return [];
}


const data = [{
    id: '1',
    name: 'test1',
    children: [
        {
            id: '11',
            name: 'test11',
            children: [
                {
                    id: '111',
                    name: 'test111'
                },
                {
                    id: '112',
                    name: 'test112'
                }
            ]

        },
        {
            id: '12',
            name: 'test12',
            children: [
                {
                    id: '121',
                    name: 'test121'
                },
                {
                    id: '122',
                    name: 'test122'
                }
            ]
        }
    ]
}];

const idList = recordTraversePath(data, '112', []);
console.log(idList);

@benzhemin
Copy link

benzhemin commented Feb 25, 2023

A more simple version

const recordTraversePath = (nodeList, targetId, traverseList) => {
    for (const node of nodeList) {
        if (node.id === targetId) {
            console.log([...traverseList, node.id]);
        }
        recordTraversePath(node.children || [], targetId, [...traverseList, node.id]);
    }
}


const data = [{
    id: '1',
    name: 'test1',
    children: [
        {
            id: '11',
            name: 'test11',
            children: [
                {
                    id: '111',
                    name: 'test111'
                },
                {
                    id: '112',
                    name: 'test112'
                }
            ]

        },
        {
            id: '12',
            name: 'test12',
            children: [
                {
                    id: '121',
                    name: 'test121'
                },
                {
                    id: '122',
                    name: 'test122'
                }
            ]
        }
    ]
}];

recordTraversePath(data, '112', []);

@1242793152
Copy link

我的思路是可以先遍历元素将其id与父id关联起来并且存储在对象中,然后再从对象中遍历查找

function getParentChain(arr,id){
let result = []
let idChain = {}
function storeIdChain(arr,idChain){
arr.forEach(ele=>{
if(ele?.children?.length){
ele.children.forEach(value=>idChain[value.id] = ele.id)
storeIdChain(ele.children,idChain)
}
})
}
storeIdChain(arr,idChain)
function findParentId(id){
let parentId = idChain[id]
if(parentId == undefined){
return
}else{
result.unshift(parentId)
idChain[parentId] && findParentId(parentId)
}
}
findParentId(id)
result.length > 0 == true ? result.push(id) : result = -1
return result
}
console.log(getParentChain(data,'111'))
console.log(getParentChain(data,'1111'))

@adele-ty
Copy link

const list = [
        {
          id: "1",
          children: [
            {
              id: "11",
              children: [ { id: "111"}, { id: "112"}],
            },
          ],
        },
 ];

function findId(id, path, obj) {
        path = [...path, obj.id];
        if (id === obj.id) return path;
        if (!obj.children) return;

        for (let child of obj.children) {
          const res = findId(id, path, child);
          if (res) return res;
        }
}

const fn = (value) => {
        let res;
        for (let item of list) {
          const path = findId(value, [], item);
          if (path) res = [...path];
        }
        return res;
};

const value = "112";
fn(value);    //  ['1', '11', '112']

@negativeentropy9
Copy link

const cityData = [
  {
    id: "1",
    name: "广东省",
    children: [
      {
        id: "11",
        name: "深圳市",
        children: [
          {
            id: "111",
            name: "南山区",
          },
          {
            id: "112",
            name: "福田区",
            children: [
              {
                id: "1121",
                name: "A街道",
              },
            ],
          },
        ],
      },
    ],
  },
];

function initCityData() {
  const cache = {};
  const acc = [];

  dfs(cityData);

  return cache;

  function dfs(data) {
    for (let { id, children } of data) {
      acc.push(id);
      cache[id] = [...acc];

      if (children) {
        dfs(children);
      }

      acc.pop();
    }
  }
}

const fn = (value) => {
  fn.cache = fn.cache || initCityData();

  return fn.cache[value];
};

console.log(fn("112")); // 输出 [1, 11, 112]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests