2025-03-24 18:58:37 +08:00
|
|
|
|
package tree;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
import java.util.Deque;
|
|
|
|
|
|
import java.util.LinkedList;
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 题目: 114. 二叉树展开为链表 (rightSideView)
|
|
|
|
|
|
* 描述:给你二叉树的根结点 root ,请你将它展开为一个单链表:
|
|
|
|
|
|
* 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
|
|
|
|
|
|
* 展开后的单链表应该与二叉树 先序遍历 顺序相同。
|
2025-07-03 15:53:15 +08:00
|
|
|
|
进阶:你可以使用原地算法(O(1) 额外空间)展开这棵树吗?
|
2025-03-24 18:58:37 +08:00
|
|
|
|
* 链接:https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
2025-07-03 15:53:15 +08:00
|
|
|
|
//二刷会普通方法 不会空间O1的
|
|
|
|
|
|
//todo:学习一下思路。
|
2025-03-24 18:58:37 +08:00
|
|
|
|
public class Flatten {
|
|
|
|
|
|
public void inOrderTraversal(TreeNode root,List<TreeNode>list){
|
|
|
|
|
|
if(root!=null) {
|
|
|
|
|
|
list.add(root);
|
|
|
|
|
|
inOrderTraversal(root.left, list);
|
|
|
|
|
|
inOrderTraversal(root.right, list);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//O(N)空间复杂度 递归前序
|
2025-07-03 15:53:15 +08:00
|
|
|
|
public void flatten2(TreeNode root) {
|
2025-03-24 18:58:37 +08:00
|
|
|
|
List<TreeNode>list=new ArrayList<>();
|
2025-07-03 15:53:15 +08:00
|
|
|
|
TreeNode tp= new TreeNode(0);
|
2025-03-24 18:58:37 +08:00
|
|
|
|
inOrderTraversal(root,list);
|
|
|
|
|
|
for (TreeNode treeNode : list) {
|
|
|
|
|
|
tp.left=null;
|
|
|
|
|
|
tp.right=treeNode;
|
|
|
|
|
|
tp=tp.right;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-03 15:53:15 +08:00
|
|
|
|
//迭代前序遍历也要会!
|
2025-03-24 18:58:37 +08:00
|
|
|
|
public void flatten1(TreeNode root) {
|
|
|
|
|
|
if (root == null) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-10 20:57:03 +08:00
|
|
|
|
Deque<TreeNode> stack = new LinkedList<>();
|
2025-03-24 18:58:37 +08:00
|
|
|
|
stack.push(root);
|
|
|
|
|
|
TreeNode prev = null;
|
|
|
|
|
|
while (!stack.isEmpty()) {
|
|
|
|
|
|
TreeNode curr = stack.pop();
|
|
|
|
|
|
if (prev != null) {
|
|
|
|
|
|
prev.left = null;
|
|
|
|
|
|
prev.right = curr;
|
|
|
|
|
|
}
|
|
|
|
|
|
TreeNode left = curr.left, right = curr.right;
|
|
|
|
|
|
if (right != null) {
|
|
|
|
|
|
stack.push(right);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (left != null) {
|
|
|
|
|
|
stack.push(left);
|
|
|
|
|
|
}
|
|
|
|
|
|
prev = curr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-03 15:53:15 +08:00
|
|
|
|
//O(1)空间 很妙
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 原地展开二叉树为单链表(先序顺序)
|
|
|
|
|
|
* 思路:Morris 遍历的变形 —— 每到一个有左子树的节点,就把
|
|
|
|
|
|
* ① 左子树插到右边
|
|
|
|
|
|
* ② 左子树最右节点 (predecessor) 的 right 指针接到原来的右子树
|
|
|
|
|
|
* ③ 左指针置空
|
|
|
|
|
|
* 然后继续沿着 curr = curr.right 前进
|
|
|
|
|
|
* 时间复杂度:O(n) — 每条边最多访问两次
|
|
|
|
|
|
* 空间复杂度:O(1) — 仅用常数指针
|
|
|
|
|
|
* https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/solutions/356853/er-cha-shu-zhan-kai-wei-lian-biao-by-leetcode-solu/?envType=study-plan-v2&envId=top-100-liked
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void flatten(TreeNode root) {
|
|
|
|
|
|
TreeNode curr = root; // 当前遍历到的节点
|
2025-03-24 18:58:37 +08:00
|
|
|
|
while (curr != null) {
|
2025-07-03 15:53:15 +08:00
|
|
|
|
if (curr.left != null) { // 只处理存在左子树的节点
|
|
|
|
|
|
TreeNode next = curr.left; // 1) 记录左子树的根,稍后会挪到右边
|
|
|
|
|
|
|
|
|
|
|
|
// 2) 找到左子树中的最右节点(先序遍历的“前驱”)
|
2025-03-24 18:58:37 +08:00
|
|
|
|
TreeNode predecessor = next;
|
|
|
|
|
|
while (predecessor.right != null) {
|
|
|
|
|
|
predecessor = predecessor.right;
|
|
|
|
|
|
}
|
2025-07-03 15:53:15 +08:00
|
|
|
|
|
|
|
|
|
|
// 3) 把当前节点原本的右子树接到 predecessor 的右侧
|
2025-03-24 18:58:37 +08:00
|
|
|
|
predecessor.right = curr.right;
|
2025-07-03 15:53:15 +08:00
|
|
|
|
|
|
|
|
|
|
// 4) 把左子树搬到右边,左指针清空
|
2025-03-24 18:58:37 +08:00
|
|
|
|
curr.left = null;
|
|
|
|
|
|
curr.right = next;
|
|
|
|
|
|
}
|
2025-07-03 15:53:15 +08:00
|
|
|
|
// 5) 向“链表”右侧移动,继续处理下一个节点
|
2025-03-24 18:58:37 +08:00
|
|
|
|
curr = curr.right;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-03 15:53:15 +08:00
|
|
|
|
|
2025-03-24 18:58:37 +08:00
|
|
|
|
}
|