101道算法JavaScript描述【二叉树】7

/**

  • // Definition for a Node.

  • function Node(val, left, right, next) {

  • this.val = val === undefined ? null : val;

  • this.left = left === undefined ? null : left;

  • this.right = right === undefined ? null : right;

  • this.next = next === undefined ? null : next;

  • };

*/

/**

  • @param {Node} root

  • @return {Node}

*/

const connect = (root) => {

const arr = [];

if (root === null) {

return root;

}

arr.push(root);

while (arr.length) {

// size 为每一层节点的总个数。 prevNode 记录前一个节点。

const size = arr.length;

let prevNode;

let node;

// 遍历每一层的节点

for (let i = 0; i < size; i += 1) {

  node = arr.shift(); // 出队列

  // 每一层最后一个节点的 next 需要置为 null

  // 因此,当前节点不是当前层的最后一个节点的话,将当前节点与前一节点连接

  if (prevNode && i < size) {

    prevNode.next = node;

  }

  if (node.left) {

    arr.push(node.left); // 左节点入队列

  }

  if (node.right) {

    arr.push(node.right); // 右节点入队列

  }

  prevNode = node;

}

}

return root;

};




**复杂度分析**



*   时间复杂度:O(n)O(n)

    

    上述解法中,树的每个节点只访问了一次,时间复杂度跟树的节点个数线性相关,因此为 O(n)O(n)。

    

*   空间复杂度:O(n)O(n)

    

    由于解法中使用到队列,且在访问最下面一层叶子节点时,空间占用达到最大,即存储了 (n+1)/2(n+1)/2 个节点,因此为 O(n)O(n)。

    



### []( )方法三 层序遍历法(指针方式)



**思路**



使用 2 个指针 `start` 和 `current` 来进行层序遍历。其中 `start` 用于标记每一层的第一个节点,`current` 用来遍历该层的其他节点。



**详解**



1.  第一步,定义一个 `start` 指针,用于标记每一层的第一个节点,`start` 指针的初始化值为 `root` 节点,只需要 `start = start.left`,就能获取到每一层的第一个节点。

2.  第二步,定义一个 `current` 指针,用来遍历该层的其他节点。然后,将同一父节点的左右节点的连接,即`current.left.next = current.right`,如图中的 `2`、`3` 节点;将相近但非同一直系父节点的节点相连,即`current.right.next = current.next.left`,如图中的 `5`、`6` 节点。



**代码**



/**

  • // Definition for a Node.

  • function Node(val, left, right, next) {

  • this.val = val === undefined ? null : val;

  • this.left = left === undefined ? null : left;

  • this.right = right === undefined ? null : right;

  • this.next = next === undefined ? null : next;

  • };

*/

/**

  • @param {Node} root

  • @return {Node}

*/

const connect = (root) => {

if (root === null) {

return root;

}

let start = root;

let current = null;

// start 为每一层的第一个节点

while (start.left) {

// 每一层节点的遍历

current = start;

while (current) {

  // 同一父节点的左右节点相连,如图中 2、3 节点相连

  current.left.next = current.right;

  // 相近但非同一直系父节点的节点相连,如图中 5、6 节点相连

  if (current.next) {

    current.right.next = current.next.left;

  }

  current = current.next;

}

start = start.left;

}

return root;

};




**复杂度分析**



*   时间复杂度:O(n)O(n)

    

    本解法中,外层循环总共执行 k 次(其中 k 为树的最大深度),内层循环根据所在的层次不同而不同,第一层1次,第二层 2 次,第 k 层 2^{k-1}2k−1 次,而 1+2+…(2^{k-1})=n1+2+…(2k−1)=n,即所有节点数之和,因此时间复杂度为 O(n)O(n)。

    

*   空间复杂度:O(1)O(1)

    

    由于解法中只申请了 2 个变量,空间复杂度与树的节点个数 n 无关,因此为 O(1)O(1)。

    



[]( )岛屿数量

----------------------------------------------------------------



给定一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。



**示例 1:**



输入:

11110

11010

11000

00000

输出: 1




**示例 2:**



输入:

11000

11000

00100

00011

输出: 3




### []( )方法一 深度优先遍历



**思路**



遍历二维数组,当节点为陆地(1)时,对当前节点的上下左右四个方向启动深度优先遍历搜索,并将计数器加 1。同时在搜索过程中,遇到海水(0)节点便停止,遇到陆地(1)节点便标记为海水(0)节点。



**详解**



1.  定义岛屿数量计数变量 `landNum`

2.  对二维数组 `grid` 进行两层遍历

3.  遍历过程中,遇到为 `1` 的陆地则将 `landNum` 自增 1,然后进入传播函数,并传入当前的坐标 `i`、`j`

4.  根据传入的坐标,判断是否超出 `grid` 边界,并判断是否为 `0`

5.  若为超出边界,或为 `0`,则停止传播

6.  若为边界内的 `1`,则将该位置变为 `0`,并对此节点的上下左右节点继续递归传播,以此实现深度优先遍历

7.  传播结束后,便可根据 `landNum` 获得岛屿数量



**代码**



/**

  • @param {character[][]} grid

  • @return {number}

*/

var numIslands = function(grid) {

let landNum = 0

for(let i = 0; i < grid.length; i++) {

    const len = grid[i].length;

    for(let j = 0; j < len; j++) {

        const target = grid[i][j]

        if (target === '1') {

            spread(grid, i, j)

            landNum++

        }

    }

}

return landNum;

};

/**

  • @param {character[][]} grid

  • @param {number} i

  • @param {number} j

*/

function spread(grid, i, j) {

if (i < 0 || j < 0 || i >= grid.length || j >= grid[i].length || grid[i][j] !== ‘1’) {

    return

}

grid[i][j] = ‘0’

spread(grid, i, j + 1);

spread(grid, i, j - 1);

spread(grid, i + 1, j);

spread(grid, i - 1, j);

}




**复杂度分析**



*   时间复杂度:O(n)O(n)

    

    n 为传入的二维网络的节点个数

    

*   空间复杂度:O(n)O(n)

    

    最坏情况下为 O(n)O(n),此时整个网格均为陆地

    



### []( )方法二 广度优先遍历



**思路**



遍历二维数组,当节点为陆地(1)时,启动广度优先遍历搜索,将节点坐标放入队列中,并将计数器加 1。在搜索过程中,遇到陆地(1)节点便标记为海水(0)节点,迭代搜索队列中的每个结点,直到队列为空。



**详解**



1.  定义岛屿数量计数变量 `landNum`

2.  对二维数组 `grid` 进行两层遍历

3.  遍历过程中,遇到为 `1` 的陆地则将 `landNum` 自增 1,然后进入传播函数,并传入当前的坐标 `i`、`j`

4.  根据传入的坐标,构造成 `queue` 队列数组

5.  循环判断 `queue` 的数组长度

6.  若数组中存在坐标,则将末尾坐标从 `queue` 中 `pop` 取出

7.  判断取出的坐标是否为边界内的 `1`,若是,则将此坐标设置为 `0`,并将此坐标的上下左右坐标存入 `queue` 数组中,以此完成广度优先遍历

8.  遍历结束后,便可根据 `landNum` 获得岛屿数量



**代码**



/**

  • @param {character[][]} grid

  • @return {number}

*/

var numIslands = function(grid) {

let landNum = 0

for(let i = 0; i < grid.length; i++) {

    const len = grid[i].length;

    for(let j = 0; j < len; j++) {

        const target = grid[i][j]

        if (target === '1') {

            spread(grid, i, j)

            landNum++

        }

    }

}

return landNum;

};

/**

  • @param {character[][]} grid

  • @param {number} i

  • @param {number} j

*/

function spread(grid, i, j) {

const queue = [[i, j]]

while (!!queue.length) {

    const [i, j] = queue.pop()

    if (grid.length > i 

        && i >= 0

        && grid[0].length > j

        && j >= 0

        && grid[i][j] === '1') {



        grid[i][j] = '0'

        queue.push([i - 1, j])

        queue.push([i + 1, j])

        queue.push([i, j + 1])

        queue.push([i, j - 1])

    }

}

}




**复杂度分析**



*   时间复杂度:O(n)O(n)

    

    n 为传入的二维网络的节点个数

    

*   空间复杂度:O(n)O(n)

    

    最坏情况下为 O(n)O(n),此时整个网格均为陆地

    



[]( )二叉树的锯齿形层次遍历

-----------------------------------------------------------------------



给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。



**示例**



给定二叉树 \[3, 9, 20, null, null, 15, 7\],



3

/ \

9 20

/  \

15 7




返回锯齿形层次遍历如下:



[

[3],

[20,9],

[15,7]

]




### []( )方法一 双栈法



**思路**



> 栈:是一种数据结构,先进后出原则。



双栈法:



1.  定义两个数组,一个接收奇数层元素,另一个接收偶数层元素。

2.  奇数层遍历完成之后,将下一层元素由左向右插入偶数层数组。

3.  先进后出原则,偶数列遍历时,取值顺序就变成了由右向左取值。

4.  偶数层遍历完成之后,将下一层元素由右向左插入奇数层数组。

5.  先进后出原则,奇数列遍历时,取值顺序就变成了由左向右取值。

6.  循环往复,形成锯齿形层次遍历



**详解**



1.  定义结果数组

2.  定义两个数组模拟栈(l2r、r2l),一个由左向右,一个由右向左

3.  将要处理的数据 push 到 l2r,进行循环

4.  每层循环定义一个 临时数组,接收当前层的结果,最后需要 push 到结果数组。

5.  第一层 l2r 就一个根数据,直接就将当前值 push 到 临时数组。为了方便交替遍历,将 l2r 的下一层数据 由左向右 push 到 r2l

6.  第二层 r2l 的数据是由左向右,先进后出,所以我们循环取值是 由右向左,将当前结果 push 到 临时数组,然后将 r2l 的下一层数据 由右向左 push 到 l2r

7.  循环往复



**代码**



const zigzagLevelOrder = function (root) {

if (!root) return [];

const res = [];

const l2r = [];

const r2l = [];

l2r.push(root);

while (l2r.length || r2l.length) {

const temp = [];

if (l2r.length) {

  while (l2r.length) {

    const cur = l2r.pop();

    temp.push(cur.val);

    if (cur.left) r2l.push(cur.left);

    if (cur.right) r2l.push(cur.right);

  }

} else if (r2l.length) {

  while (r2l.length) {

    const cur = r2l.pop();

    temp.push(cur.val);

    if (cur.right) l2r.push(cur.right);

    if (cur.left) l2r.push(cur.left);

  }

}

res.push(temp);

}

return res;

};




**复杂度分析**



*   时间复杂度:O(n)O(n)

    

    每个节点都要进栈和出栈,所以时间复杂度为 O(n)O(n)

    

*   空间复杂度:O(n)O(n)

    

    双栈每个节点的值也只记录一次,所以空间复杂度为 O(n)O(n)

    



### []( )方法二 递归



**思路**



采用递归,一层层遍历。每一层创建一个数组,奇数层元素从左向右插入数组,偶数层元素从右向左插入数组。



& 与操作符 判断奇偶



**详解**



1.  先在最层城定义返回结果数组

2.  然后看递归方法

    1.  定义两个参数。第一个 i 层数减一,对应返回结果数组的索引值;第二个 current ,当前处理对象;

    2.  首先判断结果数组当前索引的是否为第一次创建,不是创建新数组

    3.  索引为奇数,对应树形结构偶数行,从右向左插入

    4.  索引为偶数,对应树形结构奇数行,从左向右插入

    5.  然后不断递归



**代码**



const zigzagLevelOrder = function (root) {

const res = [];

dfs(0, root);

return res;

function dfs (i, current) {

if (!current) return;

// 首次进入该层递归,现在结果创建新数组用来接收结果。

if (!Array.isArray(res[i])) res[i] = [];

// 判断当前层数索引奇偶

// 奇数从前插入数组,偶数从后插入数组

if (i & 1) res[i].unshift(current.val);

else res[i].push(current.val);

// 左侧子二叉树进入递归

dfs(i + 1, current.left);

// 右侧子二叉树进入递归

dfs(i + 1, current.right);

}

};




**复杂度分析**



*   时间复杂度:O(n)O(n)

    

    因为每个节点恰好会被运算一次

    

*   空间复杂度:O(n)O(n)

    

    系统栈需要记住每个节点的值,所以空间复杂度为 O(n)O(n)
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值