欢迎光临
我的个人博客网站

第十章 树


自我测试

本篇文章的测试用例及调试方法见前言

第十章 树

树的相关术语

根节点: 位于树顶部的节点叫作根节点

内部节点: 至少有一个子节点的节点称为内部节点(7、5、9、15、13和20是内部节点)

外部节点(叶节点):没有子元素的节点称为外部节点或叶节点(3、6、8、10、12、14、18和25是叶节点)

子树:由节点和它的后代构成。例如,节点13、12和14构成了上图中树的一棵子树

深度:节点的深度取决于它的祖先节点的数量。比如,节点3有3个祖先节点(5、7和11),它的深度为3

高度:树的高度取决于所有节点深度的最大值。一棵树也可以被分解成层级。根节点在第0层,它的子节点在第1层,以此类推。上图中的树的高度为3(最大高度已在图中表示——第3层)

一个节点可以有祖先和后代。一个节点(除了根节点)的祖先包括父节点、祖父节点、曾祖父节点等。一个节点的后代包括子节点、孙子节点、曾孙节点等。例如,节点5的祖先有节点7和节点11,后代有节点3和节点6。

二叉树和二叉搜索树

二叉树中的节点最多只能有两个子节点:一个是左侧子节点,另一个是右侧子节点。这些定义有助于我们写出更高效的向/从树中插入、查找和删除节点的算法。二叉树在计算机科学中的应用非常广泛。
二叉搜索树(BST)是二叉树的一种,但是它只允许你在左侧节点存储(比父节点)小的值,在右侧节点存储(比父节点)大(或者等于)的值。上图中就展现了一棵二叉搜索树。

图例

第十章 树

基础方法

insert(key): 向二叉搜索树插入一个新的键
search(key): 在树中搜索一个键.如果存在该节点,就返回true;不存在就返回false
inOrderTraverse():通过中序遍历的方式遍历所有节点
preOrderTraverse():通过先序遍历的方式遍历所有节点
postOrderTraverse():通过后序遍历的方式遍历所有的节点
min():返回树中最小的值/键
max():返回树中最大的值/键
remove(key):从树中移除某个键

基础了解

root:表示根节点,在链表中使用head

节点:这里的树节点会有三个属性
left:表示其左侧节点

key:存放当前节点数据

right: 表示其右侧节点

代码实现

enum Compare {     LESS_THAN = -1,     BIGGER_THAN = 1,     EQUALS = 0 }  class TreeNode<T> {     key: T;     left: TreeNode<T> | undefined;     right: TreeNode<T> | undefined;      constructor(key: T) {         this.key = key;         this.left = undefined;         this.right = undefined;     } }  function defaultCompareFn<T>(parent: TreeNode<T>, child: TreeNode<T>) {     if (parent === child) {         return Compare.EQUALS     }     return parent > child ? Compare.BIGGER_THAN : Compare.LESS_THAN; }  export class BinarySearchTree<T> {     root: TreeNode<T> | undefined;     compareFn: Function;     count: number;      constructor(compareFn: Function = defaultCompareFn) {         this.root = undefined;         this.count = 0;         this.compareFn = compareFn;     }      //向二叉搜索树插入一个新的键     insert(key: T) {         // 当root没有值的时候         if (this.root == null) {             this.root = new TreeNode<T>(key);         } else {             // 当root有值时,判断当前key比root大(right)还是小(left)             return this.insertTreeNode(this.root, key);         }     }      //节点插入     insertTreeNode(cNode: TreeNode<T>, key: T) {         // 新值小于node         if (this.compareFn(cNode.key, key) === Compare.BIGGER_THAN) {             // left节点为null             if (cNode.left == null) {                 cNode.left = new TreeNode<T>(key);             } else {                 //left节点不为null就继续在左节点插入                 this.insertTreeNode(cNode.left, key)             }         }         // 新值大于node  right节点为null         else {             if (cNode.right == null) {                 cNode.right = new TreeNode<T>(key);             } else {                 // 新值没有插入又没有跳过就是插入right                 this.insertTreeNode(cNode.right, key);             }         }      }      // 搜索节点值     search(key: T) {         this.searchTreeNode(this.root, key);     }      // 搜索某个节点     searchTreeNode(node: TreeNode<T> | null, key: T): Boolean {         //如果node没有值,则返回false,表示没有找到         if (node == null) {             return false;         }         switch (this.compareFn(node.key, key)) {             //父节点等于子节点             case Compare.EQUALS:                 return true;                 break;             // 父节点小于当前数据 所以要在right节点上找             case Compare.LESS_THAN:                 return this.searchTreeNode(node.right, key)                 break;             // 父节点大于当前数据 所以应该在左节点上找             default:                 return this.searchTreeNode(node.left, key)                 break;         }     }       //通过中序遍历的方式遍历所有节点     inOrderTraverse(callBack: Function) {         this.inOrderTraverseNode(this.root, callBack);     }      inOrderTraverseNode(node: TreeNode<T> | null, callBack: Function) {         if (node == null) {             return;         }         this.inOrderTraverseNode(node.left, callBack);         callBack(node.key);         this.inOrderTraverseNode(node.right, callBack);     }      //通过先序遍历的方式遍历所有节点     preOrderTraverse(callBack: Function) {         this.preOrderTraverseNode(this.root, callBack);     }      preOrderTraverseNode(node: TreeNode<T> | null, callBack: Function) {         if (node == null) {             return;         }         callBack(node.key);         this.inOrderTraverseNode(node.left, callBack);         this.inOrderTraverseNode(node.right, callBack);     }      //通过后序遍历的方式遍历所有的节点     postOrderTraverse(callBack: Function) {         this.postOrderTraverseNode(this.root, callBack);     }      postOrderTraverseNode(node: TreeNode<T> | null, callBack: Function) {         if (node == null) {             return;         }         this.inOrderTraverseNode(node.left, callBack);         this.inOrderTraverseNode(node.right, callBack);         callBack(node.key);     }      //返回树中最小的值/键     min(): TreeNode<T> {         if (this.root == null) {             return undefined;         }         if (this.root.left == null) {             return this.root;         }         return this.minNode(this.root);     }      minNode(node: TreeNode<T> | null): TreeNode<T> {         if (node) {             if (node.left == null) {                 return node;             } else {                 return this.minNode(node.left);             }         }         return undefined;     }      //返回树中最大的值/键     max(): TreeNode<T> {         if (this.root == null) {             return undefined;         }         if (this.root.right == null) {             return this.root;         }         return this.maxNode(this.root);     }      maxNode(node: TreeNode<T> | null): TreeNode<T> {         if (node) {             if (node.right == null) {                 return node;             } else {                 return this.maxNode(node.right);             }         } else {             return undefined;         }     }      getRoot() {         return this.root;     }      //移除一个节点     remove(key: T) {         this.root = this.removeNode(this.root, key);     }      removeNode(node: TreeNode<T>, key: T): TreeNode<T> {         //判断这个树上是否有节点         if (node == undefined) {             return undefined;         }         //树上是有节点的,这个时候就要判断当前节点是否与删除的元素相同         switch (this.compareFn(node.key, key)) {             //父节点等于子节点             case Compare.EQUALS:                 node = this.removeCurrentNode(node);                 return node;                 break;             // 父节点小于当前数据 所以要在right节点上找             case Compare.LESS_THAN:                 node.right = this.removeNode(node.right, key)                 return node                 break;             // 父节点大于当前数据 所以应该在左节点上找             default:                 node.left = this.removeNode(node.left, key)                 return node                 break;         }     }      removeCurrentNode(node: TreeNode<T>): TreeNode<T> {         //分成三种情况         //一:叶结点(没有左右子节点),那么我们直接将该节点赋值为undefined         if (node.left == null && node.right == null) {             node = undefined;             return node;         }         //二:只有一个左节点或一个右节点         //左节点不存在,将父节点的右节点指针指向移除节点的右节点         if (node.left == null) {             node = node.right;             return node;         } else if(node.right == null){             node = node.left;             return node;         }          //三:有两个节点的元素         const aux = this.minNode(node.right);         node.key = aux.key;         node.right = this.removeNode(node.right,aux.key);         return node;     } }  

辅助分析:

递归遍历部分图解分析(这里只分析了左半部分):

第十章 树

从下向上看“调用栈”

第十章 树

从下向上看“调用栈”

第十章 树

从上向下看“调用栈”

书中代码

export enum Compare {   LESS_THAN = -1,   BIGGER_THAN = 1,   EQUALS = 0 }  type ICompareFunction<T> = (a: T, b: T) => number; function defaultCompare<T>(a: T, b: T): number {   if (a === b) {     return Compare.EQUALS;   }   return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; }  class Node<K> {   left: Node<K>;   right: Node<K>;    constructor(public key: K) {}    toString() {     return `${this.key}`;   } }  export default class BinarySearchTree<T> {   protected root: Node<T>;    constructor(protected compareFn: ICompareFunction<T> = defaultCompare) {}    insert(key: T) {     // special case: first key     if (this.root == null) {       this.root = new Node(key);     } else {       this.insertNode(this.root, key);     }   }    protected insertNode(node: Node<T>, key: T) {     if (this.compareFn(key, node.key) === Compare.LESS_THAN) {       if (node.left == null) {         node.left = new Node(key);       } else {         this.insertNode(node.left, key);       }     } else if (node.right == null) {       node.right = new Node(key);     } else {       this.insertNode(node.right, key);     }   }    getRoot() {     return this.root;   }    search(key: T) {     return this.searchNode(this.root, key);   }    private searchNode(node: Node<T>, key: T): boolean {     if (node == null) {       return false;     }      if (this.compareFn(key, node.key) === Compare.LESS_THAN) {       return this.searchNode(node.left, key);     } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) {       return this.searchNode(node.right, key);     }     // key is equal to node.item     return true;   }    inOrderTraverse(callback: Function) {     this.inOrderTraverseNode(this.root, callback);   }    private inOrderTraverseNode(node: Node<T>, callback: Function) {     if (node != null) {       this.inOrderTraverseNode(node.left, callback);       callback(node.key);       this.inOrderTraverseNode(node.right, callback);     }   }    preOrderTraverse(callback: Function) {     this.preOrderTraverseNode(this.root, callback);   }    private preOrderTraverseNode(node: Node<T>, callback: Function) {     if (node != null) {       callback(node.key);       this.preOrderTraverseNode(node.left, callback);       this.preOrderTraverseNode(node.right, callback);     }   }    postOrderTraverse(callback: Function) {     this.postOrderTraverseNode(this.root, callback);   }    private postOrderTraverseNode(node: Node<T>, callback: Function) {     if (node != null) {       this.postOrderTraverseNode(node.left, callback);       this.postOrderTraverseNode(node.right, callback);       callback(node.key);     }   }    min() {     return this.minNode(this.root);   }    protected minNode(node: Node<T>) {     let current = node;     while (current != null && current.left != null) {       current = current.left;     }     return current;   }    max() {     return this.maxNode(this.root);   }    protected maxNode(node: Node<T>) {     let current = node;     while (current != null && current.right != null) {       current = current.right;     }     return current;   }    remove(key: T) {     this.root = this.removeNode(this.root, key);   }    protected removeNode(node: Node<T>, key: T) {     if (node == null) {       return null;     }      if (this.compareFn(key, node.key) === Compare.LESS_THAN) {       node.left = this.removeNode(node.left, key);       return node;     } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) {       node.right = this.removeNode(node.right, key);       return node;     } else {       // key is equal to node.item        // handle 3 special conditions       // 1 - a leaf node       // 2 - a node with only 1 child       // 3 - a node with 2 children        // case 1       if (node.left == null && node.right == null) {         node = null;         return node;       }        // case 2       if (node.left == null) {         node = node.right;         return node;       } else if (node.right == null) {         node = node.left;         return node;       }        // case 3       const aux = this.minNode(node.right);       node.key = aux.key;       node.right = this.removeNode(node.right, aux.key);       return node;     }   } }  
赞(0) 打赏
未经允许不得转载:张拓的天空 » 第十章 树
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

专业的IT技术经验分享 更专业 更方便

联系我们本站主机

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏