算法之深度优先遍历和广度优先遍历

175 阅读2分钟

深度优先遍历(DFS)

深度优先遍历(Depth-First-Search)是从根节点开始追溯,直到最后一个节点,然后回溯,继续追溯下一条路径,直到到达所有的节点,如此往复,直到没有路径为止。深度DFS属于盲目搜索,无法保证搜索到的路径为最短路径,也不是在搜索特定的路径,而是通过搜索来查看有哪些路径可以选择。

简单的树形结构示例

//     1
//   2   3
// 4  5 6  7
//8         9
var tree = {
    val: 1,
    left: {
        val: 2,
        left: {
            val: 4,
            left: {
                val: 8
            }
        },
        right: {
            val: 5
        }
    },
    right: {
        val: 3,
        left: {
            val: 6
        },
        right: {
            val: 7,
            right: {
                val: 9
            }
        }
    }
}
// 深度优先遍历,利用栈,从根节点开始,依次将右、左节点入栈
// 后进先出,优先遍历后push进的数据
function dfs(tree) {
    let stack = [tree],
        result = []
    while (stack.length) {
        let last = stack.pop()
        result.push(last.val)
        let left = last.left,
            right = last.right
        if (right) stack.push(right)    
        if (left) stack.push(left)
    }
    return result
}
console.log(dfs(tree)) // [ 1, 2, 4, 8, 5, 3, 6, 7, 9 ]

广度优先遍历(BFS)

广度优先遍历(Breadth-First-Search)是从根节点开始,沿着图的宽度遍历节点,如果所有节点均被访问过,则算法终。BFS 同样属于盲目搜索,一般用队列数据结构来辅助实现BFS。

// 广度优先遍历,利用队列,先进先出,从根节点开始,依次将左节点、右节点push进数组
// 先进先出,优先遍历已经push进数组的数据
function bfs(tree) {
    let queue = [tree],
        result = []
    while (queue.length) {
        let first = queue.shift()
        result.push(first.val)
        let left = first.left,
            right = first.right
        if (left) queue.push(left)
        if (right) queue.push(right)
    }
    return result
}

console.log(bfs(tree)) // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

应用

1.树形结构转数组

利用数组转树形结构得到一个树形结构

let arr = [
    { pid: 0, id: 1, name: '1' },
    { pid: 1, id: 2, name: '2' },
    { pid: 1, id: 3, name: '3' },
    { pid: 2, id: 4, name: '4' },
    { pid: 2, id: 5, name: '5' },
    { pid: 3, id: 6, name: '6' },
    { pid: 3, id: 7, name: '7' },
    { pid: 4, id: 8, name: '8' },
    { pid: 5, id: 9, name: '9' },
];

let tree = arrayToTree(arr)

function DFS(tree){
    tree = [...tree]
    let result = [];
    while(tree.length){
        let last = tree.pop();
        result.push({ pid: last.pid, id: last.is, name: last.name });
        let children = last.children;
        if(children&&children.length){
            for(let i = children.length - 1; i >= 0; i--){
                tree.push(children[i])
            }
        }
    }
    return result
}
console.log(DFS(tree))
// [
//     { pid: 0, id: undefined, name: '1' },
//     { pid: 1, id: undefined, name: '2' },
//     { pid: 2, id: undefined, name: '4' },
//     { pid: 4, id: undefined, name: '8' },
//     { pid: 2, id: undefined, name: '5' },
//     { pid: 5, id: undefined, name: '9' },
//     { pid: 1, id: undefined, name: '3' },
//     { pid: 3, id: undefined, name: '6' },
//     { pid: 3, id: undefined, name: '7' }
// ]

function BFS(tree){
    tree = [...tree]
    let result = [];
    while(tree.length){
        let first = tree.shift();
        result.push({ pid: first.pid, id: first.is, name: first.name });
        let children = first.children;
        if(children&&children.length){
            for(let i = 0; i < children.length; i++){
                tree.push(children[i])
            }
        }
    }
    return result;
}
console.log(BFS(tree))
// [
//     { pid: 0, id: undefined, name: '1' },
//     { pid: 1, id: undefined, name: '2' },
//     { pid: 1, id: undefined, name: '3' },
//     { pid: 2, id: undefined, name: '4' },
//     { pid: 2, id: undefined, name: '5' },
//     { pid: 3, id: undefined, name: '6' },
//     { pid: 3, id: undefined, name: '7' },
//     { pid: 4, id: undefined, name: '8' },
//     { pid: 5, id: undefined, name: '9' }
// ]

2.全排列

将数组中的项进行全排列,得到所有的可能的排列结果

function fn(arr){
    let result = [];
    let len = arr.length, map = {}, path = [];
    function dfs(){
        if(path.length === len){
            result.push([...path]);
            return
        }
        for(let i = 0; i < len; i++){
            if(!map[i]){
                map[i] = true;
                path.push(arr[i]);
                dfs();
                map[i] = false;
                path.pop()
            }
        }
    }
    dfs()
    return result
}
console.log(fn([1,2,3,4]))
// [
//     [1, 2, 3, 4], [1, 2, 4, 3],
//     [1, 3, 2, 4], [1, 3, 4, 2],
//     [1, 4, 2, 3], [1, 4, 3, 2],
//     [2, 1, 3, 4], [2, 1, 4, 3],
//     [2, 3, 1, 4], [2, 3, 4, 1],
//     [2, 4, 1, 3], [2, 4, 3, 1],
//     [3, 1, 2, 4], [3, 1, 4, 2],
//     [3, 2, 1, 4], [3, 2, 4, 1],
//     [3, 4, 1, 2], [3, 4, 2, 1],
//     [4, 1, 2, 3], [4, 1, 3, 2],
//     [4, 2, 1, 3], [4, 2, 3, 1],
//     [4, 3, 1, 2], [4, 3, 2, 1]
// ]

3.合法的括号对组合

function fn(n){
    let result = [];
    function dfs(s, left, right){
        if(left < right || (left + right) > 2*n) return
        if(left === n && right === n){
            result.push(s)
            return;
        }
        dfs(s+'(', left+1, right)
        dfs(s+')', left, right+1)
    }
    dfs('', 0, 0)
    return result
}
console.log(fn(3))
// [ '((()))', '(()())', '(())()', '()(())', '()()()' ]

暂时能想到的应用就这些,以后随时添加。

以上全部内容,如有疑问,欢迎指正。

11.webp