寻找二叉树的下一个节点

开发 前端
已知一个包含父节点引用的二叉树和其中的一个节点,如何找出这个节点中序遍历序列的下一个节点?

本文转载自微信公众号「神奇的程序员k」,作者神奇的程序员K。转载本文请联系神奇的程序员k公众号。  

前言

已知一个包含父节点引用的二叉树和其中的一个节点,如何找出这个节点中序遍历序列的下一个节点?

本文就跟大家分享下这个问题的解决方案与实现代码,欢迎各位感兴趣的开发者阅读本文。

问题分析

正如前言所述,我们的已知条件如下:

  • 包含父节点引用的二叉树
  • 要查找的节点

我们要解决的问题:

  • 找出要查找节点中序遍历序列的下一个节点

接下来,我们通过举例来推导下一个节点的规律,我们先来画一颗二叉搜索树,如下所示:

  1.  
  2.    / \ 
  3.   6   13 
  4.  / \  / \ 
  5. 3  7 9  15 

例如,我们寻找6的下一个节点,根据中序遍历的规则我们可知它的下一个节点是7

  • 8的下一个节点是9
  • 3的下一个节点是6
  • 7的下一个节点是8

通过上述例子,我们可以分析出下述信息:

  • 要查找的节点存在右子树,那么它的下一个节点就是其右子树中的最左子节点
  • 要查找的节点不存右子树:
    • 当前节点属于父节点的左子节点,那么它的下一个节点就是其父节点本身
    • 当前节点属于父节点的右子节点,那么就需要沿着父节点的指针一直向上遍历,直至找到一个是它父节点的左子节点的节点

上述规律可能有点绕,大家可以将规律代入问题中多验证几次,就能理解了。

实现思路

  • 二叉树中插入节点时保存其父节点的引用
  • 调用二叉树的搜索节点方法,找到要查找的节点信息
  • 判断找到的节点是否存在右子树
  • 如果存在,则遍历它的左子树至叶节点,将其返回。
  • 如果不存在,则遍历它的父节点至根节点,直至找到一个节点与它父节点的左子节点相等的节点,将其返回。

实现代码

接下来,我们将上述思路转换为代码,本文代码中用到的二叉树相关实现请移步我的另一篇文章:TypeScript实现二叉搜索树

搜索要查找的节点

我们需要找到要查找节点在二叉树中的节点信息,才能继续实现后续步骤,搜索节点的代码如下:

  1. import { Node } from "./Node.ts"
  2.  
  3. export default class BinarySearchTree<T> { 
  4.     protected root: Node<T> | undefined; 
  5.  
  6.     constructor(protected compareFn: ICompareFunction<T> = defaultCompare) { 
  7.         this.root = undefined; 
  8.     } 
  9.    
  10.     // 搜索特定值 
  11.     search(key: T): boolean | Node<T> { 
  12.         return this.searchNode(<Node<T>>this.root, key); 
  13.     } 
  14.  
  15.     // 搜索节点 
  16.     private searchNode(node: Node<T>, key: T): boolean | Node<T> { 
  17.         if (node == null) { 
  18.             return false
  19.         } 
  20.  
  21.         if (this.compareFn(key, node.key) === Compare.LESS_THAN) { 
  22.             // 要查找的key在节点的左侧 
  23.             return this.searchNode(<Node<T>>node.leftkey); 
  24.         } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { 
  25.             // 要查找的key在节点的右侧 
  26.             return this.searchNode(<Node<T>>node.rightkey); 
  27.         } else { 
  28.             // 节点已找到 
  29.             return node; 
  30.         } 
  31.     } 

保存父节点引用

此处的二叉树与我们实现的二叉树稍有不同,插入节点时需要保存父节点的引用,实现代码如下:

  1. export default class BinarySearchTree<T> { 
  2.     // 插入方法 
  3.     insert(key: T): void { 
  4.         if (this.root == null) { 
  5.             // 如果根节点不存在则直接新建一个节点 
  6.             this.root = new Node(key); 
  7.         } else { 
  8.             // 在根节点中找合适的位置插入子节点 
  9.             this.insertNode(this.root, key); 
  10.         } 
  11.     } 
  12.    
  13.     // 节点插入 
  14.     protected insertNode(node: Node<T>, key: T): void { 
  15.         // 新节点的键小于当前节点的键,则将新节点插入当前节点的左边 
  16.         // 新节点的键大于当前节点的键,则将新节点插入当前节点的右边 
  17.         if (this.compareFn(key, node.key) === Compare.LESS_THAN) { 
  18.             if (node.left == null) { 
  19.                 // 当前节点的左子树为null直接插入 
  20.                 node.left = new Node(key, node); 
  21.             } else { 
  22.                 // 从当前节点(左子树)向下递归,找到null位置将其插入 
  23.                 this.insertNode(node.leftkey); 
  24.             } 
  25.         } else { 
  26.             if (node.right == null) { 
  27.                 // 当前节点的右子树为null直接插入 
  28.                 node.right = new Node(key, node); 
  29.             } else { 
  30.                 // 从当前节点(右子树)向下递归,找到null位置将其插入 
  31.                 this.insertNode(node.rightkey); 
  32.             } 
  33.         } 
  34.     } 
  35.  
  36. /** 
  37.  * 二叉树的辅助类: 用于存储二叉树的每个节点 
  38.  */ 
  39. export class Node<K> { 
  40.     public left: Node<K> | undefined; 
  41.     public right: Node<K> | undefined; 
  42.     public parent: Node<K> | undefined; 
  43.     constructor(public key: K, parent?: Node<K>) { 
  44.         this.left = undefined; 
  45.         this.right = undefined; 
  46.         console.log(key"的父节点", parent?.key); 
  47.         this.parent = parent; 
  48.     } 
  49.  
  50.     toString(): string { 
  51.         return `${this.key}`; 
  52.     } 

我们来测试下上述代码,验证下父节点引用是否成功:

  1. const tree = new BinarySearchTree(); 
  2. tree.insert(8); 
  3. tree.insert(6); 
  4. tree.insert(3); 
  5. tree.insert(7); 
  6. tree.insert(13); 
  7. tree.insert(9); 
  8. tree.insert(15); 

 

在保存父节点引用时折腾了好久也没实现,最后求助了我的朋友_Dreams😁。

寻找下一个节点

接下来,我们就可以根据节点的规律来实现这个算法了,实现代码如下:

  1. export class TreeOperate<T> { 
  2.     /** 
  3.      * 寻找二叉树的下一个节点 
  4.      * 规则: 
  5.      *  1. 输入一个包含父节点引用的二叉树和其中的一个节点 
  6.      *  2. 找出这个节点中序遍历序列的下一个节点 
  7.      * 
  8.      * 例如: 
  9.      *       8 
  10.      *      / \ 
  11.      *     6   13 
  12.      *    / \  / \ 
  13.      *   3  7 9  15 
  14.      * 
  15.      * 6的下一个节点是7,8的下一个节点是9 
  16.      * 
  17.      * 通过分析,我们可以得到下述信息: 
  18.      *  1. 如果一个节点有右子树,那么它的下一个节点就是其右子树中的最左子节点 
  19.      *  2. 如果一个节点没有右子树: 
  20.      *  (1). 当前节点属于父节点的左子节点,那么它的下一个节点就是其父节点本身 
  21.      *  (2). 当前节点属于父节点的右子节点,沿着父节点的指针一直向上遍历,直至找到一个是它父节点的左子节点的节点 
  22.      * 
  23.      */ 
  24.     findBinaryTreeNextNode(tree: BinarySearchTree<number>, node: number): null | Node<number> { 
  25.         // 搜索节点 
  26.         const result: Node<number> | boolean = tree.search(node); 
  27.         if (result == null) throw "节点不存在"
  28.         let currentNode = result as Node<number>; 
  29.         // 右子树存在 
  30.         if (currentNode.right) { 
  31.             currentNode = currentNode.right
  32.             // 取右子树的最左子节点 
  33.             while (currentNode.left) { 
  34.                 currentNode = currentNode.left
  35.             } 
  36.             return currentNode; 
  37.         } 
  38.  
  39.         // 右子树不存在 
  40.         while (currentNode.parent) { 
  41.             // 当前节点等于它父节点的左子节点则条件成立 
  42.             if (currentNode === currentNode.parent.left) { 
  43.                 return currentNode.parent; 
  44.             } 
  45.             // 条件不成立,继续获取它的父节点 
  46.             currentNode = currentNode.parent; 
  47.         } 
  48.         return null
  49.     } 

我们通过一个例子来测试下上述代码:

  1. // 构建二叉搜索树 
  2. const tree = new BinarySearchTree(); 
  3. tree.insert(8); 
  4. tree.insert(6); 
  5. tree.insert(3); 
  6. tree.insert(7); 
  7. tree.insert(13); 
  8. tree.insert(9); 
  9. tree.insert(15); 
  10. // 寻找下一个节点 
  11. const nextNode = treeOperate.findBinaryTreeNextNode(tree, 7); 
  12. console.log("7的下一个节点", nextNode.toString()); 

 

 

 

 

代码地址

文中完整代码如下:

  • TreeOperate.ts
  • BinarySearchTree.ts

 

责任编辑:武晓燕 来源: 神奇的程序员k
相关推荐

2020-04-27 07:05:58

二叉树左子树右子树

2021-07-16 08:57:31

迭代遍历二叉树

2021-05-06 17:46:30

二叉树数据结构

2021-04-20 08:37:14

数据结构二叉树

2021-04-19 07:47:42

数据结构二叉树Tree

2021-04-28 20:12:27

数据结构创建

2021-11-29 10:40:58

二叉树镜像节点

2022-10-26 23:58:02

二叉树数组算法

2021-03-17 08:19:22

二叉树LeetCode

2013-07-15 16:35:55

二叉树迭代器

2021-08-27 11:36:44

二叉树回溯节点

2021-12-17 14:26:58

二叉树节点数量

2021-09-29 10:19:00

算法平衡二叉树

2020-09-23 18:25:40

算法二叉树多叉树

2022-11-06 19:43:10

二叉树指针节点

2022-07-27 07:45:53

二叉树镜像函数

2021-12-05 18:25:12

二叉树路径节点

2018-03-15 08:31:57

二叉树存储结构

2022-10-12 23:25:17

二叉树父节点根节点

2021-10-12 09:25:11

二叉树树形结构
点赞
收藏

51CTO技术栈公众号