Binary Tree

General Tree

又向,联通,无环

class TreeNode {
    int key;
    List<TreeNode> children;
    public TreeNode(int key) {
        this.key = key;
        children = new ArrayList<TreeNode>();
    }
}

root //拎出来root就找到了整个树

General Graph

class GraphNode {
    int key;
    List<TreeNode> neighbors;
    public GraphNode(int key) {
        this.key = key;
        neighbors = new ArrayList<GraphNode>();
    }
}

List<GraphNode> //因为这个时候不能用一个root拎出来整张图了,需要一个list of node
//or adjacency list
//or adjacency matrix

A的neighbor里有B说明A到B有边,但是只有查了B才能知道B到A有没有边(对于有向图)

G = V + E

Given V, Dense Graph的E是O( V2V^2 ), Sparse Graph的E是O(v). 矩阵只适合node少的情况(sparse graph),而list适合node多的情况。

如果简化adj list,用list of list,这个list可以是array list, 也可以是linked list. 把vertex的index存下来,那么就可以用graph.get(i)拿到第i个node的 neighbor index 做算法题的时候,都是下面这种简化的。在实际工作中其实还是哟搞得上面的general graph,因为需要有value在

外层list的顺序重要,里面的list的数量无所谓

Iterative Traversal of Binary Tree

why iterative?

  • from stack to heap

  • for iterators

Pre-Order

root进,pop栈顶并打印,右进,左进,pop栈顶并打印... 直到栈空了 因为stack是先进后出,所以进stack时先进右后进左

Time: O(n) Space: heap上的那堆右节点 O(height)

In-Order

关键是要知道什么时候左子树被打印完了 用一个helper,when helper is null, it means the left subtree of the root is finished, the root is the top element in the stack 当helper是null的时候,当前的左都打完了,这个时候可以放右边的node进helper;最后栈空了,但是helper不是null,这个时候就可以pop打印了。

helper的意义:下一个要看的节点 如果不是null就入栈 如果是null就证明当前栈的top(左边)被打印完了,可以pop了,pop完helper里是刚才pop的节点的 right child 直到left和right都是空的时候停止

Post-Order

奇技淫巧 反过来pre-order

Time: O(2n) Space: O(n)

正派做法 记录从哪里来的

如果prev是current的parent,这是第一次看到它;如果prev是current的left child,这是第二次看到它;如果prev是curr的right child,可以打印了

  • root = stack.top

  • if previous is null --> going down (left subtree)

  • if previous is current's parent --> going down (left subtree)

go down: 有左,优先左;没有左,打印右; 都没有,打印自己并弹栈 from left: 右或者打印自己并弹栈 from right: 打印自己并弹栈 偷看栈顶 从上下来,有左压左,有右压右,左右都无,打印自己 从左上来,有右压右,无右打自己 从右上来,打印自己

Binary Search Tree

对任何一个节点,左边的所有数都比它小,右边所有都比它大 优势是search时每一步砍掉一半的子树,缩小搜索空间。

  • search() worst case O(n) average(logn)

  • insert() worst case O(n) average(logn)

  • remove() worst case O(n) average(logn)

Self-Balancing Binary Search Tree

为了解决BST不总是平衡的问题,有了两种特殊的树,自己维持self-balancing的状态,以保证以上三种操作的时间复杂度都是logn

  • AVL Tree

  • Red-Black Tree

Red-Black Tree

  • in Java: TreeMap / TreeSet

  • in C++: map/set

问题来了,重复node?那么就在Map里存一个value,也就是节点的数量。

Search in BST

Tail Recursion

最后一步且仅在最后一步调用自身 这种递归很容易写成iterative 这个写法的好处是先前存的所有stack都弹栈了。如果写成tail recursion的话 建议改写成iteration。

下面这个不是tail recursion,因为line 6这一行还在栈里。

return isBST()&&isBST()也不是tail recursion 因为最后一步是&&

另一个例子是求阶乘

Insert in BST

应该return改了的tree的root node

Delete in BST

  • leaf: 3.right=null

  • only has child on one sight: 3.right = 3.right.right / 3.right = 3.right.left

  • has children on both sides: 调右子树的最小值

    • 如果右节点没有children 它自己就是最小的 然后把该继承的(原来的left)都继承好

    • 如果右节点还有左子树 一直往左走,直到没有left为止 把最小的替换了 把最小的右子树接到原来的最小值位置

Time: O(height) Space: O(height)

Last updated