剑指Offer——二叉搜索树转换为双向链表
2017-06-20 20:35
597 查看
分析问题:
首先要明白二叉搜索树也是一种排序的数据结构,它的中序遍历就是一个不递减的顺序排列。所以如果要转换成一个排好序的双向链表,那么仅需要改变原来指向左子节点和右子节点的指针,让它们分别指向前节点和后节点即可,如图所示:
调整指针:
原先指向左子节点的指针调整为链表中指向前一个节点的指针;
原先指向右子节点的指针调整为链表中指向后一个节点的指针。
如何调整:
考虑根节点和左右子树的基本情况,如果使用递归,这种基本情况就可以考虑将同样的方法用到左右子树上。
对于这种基本情况,可以分成三个部分来看,根节点10,左子树,右子树,需要做的就是将10与左子树中的最大值连起来,然后把10和右子树中的最小值连起来。
现在有个问题就是我们并不知道左子树中的最大值和右子树中的最小值,如果我们知道就好了。但是想到递归,递归到左子树中,如果左子树已转换为双向链表,那么双向链表的最后一个节点就是我们想要的,而右子树中的第一个节点也是我们想要的。
转换代码:
public static BinaryTreeNode baseconvert(BinaryTreeNode root, BinaryTreeNode lastNode) {
if(root == null)
return lastNode;
BinaryTreeNode current = root;
if(current.leftNode != null)
lastNode = baseconvert(current.leftNode, lastNode);
current.leftNode = lastNode;
if(lastNode != null)
lastNode.rightNode = current;
lastNode = current;
if(current.rightNode != null)
lastNode = baseconvert(current.rightNode, lastNode);
return lastNode;
}
要说明的一点是,剑指Offer使用C++写的,Java写的时候有点不同,就是baseconvert也要指定返回值,不然会报nullpointer的错误,估计是因为Java中只是对象的引用,不然每次返回的lastNode都是空。
上面的代码中有两个参数,一个是根节点,一个是已经转换好的链表的最后一个节点,因为二叉搜索树中序遍历的特性,当遍历到根节点的时候,左子树已经排好序了,所以会有一个左子树已经转换好的链表,而这个链表的最后一个节点即是我们需要和根节点左连的节点。
最开始的时候lastNode为null,current为当前子树的根节点。
如果左子树存在,那么转换左子树,递归下去,递归返回之后,说明找到了链表的第一个节点,也就是4那个节点,将4的前面节点置为null,此时current为4那个节点,这个时候由于6的4这个左子树已经遍历完了,所以需要往上层返回,返回之前需要将current赋值给lastNode,说明4这个左子树的最后一个节点就是4。
由于往上返回了一层,此时的current已经是6了,将6的左节点赋值为之前返回的4,判断之前返回的lastNode是否为null,不为空说明需要把根节点和lastNode连起来,其实lastNode为null的情况就只有第一个节点会出现,其他的时候都不会出现。现在已经排好序的包括6的左子树以及6本身了,所以此时的lastNode为6。
6和4的双向连接就完成了,由于6的右子树存在,又会递归到右边子树去,由于8不存在左右子树,递归下去一层之后,current就是8这个节点,但它的左孩子为空,所以不会左边递归下去,将8的左连接与之前的lastNode连接起来,建立双向连接的一条连接,然后由于lastNode不为空,所以又把lastNode的右连接与8连接起来,至此双向连接建立,此时lastNode为8。
所以,4、6、8都已排好序,此时,lastNode为8,返回到上一层,也就是根节点10了,在这一层current为10,将current的左连接与lastNode连接起来,如果lastNode存在,将lastNode的右连接与10连接在一起,以此建立双向连接。至此,就将根节点和左子树都连接起来了,然后就是去转换右子树,现在的lastNode为10,current为14,14有左孩子,所以需要递归到下一层,下一层的current为12,12没有左孩子,所以不需要再递归,所以12是12这棵子树转换成双向链表的最左边的节点,将lastNode与12连接,也就是10与12连接,此时的lastNode就变成了12,再将12的右子树递归,由于没有右子树,所以直接返回到上一层,上一层的current是14,14与已排好序的lastNode连接,也就是12与14连接,然后lastNode变为14,递归到14的右子树,也就是current变为16,16再递归到左子树,无左子树,将16与14连接,此时的lastNode变为16,递归右子树,无右子树,所以整个递归工作完成。
找到头节点:
之前的转换只是把引用大挪移了一下,其实可以发现引用的个数并没有变化,而且头尾节点只有一个出度的引用,而现在我们如果要使用这个双向链表,需要找到头节点:
测试代码:
首先要明白二叉搜索树也是一种排序的数据结构,它的中序遍历就是一个不递减的顺序排列。所以如果要转换成一个排好序的双向链表,那么仅需要改变原来指向左子节点和右子节点的指针,让它们分别指向前节点和后节点即可,如图所示:
调整指针:
原先指向左子节点的指针调整为链表中指向前一个节点的指针;
原先指向右子节点的指针调整为链表中指向后一个节点的指针。
如何调整:
考虑根节点和左右子树的基本情况,如果使用递归,这种基本情况就可以考虑将同样的方法用到左右子树上。
对于这种基本情况,可以分成三个部分来看,根节点10,左子树,右子树,需要做的就是将10与左子树中的最大值连起来,然后把10和右子树中的最小值连起来。
现在有个问题就是我们并不知道左子树中的最大值和右子树中的最小值,如果我们知道就好了。但是想到递归,递归到左子树中,如果左子树已转换为双向链表,那么双向链表的最后一个节点就是我们想要的,而右子树中的第一个节点也是我们想要的。
转换代码:
public static BinaryTreeNode baseconvert(BinaryTreeNode root, BinaryTreeNode lastNode) {
if(root == null)
return lastNode;
BinaryTreeNode current = root;
if(current.leftNode != null)
lastNode = baseconvert(current.leftNode, lastNode);
current.leftNode = lastNode;
if(lastNode != null)
lastNode.rightNode = current;
lastNode = current;
if(current.rightNode != null)
lastNode = baseconvert(current.rightNode, lastNode);
return lastNode;
}
要说明的一点是,剑指Offer使用C++写的,Java写的时候有点不同,就是baseconvert也要指定返回值,不然会报nullpointer的错误,估计是因为Java中只是对象的引用,不然每次返回的lastNode都是空。
上面的代码中有两个参数,一个是根节点,一个是已经转换好的链表的最后一个节点,因为二叉搜索树中序遍历的特性,当遍历到根节点的时候,左子树已经排好序了,所以会有一个左子树已经转换好的链表,而这个链表的最后一个节点即是我们需要和根节点左连的节点。
最开始的时候lastNode为null,current为当前子树的根节点。
如果左子树存在,那么转换左子树,递归下去,递归返回之后,说明找到了链表的第一个节点,也就是4那个节点,将4的前面节点置为null,此时current为4那个节点,这个时候由于6的4这个左子树已经遍历完了,所以需要往上层返回,返回之前需要将current赋值给lastNode,说明4这个左子树的最后一个节点就是4。
由于往上返回了一层,此时的current已经是6了,将6的左节点赋值为之前返回的4,判断之前返回的lastNode是否为null,不为空说明需要把根节点和lastNode连起来,其实lastNode为null的情况就只有第一个节点会出现,其他的时候都不会出现。现在已经排好序的包括6的左子树以及6本身了,所以此时的lastNode为6。
6和4的双向连接就完成了,由于6的右子树存在,又会递归到右边子树去,由于8不存在左右子树,递归下去一层之后,current就是8这个节点,但它的左孩子为空,所以不会左边递归下去,将8的左连接与之前的lastNode连接起来,建立双向连接的一条连接,然后由于lastNode不为空,所以又把lastNode的右连接与8连接起来,至此双向连接建立,此时lastNode为8。
所以,4、6、8都已排好序,此时,lastNode为8,返回到上一层,也就是根节点10了,在这一层current为10,将current的左连接与lastNode连接起来,如果lastNode存在,将lastNode的右连接与10连接在一起,以此建立双向连接。至此,就将根节点和左子树都连接起来了,然后就是去转换右子树,现在的lastNode为10,current为14,14有左孩子,所以需要递归到下一层,下一层的current为12,12没有左孩子,所以不需要再递归,所以12是12这棵子树转换成双向链表的最左边的节点,将lastNode与12连接,也就是10与12连接,此时的lastNode就变成了12,再将12的右子树递归,由于没有右子树,所以直接返回到上一层,上一层的current是14,14与已排好序的lastNode连接,也就是12与14连接,然后lastNode变为14,递归到14的右子树,也就是current变为16,16再递归到左子树,无左子树,将16与14连接,此时的lastNode变为16,递归右子树,无右子树,所以整个递归工作完成。
找到头节点:
之前的转换只是把引用大挪移了一下,其实可以发现引用的个数并没有变化,而且头尾节点只有一个出度的引用,而现在我们如果要使用这个双向链表,需要找到头节点:
public static BinaryTreeNode convert(BinaryTreeNode root) { BinaryTreeNode lastNode = null; lastNode = baseconvert(root, lastNode); BinaryTreeNode headNode = lastNode; while(headNode.leftNode != null) headNode = headNode.leftNode; return headNode; }
测试代码:
public static void main(String[] args) { BinaryTreeNode root = new BinaryTreeNode(10); BinaryTreeNode six=new BinaryTreeNode(6); BinaryTreeNode four=new BinaryTreeNode(4); BinaryTreeNode eight=new BinaryTreeNode(8); BinaryTreeNode fourteen=new BinaryTreeNode(14); BinaryTreeNode twelve=new BinaryTreeNode(12); BinaryTreeNode sixteen=new BinaryTreeNode(16); root.leftNode=six; root.rightNode=fourteen; six.leftNode=four; six.rightNode=eight; four.leftNode=null; four.rightNode=null; eight.leftNode=null; eight.rightNode=null; fourteen.leftNode=twelve; fourteen.rightNode=sixteen; twelve.leftNode=null; twelve.rightNode=null; sixteen.rightNode=null; sixteen.leftNode=null; BinaryTreeNode result=convert(root); while (result!=null) { System.out.println(result.data); result=result.rightNode; } }
相关文章推荐
- 剑指offer刷题之c实现的二叉搜索树转换成双向链表
- 剑指offer 面试题27 二叉搜索树转换为排序双向链表
- 【面试题】剑指Offer-27-将二叉搜索树转换成排序的双向链表
- 剑指offer解题报告(Java版)——二叉搜索树转换为双向链表 27
- 剑指offer解题报告(Java版)——二叉搜索树转换为双向链表 27
- 剑指offer第二十六题【二叉搜索树与双向链表】c++实现
- 【剑指offer:】Q27:二叉搜索树与双向链表
- 剑指offer 二叉搜索树与双向链表
- 剑指offer--二叉搜索树与双向链表
- 【剑指Offer】二叉搜索树与双向链表
- 剑指offer题解 二叉搜索树与双向链表
- 剑指Offer——二叉搜索树与双向链表
- 9.剑指offer-二叉搜索树与双向链表
- 码农小汪-剑指Offer之24 -二叉搜索树与双向链表
- 剑指offer--二叉搜索树与双向链表
- 【剑指offer】面试题36:二叉搜索树与双向链表
- 【剑指offer】二叉搜索树转双向链表
- 剑指offer 27二叉搜索树与双向链表
- 【剑指offer】二叉搜索树与双向链表
- 【剑指offer】二叉搜索树与双向链表