算法导论:二叉搜索树
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); } }
以上是关于算法导论:二叉搜索树的主要内容,如果未能解决你的问题,请参考以下文章