二叉树--恢复二叉搜索树
Posted 算法和数据结构
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉树--恢复二叉搜索树相关的知识,希望对你有一定的参考价值。
来源:LeetCode
难度:中等
描述:
给你二叉搜索树的根节点 root ,该树中的两个节点被错误地交换。请在不改变其结构的情况下,恢复这棵树。
示例1:
示例2:
分析:
题意就是原本有一棵好的二叉搜索树,后来其中的两个节点发生了交换,让咱们找到这两个节点并交换回来。
首先咱们要了解啥是二叉搜索树,所谓的二叉搜索树通俗点讲就是根节点比左节点大,比右节点小的二叉树,因此如果二叉树中节点发生了交换,二叉搜索树整体就不再满足如上特性,而咱们也可以根据这个特性找到交换的节点,将它们交换回来即可
这里介绍递归和中序遍历两种解法,以下是具体思路和代码解法。
解题
方法一:递归法
思路:我们捋清楚原来的二叉树交换的可能情况有如下几种
根节点和左子树的某个数字交换 -> 由于正常二叉搜索的根节点大于左子树中的所有节点,所以交换后我们只要找左子树中最大的那个节点,就是所交换的那个节点
根节点和右子树的某个数字交换 -> 由于根节点小于右子树中的所有节点,所以交换后我们只要在右子树中最小的那个节点,就是所交换的那个节点
左子树和右子树的两个数字交换 -> 找左子树中最大的节点,右子树中最小的节点,即对应两个交换的数
左子树中的两个数字交换
右子树中的两个数字交换
因此我们针对每个节点,找到其左子树最大节点和右子树的最小节点,分为以下情况:
如果左子树最大节点大于根节点,右子树最小节点小于根节点,说明对应情况3
如果左子树最大节点大于根节点,右子树最小节点也大于根节点,对应情况1(右子树大于根节点天经地义)
如果左子树最大节点小于根节点,右子树最小节点小于根节点,对应情况2
情况4、5分别对根节点的左右子节点进行如上递归即可
代码:
1public void recoverTree(TreeNode root) {
2 if (root == null) {
3 return;
4 }
5 //寻找左子树中最大的节点
6 TreeNode maxLeft = getMaxOfBST(root.left);
7 //寻找右子树中最小的节点
8 TreeNode minRight = getMinOfBST(root.right);
9
10 if (minRight != null && maxLeft != null) {
11 //左边的大于根节点,右边的小于根节点,对应情况 3,左右子树中的两个数字交换
12 if ( maxLeft.val > root.val && minRight.val < root.val) {
13 int temp = minRight.val;
14 minRight.val = maxLeft.val;
15 maxLeft.val = temp;
16 }
17 }
18
19 if (maxLeft != null) {
20 //左边最大的大于根节点,对应情况 1,根节点和左子树的某个数做了交换
21 if (maxLeft.val > root.val) {
22 int temp = maxLeft.val;
23 maxLeft.val = root.val;
24 root.val = temp;
25 }
26 }
27
28 if (minRight != null) {
29 //右边最小的小于根节点,对应情况 2,根节点和右子树的某个数做了交换
30 if (minRight.val < root.val) {
31 int temp = minRight.val;
32 minRight.val = root.val;
33 root.val = temp;
34 }
35 }
36 //对应情况 4,左子树中的两个数进行了交换
37 recoverTree(root.left);
38 //对应情况 5,右子树中的两个数进行了交换
39 recoverTree(root.right);
40
41}
42//寻找树中最小的节点
43private TreeNode getMinOfBST(TreeNode root) {
44 if (root == null) {
45 return null;
46 }
47 TreeNode minLeft = getMinOfBST(root.left);
48 TreeNode minRight = getMinOfBST(root.right);
49 TreeNode min = root;
50 if (minLeft != null && min.val > minLeft.val) {
51 min = minLeft;
52 }
53 if (minRight != null && min.val > minRight.val) {
54 min = minRight;
55 }
56 return min;
57}
58
59//寻找树中最大的节点
60private TreeNode getMaxOfBST(TreeNode root) {
61 if (root == null) {
62 return null;
63 }
64 TreeNode maxLeft = getMaxOfBST(root.left);
65 TreeNode maxRight = getMaxOfBST(root.right);
66 TreeNode max = root;
67 if (maxLeft != null && max.val < maxLeft.val) {
68 max = maxLeft;
69 }
70 if (maxRight != null && max.val < maxRight.val) {
71 max = maxRight;
72 }
73 return max;
74}
方法二:中序遍历法
思路:根据二叉搜索树的特性,根节点大于左子树最大值,小于右子树最小值,所以正常的二叉搜索树的中序遍历一定是从小到大有序的,假设二叉搜索树其中的某两个节点发生了交换,那么其中序遍历结果一定不能全局有序,所以咱们只要找出中序遍历结果中乱序的那两个节点进行交换即可
举个例子:假设交换后的二叉搜索树如下:
那么其中序遍历结果则为【1、2、3、7、5、6、4】,如下
交换其中乱序的7和4即可,代码如下
代码:
1public static void recoverTree(TreeNode root) {
2 if (root == null) {
3 return;
4 }
5 List<TreeNode> treeNodeList = new ArrayList<>();
6 //中序遍历,将结果存储到List数组中
7 midOrderTraverse(root, treeNodeList);
8 if (treeNodeList.size() == 1) {
9 return;
10 }
11 TreeNode pre = null;
12 TreeNode last = null;
13 for (int i = 0; i < treeNodeList.size() - 1; ++i) {
14 //找到乱序节点 当前值比下一个值还大表明是乱序节点
15 if (treeNodeList.get(i).val > treeNodeList.get(i + 1).val) {
16 last = treeNodeList.get(i + 1);
17 if (pre == null) {
18 pre = treeNodeList.get(i);
19 }
20 }
21 }
22 //pre和last进行交换节点值,恢复二叉搜索树
23 if (pre != null && last != null) {
24 int tmp = pre.val;
25 pre.val = last.val;
26 last.val = tmp;
27 }
28}
29//中序遍历,将结果存储到List数组中
30private static void midOrderTraverse(TreeNode root, List<TreeNode> treeNodeList) {
31 if (root == null) {
32 return;
33 }
34 midOrderTraverse(root.left, treeNodeList);
35 treeNodeList.add(root);
36 midOrderTraverse(root.right, treeNodeList);
37}
以上仅是个人思路解法,觉得还不错欢迎点赞关注分享
往期精彩推荐
以上是关于二叉树--恢复二叉搜索树的主要内容,如果未能解决你的问题,请参考以下文章
数据结构~基础2~树《二叉树二叉搜索树AVL树B树红黑树》的设计~高度平衡二叉树AVL树
代码随想录Day20-Leetcode654.最大二叉树,617.合并二叉树,700.二叉搜索树中的搜索,98.验证二叉搜索树