静态查找-静态查找表结构
静态查找表是数据元素的线性表,可以是基于数组的顺序存储或以线性链表存储。/* 顺序存储结构*/
typedef struct{
ElemType *elem; /* 数组基址*/
int length; /* 表长度*/
}S_TBL;
/* 链式存储结构结点类型*/
typedef struct NODE{
ElemType elem; /* 结点的值域*/
struct NODE *next; /* 下一个结点指针域*/
}NodeType;
静态查找-顺序查找
分析查找算法的效率,通常用平均查找长度ASL 来衡量。定义:在查找成功时,平均查找长度ASL 是指为确定数据元素在表中的位置所进行的关键码比较次数的期望值。
对一个含n 个数据元素的表,查找成功时
其中:Pi 为表中第i 个数据元素的查找概率,
Ci 为表中第i 个数据元素的关键码与给定值kx 相等时,按算法定位时关键码的比较次数。显然,不同的查找方法,Ci 可以不同。
就上述算法而言,对于n 个数据元素的表,给定值kx 与表中第i 个元素关键码相等,即定位第i 个记录时,需进行n-i+1 次关键码比较,即Ci=n-i+1。则查找成功时,顺序查找的平均查找长度为:
查找不成功时,关键码的比较次数总是n+1 次。
算法中的基本工作就是关键码的比较,因此,查找长度的量级就是查找算法的时间复杂度,其为O(n)。
许多情况下,查找表中数据元素的查找概率是不相等的。为了提高查找效率,查找表需依据查找概率越高,比较次数越少;查找概率越低,比较次数就较多的原则来存储数据元素。
顺序查找缺点是当n 很大时,平均查找长度较大,效率低;优点是对表中数据元素的存储没有要求。另外,对于线性链表,只能进行顺序查找
静态查找-分块查找
88,43,14,31,78,8,62,49,35,71,22,83,18,52
按关键码值31,62,88 分为三块建立的查找表及其索引表如下:
插入排序—直接插入排序
设有n 个记录,存放在数组r 中,重新安排记录在数组中的存放顺序,使得按关键码有序。即
r[1].key≤r[2].key≤……≤r[n].key
先来看看向有序表中插入一个记录的方法:
设1<j≤n,r[1].key≤r[2].key≤……≤r[j-1].key,将r[j]插入,重新安排存放顺序,使得r[1].key≤r[2].key≤……≤r[j].key,得到新的有序表,记录数增1。
【算法10.1】
- r[0]=r[j]; //r[j]送r[0]中,使r[j]为待插入记录空位i=j-1; //从第i 个记录向前测试插入位置,用r[0]为辅助单元, 可免去测试i<1。
- 若r[0].key≥r[i].key,转④。//插入位置确定
- 若r[0].key < r[i].key 时,r[i+1]=r[i];i=i-1;转②。//调整待插入位置
- r[i+1]=r[0];结束。//存放待插入记录
直接插入排序方法:仅有一个记录的表总是有序的,因此,对n 个记录的表,可从第二个记录开始直到第n 个记录,逐个向有序表中进行插入操作,从而得到n 个记录按关键码有序的表。
【算法10.2】
void InsertSort(S_TBL &p)
{ for(i=2;i<=p->length;i++)
if(p->elem[i].key < p->elem[i-1].key) /*小于时,需将elem[i]插入有序表*/
{ p->elem[0].key=p->elem[i].key; /*为统一算法设置监测*/
for(j=i-1;p->elem[0].key < p->elem[j].key;j--)
p->elem[j+1].key=p->elem[j].key; /*记录后移*/
p->elem[j+1].key=p->elem[0].key; /*插入到正确位置*/
}
}
【效率分析】
空间效率:仅用了一个辅助单元。
时间效率:向有序表中逐个插入记录的操作,进行了n-1 趟,每趟操作分为比较关键码和移动记录,而比较的次数和移动记录的次数取决于待排序列按关键码的初始排列。
最好情况下:即待排序列已按关键码有序,每趟操作只需1 次比较2 次移动。总比较次数=n-1 次,总移动次数=2(n-1)次。
最坏情况下:即第j 趟操作,插入记录需要同前面的j 个记录进行j 次关键码比较,移动记录的次数为j+2 次。
插入排序—折半插入排序
直接插入排序的基本操作是向有序表中插入一个记录,插入位置的确定通过对有序表中记录按关键码逐个比较得到的。平均情况下总比较次数约为n2/4。既然是在有序表中确定插入位置,可以不断二分有序表来确定插入位置,即一次比较,通过待插入记录与有序表居中的记录按关键码比较,将有序表一分为二,下次比较在其中一个有序子表中进行,将子表又一分为二。这样继续下去,直到要比较的子表中只有一个记录时,比较一次便确定了插入位置。
二分判定有序表插入位置方法:
- low=1;high=j-1;r[0]=r[j];
// 有序表长度为j-1,第j 个记录为待插入记
//设置有序表区间,待插入记录送辅助单元 - 若low>high,得到插入位置,转⑤
- low≤high,m=(low+high)/2; // 取表的中点,并将表一分为二,确定待插入区间*/
- 若r[0].key<r[m].key,high=m-1; //插入位置在低半区否则,low=m+1; // 插入位置在高半区转②
- high+1 即为待插入位置,从j-1 到high+1 的记录,逐个后移,r[high+1]=r[0];放置待插入记录。
void InsertSort(S_TBL *s)
{ /* 对顺序表s 作折半插入排序*/
for(i=2;i<=s->length;i++)
{ s->elem[0]=s->elem[i]; /* 保存待插入元素*/
low=i;high=i-1; /* 设置初始区间*/
while(low<=high) /* 该循环语句完成确定插入位置*/
{ mid=(low+high)/2;
if(s->elem[0].key>s->elem[mid].key)
low=mid+1; /* 插入位置在高半区中*/
else high=mid-1; /* 插入位置在低半区中*/
}/* while */
for(j=i-1;j>=high+1;j--) /* high+1 为插入位置*/
s->elem[j+1]=s->elem[j]; /* 后移元素,留出插入空位*/
s->elem[high+1]=s->elem[0]; /* 将元素插入*/
}/* for */
}/* InsertSort */
【时间效率】
确定插入位置所进行的折半查找,关键码的比较次数至多为,次,移动记录的次数和直接插入排序相同,故时间复杂度仍为O(n2)。是一个稳定的排序方法。
插入排序—表插入排序
表插入排序,就是通过链接指针,按关键码的大小,实现从小到大的链接过程,为此需增设一个指针项。操作方法与直接插入排序类似,所不同的是直接插入排序要移动记录,而表插入排序是修改链接指针。用静态链表来说明。#define SIZE 200
typedef struct{
ElemType elem; /*元素类型*/
int next; /*指针项*/
}NodeType; /*表结点类型*/
typedef struct{
NodeType r[SIZE]; /*静态链表*/
int length; /*表长度*/
}L_TBL; /*静态链表类型*/
假设数据元素已存储在链表中,且0 号单元作为头结点,不移动记录而只是改变链指针域,将记录按关键码建为一个有序链表。首先,设置空的循环链表,即头结点指针域置0,并在头结点数据域中存放比所有记录关键码都大的整数。接下来,逐个结点向链表中插入即可。
【例10.2】表插入排序示例
重排记录方法:按链表顺序扫描各结点,将第i 个结点中的数据元素调整到数组的第i 个分量数据域。因为第i 个结点可能是数组的第j 个分量,数据元素调整仅需将两个数组分量中数据元素交换即可,但为了能对所有数据元素进行正常调整,指针域也需处理。
【算法10.3】
- j=l->r[0].next;i=1; //指向第一个记录位置,从第一个记录开始调整
- 若i=l->length 时,调整结束;否则,
a. 若i=j,j=l->r[j].next;i++;转(2) //数据元素应在这分量中,不用调整,处理下一个结点
b. 若j>i,l->r[i].elem<-->l->r[j].elem; //交换数据元素
p=l->r[j].next; // 保存下一个结点地址
l->r[j].next=l->[i].next;l->[i].next=j; // 保持后续链表不被中断
j=p;i++;转(2) // 指向下一个处理的结点
c. 若j<i,while(j<i) j=l->r[j].next;//j 分量中原记录已移走,沿j的指针域找寻原记录的位置转到(a)