一、二叉树就是这么简单

首先,我们来讲讲什么是树:

树是一种非线性的数据结构,相对于线性的数据结构(链表、数组)而言,树的平均运行时间更短(往往与树相关的排序时间复杂度都不会高)

在现实生活中,我们一般的树长这个样子的:

但是在编程的世界中,我们一般把树“倒”过来看,这样容易我们分析:

二.二叉树的递归定义

1.要么二叉树没有根节点,是一棵空树。
2.要么二叉树由根节点、左子树、右子树组成,且左子树和右子树都是二叉树。

可以参考上面的图,每个节点都有2个子树,最下面的叶子节点,子树为nil

三.根据递归定义,用链式存储,实现

树由节点组成,每个节点的数据结构是这样的

一个数据、两个指针(如果有节点就指向节点、没有节点就指向nil)

二叉树每个结点有两条出边,因此指针域有两个——分别指向左子树和右子树的根结点地址,因此右把这种链表叫做二叉链表。其定义方式如下:
// 二叉树结构定义 
type node struct {
    data  int    // 数据域
    left  *node  // 指向左子树的根结点
    right *node  // 指向右子树的根结点 
}

如果需要新建结点,可以使用下面的函数

func NewNode(data int) *node {
    return &node{data: data}
}

四.二叉树的基本操作

二叉树的常用操作有以下几个:
二叉树的建立、
以及二叉树结点的查找、修改、插入与删除。

本节主要介绍二叉树结点 查找、修改、插入、建树的通用思想。

1. 二叉树结点的查找

查找操作是指在给定数据域的条件下,在二叉树中找到所有数据域为给定数据域的结点
可以使用递归来完成查找修改操作。
1.先判断当前结点是否是需要查找的结点:
2.如果不是,则分别往该结点的左孩子和右孩子递归,直到当前结点为 NULL 为止。

对于有序的二叉树,左子节点小于当前节点,右子节点大于当前节点
func (nd *node) Search(dt int) *node {
    if nd == nil {
        return nil
    }
    //1
    if dt == nd.data {  
        return nd
    }

    //2 大于当前节点,递归右边
    if dt > nd.data {
        return nd.right.Search(dt)
    }
    //2 大于当前节点,递归左边
    if dt < nd.data {
        return nd.left.Search(dt)
    }
    return nil
}

2. 二叉树结点的插入

把数据插入到指定的位置,这里的数据唯一不重复。
1.先判断当前结点和插入的值的比较,如果等于,就跳过:
2.如果数据大于本节点,就插入到右孩子,如果右孩子为nil,直接插入,有值继续比较插入
3.如果数据小于本节点,就插入到左孩子,如果左孩子为nil,直接插入,有值继续比较插入

对于有序的二叉树,左子节点小于当前节点,右子节点大于当前节点
func (nd *node) Insert(newNode *node) {
    //1
    if newNode.data == nd.data {
        return
    }
    //2
    if newNode.data > nd.data {
        if nd.right == nil {
            nd.right = newNode
        } else {
            nd.right.Insert(newNode)
        }
    } else { //3 小于 继续比较插入到 左孩子
        if nd.left == nil {
            nd.left = newNode
        } else {
            nd.left.Insert(newNode)
        }
    }
}

3. 二叉树创建

root := data.NewNode(250)
for i := 0; i < 14; i++ {
    n := rand.Intn(500)
    t.Log("i=", i, "的随机数是", n)
    root.Insert(data.NewNode(n))
}

谢谢您的观看,欢迎关注我的公众号。

image.png