Algorithm/src/main/java/tree/Flatten.java
2025-09-10 20:57:03 +08:00

99 lines
3.6 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package tree;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
/**
* 题目: 114. 二叉树展开为链表 (rightSideView)
* 描述:给你二叉树的根结点 root ,请你将它展开为一个单链表:
* 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
* 展开后的单链表应该与二叉树 先序遍历 顺序相同。
进阶你可以使用原地算法O(1) 额外空间)展开这棵树吗?
* 链接https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/
*/
//二刷会普通方法 不会空间O1的
//todo:学习一下思路。
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)空间复杂度 递归前序
public void flatten2(TreeNode root) {
List<TreeNode>list=new ArrayList<>();
TreeNode tp= new TreeNode(0);
inOrderTraversal(root,list);
for (TreeNode treeNode : list) {
tp.left=null;
tp.right=treeNode;
tp=tp.right;
}
}
//迭代前序遍历也要会!
public void flatten1(TreeNode root) {
if (root == null) {
return;
}
Deque<TreeNode> stack = new LinkedList<>();
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;
}
}
//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; // 当前遍历到的节点
while (curr != null) {
if (curr.left != null) { // 只处理存在左子树的节点
TreeNode next = curr.left; // 1) 记录左子树的根,稍后会挪到右边
// 2) 找到左子树中的最右节点(先序遍历的“前驱”)
TreeNode predecessor = next;
while (predecessor.right != null) {
predecessor = predecessor.right;
}
// 3) 把当前节点原本的右子树接到 predecessor 的右侧
predecessor.right = curr.right;
// 4) 把左子树搬到右边,左指针清空
curr.left = null;
curr.right = next;
}
// 5) 向“链表”右侧移动,继续处理下一个节点
curr = curr.right;
}
}
}