99. 恢复二叉搜索树-H

99. 恢复二叉搜索树-H

label: LNR、Morris遍历算法

二叉搜索树中的两个节点被错误地交换。

请在不改变其结构的情况下,恢复这棵树。

示例 1:

输入: [1,3,null,null,2]

   1
  /
 3
  \
   2

输出: [3,1,null,null,2]

   3
  /
 1
  \
   2

示例 2:

输入: [3,1,4,null,null,2]

  3
 / \
1   4
   /
  2

输出: [2,1,4,null,null,3]

  2
 / \
1   4
   /
  3

进阶:

使用 O(n) 空间复杂度的解法很容易实现。
你能想出一个只使用常数空间的解决方案吗?

分析:
直接LNR遍历确定错误的两个节点,交换即可,但是这样空间复杂度是O(logN),是不符合题意的

不用额外空间遍历二叉树的Morris算法: 后来,看讨论区,原来还有不实用栈空间的算法–Morris算法,它是利用叶子节点的NULL指针完成的一个关联。

参考这个人的博客

LNR: 先看左孩子,如果存在,则访问左孩子的右路径到底,将底部叶子右连接指向当前节点,这样的效果是,当我们在遍历的时候,访问完叶子,就可以通过右连接访问到后继,通过这个连接访问到后继后,我们需要重置该连接,恢复原结构。
如何判断是修改的右连接还是这个树原本的?只需再次走一遍它左孩子的右路径,能够再次访问到自己,就是修改后的连接。

package main

import "fmt"

//99. Recover Binary Search Tree

type TreeNode struct {
 Val int
 Left *TreeNode
 Right *TreeNode
}
var ret []*TreeNode
var pre_g *TreeNode
func LNR(root*TreeNode){
	if root==nil{
		return
	}
	LNR(root.Left)
	if pre_g==nil{ pre_g=root }else{
		if pre_g.Val>root.Val{
			ret=append(ret,pre_g)
			ret=append(ret,root)
		}
		pre_g=root
	}
	LNR(root.Right)
}
/*
执行用时 :12 ms, 在所有 Go 提交中击败了100.00%的用户
内存消耗 :6.8 MB, 在所有 Go 提交中击败了68.42%的用户
*/
func recoverTree_LNR(root *TreeNode)  {
	ret=nil;pre_g=nil
	//LNR(root)
	LNR_mirrors(root)
	if len(ret)==2{
		ret[0].Val,ret[1].Val=ret[1].Val,ret[0].Val
	}else if len(ret)==4{
		ret[0].Val,ret[3].Val=ret[3].Val,ret[0].Val
	}
}
/*
执行用时 :24 ms, 在所有 Go 提交中击败了78.38%的用户
内存消耗 :6.5 MB, 在所有 Go 提交中击败了100.00%的用户
*/
func LNR_mirrors(root*TreeNode){
	tmp:=root
	for tmp!=nil{
		if tmp.Left!=nil{
			pre:=tmp.Left
			for pre.Right!=nil&&pre.Right!=tmp{
				pre=pre.Right
			}
			if pre.Right==tmp{
				fmt.Print(tmp.Val," ")
				if pre_g!=nil&&pre_g.Val>tmp.Val{
					ret=append(ret,pre_g)
					ret=append(ret,tmp)
				}
				pre.Right=nil
				pre_g=tmp
				tmp=tmp.Right
			}else{
				pre.Right=tmp
				tmp=tmp.Left
			}
		}else{
			fmt.Print(tmp.Val," ")
			if pre_g!=nil&&pre_g.Val>tmp.Val{
				ret=append(ret,pre_g)
				ret=append(ret,tmp)
			}
			pre_g=tmp
			tmp=tmp.Right
		}
	}
}
/*
执行用时 :24 ms, 在所有 Go 提交中击败了78.38%的用户
内存消耗 :6.8 MB, 在所有 Go 提交中击败了68.42%的用户
*/
//morris算法,时间复杂度O(N),空间复杂度O(1)
func recoverTree(root *TreeNode)  {
	var ret []*TreeNode
	var pre_g *TreeNode
	tmp:=root
	for tmp!=nil{
		if tmp.Left!=nil{
			pre:=tmp.Left
			for pre.Right!=nil&&pre.Right!=tmp{
				pre=pre.Right
			}
			if pre.Right==tmp{
				fmt.Print(tmp.Val," ")
				if pre_g!=nil&&pre_g.Val>tmp.Val{
					ret=append(ret,pre_g)
					ret=append(ret,tmp)
				}
				pre.Right=nil
				pre_g=tmp
				tmp=tmp.Right
			}else{
				pre.Right=tmp
				tmp=tmp.Left
			}
		}else{
			fmt.Print(tmp.Val," ")
			if pre_g!=nil&&pre_g.Val>tmp.Val{
				ret=append(ret,pre_g)
				ret=append(ret,tmp)
			}
			pre_g=tmp
			tmp=tmp.Right
		}
	}
	if len(ret)==2{
		ret[0].Val,ret[1].Val=ret[1].Val,ret[0].Val
	}else if len(ret)==4{
		ret[0].Val,ret[3].Val=ret[3].Val,ret[0].Val
	}
}
func main() {
	h1:=TreeNode{1,nil,nil}
	h2:=TreeNode{2,nil,nil}
	h3:=TreeNode{3,nil,nil}
	//h4:=TreeNode{4,nil,nil}
	//h3.Left=&h1;h3.Right=&h4;h4.Left=&h2;root:=&h3
	h1.Left=&h3;h3.Right=&h2;root:=&h1
	//h2.Right=&h1;root:=&h2
	//h3.Right=&h1;h1.Left=&h2;root:=&h3
	recoverTree(root)
	fmt.Println()
	show(root)
}
func show(root *TreeNode){
	if root==nil{
		return
	}
	show(root.Left)
	fmt.Print(root.Val," ")
	show(root.Right)
}

你可能感兴趣的:(99. 恢复二叉搜索树-H)