描述:将一个二叉搜索树转为一个双向链表,要求不能创建新节点,只能调整树中节点指针的指向;
解题思路分析
:
-
递归:
- 递归的整体把握更好,将整个二叉树抽象为一个三节点的二叉树
- 我们只需要关心两点:
第一点
:整个链表的第一个节点在哪里 ?- 找到第一点好解决,递归调用当前方法,输入参数为 root.left ,直达最左侧的叶子节点
第二点
:在任何局部如何互连 ?:左-根-右- 如何互连?关键点在于一个全局指针 last
- 我们每次往链表上连上一个点,就把last指针指向当前链表的最后一点
- 这样的好处就是,不管当前互连进行到那个局部,我们总能知道我们链表的最后一个节点
- 这样我们就非常方便的往链表后面添加互连新的节点
//伪代码 // 1、找到左孩子,连接 root TreeNode left = convert(roo.left); last=left; // last首先指向左孩子 last.right = root ; // 左孩子指向根 root.left = last ; // 根指向左孩子 完成互连 // 2、last 指向 root ,因为此时root已经是链表的最后 last = root ; // 3、 同第一步一样,连接右孩子 TreeNode right = convert(roo.right); root.right = right ; // root 指向 右孩子 right.left = last ; // 右孩子 指向 左孩子 last = right ; // last 指向 右孩子 (当前链表的最后一个节点)
-
非递归:
-
非递归的方法实际就是
中序遍历
的过程-
沿着左侧分支一直下行,将沿途的节点压入栈中
-
依次弹出栈顶的元素,进行操作:
-
若为最左侧的点,即第一个节点,用
root
指针保存 , 同时也用pre
指针指向它 -
从第二个节点开始:弹栈 - 互连 - 遍历右孩子(如果存在)
-
cur
:表示当前节点 ,pre
:链表中的最后一个节点 -
//弹栈 Node cur = stack.pop(); //互连 pre.right = cur ; cur.left = pre ; pre = cur ; //同递归一样,每次互连后,pre 都向后移动 //遍历右孩子 cur = cur.right ;
围绕以上这几个步骤,反复的循环!直到栈空
-
-
-
-
1、非递归法
//节点类
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
import java.util.*;
public class Solution {
public TreeNode Convert(TreeNode pRootOfTree) {
//非递归法
//退化情况,节点为空
if(pRootOfTree == null ) return null;
//创建一个辅助栈
Stack<TreeNode> stack= new Stack<TreeNode>();
//将引用cur指向root,pre指向遍历节点的的前一个节点,开始为null
TreeNode cur = pRootOfTree ;
TreeNode pre = null ;
Boolean flag = true ; //用于控制流程 ,找到第一个中序遍历节点
while(cur != null || !stack.isEmpty()){
while( cur != null ){
//一直下行,直到找到最左侧节点,每遍历一个就入栈,最后入栈的即为中序遍历第一点
stack.push(cur);
cur = cur.left ;
}
//每次弹出一个节点
cur = stack.pop();
if(flag){
pRootOfTree = cur; //将第一个节点节点用 pRootOfTree 保存:用于最后返回
pre = pRootOfTree; //同时将pre也指向当前第一节点:使用pre进行后移
flag = false ; //找到后修改标记
}else{
pre.right = cur ; // 将当前的 pre 的后继指向 cur
cur.left = pre ; // 将cur 的前驱指向 pre
pre = cur ; //将 pre指针指向 下一个连接上的节点
}
// 栈中保存的广义上来说
// 都是每一层次的左孩子同时也是 root 节点,所以在遍历往后
/*
4 栈中保存的是:(底)【4,2,1
2 5 所以先弹出 左孩子1,然后1没有右孩子
1 3 6 下一个弹出2(root),即 1(left) 的 父亲 ,然后再访问root 的3(Right)
以上就是一个局部的中序遍历:1 -> 2 -> 3
*/
// 所以应该访问其右孩子,才满足: 左->根->右
cur = cur.right ;
}
return pRootOfTree ;
}
}
2、递归法
public class Solution {
//定义一个全局指针,指向当前已经形成的双向两边的最后一个节点
TreeNode last = null;
public TreeNode Convert(TreeNode root) {
if(root == null ) return null;
//当前节点的左右孩子都为空,返回当前节点
if(root.left == null && root.right == null ){
last=root;
return root;
}
//1.将当前节点的左子树构成双向链表
TreeNode left = Convert(root.left);
if(left!=null){
last.right=root;//将last的后继指向root
root.left=last; //将root的前驱指向last
}
last=root; // 将last指向root,即当前已连接链表的最后一个
//2.将当前节点的右子树构成双向链表
TreeNode right = Convert(root.right);
//注意要,调用了convert方法之后,只要right不为空,全局last指针会改变,指向right
//所以在下面的连接中,要用root与right相连
//即 left-> root(last在这里)
//再 left-> root->right(last在这里)
if(right!=null){
root.right=right;//将last的后继指向right
right.left=root; //将right的前驱指向last
}
return left!=null?left:root; //最终返回:有左链,返回左链,否则返回root
}
}