查找算法 数据结构

查找算法总体分为静态查找,和动态查找,其中静态查找法,不改变查找表结构,动态查找表,可以进行插入和删除操作。



一、查找的基本概念

查找,也可称检索,是在大量的数据元素中找到某个特定的数据元素而进行的工作。查找是一种操作。

二、顺序查找
针对无序序列的一种最简单的查找方式。
时间复杂度为O(n)。

三、折半查找

针对已排序序列的一种查找方式。并且只适用于顺序存储结构的序列。要求序列中的元素基本不变,在需要做删除和插入操作的时候,会影响检索效率。

时间复杂度为O(logN)查找的最大次数为二叉树的深度log(n)向下取整+1
五、散列(hash)表
关键字:哈希函数、装填因子、冲突、同义词;
关键字和和存储的地址建立一个对应的关系:
Add = Hash(key);
 解决冲突方法:

开放定址法 – 探测方式:线性探测、二次探测。
链地址法 – 利用链表的方式。

查找找效率不依赖于数据长度n,查找效率非常快,很多能达到O(1),查找的效率是a(装填因子)的函数,而不是n的函数。因此不管n多大都可以找到一个合适的装填因子以便将平均查找长度限定在一个范围内。

以上文字 转自:http://www.cnblogs.com/mcgrady/archive/2013/08/18/3266065.html

下面主要分析折半查找和哈希查找

折半查找主要用于有序的序列。当看到在有序的序列中查找某个数时应该想到折半查找。快速排序中的int Partition(int *a,int start,int end)函数实现的功能是取一个枢纽元素将数组分成两部分一部分比枢纽元素大,一部分比枢纽元素小。  将Partition 函数结合折半查找的思想可以解决很多问题。比如查找数组中第K个大的元素,取数组中最小的K个数数组中出现次数超过一半的数。

一定要注意二叉查找只针对有序的数组:二分查找的基本思想:假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<algorithm>
using namespace std;
//非递归算法
//在有序的数组a[low...high]中查找元素key默认为从小到大排序是否存在存在则返回元素所在索引不存在则返回-1
int BinarySearch(int *a,int low,int high,int key)
{
    if(a==NULL||low>high)
	{
	    return -1;
	}
    int mid;
	while(low<=high)
	{
		 mid=(low+high)/2;
		if(a[mid]==key)
		{
		     return mid;
		}
		else if(a[mid]>key)
		{
		     high=mid-1;
		}
		else if(a[mid]<key)
		{
		    low=mid+1;
		}
	}
	return -1;
}
//二分查找的递归算法
在有序的数组a[low...high]中查找元素key默认为从小到大排序是否存在存在则返回元素所在索引不存在则返回-1
int BinarySearch2(int *a,int low,int high,int key)
{
    if(a==NULL||low>high)
	{
	    return -1;
	}
	 int mid;
	 if(low<=high)
	 {
		  mid=(low+high)/2;
	      if(a[mid]==key)
		  {
		      return mid;
		  }
		  else if(a[mid]>key)
		  {
		      return BinarySearch2(a,low,mid-1,key);
		  }
		  else if(a[mid]<key)
		  {
		       return BinarySearch2(a,mid+1,high,key);
		  }
	 }
	 return -1;
}
int main()
{
	srand(time(NULL));
	int a[10]={8,9,9,2,1,1,4,7,1,3};
	for(int i=0;i<10;i++)
	{
		a[i]=rand()%10;
	}
	sort(a,a+sizeof(a)/sizeof(a[0]));
	for(int i=0;i<10;i++)
	{
		cout<<a[i]<<"  ";
	}
	cout<<endl;
	int index=0;
	index=BinarySearch(a,0,9,8);uu
	cout<<"index="<<index<<endl;
	index=BinarySearch2(a,0,9,8);
	cout<<"index="<<index<<endl;
  return 0;
}

哈希查找:

哈希查找是指; 根据哈希函数得到数据元素的存储地址的进行查找的一种方法,其查找的时间复杂度为O(1) 是典型的以空间换取时间的方法

哈希函数是     将数据元素映射为数据元素存储地址的一种方法

哈希查找的操作步骤:

⑴用给定的哈希函数构造 哈希表
⑵根据选择的冲突处理方法解决地址冲突;
⑶在哈希表的基础上执行哈希查找。

哈希查找的核心是: 构造哈希函数,和处理冲突:

假设数据元素为key   哈希函数为  int Hash(int key);      则数据元素key的存储地址为address=Hash(key)   对于用数组来实现哈希表来说 Hash(key) 即为数据元素key

在数组中的索引

构造哈希函数的方法有:

1 直接定址法:   Hash(key)= key  或    Hash(key)=a*key+b   其中a和b是常数 

直接定制法比较有用: 对于统计字符的个数可以用直接定制法构造哈希函数,用数组实现简单的哈希表。a['b'] ++;

2除留余数法:这个方法比较常用也比较容易实现:

数据元素key的地址Hash(key)=key%p   其中p<=m; m为哈希表的长度。根据经验p 一般取小于m 的质数(最好接近m) 或包含小于20的质因数的合数

个人觉得掌握这两种方法就够了。其方法,要用到再看。

3 数字分析法

4平方取中法: 假设数据元素为1234 它的平方就是1522756   取中间的三位227作为地址

5 折叠法:

处理冲突的方法有:

1开放定址法 Hash(key) =(Hash(key)+di)mod m    其中m为哈希表的长度 当di=1,2,3,...m-1 时称为线性探测法。而当di= 1^2,-1^2,2^2,-1^2...时称为二次探测法。

自己写程序时可以取di=1,2,3,......

2 链地址法:在查找表中的每个记录中增加一个链域,链域中存放一个具有相同哈希函数值的记录的存储地址,利用链域就把若干个发生冲突的记录链接在一个链表内,当链域的值为NUL 时表示没有后继结点了


哈希表的装填因子:  a=表中装入的记录数/哈希表的长度

查找找效率不依赖于数据长度n,查找效率非常快,很多能达到O(1),查找的效率是a(装填因子)的函数,而不是n的函数。因此不管n多大都可以找到一个合适的装填因子以便将平均查找长度限定在一个范围内。

a越小发生冲突的可能性越小。其实就是用空间换取时间嘛。空间牺牲越多冲突当然少了。。。

哈希查找之所以快:是因为数据元素和存储地址之间直接建立了映射关系

下面用数组实现哈希表的操作,用除留余数法构造哈希函数,以及开放地址法中线性探测来处理冲突:


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<algorithm>
using namespace std;
#define HashTableSize 100 //定义哈希表的长度
#define NULLKEY 0x7fffffff // 用于初始化哈希表用  0x7fffffff为32位整数所能取的最大值,一般元素不会取到这个值
typedef int HashTable[HashTableSize];//定义哈希表的结构
//用除留余数法 构造哈希函数Hash() 返回值为数据元素的存储地址,对于数组就是元素所在的索引
int Hash(int key)
{
     int address;
	 address=key%HashTableSize;  //  其实此处最后用一个小于HashTableSize 的质数
	 return address;
}
//初始化哈希表
void InitHashTable(HashTable hashtable)
{
     for(int i=0;i<HashTableSize;i++)
	 {
	      hashtable[i]=NULLKEY;
	 }
}
//有时需要统计键值key 的信息 可以定义一个结构体 typedef struct Key{int key;int count;} typedef Key HashTable[HashTableSize];
void InsertHashTable(HashTable hashtable,int key)
{
     int address=Hash(key);
	 while(hashtable[address]!=NULLKEY)//若不等于NULLKEY 表示冲突
	 {
	      address=(address+1)%HashTableSize;//开放定址法处理冲突
	 }
	 hashtable[address]=key;
}
int SearchHashTable(HashTable hashtable,int key)//返回值为元素key 所在的索引 ,返回-1 表示没找到
{
    int address=Hash(key);
	while(hashtable[address]!=key)
	{
	   address=(address+1)%HashTableSize;//查找的时候也需要用开放定址法查找到冲突元素的地址
	   if(hashtable[address]==NULLKEY||address==Hash(key))//如果地址返回一开始的地址或找到NULLKEY 元素表示HashTable 表中不存在元素key
	   {
	       return -1;
	   }
	}
	return address;
}
int main()
{
	HashTable ht;
	InitHashTable(ht);
	//srand(time(NULL));
	for(int i=0;i<50;i++)
	{
		int temp=rand()%30;
	     InsertHashTable(ht,temp);
		 cout<<temp<<" ";
	}
	cout<<endl;

	for(int i=0;i<HashTableSize;i++)
	{
		
		 cout<<ht[i]<<" ";
	}
	cout<<endl;
	cout<<"查找元素11 "<<SearchHashTable(ht,11)<<endl;
	cout<<"查找元素15 "<<SearchHashTable(ht,15)<<endl;
    return 0;
}
































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值