主页

遍历

遍历

数组的遍历

 for/while

有进无出,所以只有前序位置。只适用于元素是固定的情况。

1
2
3
4
5
6
// 数组的while/for遍历
function arr_traverse(arr){
    for(let i=0; i<arr.length; i++){
        console.log(arr[i]); // 前序位置
    }
}

递归

拥有后序位置,能回退,所以可以处理选择的情况。

比如,本来可能用for/while遍历的,但后来分析发现到达i=3时,可能出现多种情况的选择,需要每一种情况都去尝试一下,此时就可以用递归的遍历来解决。

1
2
3
4
5
6
7
8
9
// 数组的递归遍历
function arr_traverse(arr, i = 0){
    if(i >= arr.length){
        return;
    }
    console.log(arr[i]); // 前序位置
    arr_traverse(arr, i+1);
    // 后序位置
}

  

链表的遍历

for/while

1
2
3
4
5
6
// 链表的while/for遍历
function link_traverse(root){
    for(let p = root; !!p; p = root.next){
        console.log(p.val); // 前序位置
    }
}

递归

1
2
3
4
5
6
7
8
9
// 链表的递归遍历
function link_traverse(root){
    if(!root){
        return;
    }
    console.log(root.val); // 前序位置
    link_traverse(root.next);
    // 后序位置
}

 

二叉树遍历

for/while迭代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 二叉树的for/while迭代前序遍历
function twotree_traverse(root) {
    if (root == null) return;
    let toVisit = [root], cur;
    while (toVisit.length > 0) {
        cur = toVisit.pop();
        // 前序位置
        console.log(cur.val);
        if (cur.right) toVisit.push(cur.right); // 右节点入栈           
        if (cur.left) toVisit.push(cur.left); // 左节点入栈       
    }
}
 
// 二叉树的for/while迭代中序遍历
function twotree_traverse(root) {
    let toVisit = [], cur = root;
    while (cur || toVisit.length > 0) {
        while (cur) {
            // 前序位置
            toVisit.push(cur); // 添加根节点               
            cur = cur.left; // 循环添加左节点           
        }
        cur = toVisit.pop(); // 当前栈顶已经是最底层的左节点了,取出栈顶元素,访问该节点
        // 中序位置           
        console.log(cur.val);
        cur = cur.right; // 添加右节点  
    }
}
 
// 二叉树的for/while迭代后序遍历
function twotree_traverse(root) {
    let toVisit = [], cur = root, pre = null;
    while (cur || toVisit.length > 0) {
        while (cur) {
            toVisit.push(cur); // 添加根节点               
            cur = cur.left; // 循环添加左节点           
        }
        cur = toVisit[toVisit.length - 1]; // 已经访问到最左的节点了          
        //在不存在右节点或者右节点已经访问过的情况下,访问根节点         
        if (!cur.right || cur.right === pre) {
            toVisit.pop();
            // 后序位置
            console.log(cur.val);
            pre = cur;
            cur = null;
        } else {
            cur = cur.right; // 右节点还没有访问过就先访问右节点       
        }
    }
}

递归

1
2
3
4
5
6
7
8
9
10
11
// 二叉树的递归遍历
function twotree_traverse(root){
    if(!root){
        return;
    }
    console.log(root.val);// 前序位置
    twotree_traverse(root.left);
    console.log(root.val); // 中序位置
    twotree_traverse(root.right);
    console.log(root.val);// 后序位置
}

  

对于数组表示的完全二叉树的结构: arr = ['c', null, 'd', 'k', 'u', null, 'i', ...]

层序遍历,直接就是正序遍历arr数组

对于深度优先的遍历,不管是迭代法还是递归法,都可以用前面的方法。因为根节点已知为0,对于任一节点i,其子节点为 2*i+1 和 2*i+2。

多叉树的遍历

递归

1
2
3
4
5
6
7
8
9
10
11
// 多叉树的遍历
function nTree_traverse(root){
    if(!root){
        return;
    }
    root.children.forEach(child=>{ // 遍历该节点的所有子元素
        // 前序位置
        nTree_traverse(child);
        // 后序位置
    });
}

for/while迭代的前序遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 多叉树的dsf前序遍历
function nTree_traverse(root) {
    let q = [root];
    while (q.length > 0) {
        let node = q.pop();
        if(!node){
            continue;
        }
        // 在这里输出
        console.log(node.val);
        for (let i = node.children.length - 1; i > -1; i--) { // 注意,这里是倒序遍历
            let child = node.children[i];
            q.push(child);
        }
    }
}

for/while迭代的后序遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 多叉树的dsf后序遍历
function nTree_traverse(root) {
    let q = [root], result = [];
    while (q.length > 0) {
        let node = q.pop();
        if (!node) {
            continue;
        }
        // 记录到一个中间数组中
        result.push(node); // 注意,这里是放到最前面
        for (let i = 0; i < node.children.length; i++) { // 注意,这里是正序遍历
            let child = node.children[i];
            q.push(child);
        }
    }
    for (let i = result.length - 1; i > -1; i--) { // 这里倒序遍历
        // 在这里输出
        console.log(node.val);
    }
}

for/while迭代bsf遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 多叉树的bsf遍历
function nTree_traverse(root) {
    let q = [root];
    while (q.length > 0) {
        let newQ = [];
        for (let i = 0; i < q.length; i++) {
            let node = q[i];
            if (!node) { continue; }
            // 在这里输出
            console.log(node.val);
            node.children.forEach(child => { // 遍历该节点的所有子元素
                newQ.push(child);
            });
        }
        q = newQ;
    }
}

  

图的遍历

无向图中如果没有环存在,则这个图等价于一棵或多棵树(也就是说,任意一个图,只要去掉其所有的环,就是N(n>=1)棵树)

而如果将一棵树中的两节点连接,就能得到环,这样就是一个图了。

所以图相比于树,最主要的就是可能多了些环而已。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 图的遍历
let visited = {};
function graph_traverse(root){
    if(!root){
        return;
    }
    if(visited[root.id]){ // 防止进入环,产生死循环(如果已经知道图中没有环,就可以不要这个判断)
        return;
    }
    visited[root.id] = true; // 标记已经遍历
    root.children.forEach(child=>{ // 遍历该节点的所有子元素
        // 前序位置
        nTree_traverse(child);
        // 后序位置
    });
}

  

 

 

 

 

 

d

 

posted @   平凡人就做平凡事  阅读(239)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示