一 概述
在有序递增顺序表L中,使用最少的时间查找数值为x的元素,若找到则将其与后继元素位置交换,若找不到则将其插入表中并使表中并使表中的元素仍然递增有序。
二 算法思想
对于有序递增顺序表的查找问题,我们可以采用顺序查找,也可以使用折半查找。显然后者比前者效率高,所以使用折半查找会比较快。
三 代码实现
顺序查找:从表的一端开始,依次将记录的关键字和给定的值进行比较,若某个记录的关键字和给定的值相等,则查找成功;反之,若扫描整个表后,仍未找到关键字和给定值相等的记录,则查找失败。顺序查找方法既适用于线性表的顺序存储结构,又使用于线性表的链式存储结构。
#include<iostream>//输入头
using namespace std;//标准输入输出
typedef int Status;
typedef int ElemType;
#define MAXSIZE 100
#define ERROR -1
#define OK 0
typedef struct {
ElemType *elem;
int length;
}SqList;
Status init_Queue(SqList &L){
L.elem = new ElemType[MAXSIZE]; //初始化最大长度的顺序表
if(!L.elem){
return ERROR; //初始化失败
}
L.length = 0; //初始时数据表为空
return OK;
}
Status FindAndSwop_Queue(SqList &L,ElemType x) {
ElemType temp;
int i=0,j=0;
while(i<L.length) {
if(L.elem[i]==x && i != L.length -1) {
temp = L.elem[i];
L.elem[i] = L.elem[i+1];
L.elem[i+1] = temp;
break;
}
if(x > L.elem[L.length-1]){
L.elem[L.length] = x;
L.length++;
}else if(L.elem[i] < x && x < L.elem[i+1]){
for(j = L.length - 1; j >= i+1; j--){
L.elem[j+1] = L.elem[j];
}
++L.length;
L.elem[i+1] = x;
break;
}
i++;
}
return OK;
}
int main(){
SqList L;
ElemType x;
cout<<"1.初始化顺序表!\n";
cout<<"2.输入6个不同的数字!\n";
cout<<"3.查询x并且保持有序的序列!\n";
cout<<"0.退出!"<<endl<<endl;
int elect = -1;
while(elect !=0){
cout<<"请选择你的后续操作:";
cin>>elect;
switch(elect){
case 1:
if(!init_Queue(L)){
cout<<"初始化顺序表成功!"<<endl;
}else{
cout<<"初始化顺序表失败!"<<endl;
}
break;
case 2:
cout<<"请输入6不同数字:" ;
for(int i = 0; i < 6; i++){
cin>>L.elem[i];
L.length = 6;
}
break;
case 3:
cout<<"请输入要查找的数字:";
cin>>x;
FindAndSwop_Queue(L,x);
cout<<"查询x并且保持有序的序列为:" ;
for(int i = 0; i < L.length; i++ ){
cout<<L.elem[i]<<" ";
}
cout<<endl;
break;
}
}
return 0;
}
顺序查找的结果:
折半查找(二分查找):折半查找是一种效率比较高的查找方法,但是,折半查找要求线性表必须采用顺序存储结构,而且表中的元素按关键字有序排序。该算法已经定义了递增,所以在折半查找的过程中,从表中间记录开始,如果给定值和中间记录关键字相等,则查找成功;如果给定值大于或者小于中间记录的关键字,则在表中大于或者小于中间记录的那一半中查找,这样重复操作,直到查找成功,或者在某一步中查找区间为空,则代表查找失败。
折半查找每一次查找比较都使查找范围缩小一半,与顺序查找相比,很显然会提高查找效率。为了标记查找过程中每一次的查找区间,我们可以定义low和high来表示当前查找区间的下界和上界,mid为区间的中间位置。
/*
折半查找的步骤:
1.设置查找区间初值,low为1,high为表长度
2.当low小于high时,循环执行一下操作:
。min取值为low和high的中间值;
。将给定值key与中间位置记录的关键字进行比较,若相等则查找成功,返回中间位置mid;
。若不相等则利用中间位置记录将表对分成前,后两个子表。如果key比中间位置记录的关键字
小,则high重新取值为mid-1,否则low重新取值为mid+1;
3.循环结束,说明查找区间为空,则查找失败,返回0;
*/
#include<iostream>//输入头
using namespace std;//标准输入输出
typedef int Status;
typedef int ElemType;
#define MAXSIZE 100
#define ERROR -1
#define OK 0
typedef struct {
ElemType *elem;
int length;
}SqList;
Status init_Queue(SqList &L){
L.elem = new ElemType[MAXSIZE]; //初始化最大长度的顺序表
if(!L.elem){
return ERROR; //初始化失败
}
L.length = 0; //初始时数据表为空
return OK;
}
Status FindAndSwop_Queue(SqList &L,ElemType x) {
ElemType temp;
int low = 0, high = L.length-1, mid,i;
while(low <= high) {
//在循环体中初始化mid值,是为了在动态变化中mid值也会动态变化。
mid = (low+high)/2;
//mid < L.length表示若是最后一个元素与x相等,则不存在与其后继交换的操作。
if(L.elem[mid] == x && mid < L.length - 1){
temp = L.elem[mid];
L.elem[mid] = L.elem[mid+1];
L.elem[mid+1] = temp;
break;
}else if(L.elem[mid] < x) {
low = mid + 1;
}else{
high = mid -1;
}
}
if(low > high){
//当high<low的时候,说明查找区间不存在,也即没有关键字为x的元素,此时选择插入值为x的元素
//于此同时可得,关键字为x的元素值属于区间L.elem[high]<x<L.elem[low];
for(i = L.length - 1; i > high ; i--) {
L.elem[i+1] = L.elem[i];
}
++L.length;
L.elem[i+1] = x;
}
return OK;
}
int main(){
SqList L;
ElemType x;
cout<<"1.初始化顺序表!\n";
cout<<"2.输入6个不同的数字!\n";
cout<<"3.查询x并且保持有序的序列!\n";
cout<<"0.退出!"<<endl<<endl;
int elect = -1;
while(elect !=0){
cout<<"请选择你的后续操作:";
cin>>elect;
switch(elect){
case 1:
if(!init_Queue(L)){
cout<<"初始化顺序表成功!"<<endl;
}else{
cout<<"初始化顺序表失败!"<<endl;
}
break;
case 2:
cout<<"请输入6不同数字:" ;
for(int i = 0; i < 6; i++){
cin>>L.elem[i];
L.length = 6;
}
break;
case 3:
cout<<"请输入要查找的数字:";
cin>>x;
FindAndSwop_Queue(L,x);
cout<<"查询x并且保持有序的序列为:" ;
for(int i = 0; i < L.length; i++ ){
cout<<L.elem[i]<<" ";
}
cout<<endl;
break;
}
}
return 0;
}
折半查找的结果:
四 顺序查找与折半查找的特点
顺序查找的优点:对于表结构无任何要求,既适用于顺序结构,也适用于链式结构,无论记录是否按关键字有序均可应用。
顺序查找的缺点:平均查找长度较大,查找效率较低,所以当表长很大的时候,不适合使用顺序查找。
折半查找的优点:折半查找的次数比较少,查询效率高。
折半查找的缺点:折半查找对表结构要求比较高,只能用于顺序存储的有序表,查找前需要排序,而排序本身是一种费时的运算。同时为了保持顺序表的有序性,对有序表进行插入和删除时,平均比较和移动表中一半元素,同样比较费时,故折半查找不适合数据元素经常变动的线性表。