二叉树
后序遍历:状态转移与结果保存类型,需要集齐两个子节点信息来决定父节点信息。
前序遍历:前置拆分与处理
中序遍历:有序二叉树的处理
重建二叉树:前序遍历
对于任意一颗树而言,前序遍历的形式总是
[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]
即根节点总是前序遍历中的第一个节点。而中序遍历的形式总是
[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]
只要我们在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目。由于同一颗子树的前序遍历和中序遍历的长度显然是相同的,因此我们就可以对应到前序遍历的结果中,对上述形式中的所有左右括号进行定位。
考虑使用哈希表来帮助我们快速地定位根节点。对于哈希映射中的每个键值对,键表示一个元素(节点的值),值表示其在中序遍历中的出现位置。在构造二叉树的过程之前,我们可以对中序遍历的列表进行一遍扫描,就可以构造出这个哈希映射。在此后构造二叉树的过程中,我们就只需要 O(1)O(1) 的时间对根节点进行定位了。
https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/solution/mian-shi-ti-07-zhong-jian-er-cha-shu-by-leetcode-s/
也是三种遍历的方法,这里用的是前序遍历
因为每一次都是先确认根节点,然后再去确认左右孩子。
class Solution {
private Map<Integer, Integer> indexMap;
public TreeNode myBuildTree(int[] preorder, int[] inorder,
int preorder_left, int preorder_right,
int inorder_left, int inorder_right) {
if (preorder_left > preorder_right) {
return null;
}
// 前序遍历中的第一个节点就是根节点
int preorder_root = preorder_left;
// 在中序遍历中定位根节点
int inorder_root = indexMap.get(preorder[preorder_root]);
// 先把根节点建立出来
TreeNode root = new TreeNode(preorder[preorder_root]);
// 得到左子树中的节点数目
int size_left_subtree = inorder_root - inorder_left;
// 递归地构造左子树,并连接到根节点
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
// 通过root.left的方式直接连接即可
root.left = myBuildTree(preorder, inorder, preorder_left + 1,
preorder_left + size_left_subtree, inorder_left,
inorder_root - 1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root.right = myBuildTree(preorder, inorder,
preorder_left + size_left_subtree + 1, preorder_right,
inorder_root + 1, inorder_right);
return root;
}
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n = preorder.length;
// 构造哈希映射,帮助我们快速定位根节点
indexMap = new HashMap<Integer, Integer>();
for (int i = 0; i < n; i++) {
indexMap.put(inorder[i], i);
}
return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
}
}
class Solution {
TreeNode res = new TreeNode();
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder.length == 0 || inorder.length == 0) {
return null;
}
List<Integer> preorderList = Arrays.stream(preorder).boxed().collect(Collectors.toList());
List<Integer> inorderList = Arrays.stream(inorder).boxed().collect(Collectors.toList());
dfs(preorderList, inorderList, res);
return res;
}
private void dfs(List<Integer> preorder, List<Integer> inorder, TreeNode cur) {
if(preorder == null || preorder.size() == 0 || inorder.size() == 0) {
return;
}
if(preorder.size() == 1) {
cur.val = preorder.get(0);
return;
}
cur.val = preorder.get(0);
// inorder
List<Integer> leftIn = new ArrayList<>();
List<Integer> rightIn = new ArrayList<>();
getInOrderList(inorder, leftIn, rightIn, cur.val);
// preorder
List<Integer> leftPre = new ArrayList<>();
List<Integer> rightPre = new ArrayList<>();
getPreOrderList(preorder, leftPre, rightPre, leftIn.size());
if(leftIn.size() != 0) {
cur.left = new TreeNode();
dfs(leftPre, leftIn, cur.left);
} else {
cur.left = null;
}
if(rightIn.size() != 0) {
cur.right = new TreeNode();
dfs(rightPre, rightIn, cur.right);
} else {
cur.right = null;
}
}
private void getInOrderList(List<Integer> inorder,
List<Integer> leftIn, List<Integer> rightIn, int val) {
boolean left = true;
for(Integer tmp : inorder) {
if(tmp == val) {
left = false;
continue;
}
if(tmp != val && left) {
leftIn.add(tmp);
} else {
rightIn.add(tmp);
}
}
}
private void getPreOrderList(List<Integer> preorder,
List<Integer> leftPre, List<Integer> rightPre, int size) {
for(int i = 1; i < preorder.size(); i++) {
if(i - 1 < size) {
leftPre.add(preorder.get(i));
} else {
rightPre.add(preorder.get(i));
}
}
}
}
中序遍历题目
两个节点被错误地交换后对原二叉搜索树造成了什么影响。对于二叉搜索树,我们知道如果对其进行中序遍历,得到的值序列是递增有序的,而如果我们错误地交换了两个节点,等价于在这个值序列中交换了两个值,破坏了值序列的递增性。
如果在一个递增的序列中交换两个值会造成什么影响。假设有一个递增序列 a=[1,2,3,4,5,6,7]a=[1,2,3,4,5,6,7]。如果我们交换两个不相邻的数字,例如 2 和 6,原序列变成了 a=[1,6,3,4,5,2,7],那么显然序列中有两个位置不满足 ai<ai+1,因此只要我们找到这两个位置,即可找到被错误交换的两个节点。
如果我们交换两个相邻的数字,例如 2 和 3,此时交换后的序列只有一个位置不满足 ai<ai+1。因此整个值序列中不满足条件的位置或者有两个,或者有一个。
思路:
中序遍历拿到所有元素
检查出现的乱序位置以及个数
DFS再次遍历树,交换两个元素值即可
class Solution {
public void recoverTree(TreeNode root) {
List<Integer> nums = new ArrayList<Integer>();
inorder(root, nums);
int[] swapped = findTwoSwapped(nums);
recover(root, 2, swapped[0], swapped[1]);
}
public void inorder(TreeNode root, List<Integer> nums) {
if (root == null) {
return;
}
inorder(root.left, nums);
nums.add(root.val);
inorder(root.right, nums);
}
public int[] findTwoSwapped(List<Integer> nums) {
int n = nums.size();
int x = -1, y = -1;
for (int i = 0; i < n - 1; ++i) {
if (nums.get(i + 1) < nums.get(i)) {
y = nums.get(i + 1);
if (x == -1) {
x = nums.get(i);
} else {
break;
}
}
}
return new int[]{x, y};
}
public void recover(TreeNode root, int count, int x, int y) {
if (root != null) {
if (root.val == x || root.val == y) {
root.val = root.val == x ? y : x;
if (--count == 0) {
return;
}
}
recover(root.right, count, x, y);
recover(root.left, count, x, y);
}
}
}