树
什么是树
客观世界中许多事物存在层次关系
人类社会家谱
社会组织结构
图书信息管理
分层次组织在管理上具有更高的效率!
数据管理的基本操作之一:查找
查找:根据某个给定关键字K,从集合R中找出关键字与K相同的记录
静态查找:集合中记录是固定的
没有插入和删除操作,只有查找
动态查找:集合中是动态变化的
除了查找,还可能发生插入和删除
静态查找
方法一:顺序查找
时间复杂度O(n)
哨兵
在查找方向的尽头设置哨兵元素,免去了查找过程中每次比较后都要判断查找位置是否越界的小技巧,看似与原先差别不大,但是总数据较多时,效率提高很明显,是非常好的编程技巧。当然,“哨兵”也不一定在数组开始,也可以再末尾”
方法二:二分查找
假设n个数据元素的关键字满足有序k1<k2<k3<…<kn
并且是连续存放(只能是数组),那么可以进行二分查找。
int BinarySearch( List Tbl,ElementType K)
{
int left,right,mid,NoFound = -1;
left = 1;
right = Tbl->Length;
while(left <= right)
{
mid = (left+right)/2;
if(K<Tbl->Element[mid]) right = mid - 1;
else if( K > Tbl->Element[mid]) left = mid + 1;
else return mid;
}
return NotFound;
}
二分查找算法具有对数的时间复杂度O(logN)
树的定义
树(Tree):n个结点构成的有限集合。
当n等于0时,称为空树
对于任何一棵非空树,它具备以下性质:
树中有一个称为“根(Root)”的特殊节点,用r表示;
其余节点可分为m个互不相交的有限集T1,T2,…Tm,其中每个集合本身又是一棵树,称为原来树的“子树(SubTree)”
子树是不相交的;
除了根节点,每个结点有且仅有一个父节点;
一棵N个结点的树有N-1条边。
树的表示
儿子-兄弟表示法
每个指针有两个指针域,左边指向孩子结点,右边指向兄弟节点
二叉树的定义
二叉树几个重要性质
一个二叉树第i层的最大结点数为:2的i-1次方
深度为K的二叉树有最大结点总数为:2的k-1次方,看k>=1
对任何非空二叉树T,若n0表示叶节点的个数,n2是度为2的非叶节点的个数,那么两者满足关系n0=n2+1;
二叉树的抽象数据类型定义
类型名称:二叉树
数据对象集:一个有穷的结点集合。
若不为空,则由根节点和其左右二叉子树组成。
操作集:BT属于BinTree,Item属于ElementType,重要操作有:
1.Boolean IsEmpty(BinTree BT):判断BT是否为空;
2.void Traversal(BinTree BT):遍历,按某顺序访问每个结点;
3.BinTree CreatBinTree(): 创建一个二叉树。
二叉树的存储结构
1.顺序存储结构
完全二叉树:
按从上到、从左到右顺序存储
n个结点的完全二叉树的结点父子关系:
非根节点的父节点的序号是i/2;
结点的左孩子结点的序号是2i;
结点的右孩子的序号是2i+1;
一般二叉树也可以采用这种结构,但会造成空间浪费…
2.链表存储
typedef struct TreeNode *BinTree;
typedef BinTree Position;
struct TreeNode{
ElementType Data;
BinTree Left;
BinTree Right;
}
二叉树的遍历
1.先序遍历
遍历过程:
访问根节点;
先序遍历其左子树;
先序遍历其右子树。
void PreOrderTraversal(BinTree BT)
{
if(BT){
printf("%d",BT->Data);
PreOrderTraversal(BT->Left);
PreOrderTraversal(BT->Right);
}
}
2.中序遍历
遍历过程为:
中序遍历其左子树;
访问根节点;
中序遍历其右子树。
void InOrderTraversal(BinTree BT)
{
if(BT){
InOrderTraversal(BT->Left);
printf("%d",BT->Data);
InderTraversal(BT->Right);
}
}
3.后序遍历
遍历过程为:
后序遍历其左子树;
后序遍历其右子树;
访问根节点。
void PostOrderTraversal(BinTree BT)
{
if(BT)
{
PostOrderTraversal(BT->Left);
PostOrderTraversal(BT->Right);
printf("%d",BT->Data);
}
}
二叉树的非递归遍历
中序遍历非递归遍历算法
非递归算法实现的基本思路:使用堆栈
遇到一个节点,就把它压栈,并去遍历她的左子树;
当左子树遍历结束后,从栈顶弹出这个结点并访问它;
然后按照其右指针再去中序遍历该结点的右子树。
void InOrderTraversal(BinTree BT)
{
BinTree T=BT;
Stack S = CreateStack(MaxSize);
while(T||!IsEmpty(S)){
Push(S,T);
T = T->Left;
}
if(!IsEmpty(S)){
T = Pop(S);
printf("%5d",T->Data);
T = T->Right;
}
}
层序遍历
二叉树遍历的核心问题:二维结构的线性化
从结点访问其左右儿子结点
访问左儿子后,右儿子结点怎么办?
需要一个存储结构保存暂时不访问的结点
存储结构:堆栈,序列
队列实现:遍历从根节点开始,首先将根节点入队,然后开始执行循环:结点出队、访问该节点、其左右儿子入队
层序基本过程: 先根节点入队,然后:
1.从队列中取出一个元素;
2.访问该元素所指结点;
3.若该元素所指结点的左右孩子结点非空,则将其左右孩子的指针顺序入队。
void LevelOrderTraversal ( BinTree BT)
{
Queue Q;
BinTree T;
if(!BT) return; /*若是空树直接返回*/
Q = CreatQueue(MaxSize); /*创建并初始化队列Q*/
AddQ(Q,BT);
while(!IsEmpty(Q))
{
T = DeleteQ(Q);
printf("%d\n",T->Data);
if(T->Left) AddQ(Q,T->Left);
if(T->Right) AddQ(Q,T->Right);
}
}