【挑战程序设计】 搜索


基本搜索法有:线性搜索、二分搜索、散列法。

线性搜索

线性搜索就是从数组开头顺次访问各元素,检查该元素是否与目标值相等。若未发现目标值,则返回一个特殊值说明情况。

题目

请编写一个程序,输入包含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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值