面试题:二叉树的非递归前序遍历


写在前面

上一篇文章面试题:二叉树的非递归中序遍历介绍了非递归中序遍历,举一反三,本文来介绍一下非递归前序遍历。

写完非递归前序遍历后发现,思路和代码根非递归中序遍历差不多,代码相似度高达90%,先看代码

代码

// 定义节点
type Node struct {
	Val         int
	Left, Right *Node
}

func NewTree(data []int) *Node {
	n := len(data)
	if n == 0 {
		return nil
	}
	if n == 1 {
		return &Node{Val: data[0]}
	}

	nodes := make([]*Node, 0, n)
	for i := range data {
		if data[i] == 0 {
			// TODO 假设0表示空节点
			continue
		}
		tmpNode := &Node{
			Val: data[i],
		}

		if i == 0 {
			// 第一个节点
			nodes = append(nodes, tmpNode)
			continue
		}

		parentIdx := (i - 1) / 2
		if i%2 == 1 {
			// 左孩子
			nodes[parentIdx].Left = tmpNode
		} else {
			// 右孩子
			nodes[parentIdx].Right = tmpNode
		}
		nodes = append(nodes, tmpNode)
	}
	return nodes[0]
}

// 递归
func TreePreVisit1(root *Node) []int {
	if root == nil {
		return nil
	}
	if root.Left == nil && root.Right == nil {
		return []int{root.Val}
	}
	result := []int{root.Val}
	result = append(result, TreePreVisit1(root.Left)...)
	result = append(result, TreePreVisit1(root.Right)...)
	return result
}

// 二叉树的非递归前序遍历
func TreePreVisit(root *Node) []int {
	result := []int{}
	// 1.定义一个栈
	s := newStack()
	p := root
	for true {
		for p != nil {
			// 访问栈顶元素
			result = append(result, p.Val)
			// p入栈
			s.push(p)
			p = p.Left
		}
		// 从栈顶出战
		if s.empty() {
			break
		}
		p = s.pop()

		// 指向右孩子
		p = p.Right
	}

	return result
}

// 定义一个栈
type stack struct {
	data []*Node
	top  int
}

func newStack() *stack {
	return &stack{
		data: make([]*Node, 100), // TODO 先假设所有节点数量小于100
		top:  -1,
	}
}

func (s *stack) push(node *Node) {
	s.top++
	s.data[s.top] = node
}

func (s *stack) pop() *Node {
	if s.empty() {
		return nil
	}
	node := s.data[s.top]
	s.top--
	return node
}

func (s *stack) empty() bool {
	return s.top == -1
}

代码根中序的相比较发现只有一点点区别。前序和中序算法思路都是一样的,都是访问父节点和入栈。唯一区别是这两个步骤的先后顺序不一样。

  • 非递归中序遍历:先入栈,再出栈访问
  • 非递归前序遍历:先访问,再入栈

参考

1]面试题:二叉树的非递归中序遍历


文章作者: Alex
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Alex !
  目录