二叉查找树(三)
2012-05-10 16:30
267 查看
我们知道二叉查找树是一种数据结构,它支持多种动态集合的操作,包括:查询,最大值,最小值,前驱,后继,插入和删除等操作。那么我们在前一篇已经创建了二叉查找树,那么我们来实现二叉查找树的各种操作吧。(*^__^*) (以下纯属个人理解,个人原创,理解不当的地方,请指正,谢谢)
我们来看二叉树的遍历操作,所谓遍历,顾名思义,就是能够依次的访问二叉查找树中的各个结点。在数据结构课堂上,我们知道,遍历方式有深度优先和广度优先遍历,深度优先又包括前序、中序和后序遍历;广度优先遍历,在二叉树的范畴中,就是层序遍历,就是先遍历某一层的结点,然后再遍历下一层的结点。好了,一个个的来介绍吧。
前序遍历
前序遍历,就是先遍历父结点,然后遍历左子树,最后遍历右子树的遍历方法,所谓前序,指的是遍历父结点发生在遍历左右子树之前。
前序遍历的递归算法很简单,代码如下:
这个算法,没什么难点可说,就是先访问当前结点,然后递归访问当前结点的左子树,最后递归访问当前结点的右子树。因为要遍历所有节点,所以该算法的时间复杂度为O(n);
递归往往意味着低效,那么,如果不用递归的算法,如果实现前序遍历呢?这里有两个方法,都是借助“栈”来实现的。
方法1是模拟递归算法的实现效果,具体代码如下:
方法1每次都将遇到的节点压入栈,当左子树遍历完毕后才从栈中弹出最后一个访问的节点,访问其右子树。在同一层中,不可能同时有两个节点压入栈,因此栈的大小空间为O(h),h为二叉树高度。时间方面,每个节点都被压入栈一次,弹出栈一次,访问一次,复杂度为O(n)。
方法2是直接模拟递归来实现的,代码如下:
方法2每次将节点压入栈,然后弹出,压右子树,再压入左子树,在遍历过程中,遍历序列的右节点依次被存入栈,左节点逐次被访问。同一时刻,栈中元素为m-1个右节点和1个最左节点,最高为h,所以空间也为O(h);每个节点同样被压栈一次,弹栈一次,访问一次,时间复杂度O(n)。
中序遍历
中序遍历,就是先遍历左子树,然后遍历父结点,最后遍历右子树,所谓中序,是指遍历父结点在遍历左子树和遍历右子树之间。同时,根据二叉查找树的性质,中序遍历的输出结果,恰好就是排序之后的序列。
中序遍历的递归算法也很简单,具体代码如下:
先递归遍历左子树,然后访问父结点,最后递归遍历右子树,上面的算法很简单,不多说。
同理,非递归该如何实现二叉查找树的中序遍历呢?同样,也需要借助“栈”来实现。代码如下:
该算法与前序遍历的非递归算法1非常的相近,时间和空间复杂度也相同。至于对应的第二种非递归中序遍历方法,没有想到,牛逼的童鞋们自己来补充吧~
后续遍历
后序遍历,就是先遍历左子树,然后遍历右子树,最后遍历父结点,所谓后序,是指遍历父结点在遍历左右子树之后。
同样,后序遍历的递归算法也很简单,代码如下:
先递归遍历左子树,然后递归遍历右子树,最后父结点。
那么,后序遍历的递归算法该如何写呢?这里同样有两个方法,方法1用单栈实现,方法2用双栈实现。
方法1代码如下:
方法1是根据访问前后元素的关系来进行的算法,根据关系的不同,发生的行为也就不同。
方法2的具体代码为:
方法2利用一个栈出栈并压入到另一个栈的手法,对二叉查找树进行了后序遍历。
层序遍历
层序遍历,就是按照二叉树的层次结构,逐层进行遍历的方法。层序遍历需要借助“队列”来实现,具体的代码如下:
上面的算法,先将根结点入队,然后出队,遍历输出,然后将左孩子和右孩子分别入队,依次循环下去,知道队列为空为止。
好了,遍历就先写到这儿吧。
ps:有写的不好的,请各位童鞋指正。
我们来看二叉树的遍历操作,所谓遍历,顾名思义,就是能够依次的访问二叉查找树中的各个结点。在数据结构课堂上,我们知道,遍历方式有深度优先和广度优先遍历,深度优先又包括前序、中序和后序遍历;广度优先遍历,在二叉树的范畴中,就是层序遍历,就是先遍历某一层的结点,然后再遍历下一层的结点。好了,一个个的来介绍吧。
前序遍历
前序遍历,就是先遍历父结点,然后遍历左子树,最后遍历右子树的遍历方法,所谓前序,指的是遍历父结点发生在遍历左右子树之前。
前序遍历的递归算法很简单,代码如下:
/** * 递归前序遍历以x为根的二叉树 * @author Alfred * @param x 根结点 */ public void preOrderTreeWalk(TreeNode x){ if(x != null){ System.out.print(x);//访问形式为打印输出一下 preOrderTreeWalk(x.getLeft()); preOrderTreeWalk(x.getRight()); } }
这个算法,没什么难点可说,就是先访问当前结点,然后递归访问当前结点的左子树,最后递归访问当前结点的右子树。因为要遍历所有节点,所以该算法的时间复杂度为O(n);
递归往往意味着低效,那么,如果不用递归的算法,如果实现前序遍历呢?这里有两个方法,都是借助“栈”来实现的。
方法1是模拟递归算法的实现效果,具体代码如下:
/** * 非递归前序遍历以x为根结点的二叉树 * @author Alfred * @param x 根结点 */ public void preOrderTreeWalkNonrecursive1(TreeNode x){ //借助栈来实现。 Stack<TreeNode> stack = new Stack<TreeNode>(); while(x != null || !stack.empty()){ if(x != null){ System.out.print(x);//遍历输出 stack.push(x);//压栈 x = x.getLeft(); }else{ x = stack.pop();//出栈 x = x.getRight(); } } }
方法1每次都将遇到的节点压入栈,当左子树遍历完毕后才从栈中弹出最后一个访问的节点,访问其右子树。在同一层中,不可能同时有两个节点压入栈,因此栈的大小空间为O(h),h为二叉树高度。时间方面,每个节点都被压入栈一次,弹出栈一次,访问一次,复杂度为O(n)。
方法2是直接模拟递归来实现的,代码如下:
/** * 非递归前序遍历以x为根结点的二叉树 * @author Alfred * @param x 根结点 */ public void preOrderTreeWalkNonrecursive2(TreeNode x){ Stack<TreeNode> stack = new Stack<TreeNode>(); if(x != null){ stack.push(x); while(!stack.empty()){ TreeNode tmpNode = stack.pop(); System.out.print(tmpNode);//遍历输出 if(tmpNode.getRight() != null){ stack.push(tmpNode.getRight()); } if(tmpNode.getLeft() != null){ stack.push(tmpNode.getLeft()); } } } }
方法2每次将节点压入栈,然后弹出,压右子树,再压入左子树,在遍历过程中,遍历序列的右节点依次被存入栈,左节点逐次被访问。同一时刻,栈中元素为m-1个右节点和1个最左节点,最高为h,所以空间也为O(h);每个节点同样被压栈一次,弹栈一次,访问一次,时间复杂度O(n)。
中序遍历
中序遍历,就是先遍历左子树,然后遍历父结点,最后遍历右子树,所谓中序,是指遍历父结点在遍历左子树和遍历右子树之间。同时,根据二叉查找树的性质,中序遍历的输出结果,恰好就是排序之后的序列。
中序遍历的递归算法也很简单,具体代码如下:
/** * 递归中序遍历以x为根的二叉树 * @author Alfred * @param x 根结点 */ public void inOrderTreeWalk(TreeNode x){ if(x != null){ inOrderTreeWalk(x.getLeft()); System.out.print(x); inOrderTreeWalk(x.getRight()); } }
先递归遍历左子树,然后访问父结点,最后递归遍历右子树,上面的算法很简单,不多说。
同理,非递归该如何实现二叉查找树的中序遍历呢?同样,也需要借助“栈”来实现。代码如下:
/** * 非递归中序遍历以x为根结点的二叉树 * @author Alfred * @param x 根结点 */ public void inOrderTreeWalkNonrecursive(TreeNode x){ Stack<TreeNode> stack = new Stack<TreeNode>(); while(x != null || !stack.empty()){ if(x != null){ stack.push(x); x = x.getLeft(); }else{ x = stack.pop(); System.out.print(x);//遍历输出 x = x.getRight(); } } }
该算法与前序遍历的非递归算法1非常的相近,时间和空间复杂度也相同。至于对应的第二种非递归中序遍历方法,没有想到,牛逼的童鞋们自己来补充吧~
后续遍历
后序遍历,就是先遍历左子树,然后遍历右子树,最后遍历父结点,所谓后序,是指遍历父结点在遍历左右子树之后。
同样,后序遍历的递归算法也很简单,代码如下:
/** * 递归后序遍历以x为根的二叉树 * @author Alfred * @param x 根结点 */ public void postOrderTreeWalk(TreeNode x){ if(x != null){ postOrderTreeWalk(x.getLeft()); postOrderTreeWalk(x.getRight()); System.out.print(x); } }
先递归遍历左子树,然后递归遍历右子树,最后父结点。
那么,后序遍历的递归算法该如何写呢?这里同样有两个方法,方法1用单栈实现,方法2用双栈实现。
方法1代码如下:
/** * 非递归后序遍历以x为根结点的二叉树 * @author Alfred * @param x 根结点 */ public void postOrderTreeWalkNonrecursive1(TreeNode x){ Stack<TreeNode> stack = new Stack<TreeNode>(); TreeNode prev = null; TreeNode curr = null; if(x != null){ stack.push(x); } while(!stack.empty()){ curr = stack.peek(); if(prev == null || prev.getLeft() == curr || prev.getRight() == curr){ if(curr.getLeft() != null){ stack.push(curr.getLeft());//压左孩子 }else if(curr.getRight() != null){ stack.push(curr.getRight());//压右孩子 } }else if(curr.getLeft() == prev){ if(curr.getRight() != null){ stack.push(curr.getRight());//压右孩子 } }else{ System.out.print(curr);//遍历输出 stack.pop(); } prev = curr; } }
方法1是根据访问前后元素的关系来进行的算法,根据关系的不同,发生的行为也就不同。
方法2的具体代码为:
/** * 非递归后序遍历以x为根结点的二叉树 * @author Alfred * @param x 根结点 */ public void postOrderTreeWalkNonrecursive2(TreeNode x){ Stack<TreeNode> stack = new Stack<TreeNode>(); Stack<TreeNode> output = new Stack<TreeNode>(); TreeNode curr = null; if(x != null){ stack.push(x); } while(!stack.empty()){ curr = stack.pop(); output.push(curr);//存放到输出地栈里面 if(curr.getLeft() != null){ stack.push(curr.getLeft());//压左孩子 } if(curr.getRight() != null){ stack.push(curr.getRight());//压右孩子 } } while(!output.empty()){ TreeNode tmpNode = output.pop(); System.out.print(tmpNode);//打印输出 } }
方法2利用一个栈出栈并压入到另一个栈的手法,对二叉查找树进行了后序遍历。
层序遍历
层序遍历,就是按照二叉树的层次结构,逐层进行遍历的方法。层序遍历需要借助“队列”来实现,具体的代码如下:
/** * 层序遍历二叉树 * @author Alfred * @param x 根结点 */ public void levelOrderTreeWalk(TreeNode x){ Queue<TreeNode> queue = new LinkedList<TreeNode>(); TreeNode tmpNode = null; if(x != null){ queue.offer(x); } while(!queue.isEmpty()){ tmpNode = queue.poll(); System.out.print(tmpNode);//打印输出 if(tmpNode.getLeft() != null){ queue.offer(tmpNode.getLeft());//左孩子入队 } if(tmpNode.getRight() != null){ queue.offer(tmpNode.getRight());//右孩子入队 } } }
上面的算法,先将根结点入队,然后出队,遍历输出,然后将左孩子和右孩子分别入队,依次循环下去,知道队列为空为止。
好了,遍历就先写到这儿吧。
ps:有写的不好的,请各位童鞋指正。
相关文章推荐
- c#数据结构———二叉查找树
- 动态二叉查找树
- 数据结构与算法分析-二叉查找树的实现
- 查找、检索 算法-总结3 平衡二叉查找树 [AVL]
- 算法学习--二叉查找树
- 【数据结构与算法基础】二叉查找树 / Binary Search Tree
- java 实现的二叉查找树
- 程序员面试100题之十三:求二叉查找树的镜像
- C++算法(一)之二叉查找树
- java实现二叉查找树
- 二叉树和二叉查找树
- 二叉查找树
- 二叉查找树 - C语言实现(摘自数据结构与算法分析 C语言描述)
- 【学习点滴-数据结构-二叉树】二叉查找树源码实现
- 二叉查找树(四)
- BinarySearchTree(2)二叉查找树链式非递归实现
- 数据结构之二叉排序树或者二叉查找树(java版)
- 二叉查找树的实现
- 【c++模板实现】二叉查找树
- 二叉查找树的简单操作