算法导论:二叉搜索树

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法导论:二叉搜索树相关的知识,希望对你有一定的参考价值。

定义:

(0)二叉树

(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;

(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;

(3)左、右子树也分别为二叉排序树

数据结构定义

public class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;

    public TreeNode(int val){
        this.val = val;
        this.left = left=null;
        this.right = right=null;
    }
}

 

插入元素

由于元素互异,插入元素的位置一定在叶子结点,递归插入程序

    public TreeNode insertTree(TreeNode root,int key){
        if(root == null){
            root = new TreeNode(key);
            return root;
        }else if(key < root.val){
            root.left = insertTree(root.left,key);
        }else{
            root.right = insertTree(root.right,key);
        }
        return root;
    }

查找最小值

根据二叉搜索树的定义:左孩子比根节点小,右孩子比根节点大,最小值一定在最左的子树上,同时该子树一定没有左孩子,可以一直left的找

 

    /**
     * 递归找最小值
     * @param root
     * @return
     */
    public TreeNode iterativeTreeMin(TreeNode root){
        if(root.left!=null&&root.left.left==null)
            return root.left;
        return iterativeTreeMin(root.left);
    }
    /**
     * 查找最小节点,最小节点一定在下层的最左侧,同时该节点不能有左孩子
     * 时间复杂度O(h)
     * @param root
     * @return
     */
    public TreeNode treeMin(TreeNode root){
        while(root.left!=null){
            root = root.left;
        }
        return root;
    }

查找最大值

最大值一定在最右的子树,同时这个子树一定没有右孩子

    /**
     * 递归查找最大值
     * @param root
     * @return
     */
    public TreeNode iterativeTreeMax(TreeNode root){
        if(root.right!=null&&root.right.right==null)
            return root.right;
        return iterativeTreeMax(root.right);
    }
    /**
     * 查找最大结点,最大结点一定在下层的最右侧,同时该节点不能有右孩子
     * 时间复杂度O(h)
     * @param root
     * @return
     */
    public TreeNode treeMax(TreeNode root){
        while(root.right!=null){
            root = root.right;
        }
        return root;
    }

查找结点是否存在

递归和非递归

 

    /**
     * 循环方式查找 key是否在排序二叉树中
     * 这个效率更高,具体多少?
     * @param root
     * @param key
     * @return
     */
    public TreeNode iterativeTreeSearch(TreeNode root,TreeNode key){
        while(root!=null &&root.val!=key.val){
            if(key.val < root.val)
                root = root.left;
            else
                root = root.right;
        }
        return root;
    }
    /**
     * 递归查找 key节点是否在二叉树root中.
     * 时间复杂度O(h)
     * @param root
     * @param key
     * @return
     */
    public TreeNode treeSearch(TreeNode root,TreeNode key){
        if(key == null || key.val == root.val){
            return key;
        }
        if(key.val < root.val)
            return treeSearch(root.left,key);
        else
            return treeSearch(root.right,key);
    }

前序遍历

遍历规则:根左右

    /**
     * 递归前序遍历
     * @param root
     * @param result
     */
    public void preorderTree(TreeNode root,ArrayList<Integer> result){
        if(root==null)
            return;
        result.add(root.val);
        preorderTree(root.left,result);
        preorderTree(root.right,result);
    }

递归的一般都能写出来,如何写出非递归程序?

遍历规则:根左右

对两层三个结点情况:先输出根结点,在输出左结点的时候需要先保存右结点的值,输出左结点,再输出保存的右结点

对树的层数多的时候,这里需要保存右结点所在的子树,输出左结点值,对左结点同上操作。

这里利用栈保存中间结点。

    /**
     * 循环前序遍历
     * @param root
     * @param result
     */
    public void iterativePreorderTree(TreeNode root,ArrayList<Integer> result){
        Stack<TreeNode> stack = new Stack<TreeNode>();
        while(root!=null || !stack.empty()){
            result.add(root.val);
            if(root.right!=null)
                stack.push(root.right);
            root = root.left;
            if(root == null && !stack.empty()){
                root = stack.pop();
            }
        }
    }

中序遍历

遍历规则:左根右

递归很简单

    /**
     * 递归中序遍历二叉树
     * 时间复杂度O(N)
     * @param root
     * @param result
     */
    public  void inorderTree(TreeNode root,ArrayList<Integer> result){
        if(root == null)
            return;
        inorderTree(root.left,result);
        result.add(root.val);
        inorderTree(root.right,result);
    
    }

 

非递归方式,需要利用栈保存左结点信息

    /**
     * 循环方式中序遍历,用栈存放中间节点
     * @param root
     * @param result
     */
    public void iterativeInorderTree(TreeNode root,ArrayList<Integer> result){
        Stack<TreeNode> stack = new Stack<TreeNode>();
        while(root!=null || !stack.empty()){
            while(root!=null){ // 向左侧走
                stack.push(root);
                
                root = root.left;
            }
            root = stack.pop();// 取出最左侧的结点
            
            result.add(root.val);
            
            root = root.right; // 最左侧节点的右节点,若不存在在上面的while循环也会运行
        }
    }

后序遍历

遍历规则:左右根

递归方式

    /**
     * 递归后续遍历
     * @param root
     * @param result
     */
    public void postorderTree(TreeNode root,ArrayList<Integer> result){
        if(root == null)
            return;
        postorderTree(root.left,result);
        postorderTree(root.right,result);
        result.add(root.val);
    }

非递归

理解不透

/**
     * 非递归后序遍历
     * @param root
     * @param result
     */
    public void iterativePostorderTree(TreeNode root,ArrayList<Integer> result){
         
        Stack<TreeNode> stack = new Stack<TreeNode>();
        if (root == null) return;
        boolean flag = true;
        while(flag){  
            while(root.left != null || root.right != null){  
                if (root.left != null){  
                    stack.push(root);  
                    root = root.left;  
                }  
                else{  
                    stack.push(root);  
                    root = root.right;  
                }  
            }  
            TreeNode y = stack.peek();  
            while (root == y.right || y.right == null){  
                result.add(root.val);  
                stack.pop();
                if (stack.empty()){  
                    flag = false;  
                    result.add(y.val);  
                    break;  
                }  
                root = y;  
                y = stack.peek();  
            }  
            if (root == y.left && y.right != null){  
                result.add(root.val);  
                root = y.right;  
            }  
        }  
    }

版本2

 

    /**
     * 后序遍历非递归 2 
     * @param root
     * @param result
     */
    public void iterativePostorderTree2(TreeNode root,ArrayList<Integer> result){
            Stack<TreeNode> stack = new Stack<TreeNode>();
            TreeNode prev = null; // previously traversed node
            TreeNode curr = root;

            if (root == null) {
                return;
            }

            stack.push(root);
            while (!stack.empty()) {
                curr = stack.peek();
                if (prev == null || prev.left == curr || prev.right == curr) { // traverse down the tree
                    if (curr.left != null) {
                        stack.push(curr.left);
                    } else if (curr.right != null) {
                        stack.push(curr.right);
                    }
                } else if (curr.left == prev) { // traverse up the tree from the left
                    if (curr.right != null) {
                        stack.push(curr.right);
                    }
                } else { // traverse up the tree from the right
                    result.add(curr.val);
                    stack.pop();
                }
                prev = curr;
            }

    }

 

求树的深度

递归

 

    /**
     * 递归求树的深度
     * @param root
     * @param hight
     * @return
     */
    public int depth(TreeNode root,int hight){
        if(root == null)
            return hight;
        return Math.max(depth(root.left,hight), depth(root.right,hight))+1;
    }

 

 

 

层次遍历

规则:每一次打印一个结点的时候,如果该结点有子结点,则把该结点的子结点放到一个队列的尾部。接下来到队列的头部取出最早进入队列的结点。

重复前面的打印操作,直到队列中所有的结点都被打印出来为止。

    public void levelOrder(TreeNode root,ArrayList<ArrayList<Integer>> tree){
         Queue<TreeNode> queue = new LinkedList<TreeNode>();
            
            if(root == null)
                return ;
            queue.offer(root);
            while(!queue.isEmpty()){
                ArrayList<Integer> list = new ArrayList<Integer>();
                int size = queue.size();
                for(int i=0;i<size;i++){
                    TreeNode head = queue.poll();
                    list.add(head.val);
                    if(head.left!=null){
                        queue.offer(head.left);
                    }
                    if(head.right!=null){
                        queue.offer(head.right);
                    }
                }
                tree.add(list);
            }
    }

 

以上是关于算法导论:二叉搜索树的主要内容,如果未能解决你的问题,请参考以下文章

算法导论习题—二叉搜索树红黑树区间树

算法导论二叉搜索树

算法导论之红黑树的学习

算法导论笔记——第十二~十四章 数据结构树

算法导论 红黑树 实现

二叉查找树