基本搜索法有:线性搜索、二分搜索、散列法。
线性搜索
线性搜索就是从数组开头顺次访问各元素,检查该元素是否与目标值相等。若未发现目标值,则返回一个特殊值说明情况。
题目
请编写一个程序,输入包含n个整数的数列S以及包含q个不重复整数的数列T,输出既包含于T也包含于S的整数个数C。
思路
向线性搜索中引入“标记”可以将算法效率提高常数倍。“标记”就是我们在数组等数据结构中设置的一个拥有特殊值的元素。
在线性搜索中,我们可以把含有目标关键字的数据放在数组末尾,用作标记。
代码
#include <stdio.h>
/*线性搜索*/
int search(int A[],int n,int key){
int i=0;
A[n]=key;//设置标记
while(A[i]!=key) i++;
return i!=n;
}
int main(){
int i,n,A[1000+1],q,key,sum=0;
scanf("%d",&n);
for(i=0;i<n;i++) scanf("%d",&A[i]);
scanf("%d",&q);
for(i=0;i<q;i++){
scanf("%d",&key);
if(search(A,n,key)) sum++;
}
printf("%d\n",sum);
return 0;
}
二分搜索
有序数列
二分搜索的算法:
1、将整个数组作为搜索范围
2、检查位于搜索范围正中央的元素
3、如果中央元素的关键字与目标关键字一致则结束搜索
4、如果目标关键字小于中央元素的关键字,则以前半部分为搜索范围重新执行2;如果大于,则以后半部分为搜索范围重新执行2
题目
请编写一个程序,输入包含n个整数的数列以及包含q个不重复整数的数列T,输出既包含T也包含于S的整数的个数C。
限制:S的元素按升序排列
思路
充分利用限制,S的元素按升序排列。所以采用二分搜索法。
排序后使用二分搜索法的思路可以应用到很多问题中。
代码
#include <stdio.h>
int A[10000];
int n;
/*二分搜索法*/
int binarySearch(int key){
int left=0;
int right=n;
int mid;
while(left<right){
mid=(left+right)/2;
if(key==A[mid]) return 1;
else if(key<A[mid]) right=mid;
else if(key>A[mid]) left=mid+1;
}
return 0;
}
int main(){
int i,q,k,sum=0;
scanf("%d",&n);
for(i=0;i<n;i++){
scanf("%d",&A[i]);
}
scanf("%d",&q);
for(i=0;i<q;i++){
scanf("%d",&k);
if(binarySearch(k)) sum++;
}
printf("%d",sum);
return 0;
}
散列法
题目
请实现一个能执行以下命令的简易“字典”。
1、insert str :向字典中添加字符
2、find str :当前字典包含str时输出yes,不包含时输出no。
限制:输入的字符串仅有A、C、G、T组成。
思路
感觉蛮重要的,除了我没咋看懂之外都挺好的。嗐,记住就行。
散列法可以根据各元素来确定存储位置,然后将位置保管在散列表中,从而实现数据的高速搜索。其中散列表是一种数据结构,能对包含关键字的数据结合高效地执行动态插入、搜索和删除操作。
散列表由容纳了m个元素的数组T,以及根据数据关键字决定数组下标的函数共同组成。
散列函数(h(k))根据k值求数组T下标的函数,它的返回值成为散列值。
h(k)=k mod m(取余运算)
但是仅有这一个运算,会发生不同key对应同一散列值的情况,发生“冲突”。
H(k)=h(k,i)=(h1(k) + i*h2(k)) mod m
散列函数h(k,i)拥有关键字k和整数i两个参数,这里的i是发生冲突后计算下一个散列值的次数。即只要散列函数H(k)起了冲突,就会依次调用h(k,0)、h(k,1)…直到不发生冲突为止,然后返回这个h(k,i)的值作为散列值。
代码
#include <stdio.h>
#include <string.h>
#define M 1046527
#define NIL (-1)
#define L 14
char H[M][L];//散列表
/*将字符转换为数值*/
int getChar(char ch){
if(ch=='A') return 1;
else if(ch=='C') return 2;
else if(ch=='G') return 3;
else if(ch=='T') return 4;
else return 0;
}
//将字符串转换为数值并生成Key
long long getKey(char str[]){
long long sum=0,p=1,i;
for(i=0;i<strlen(str);i++){
sum += p*(getChar(str[i]));
p *=5;
}
return sum;
}
int h1(int key){return key%M;}
int h2(int key){return 1+(key%(M-1));}
//int h(int key,int i){return (h1(key)+i*h2(key))%M;}
int find(char str[]){
long long key,i,h;
key=getKey(str);
for(i=0;;i++){
h = ( h1(key) + i*h2(key)) % M;
if(strcmp(H[h],str)==0) return 1;//strcmp两个字符串是否相等
else if(strlen(H[h]==0)) return 0;
}
return 0;
}
int insert(char str[]){
long long key,i,h;
key=getKey(str);
for(i=0;;i++){
h = ( h1(key) + i*h2(key)) % M;
if(strcmp(H[h],str)==0) return 1;
else if(strlen(H[h])==0){
strcpy(H[h],str);//copy函数
return 0;
}
}
return 0;
}
int main(){
int i,n,h;
char str[L],com[9];
for(i=0;i<M;i++) H[i][0]='\0';
scanf("%d",&n);
for(i=0;i<n;i++){
scanf("%s %s",com,str);//赋值,数组前面不加&
if(com[0]=='i'){insert(str);}
else{
if(find(str)){printf("yes\n");}
else{printf("no\n");}
}
}
return 0;
}
借助标准库搜索
迭代器
迭代器是一种对象,用来对STL容器的元素进行迭代处理。它指向容器内部的特定位置。
运算符 | 作用 |
---|---|
++ | 让迭代器指向下一个元素 |
==,!= | 判断两个迭代器是否指向同一位置并返回结果 |
= | 将右侧的值代入左侧迭代器所引用元素的位置 |
* | 返回该位置的元素 |
优势在于,对任何种类的容器都可以用同一种方法顺次访问其元素。
可以把迭代器看成通过通用接口(函数等使用方法)对容器进行迭代处理的指针。
通过迭代器对向量元素进行读写操作:
#include <iostream>
#include <vector>
using namespace std;
/*打印*/
void print(vector<int> v){
//从向量开头顺次访问
vector<int>::iterator it;
for(it=v.begin();it!=v.end();it++){
cout<<*it;
}
cout<<endl;
}
int main(){
int n=4;
vector<int> v;
for(int i=0;i<n;i++){
int x;
cin>>x;
v.push_back(x);
}
print(v);
vector<int>::iterator it=v.begin();
*it=3;//将3赋值给开头元素v[0]
it++;//前移动一个位置
(*it)++;//v[1]的元素加1
print(v);
return 0;
}
low_bound
二分搜索方面,STL在库中提供了binary_search,lower_bound,upper_bound。
lower_bound是一种应用于有序数据范围内的算法,它可以返回一个迭代器,这个迭代器指向第一个不小于指定值value的元素。这样即可以找出第一个能够恰当插入value的位置,又能维持指定范围内的元素顺序(有序状态)。
相应的,upper_bound返回的迭代器指向第一个大于指定值value的元素。
#include <iostream>
#include <algorithm>
using namespace std;
int main(){
int A[14]={1,1,2,2,2,4,5,5,6,8,8,10,15};
int *pos;
int idx;
pos=lower_bound(A,A+14,3);
idx=distance(A,pos);
cout<<"A["<<idx<<"]="<<*pos<<endl;//A[5]=4
pos=lower_bound(A,A+14,2);
idx=distance(A,pos);
cout<<"A["<<idx<<"]="<<*pos<<endl;//A[2]=2
return 0;
}