静态查找
1、顺序查找
/* 哨兵技巧:哨兵放在Element[0], 其他元素从1开始放置 */
int SequentialSearch (List Tbl, ElementType K)
{ /* 在Element[1] ~ Element[n] 中查找关键字为K的数据元素 */
int i;
Tbl->Element[0] = K; // 建立哨兵
for(i = Tbl->Length; Tbl->Element[i] != K; i--);
return i; // 超找成功后返回所在单元下标,不成功后返回0
}
typedef struct LNode *List;
struct LNode
{
ElementType Element[MAXSIZE];
int Length;
};
二分查找 前提:1、n个元素必须有序查找(比如从小到大等) 2、必须是连续存放的数组
例1: 假设有13个数据元素,按关键字由小到大顺序存放,二分查找关键字为444的数据元素
例2:查找关键字为43 的元素
解: 1. letft=1, right=13, mid=(1+13)/2=7; 100>43, 要查找的元素在右半段,左移right指针
2.left=1, right=mid-1=6, mid=(1+6)/2=3; 39<43, 要查的元素在本次分格的右半段,left 指针右移至mid+1位置
3.left=4,right=6, mid=(4+6)/2=5; 51>43, 要查的元素在该段左半段,right指针左移至mid-1
4.left=4, right=4, mid=4; 45>43,要查的元素在该断的左半段,right指针丛至mind-1
5.left=4, right=3, left>right---> 错误!退出,未找到43
例3:(判断)在二分查找的程序实现中,如果left和right的更新不是取mid+1和mid-1而是都取mid,程序也是正确的。 (X)
例4: 11个元素的二分查找判定树 (注意:每个节点代表mid值)
1.每个节点需要查找的次数恰好为其层数 2. 查找成功时,查找次数不会超过树的深度 3. n个结点的判定树的的深度
为 [log n]+1
4.ASL平均成功查找次数=(查找W次*共有几个数+查找W-1次*共几个数+查找W-2次*共几个数+.....+查找1次*共几个数)/总数
树
一、定义
1、字数不相交
2、除根结点外,每个结点仅有一个父结点
3、一棵N个结点的树有N-1条边
例1:有一棵m棵树的集合(也叫森林)公有k条边,问这m棵树公有多少个结点?
(k+m)
4、结点的度:结点子树个数; 树的度:最大的结点的度
5、叶结点:度=0
6、父结点、子结点、兄弟结点
7、路径:从哪到哪的方法 路径长度:路径所包含边的个数
8、祖先结点:某结点--->根节点经过的所有的结点是他的; 子孙结点:某结点以后的所有可以经过的结点是他的
9、结点的层次:规定根结点在1层
10、树的深度:所有结点中的最大层次
树的表示
例2:在“儿子-兄弟”法的树中,如果从根节点开始访问其次子的次子,怎样全都访问长子,才能使他们所经过的结点数相等?
答:长子-长子-长子-长子
例3: (1)若每个结点有3个指针域,有n个结点-------> 一共就有3n个指针域,但是整个树仅有n-1条边,意味着有2n+1个指针域被浪费
(2)当用“儿子-兄弟”表示法表示二叉树,n个结点-------> 一共有2n个指针域, 有n-1个指针域被利用,有n+1个指针域被浪费,比第一种利用率高
二叉树:
1、分类:斜二叉树、完美(满)二叉树、完全二叉树(允许缺掉后面的结点,其他与满二叉树的编号相同)
2、性质: 1)第i层最大节点数为 2)若有k层,总结点数为 3)结点有三类:叶结点n0(无儿子)、有1个儿子n1 、有2个儿子n2 存在公式: 总结点数=n0 + n1 + n2 有边数公式 总结点数-1 = ( 结点有i个儿子 * i ) 得出: n0 = n2 + 1
二叉树存储结构:
1、顺序存储
2、链表存储
二叉树的遍历 重点看printf() 在什么位置
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);
InOrderTraversal( 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 ); // 创建并初始化堆栈S
while( T || !IsEmpty(S) ){ //外层循环:判断T->Right是否为空,若不空,从第二次循环开始就开始遍历左子树的右子树(然后再遍历右子树的左子树)
while(T){ //内循环:当T不为空,将T压栈,并遍历T的左子树
Push(S, T);
T = T->Left;
}
if( !Empty(S) ){ //内循环结束后,即T->Left==NULL,若S不为空,首先弹T出栈,T为出栈元素;再打印T的值;并指向T的右子树
T = Pop(S);
printf("%5d", T->Data);
T = T->Right;
}
}
}
先序遍历非递归算法
void InOrderTraversal( BinTree BT )
{
BinTree T BT;
Stack S = CreateStack( MaxSize );
while( T || !IsEmpty(S) ){ // 外循环:
while(T){ // 内循环:先打印根节点,在往左下遍历
printf("%5d", T->Data);
Push(S, T); // 将此根节点压入栈中,再向左下遍历
T = T->Left;
}
if( !IsEmpty(S) ){ // 判断栈是否为空,若不是,则弹出T——栈顶元素,并遍历该T的右子树
// 疑问:假如右子树也为空,怎样回到根节点?
T = Pop(S);
T = T->Right;
}
}
}
层序遍历
要点:1.一层一层看 2. 出那个,放哪个的左右儿子
void LevelOrderTraversal( BinTree BT )
{
Queue Q;
BinTree T;
if ( !BT ) return;
Q = CreateQueue( MaxSize );
AddQ( Q, BT );
while ( !IsEmpty(Q) ){
T = Delete( Q );
printf("%d\n", T->Data);
if( T-Left ) AddQ( Q, T->Left );
if( T->Right ) AddQ( Q, T->Right );
}
}
例1: 求二叉树的叶子结点
(方法:二叉树的遍历算法中增加检测结点的“左右子树是否都为空”)
void PreOrderPrintLeaves( BinTree BT )
{
if( BT ){
if ( !BT->Left && !BT->Right )
printf("%d", BT->Data);
PreOrderPrintLeaves( BT->Left );
PreOrderPrintLeaves( BT->Right );
}
}
例2:求二叉树的高度
(方法:分段,分别求左右子树的高度--》比较--》 +1)
void PostOrderGetHeight( BinTree BT )
{ int HL, HR, MaxH;
if( BT ){
HL = PostOrderGetHeight( BT->left ); // 先遍历左边子树的深度
HR = PostOrderGetHeight( BT->Right);
MaxH = ( HL > HR ) ? HL : HR ;
return (MaxH + 1);
}
}
else return 0;
例3: 二元运算表达式树及其遍历
例4: 两种遍历序列确定二叉树: 中序 + 前/后
前序: 根 左 右 中序: 左 根 右 按照这个规律,可以依次确定每一个小左子树的跟 的左子树 的右子树 嘻嘻嘻...
已知: 前序序列为 a b c d e f g h i j 中序序列为: c b e d a h g i j f
二叉树表示 (用结构数组)
#define MaxTree 10
#define ElementType char
#define Tree int
#define Null -1
struct TreeNode
{
ElementType Element;
Tree Left;
Tree Right;
} T1[MaxTree], T2[MaxTree];
注意:可以通过观察哪个数组没有被任何指针指向,判断根节点
-
程序——判断两棵树是否同构
1. main主函数
int main()
{
Tree R1, R2;
R1 = BuiltTree(T1);
R2 = BuiltTree(T2);
if(Isomorphic(R1, R2)) printf("Yes\n");
else printf("No\n");
return 0;
}
2. 创建BuildTree
// 将T[i]数组遍历一遍,看有没有数组指向它
Tree BuildTree(struct TreeNode T[])
{
scanf("%d \n", &N); // 输入有几个结点
if(N){ // 若输入的结点不为0
for(i=0;i<N;i++) check[i]=0; // 将每一个结点的check值设为0
for(i=0;i<N;i++){ // 依次输入每个节点的基本信息
scanf("%c%c%c\n", &T[i].Element, &cl, &cr); //cl、cr分贝表示左、右结点的值
你的出路在哪
if(cl != '-'){
T[i].Left = cl-'0'; // 原来输入的是字符型的,减去字符0,得到数值0
check[T[i].Left] = 1;
//若一个节点指向了某一个位置,则把那个位置结点check设为1
}
else T[i].Left = Null;
if(
}
for(i=0; i<N; i++)
if( !check(T[i]) ) break; // 依次判断根节点是否为0
Root = i;
}
return Root; // 返回根节点
}
3. 判断同构
int Isomorphic( Tree R1, Tree R2)
{
if( (R1==Null) && (R2==Null)) return 1; // 两棵树的根节点都为空
if( ((R1==Null)&&(R2!=Null)) || ((R1!=Null)&&(R2==Null)) ) return 0; //一棵空一颗不空的情况,非同构
if( T1[R1].Element != T2[R2].Element ) return 0; // 根节点的数值不等,直接舍弃
if( (T1[R1].Left == Null) && (T2[R2].Left == Null) ) return Isomorphic(T1[R1].Right, T2[R2].Right); // 两棵树的左子树为空,沿右子树向下迭代
if( (T1[R1].Left != Null)&&(T2[R2].Left != Null)&&((T1[T1[R1].Left].Element)==(T2[T2[R2].Left].Element)) ) // 两棵树左子树都不全为空,且根节点的值相等,值得迭代,先迭代树1树2的左子树,再迭代树1树2的右子树
return( (Isomorphic(T1[R1].Left, T2[R2].Left) && Isomorphic(T1[R1].Right, T2[R2].Right) );
else // 若某一情况不符合上面的假设,分别交叉迭代树1左子树和树2右子树,以及树1右子树和树2左子树,看是否交叉相等
return( Isomorphic( T1[R1].Left, T2[R2].Right) && Isomorphic( T1[R1].Right, T2[R2].Left));
}