🌲数据结构 | 到底什么是树?

189 阅读4分钟

什么是树?

  • 一种非线性数据结构,用来模拟具有树状结构性质的数据集合。

    树作为一种树状的数据结构,其数据节点之间的关系也如大树一样,将有限个节点根据不同层次关系进行排列,从而形成数据与数据之间的父子关系。常见的树的表示形式更接近“倒挂的树”,因为它将根朝上,叶朝下。

    树的数据存储在结点中,每个结点有零个或者多个子结点。没有父结点的结点在最顶端,成为根节点;

4p0k1wz3bj.jpeg

有哪些树?

  1. 普通树

    • 定义:任意节点的子节点数量不限,无特定约束。
    • 应用:文件系统目录结构、组织架构表示。
  2. 二叉树

    • 定义:顾名思义,就是每个节点的子节点的数量最多只有两个(左右节点)。二叉树又有很多具体分类。

二叉树

与链表类似,二叉树的基本单元是节点,每个节点包含值、左子节点引用和右子节点引用。

// 二叉树节点结构体 
type TreeNode struct {
    Val   int
    Left  *TreeNode
    Right *TreeNode
}

// 实例化一颗树
func NewTreeNode(v int) *TreeNode {
    return &TreeNode{
        Left:  nil, // 左子节点指针
        Right: nil, // 右子节点指针
        Val:   v,   // 节点值
    }
}

二叉树作为平时最场景的树,也有有很多分类。

  1. 满二叉树:所有非叶子节点都有两个子节点,且所有叶子在同一层。

hzzaljeooz - 副本.jpeg

  1. 完全二叉树:除了最后一层结点,其它层的结点数都达到了最大值;同时最后一层的结点都是按照从左到右依次排布

hzzaljeooz.jpeg

  1. 二叉排序树(二叉搜索树) :若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉排序树。

    • 特性:数据是排好序的,顺序为左结点<根节点<右结点,二叉排序树的中序遍历结果是有序的。
  2. **平衡二叉树(VAL树):**也属于是一种二叉排序树,只不过多一个新特性:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树

9q7g8zu515.jpeg

二叉树的遍历

二叉树常见的遍历方式包括层序遍历、前序遍历、中序遍历和后序遍历。

  • 层序遍历

    从顶部到底部逐层遍历二叉树,并在每一层按照从左到右的顺序访问节点。层序遍历本质上属于广度优先遍历,广度优先遍历通常借助“队列”来实现。

    func levelOrder(root *TreeNode) []any {
    	// 初始化一个切片,用于保存遍历序列
    	nums := make([]any, 0)
    	// 初始化一个队列,用于保存节点
    	queue := list.New()
    	// 将根节点入队
    	queue.PushBack(root)
    	// 当队列不为空时,循环遍历
    	for queue.Len() > 0 {
    		// 获取队首元素
    		node := queue.Front()
    		// 将队首元素出队
    		queue.Remove(node)
    		// 获取节点值
    		num := node.Value.(*TreeNode).Val
    		// 将节点值保存到切片中
    		nums = append(nums, num)
    		// 如果左子树不为空,将左子树入队
    		if node.Value.(*TreeNode).Left != nil {
    			queue.PushBack(node.Value.(*TreeNode).Left)
    		}
    		// 如果右子树不为空,将右子树入队
    		if node.Value.(*TreeNode).Right != nil {
    			queue.PushBack(node.Value.(*TreeNode).Right)
    		}
    	}
    	return nums
    }
    
  • 前、中、后序遍历

    属于深度优先遍历,它体现了一种“先走到尽头,再回溯继续”的遍历方式。

    • 其实这三种遍历方式思路是一样的,无非就是根节点遍历的顺序不一样而已。

      • 前序遍历:根 → 左 → 右
      • 中序遍历:左 → 根 → 右
      • 后序遍历:左 → 右 → 根
    func TraversalHelper(root *TreeNode) []int {
    	var result []int
    	if root == nil {
    		return result
    	}
    		/**  result = append(result, root.Val)
    				 这一行的位置决定了是什么遍历方式
    				 如果在left前面 就是 前序遍历
    				 在中间 就是 中序遍历
    				 再right后面 就是 后序遍历
    		**/
    		
    	// 前序遍历	
    	// result = append(result, root.Val)
    	// result = append(result, TraversalHelper(root.Left)...)
    	// result = append(result, TraversalHelper(root.Right)...)
    	
    	// 中序遍历
    	result = append(result, TraversalHelper(root.Left)...)
    	result = append(result, root.Val)
    	result = append(result, TraversalHelper(root.Right)...)
    
    	// 后序遍历
    	// result = append(result, TraversalHelper(root.Left)...)
    	// result = append(result, TraversalHelper(root.Right)...)
    	// result = append(result, root.Val)
    	
    	return result
    }
    

互动话题:二叉树用数组怎么表示?一个有序数组怎么转换成平衡二叉树?