自我测试

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

树的相关术语

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

内部节点: 至少有一个子节点的节点称为内部节点(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;
    }
  }
}

posted on 2020-11-22 18:40  人生之外的路途  阅读(182)  评论(0编辑  收藏  举报