【老鸟学算法】二元查找树转变成排序的双向链表——算法思想及java实现
来源:互联网 发布:三国群英战辅助软件 编辑:程序博客网 时间:2024/05/22 13:38
把二元查找树转变成排序的双向链表(树)(来自July大神的《算法面试100题》,果断抱大腿呀)
题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。
要求不能创建任何新的结点,只调整指针的指向。
10
/ |
6 14
/ | / |
4 8 12 16
转换成双向链表
4=6=8=10=12=14=16。
二叉查找树的特点:任意节点的左子树都要小于当前节点;右子树都要大于当前节点。
特点:查询某个值,需要的时间复杂度为O(lgN)。
现在要求将其由树状结构改造成线性结构的双向链表,感觉重点在于,获得当前节点左子树范围内最右节点(也是左子树最大值节点),以及右子树范围内最左节点(也是右子树最小值节点),然后,调整这两个节点与当前节点左右顺序。以本题为例,就是要调整8、10之间的关系,和12、10之间的关系(8的右节点为10,10的左节点为8;12的左节点为10,10的右节点为12)。
当然,这种三层关系的树看起来很简单,如果层次多了,就需要分析,如何将其中的共性抽象出来。
也是最近两天才看到July大神的这个帖子,目前还没看到答案那块。不过他的提示,结构体,猜测他用的是C++实现的。C++自己也忘的差不多了,这边就用Java实现下。
数据结构,依然沿用July的value,left,right方式。
算法思路:
1. 树根节点,分左右子树。先将“当前节点左子树范围内最右节点”leftR找出来,再将“右子树范围内最左节点”rightL找出来。(这两步放在一开始,是因为此时左右子树内的关系还没改变,先取出来,时间消耗O(lgN)。只是查找到节点,空间上只用到一个索引,不会产生新的内存分配)
2. 若左子树为叶子节点,则直接设置其右向索引指向其父节点,左向递归结束;否则,将此节点作为根节点,递归调用第1步。
3. 若右子树为叶子节点,则直接设置其左向索引指向其父节点,右向递归结束;否则,将此节点作为根节点,递归调用第1步。
4. 设置根节点的左向节点为leftR,leftR的右向节点为根节点(其左向节点,在2、3两步的递归过程中,已经赋值);设置根节点的右向节点为rightL,rightL的左向节点为根节点(其右向节点,在2、3两步的递归过程中,已经赋值)。
算法思路看起来还是比较简单的(时间复杂度O(lgN)),这里要说明的是,与简单递归思路,“只递归调用本方法”不同,采用“主方法的两个字方法递归调用主方法”实现的。这样看起来,模块清晰,易于设计。
题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。
要求不能创建任何新的结点,只调整指针的指向。
10
/ |
6 14
/ | / |
4 8 12 16
转换成双向链表
4=6=8=10=12=14=16。
二叉查找树的特点:任意节点的左子树都要小于当前节点;右子树都要大于当前节点。
特点:查询某个值,需要的时间复杂度为O(lgN)。
现在要求将其由树状结构改造成线性结构的双向链表,感觉重点在于,获得当前节点左子树范围内最右节点(也是左子树最大值节点),以及右子树范围内最左节点(也是右子树最小值节点),然后,调整这两个节点与当前节点左右顺序。以本题为例,就是要调整8、10之间的关系,和12、10之间的关系(8的右节点为10,10的左节点为8;12的左节点为10,10的右节点为12)。
当然,这种三层关系的树看起来很简单,如果层次多了,就需要分析,如何将其中的共性抽象出来。
也是最近两天才看到July大神的这个帖子,目前还没看到答案那块。不过他的提示,结构体,猜测他用的是C++实现的。C++自己也忘的差不多了,这边就用Java实现下。
数据结构,依然沿用July的value,left,right方式。
算法思路:
1. 树根节点,分左右子树。先将“当前节点左子树范围内最右节点”leftR找出来,再将“右子树范围内最左节点”rightL找出来。(这两步放在一开始,是因为此时左右子树内的关系还没改变,先取出来,时间消耗O(lgN)。只是查找到节点,空间上只用到一个索引,不会产生新的内存分配)
2. 若左子树为叶子节点,则直接设置其右向索引指向其父节点,左向递归结束;否则,将此节点作为根节点,递归调用第1步。
3. 若右子树为叶子节点,则直接设置其左向索引指向其父节点,右向递归结束;否则,将此节点作为根节点,递归调用第1步。
4. 设置根节点的左向节点为leftR,leftR的右向节点为根节点(其左向节点,在2、3两步的递归过程中,已经赋值);设置根节点的右向节点为rightL,rightL的左向节点为根节点(其右向节点,在2、3两步的递归过程中,已经赋值)。
算法思路看起来还是比较简单的(时间复杂度O(lgN)),这里要说明的是,与简单递归思路,“只递归调用本方法”不同,采用“主方法的两个字方法递归调用主方法”实现的。这样看起来,模块清晰,易于设计。
最后,附上Java版源码:
/** * 自定义双向链表 * 二元查找树转变成排序的双向链表 * @author Maxy */public class MyLinkedNode{ private int value;//节点值 private MyLinkedNode left;//左值节点 private MyLinkedNode right;//右值节点 /** * 节点值只能初始化时写入 * @param value */ public MyLinkedNode(int value) { this.value = value; } public MyLinkedNode getLeft() { return left; } public void setLeft(MyLinkedNode left) { this.left = left; } public MyLinkedNode getRight() { return right; } public void setRight(MyLinkedNode right) { this.right = right; } /** * 只提供查询节点值 * @return */ public int getValue() { return value; } @Override public String toString() { return "" + value; } /** * 右向打印 */ public void printToRight() { System.out.print(value + " "); if (null != right) { right.printToRight(); } } /** * 左向打印 */ public void printToLeft() { System.out.print(value + " "); if (null != left) { left.printToLeft(); } } //测试桩 public static void main(String[] args) { //初始化2叉查找树:左子树节点值>本节点值>右子树节点值 MyLinkedNode v10 = new MyLinkedNode(10); MyLinkedNode v6 = new MyLinkedNode(6); MyLinkedNode v14 = new MyLinkedNode(14); MyLinkedNode v4 = new MyLinkedNode(4); MyLinkedNode v8 = new MyLinkedNode(8); MyLinkedNode v12 = new MyLinkedNode(12); MyLinkedNode v16 = new MyLinkedNode(16); //原题基础上扩展一层叶子节点 MyLinkedNode v3 = new MyLinkedNode(3); MyLinkedNode v5 = new MyLinkedNode(5); MyLinkedNode v7 = new MyLinkedNode(7); MyLinkedNode v9 = new MyLinkedNode(9); MyLinkedNode v11 = new MyLinkedNode(11); MyLinkedNode v13 = new MyLinkedNode(13); MyLinkedNode v15 = new MyLinkedNode(15); MyLinkedNode v17 = new MyLinkedNode(17); //构建二叉树关系 v10.setLeft(v6); v10.setRight(v14); v6.setLeft(v4); v6.setRight(v8); v14.setLeft(v12); v14.setRight(v16); //扩展层 v4.setLeft(v3); v4.setRight(v5); v8.setLeft(v7); v8.setRight(v9); v12.setLeft(v11); v12.setRight(v13); v16.setLeft(v15); v16.setRight(v17); //达到题目要求,因为已经有序,只需要对叶子节点以及其直接父节点的调整 processTreeToLinked(v10); //最后,如果处理成功,则右向打印v3时,会升序打印出来 System.out.print("升序打印:"); v3.printToRight(); System.out.println(); //左向打印v17,会降序打印 System.out.print("降序打印:"); v17.printToLeft(); } /** * 递归主方法 * 不借助新的节点,对叶子节点以及其直接父节点的调整 * @param node */ private static void processTreeToLinked(MyLinkedNode node) { //为空不操作 if (null == node) { return; } //获得左子树的最右节点(最大值) MyLinkedNode leftR = getMostRightNode(node.getLeft()); //获得右子树的最左节点(最小值) MyLinkedNode rightL = getMostLeftNode(node.getRight()); //左子树非空,递归处理左子树 if (null != node.getLeft()) { processLeftNode(node.getLeft(), node); } //右子树非空,递归处理右子树 if (null != node.getRight()) { processRightNode(node, node.getRight()); } //若左子树最右节点非空,调整与根节点相邻 if (null != leftR) { leftR.setRight(node); node.setLeft(leftR); } //若右子树的最左节点非空,调整与根节点相邻 if (null != rightL) { rightL.setLeft(node); node.setRight(rightL); } } /** * 递归左子方法 * 处理左子树 * @param left * @param node */ private static void processLeftNode(MyLinkedNode left, MyLinkedNode node) { //若左子树为叶子节点,直接将其右向索引指向父节点,并返回 if (isLeafNode(left)) { left.setRight(node); return; } //本节点当做根节点,递归调用 processTreeToLinked(left); } /** * 递归右子方法 * 处理右子树 * @param node * @param right */ private static void processRightNode(MyLinkedNode node, MyLinkedNode right) { //若右子树为叶子节点,直接将其左向索引指向父节点,并返回 if (isLeafNode(right)) { right.setLeft(node); return; } //本节点当做根节点,递归调用 processTreeToLinked(right); } /** * 判断节点是否为叶子节点 * @param node * @return */ private static boolean isLeafNode(MyLinkedNode node) { return (null == node.getLeft()) && (null == node.getRight()); } /** * 获取最右节点 * @param left * @return */ private static MyLinkedNode getMostRightNode(MyLinkedNode left) { //若节点为空,直接返回空 if (null == left) { return null; } //若此节点右节点为空,直接返回此节点 if (null == left.getRight()) { return left; } return getMostRightNode(left.getRight()); } /** * 获取最左节点 * @param left * @return */ private static MyLinkedNode getMostLeftNode(MyLinkedNode right) { //若节点为空,直接返回空 if (null == right) { return null; } //若此节点的左节点为空,直接返回此节点 if (null == right.getLeft()) { return right; } return getMostLeftNode(right.getLeft()); }}
欢迎拍砖。- 【老鸟学算法】二元查找树转变成排序的双向链表——算法思想及java实现
- 把二元查找树转变成排序的双向链表——Java实现
- 每天一道算法题(8)——把二元查找树转变成排序的双向链表
- 每日一道算法题1 ——把二元查找树转变成排序的双向链表
- 算法面试100题——1、把二元查找树转变成排序的双向链表
- 把二元查找树转变成排序的双向链表--算法001
- 每天一算法(把二元查找树转变成排序的双向链表 )
- 算法-把二元查找树转变成排序的双向链表
- [面试中的算法]把二元查找树转变成排序的双向链表
- 算法与数据结构面试题(1)-把二元查找树转变成排序的双向链表
- 面试算法001-- 把二元查找树转变成排序的双向链表
- 每天学一个算法(2)------二元查找树转变成排序的双向链表
- Java实现: 把二元查找树转变成排序的双向链表(树)
- 把二元查找树转变成排序的双向链表
- 把二元查找树转变成排序的双向链表
- 二元查找树转变成排序的双向链表
- 二元查找树转变成排序的双向链表
- 二元查找树转变成排序的双向链表
- windows cmake mingw生成
- Ubuntu中使用alias设置命令别名
- gentoo最新的更新新闻 - 2013-08-23 emerge的日志和输出的消息语言默认仅支持英文了
- hdu 4698 - Counting(思路)
- android交叉编译c程序
- 【老鸟学算法】二元查找树转变成排序的双向链表——算法思想及java实现
- android修改虚拟内存(方法)
- 【Linux】kickstart安装时开启密码设置和 GRUB
- android如何设置adb root权限
- Asterisk配置文件说明列表
- 修改android4.0的鼠标箭头图标(方法)
- 如何手工创建release版本的dmp文件
- android文件系统system-ramdisk-userdata三者之间的关系
- get weblogic version/jdk version/ os/