目录
- [1. 恢复二叉搜索树](#1. 恢复二叉搜索树)
- [2. 有序链表转换二叉搜索树](#2. 有序链表转换二叉搜索树)
1. 恢复二叉搜索树
给你二叉搜索树的根节点 root ,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树 。
示例 1:

输入:root = 1,3,null,null,2
输出:3,1,null,null,2
解释:3 不能是 1 的左孩子,因为 3 > 1 。交换 1 和 3 使二叉搜索树有效。
示例2:

输入:root = 3,1,4,null,null,2
输出:2,1,4,null,null,3
解释:2 不能在 3 的右子树中,因为 2 < 3 。交换 2 和 3 使二叉搜索树有效。
思路
中序遍历后能得到二叉搜索树的升序排列,如果有两个位置交换了顺序,分两种情况,一是相邻位置交换顺序,则中序遍历后只有一个位置是x>y(x在y前面),二是不相邻位置交换顺序,则中序遍历后会有两个位置是x>y。这里通过一个语句即可满足这两种情况
java
# pred表示中序遍历中的前驱节点,root表示当前节点
# 第一次遇到时,x,y都会被赋值
# 第二次遇到时,y会被赋值,x不会
# 以1 2 3 4 5 6 7 为例
# 交换相邻元素 1 2 4 3 5 6 7
# 遍历完后 x=4 y=3
# 交换不相邻元素 1 6 3 4 5 2 7
# 遍历完后 x=6 y=2
if(pred != null && root.val<pred.val){
y=root;
if(x==null){
x = pred;
}
}
因此最简单的做法是中序遍历,得到整个升序数组,再找出非升序的位置即可。但空间复杂度会是 O ( n ) O(n) O(n),这里 n n n是树的节点数,为了减小空间复杂度,可以采用Morris遍历,将空间复杂度降为 O ( 1 ) O(1) O(1)
java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public void recoverTree(TreeNode root) {
TreeNode x = null,y = null,pred = null, predecessor = null;
while(root!=null){
if(root.left!=null){
//第一次先建立当前节点与左子树的前驱联系
//并遍历左子树
//第二次进来时遍历当前节点,并且消除前驱联系
//建立前驱节点
predecessor = root.left;
while(predecessor.right!=null&&predecessor.right!=root){
predecessor = predecessor.right;
}
if(predecessor.right==null){//建立前驱节点连接
predecessor.right = root;
root = root.left;
}else{
//遍历当前节点
if(pred!=null&&root.val<pred.val){
y = root;
if(x==null){
x = pred;
}
}
pred = root;
predecessor.right = null;
root = root.right;
}
}else{
//遍历当前节点
if(pred!=null&&root.val<pred.val){
y = root;
if(x==null){
x = pred;
}
}
pred = root;
//所有节点均有右子节点,因为前面已经建立了前驱联系
root = root.right;
}
}
swap(x,y);
}
public void swap(TreeNode x, TreeNode y){
int temp = x.val;
x.val = y.val;
y.val = temp;
}
}
时间复杂度: O ( n ) O(n) O(n) n是节点个数
空间复杂度: O ( 1 ) O(1) O(1)
2. 有序链表转换二叉搜索树
给定一个单链表的头节点 head ,其中的元素 按升序排序 ,将其转换为 平衡 二叉搜索树。
示例 1:

输入: head = -10,-3,0,5,9
输出: 0,-3,9,-10,null,5
解释: 一个可能的答案是0,-3,9,-10,null,5,它表示所示的高度平衡的二叉搜索树。
示例2:
输入: head = \[\]
输出: \[\]
思路
平衡二叉搜索树左右节点数相差不大,可以先找到中间节点,然后把中间点的左边作为左子树,右边作为右子树。这里可以拟合中序遍历的思想,中序遍历的结果就是链表的结果。先遍历左子树,遍历完左子树,再访问当前节点,再遍历右子树
java
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
ListNode globalHead;
public TreeNode sortedListToBST(ListNode head) {
int len = getLength(head);
globalHead = head;
return buildTree(0,len-1);
}
public int getLength(ListNode head){
int res = 0;
while(head!=null){
res++;
head = head.next;
}
return res;
}
public TreeNode buildTree(int left,int right){
if(left>right){
return null;
}
//先占个位置,模拟中序遍历
TreeNode root = new TreeNode();
int mid = (left+right+1)/2;
root.left = buildTree(left,mid-1);
//左子树都遍历完了,再给当前节点赋值,此时globalHead就处在当前节点位置
root.val = globalHead.val;
globalHead = globalHead.next;
root.right = buildTree(mid+1,right);
return root;
}
}
时间复杂度: O ( n ) O(n) O(n) n是节点个数
空间复杂度: O ( 1 ) O(1) O(1)