欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

树的遍历

程序员文章站 2022-05-19 20:55:18
...

普通遍历

普通遍历的意思是时间复杂度为O(N),空间复杂度为O(h)的遍历算法,区别于后边学习的Morris遍历

递归遍历

  树的定义本身就是递归的,所以非常适合用递归解决问题,树的遍历同样可以用递归来解决。递归的模型非常简单。先中后序的基本都是一样的。

先序遍历

func preOrder(root *node) {
	if root == nil {
		return
	}

	fmt.Println(root.value)
	preOrder(root.left)
	preOrder(root.right)
}

func PreOrderRecur(btree *btree) error {
	if btree == nil {
		return errors.New("the btree is not exist")
	}
	fmt.Println("----------------------------------")
	fmt.Println("PreOrder")
	preOrder(btree.root)
	fmt.Println("----------------------------------")

	return nil
}

中序遍历

func inOrder(root *node) {
	if root == nil {
		return
	}

	inOrder(root.left)
	fmt.Println(root.value)
	inOrder(root.right)
}

func InOrderRecur(btree *btree) error {
	if btree == nil {
		return errors.New("the btree is not exist")
	}
	fmt.Println("----------------------------------")
	fmt.Println("InOrder")
	inOrder(btree.root)
	fmt.Println("----------------------------------")

	return nil
}

后序遍历

func posOrder(root *node) {
	if root == nil {
		return
	}

	posOrder(root.left)
	posOrder(root.right)
	fmt.Println(root.value)
}

func PosOrderRecur(btree *btree) error {
	if btree == nil {
		return errors.New("the btree is not exist")
	}
	fmt.Println("----------------------------------")
	fmt.Println("PostOrder")
	posOrder(btree.root)
	fmt.Println("----------------------------------")

	return nil
}

递归总结

  虽然递归的代码非常简单,但是它的本质一定要清楚。这个本质是听左神讲的,非常精辟。递归的本质是每个节点都会访问三次,而递归的先中后序的遍历无非是你把打印行为选择在哪一次。

层次遍历

  层次遍历就是从上到下,从左到右的访问每一个节点。

func LevelTranversal(btree *btree) error {
	if btree == nil {
		return errors.New("the btree is not exist")
	}

	fmt.Println("----------------------------------")
	fmt.Println("level tranversal")

	if btree.root == nil {
		return nil
	}

	queue := list.New()
	queue.PushFront(btree.root)

	var cur *node
	for queue.Len() != 0 {
		cur = queue.Back().Value.(*node)
		queue.Remove(queue.Back())

		fmt.Println(cur.value)

		if cur.left != nil {
			queue.PushFront(cur.left)
		}

		if cur.right != nil {
			queue.PushFront(cur.right)
		}
	}

	fmt.Println("----------------------------------")

	return nil
}

  就是广度优先遍历,采用一个队列,初始把根节点压入队列,每次从队列弹出一个元素打印,然后将左右两个子节点依次压入队列,重复直到队列为空

非递归遍历

  虽然递归的实现先中后序的遍历非常简单直观,但是系统栈容量不是很大,当树中深度过深时,可能会导致系统栈爆掉,所以我们为了避免这个问题,就需要我们自己使用用户栈去实现非递归的遍历

先序遍历

  非递归的先序遍历和层次遍历的实现基本一致,无非就是队列换成了栈,压入左右孩子的顺序相反

func PreOrder(btree *btree) error {
	if btree == nil {
		return errors.New("the btree is not exist")
	}

	fmt.Println("----------------------------------")
	fmt.Println("PreOrder")

	if btree.root == nil {
		return nil
	}

	stack := list.New()
	stack.PushFront(btree.root)

	var cur *node
	for stack.Len() != 0 {
		cur = stack.Front().Value.(*node)
		stack.Remove(stack.Front())

		fmt.Println(cur.value)

		if cur.right != nil {
			stack.PushFront(cur.right)
		}

		if cur.left != nil {
			stack.PushFront(cur.left)
		}
	}

	fmt.Println("----------------------------------")

	return nil
}

中序遍历

  1. 初始使用一个游标指向根节点
  2. 如果游标指向的不是空则压栈,并移动到左子树
  3. 如果左子树不为空则重复2,否则弹栈打印,并将游标指向弹出的节点的右子树
    func InOrder(btree *btree) error {
    	if btree == nil {
    		return errors.New("the btree is not exist")
    	}

    	fmt.Println("----------------------------------")
    	fmt.Println("InOrder")

    	if btree.root == nil {
    		return nil
    	}

    	var top *node
    	cur := btree.root
    	stack := list.New()

    	for stack.Len() != 0 || cur != nil {
    		if cur != nil {
    			stack.PushFront(cur)
    			cur = cur.left
    			continue
    		}

    		top = stack.Front().Value.(*node)
    		stack.Remove(stack.Front())

    		fmt.Println(top.value)
    		cur = top.right
    	}

    	return nil
    }

  总结一下其实就是模仿了递归,第一次访问压栈,第二次打印,第三次压右孩子

后序遍历

  非递归的后序遍历和层次遍历也很像,压入左右孩子的性顺序也相同,只不过使用的是栈,而且需要把从栈中弹出的元素都添加到打印栈中

func PosOrder(btree *btree) error {
	if btree == nil {
		return errors.New("the btree is not exist")
	}

	fmt.Println("----------------------------------")
	fmt.Println("PosOrder")

	if btree.root == nil {
		return nil
	}

	stackRecur := list.New()
	stackPrint := list.New()

	stackRecur.PushFront(btree.root)

	var cur *node
	for stackRecur.Len() != 0 {
		cur = stackRecur.Front().Value.(*node)
		stackRecur.Remove(stackRecur.Front())

		if cur.left != nil {
			stackRecur.PushFront((cur.left))
		}

		if cur.right != nil {
			stackRecur.PushFront(cur.right)
		}

		stackPrint.PushFront(cur)
	}

	for stackPrint.Len() != 0 {
		cur = stackPrint.Front().Value.(*node)
		stackPrint.Remove(stackPrint.Front())

		fmt.Println(cur.value)
	}

	return nil
}