什么是树?
像我们平时使用的U盘,里面的目录和子目录之间的关系就是有层次关系的树。
顺序查找:(哨兵的设立)
说明:
下标为0的位置不是我们要查找的数据,而是我们所设立的哨兵,我们要查找的数据是在Element[1]~Element[10]之间查找的。建立哨兵还有一个作用就是防止数组下标越界,也就是不需要添加i>0的条件了。当从Element[10]开始找,找到Element[1],如果还没有找到,再往上找就碰到了Element,此时设置的哨兵就拦截了,停止查找。这样也防止了每一次循环就要去判断是不是到达了数组边界的问题。通过哨兵,再循环的时候就少写一个判断分支。
我们最终循环结束,得到一个i返回值,通过判断i值,我们就可以知道是否查找到了,如果k!=0,那么就是在Element[10]~Element[1]之间找到了K值,找到了我们需要查找到的值。
但是如果K=0,那么就是找到了哨兵,在Element[10]~Element[1]之间没有查找到我们想要的值。没查找到我们需要的值。
显然,这样查找的效率并不高,那有没有效率高的查找方法呢?
有————————二分查找!
二分查找:
二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
left>right,发生了left、right边界错位,所以查找失败,结束。
说明:
1、上面的树中的数字都代表所查找元素的下标
2、ASL:表示平均查找长度。
我们能不能把数据不一定放在数组当中,就按照这样(上图)一个层次化的结构来存储我们的数据,是不是也会达到二分查找的效果?
这就是我们后面要讲的:我们用树的形式来存储我们需要的数据,使得我们的一些查找过程更加方便————查找树!
查找树和二分查找的效率上可以得到差不多的效果都是log2n(log以2为底)。
树的定义:
树是保证结点联通的最小的一种连接方式(边最少的连接方式),因为随便删去树中的一条边,就整体就不是一颗树了。
树的表示:
上面这种表示方法我们发现有三个指针域的,有两个指针域的,还有一个指针域的,那么整个结构的形式不一样,会给我们后面的程序实现带来困难。
但是如果将上面的结构统一设置成都有三个指针域,那这样结构就统一了,但是,这样又造成了大量的空间浪费问题,因为时间上,我们的边只有n-1条边,但是都使用3个指针域的话,那么就会造成3n-(n-1)=2n+1个指针的空间浪费。
儿子–兄弟表示法
第一个指针域FirstChild:表示的是“长子”,即第一个孩子结点(子节点)。
第二个指针域:NextSibling:表示的是它的下一个兄弟结点。
我们发现这样的结构每一个结点都有两个指针域,只造成了2n-(n-1)=n+1个指针域的浪费,浪费的空间也不算大。
代码:
typedef struct TNode *Position;
typedef Position BinTree; /* 二叉树类型 */
struct TNode{ /* 树结点定义 */
ElementType Data; /* 结点数据 */
BinTree Left; /* 指向左子树 */
BinTree Right; /* 指向右子树 */
};