Morris遍历——二叉树遍历时间复杂度O(N)&额外空间复杂度O(1)

package Morris

import (
	"fmt"
	"math"
	"testing"
)




/*
morris 遍历,遍历完一棵二叉树,可以实现二叉树的先序中序后序,可以做到时间复杂度O(N),额外空间复杂度O(1)
笔试以最快Ac为目标,面试时候可以吹牛逼

kmp 的 morris 和 morris遍历中的morris 是一个人

二叉树的遍历,递归非递归都需要额外空间
时间复杂度 O(N) ,  额外空间复杂度 O(h) 和高度相关


流程
当前节点cur,一开始 cur来到整棵树的头
1.如果cur 无左树,cur直接向右移动 cur = cur.Right
2.如果cur 有左树,找到cur左树最右节点 mostRight       [ 。。。  .Right.Right....]
   2.1 如果mostRight 的右指针指向nil, mostRight.Right = cur, cur = cur.Left   改指针走向
   2.2 mostRight 的右指针 指向cur,  mostRight.Right = nil , cur = cur.Right
3. cur = nil 时候, 循环结束

                       1
                    /     \
                 2         3
               /  \       /  \
              4    5     6    7
忘掉先序,中序,后序概念
cur 依次到达每个节点的顺序 叫 morris序

------------------------------------------------------
                       1
                    /     \
                 2         3
               /  \       /  \
              4    5     6    7
------------------------------------------------------
morris序: 1

         有左树
                       1 < cur
                    /     \
                 2         3
               /  \       /  \
              4    5     6    7
          来到左树最右节点
------------------------------------------------------
morris序: 1 2

          左树最右节点的右指针为nil
                         1 < cur
                    /   /|    \
            cur> 2      |      3
               /   \    |     /  \
              4     5   |    6    7
                     \__|
          左树最右节点的右指针指向cur,cur 来到cur的 left



                         1
                    /   /|    \
            cur> 2      |      3
               /   \    |     /  \
              4     5   |    6    7
                     \__|

------------------------------------------------------

          有左树
                         1
                    /   /|    \
             cur> 2     |      3
               / /|\    |     /  \
             4   |  5   |    6    7
              \__|   \__|
         来到左树最右节点,让左树最右节点的右指针指向cur
------------------------------------------------------
morris序: 1 2 4

                         1
                    /   /|    \
                  2     |      3
               / /|\    |     /  \
        cur> 4   |  5   |    6    7
              \__|   \__|

           cur没有左树,cur向右移动,来到2,但是2已经被走过了

morris序: 1 2 4 2
                         1
                    /   /|    \
             cur> 2     |      3
               / /|\    |     /  \
             4   |  5   |    6    7
              \__|   \__|


让右指针指向nil,cur往右移动,来到5


                         1
                    /   /|    \
             cur> 2     |      3
               /   \    |     /  \
             4      5   |    6    7
              \      \__|
              nil
                         1
                    /   /|    \
           cur>   2     |      3
               /   \    |     /  \
             4      5   |    6    7
                   /|\__|
                   |
                  cur


morris序: 1 2 4 2 5

         cur 没有最右节点,来到1

                         1 <cur
                    /   /|    \
           cur>   2     |      3
               /   \    |     /  \
             4      5   |    6    7
                     \__|

morris序: 1 2 4 2 5 1

            恢复5的右节点指向nil,cur 往右移动
                       1
                    /     \
                 2          3 <cur
               /  \        /  \
              4     5     6    7

morris序: 1 2 4 2 5 1 3


                       1
                    /     \
                 2          3
               /  \       / /| \
              4     5    6  |    7
                        /|\_|
                        |
                       cur

morris序: 1 2 4 2 5 1 3 6


morris序: 1 2 4 2 5 1 3 6 3
                       1
                    /     \
                 2          3 <cur
               /  \        /  \
              4     5     6    7

                       1
                    /     \
                 2          3
               /  \        /  \
              4     5     6    7 <cur

morris序: 1  2  4  2  5  1  3 6 3 7


什么样的树都可以遍历完

任何一棵树,只要有左树,都会来到2次
没有左树的节点,只会来到1次
一个节点往右走,再也不会回来了
  */

type Node struct {
	Value int
	Left  *Node
	Right *Node
}



func morris(head *Node) {
	if head == nil {
		return
	}
	cur := head
	var mostRight *Node
	for cur != nil {           // cur 不断往左或往右跳,一旦为空就出来
		mostRight = cur.Left  //cur 有没有左树

		if mostRight != nil {
			// 找到cur 左树上真实的最右节点
			for mostRight.Right != nil && mostRight.Right != cur {  // 第一次 nil, 第二次 cur
				mostRight = mostRight.Right
			}
			// 从for中出来,mostRight 一定是cur左树上的最右节点
			if mostRight.Right == nil {
				mostRight.Right = cur
				cur = cur.Left
				continue    // continue 最外层for
			}else {         // mostRight.Right != nil => mostRight.Right == cur
				mostRight.Right = nil
			}
		}
		cur = cur.Right
	}
}

func TestMorris(t *testing.T)  {
	head := &Node{
		Value: 1,
		Left:  &Node{
			Value: 2,
			Left:  &Node{
				Value: 4,
				Left:  nil,
				Right: nil,
			},
			Right: &Node{
				Value:  5,
				Left:  nil,
				Right: nil,
			},
		},
		Right: &Node{
			Value: 3,
			Left:  &Node{
				Value: 6,
				Left:  nil,
				Right: nil,
			},
			Right: &Node{
				Value:  7,
				Left:  nil,
				Right: nil,
			},
		},
	}

	morrisPre(head)
	morrisIn(head)
	morrisPos(head)
}



// 先序    第一次来到节点的时候打印,第二次来到的时候不打印
// 中序    第二次来到节点的时候打印,能回到2次的不打印(有左树的),第二次回归时打印,只能来到1次的直接打印(没有左树的)
// 后序    比较复杂
func morrisIn(head *Node) {  //中序
	if head == nil {
		return
	}
	cur := head
	var mostRight *Node
	for cur != nil {           // cur 不断往左或往右跳,一旦为空就出来
		mostRight = cur.Left   //cur 有没有左树
		if mostRight != nil {
			// 找到cur 左树上真实的最右节点
			for mostRight.Right != nil && mostRight.Right != cur {  // 第一次 nil, 第二次 cur
				mostRight = mostRight.Right
			}
			// 从for中出来,mostRight 一定是cur左树上的最右节点
			if mostRight.Right == nil {
				mostRight.Right = cur
				cur = cur.Left
				continue    // continue 最外层for
			}else {         // mostRight.Right != nil => mostRight.Right == cur
				mostRight.Right = nil
			}
		}
		fmt.Print(cur.Value , "\t")
		cur = cur.Right
	}
	fmt.Println()
}

func morrisPre(head *Node) {  //先序
	if head == nil {
		return
	}
	cur := head
	var mostRight *Node
	for cur != nil {           // cur 不断往左或往右跳,一旦为空就出来
		mostRight = cur.Left  //cur 有没有左树
		if mostRight != nil {
			// 找到cur 左树上真实的最右节点
			for mostRight.Right != nil && mostRight.Right != cur {  // 第一次 nil, 第二次 cur
				mostRight = mostRight.Right
			}
			// 从for中出来,mostRight 一定是cur左树上的最右节点
			if mostRight.Right == nil {
				mostRight.Right = cur

				fmt.Print(cur.Value,"\t")

				cur = cur.Left
				continue    // continue 最外层for
			}else {         // mostRight.Right != nil => mostRight.Right == cur
				mostRight.Right = nil
			}
		} else {
			fmt.Print(cur.Value,"\t")
		}
		cur = cur.Right
	}
	fmt.Println()
}



// 打印时机,就放在能回到自己两次,且第二次回到自己的时候
// 打印他左树的右边界,逆序打印
// 整棵树可以被子树的右边界分解掉
// 不能放到栈里,用 [ 反转链表 ] 打印完再调回来 时间复杂度O(N)

func morrisPos(head *Node) {  //后序
	if head == nil {
		return
	}
	cur := head
	var mostRight *Node
	for cur != nil {           // cur 不断往左或往右跳,一旦为空就出来
		mostRight = cur.Left   //cur 有没有左树
		if mostRight != nil {
			// 找到cur 左树上真实的最右节点
			for mostRight.Right != nil && mostRight.Right != cur {  // 第一次 nil, 第二次 cur
				mostRight = mostRight.Right
			}
			// 从for中出来,mostRight 一定是cur左树上的最右节点
			if mostRight.Right == nil {
				mostRight.Right = cur
				cur = cur.Left
				continue    // continue 最外层for
			}else {         // mostRight.Right != nil => mostRight.Right == cur
				mostRight.Right = nil

				printEdge(cur.Left)
			}
		}
		cur = cur.Right
	}


	printEdge(head)


	fmt.Println()
}


// 线索二叉树

func printEdge(head *Node)  {
	tail := reverseEdge(head)
	cur := tail
	for cur != nil {
		fmt.Print(cur.Value, "\t")
		cur = cur.Right
	}
	reverseEdge(tail)
}

func reverseEdge(from *Node) *Node  {
	var pre, next *Node
	for from != nil {
		next = from.Right
		from.Right = pre
		pre = from
		from = next
	}
	return pre
}



/*
扩展: 判断一棵树是不是搜索二叉树
 */

//任何一个节点左树头节点比自己小,右树头节点比自己大

func morrisIsBST(head *Node) bool {
	if head == nil {
		return true
	}
	cur := head
	var mostRight *Node
	pre := (*int)(nil)
	
	for cur != nil {           // cur 不断往左或往右跳,一旦为空就出来
		mostRight = cur.Left  //cur 有没有左树

		if mostRight != nil {
			// 找到cur 左树上真实的最右节点
			for mostRight.Right != nil && mostRight.Right != cur {  // 第一次 nil, 第二次 cur
				mostRight = mostRight.Right
			}
			// 从for中出来,mostRight 一定是cur左树上的最右节点
			if mostRight.Right == nil {
				mostRight.Right = cur
				cur = cur.Left
				continue    // continue 最外层for
			}else {         // mostRight.Right != nil => mostRight.Right == cur
				mostRight.Right = nil
			}
		}
		
		if pre != nil && *pre >= cur.Value {
			return false
		}
		*pre = cur.Value
		cur = cur.Right
	}
	return true
}





/*
一棵二叉树,所有叶节点到头部距离最短的(二叉树的最小高度)
 */


/*
方法1,二叉树的递归套路
 */

func minHeight1(head *Node) int {
	if head == nil {
		return 0
	}
	return p(head)
}

func p(x *Node) int {
	if x.Left == nil && x.Right == nil {
		return 1
	}
	//左右子树起码有一个不为空
	leftH := math.MinInt
	if x.Left != nil {
		leftH = p(x.Left)
	}
	rightH := math.MaxInt
	if x.Right != nil {
		rightH = p(x.Right)
	}
	return 1 + Min(leftH,rightH)
}

func Min(a, b int) int  {
	if a > b {
		return b
	}
	return a
}







/*
整个过程用有限几个变量
1 cur 高度
2 cur 此时是叶节点,记录一下高度, Min(min, curH)
 */



func minHeight2forMorris(head *Node) int {
	if head == nil {
		return 0
	}
	cur := head
	var mostRight *Node
	curLevel := 0
	minHeight := math.MaxInt
	for cur != nil {           // cur 不断往左或往右跳,一旦为空就出来
		mostRight = cur.Left  //  cur 有没有左树

		if mostRight != nil {
			rightBoardSize := 1
			// 找到cur 左树上真实的最右节点
			for mostRight.Right != nil && mostRight.Right != cur {  // 第一次 nil, 第二次 cur
				rightBoardSize++
				mostRight = mostRight.Right
			}
			// 从for中出来,mostRight 一定是cur左树上的最右节点
			if mostRight.Right == nil {  // 第一次到达
				curLevel++
				mostRight.Right = cur
				cur = cur.Left
				continue    // continue 最外层for
			}else {         // mostRight.Right != nil => mostRight.Right == cur
				//第二次到达
				if mostRight.Left == nil {
					minHeight = Min(minHeight,curLevel)
				}
				curLevel -= rightBoardSize
				mostRight.Right = nil
			}
		}else { // 只有一次到达
			curLevel++
		}
		cur = cur.Right
	}
	
	finalRight := 1
	cur = head
	for cur.Right != nil {
		finalRight++
		cur = cur.Right
	}
	if cur.Left == nil && cur.Right == nil {
		minHeight = Min(minHeight,finalRight)
	}
	return minHeight 
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

metabit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值