九大查找算法,值得收藏(二)

简介: 九大查找算法,值得收藏

4 斐波那契查找


斐波那契查找与折半查找很相似,他是根据斐波那契序列的特点对有序表进行分割的。他要求开始表中记录的个数为某个斐波那契数小1,及n=F(k)-1;开始将k值与第F(k-1)位置的记录进行比较(及mid=low+F(k-1)-1).


算法思路:


相等,mid位置的元素即为所求


大于,low=mid+1,k-=2;


小于,high=mid-1,k-=1。


说明:


low=mid+1说明待查找的元素在[mid+1,high]范围内,k-=2 说明范围[mid+1,high]内的元素个数为n-(F(k-1))= Fk-1-F(k-1)=Fk-F(k-1)-1=F(k-2)-1个,所以可以递归的应用斐波那契查找。


代码:


#include "stdafx.h"
#include <memory>
#include  <iostream>
using namespace std;
const int max_size=20;//斐波那契数组的长度
/*构造一个斐波那契数组*/ 
void Fibonacci(int * F)
{
    F[0]=0;
    F[1]=1;
    for(int i=2;i<max_size;++i)
        F[i]=F[i-1]+F[i-2];
}
/*定义斐波那契查找法*/  
int FibonacciSearch(int *a, int n, int key)  //a为要查找的数组,n为要查找的数组长度,key为要查找的关键字
{
  int low=0;
  int high=n-1;
  int F[max_size];
  Fibonacci(F);//构造一个斐波那契数组F 
  int k=0;
  while(n>F[k]-1)//计算n位于斐波那契数列的位置
      ++k;
  int  * temp;//将数组a扩展到F[k]-1的长度
  temp=new int [F[k]-1];
  memcpy(temp,a,n*sizeof(int));
  for(int i=n;i<F[k]-1;++i)
     temp[i]=a[n-1];
  while(low<=high)
  {
    int mid=low+F[k-1]-1;
    if(key<temp[mid])
    {
      high=mid-1;
      k-=1;
    }
    else if(key>temp[mid])
    {
     low=mid+1;
     k-=2;
    }
    else
    {
       if(mid<n)
           return mid; //若相等则说明mid即为查找到的位置
       else
           return n-1; //若mid>=n则说明是扩展的数值,返回n-1
    }
  }  
  delete [] temp;
  return 0;
}
int main()
{
    int a[] = {0,1,4,35,47,53,62,78,88,99};
    int key=47;
    int index=FibonacciSearch(a,sizeof(a)/sizeof(int),key);
  if(index == 0)
  {
     cout<<"没有找到"<<key;
  }
  else
  {
       cout<<key<<" 的位置是:"<<index;
  }
    return 0;
}

5 哈希查找


哈希表:


我们使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函数, 也叫做散列函数),使得每个元素的关键字都与一个函数值(即数组下标)相对应,于是用这个数组单元来存储这个元素;也可以简单的理解为,按照关键字为每一个元素"分类",然后将这个元素存储在相应"类"所对应的地方。但是,不能够保证每个元素的关键字与函数值是一一对应的,因此极有可能出现对于不同的元素,却计算出了相同的函数值,这样就产生了"冲突",换句话说,就是把不同的元素分在了相同的"类"之中。后面我们将看到一种解决"冲突"的简便做法。


"直接定址"与"解决冲突"是哈希表的两大特点。


哈希函数:


规则:通过某种转换关系,使关键字适度的分散到指定大小的的顺序结构中,越分散,则以后查找的时间复杂度越小,空间复杂度越高。


算法思路:


如果所有的键都是整数,那么就可以使用一个简单的无序数组来实现:将键作为索引,值即为其对应的值,这样就可以快速访问任意键的值。这是对于简单的键的情况,我们将其扩展到可以处理更加复杂的类型的键。


用给定的哈希函数构造哈希表;


根据选择的冲突处理方法(常见方法:拉链法和线性探测法)解决地址冲突;


在哈希表的基础上执行哈希查找;


代码:

#include<stdio.h>
#include<stdlib.h>
#define SUCCESS 1
#define UNSUCCESS 0
#define OVERFLOW -1
#define OK 1
#define ERROR -1
#define MAXNUM 9999   // 用于初始化哈希表的记录 key
typedef int Status;
typedef int KeyType;
// 哈希表中的记录类型
typedef struct 
{
  KeyType key;
}RcdType;
// 哈希表类型
typedef struct 
{
  RcdType *rcd;
  int size;
  int count;
  int *tag;
}HashTable;
// 哈希表每次重建增长后的大小
int hashsize[] = { 11, 31, 61, 127, 251, 503 };
int index = 0;
// 初始哈希表
Status InitHashTable(HashTable &H, int size) 
{
  int i;
  H.rcd = (RcdType *)malloc(sizeof(RcdType)*size);
  H.tag = (int *)malloc(sizeof(int)*size);
  if (NULL == H.rcd || NULL == H.tag) return OVERFLOW;
  KeyType maxNum = MAXNUM;
  for (i = 0; i < size; i++) 
  {
    H.tag[i] = 0;
    H.rcd[i].key = maxNum;
  }
  H.size = size;
  H.count = 0;
  return OK;
}
// 哈希函数:除留余数法
int Hash(KeyType key, int m) 
{
  return (3 * key) % m;
}
// 处理哈希冲突:线性探测
void collision(int &p, int m) 
{
  p = (p + 1) % m;
}
// 在哈希表中查询
Status SearchHash(HashTable H, KeyType key, int &p, int &c) 
{
  p = Hash(key, H.size);
  int h = p;
  c = 0;
  while ((1 == H.tag[p] && H.rcd[p].key != key) || -1 == H.tag[p]) 
  {
    collision(p, H.size);  c++;
  }
  if (1 == H.tag[p] && key == H.rcd[p].key) return SUCCESS;
  else return UNSUCCESS;
}
//打印哈希表
void printHash(HashTable H)
{
  int  i;
  printf("key : ");
  for (i = 0; i < H.size; i++)
    printf("%3d ", H.rcd[i].key);
  printf("\n");
  printf("tag : ");
  for (i = 0; i < H.size; i++)
    printf("%3d ", H.tag[i]);
  printf("\n\n");
}
// 函数声明:插入哈希表
Status InsertHash(HashTable &H, KeyType key);
// 重建哈希表
Status recreateHash(HashTable &H) 
{
  RcdType *orcd;
  int *otag, osize, i;
  orcd = H.rcd;
  otag = H.tag;
  osize = H.size;
  InitHashTable(H, hashsize[index++]);
  //把所有元素,按照新哈希函数放到新表中
  for (i = 0; i < osize; i++) 
  {
    if (1 == otag[i]) 
    {
      InsertHash(H, orcd[i].key);
    }
  }
  return OK;
}
// 插入哈希表
Status InsertHash(HashTable &H, KeyType key) 
{
  int p, c;
  if (UNSUCCESS == SearchHash(H, key, p, c)) 
  { //没有相同key
    if (c*1.0 / H.size < 0.5) 
    { //冲突次数未达到上线
      //插入代码
      H.rcd[p].key = key;
      H.tag[p] = 1;
      H.count++;
      return SUCCESS;
    }
    else recreateHash(H); //重构哈希表 
  }
  return UNSUCCESS;
}
// 删除哈希表
Status DeleteHash(HashTable &H, KeyType key) 
{
  int p, c;
  if (SUCCESS == SearchHash(H, key, p, c)) 
  {
    //删除代码
    H.tag[p] = -1;
    H.count--;
    return SUCCESS;
  }
  else return UNSUCCESS;
}
int main()
{
  printf("-----哈希表-----\n");
  HashTable H;
  int i;
  int size = 11;
  KeyType array[8] = { 22, 41, 53, 46, 30, 13, 12, 67 };
  KeyType key;
  //初始化哈希表
  printf("初始化哈希表\n");
  if (SUCCESS == InitHashTable(H, hashsize[index++])) printf("初始化成功\n");
  //插入哈希表
  printf("插入哈希表\n");
  for (i = 0; i <= 7; i++) 
  {
    key = array[i];
    InsertHash(H, key);
    printHash(H);
  }
  //删除哈希表
  printf("删除哈希表中key为12的元素\n");
  int p, c;
  if (SUCCESS == DeleteHash(H, 12)) 
  {
    printf("删除成功,此时哈希表为:\n");
    printHash(H);
  }
  //查询哈希表
  printf("查询哈希表中key为67的元素\n");
  if (SUCCESS == SearchHash(H, 67, p, c)) printf("查询成功\n");
  //再次插入,测试哈希表的重建
  printf("再次插入,测试哈希表的重建:\n");
  KeyType array1[8] = { 27, 47, 57, 47, 37, 17, 93, 67 };
  for (i = 0; i <= 7; i++) 
  {
    key = array1[i];
    InsertHash(H, key);
    printHash(H);
  }
  getchar();
  return 0;
}

6 二叉树查找


二叉查找树是先对待查找的数据进行生成树,确保树的左分支的值小于右分支的值,然后在就行和每个节点的父节点比较大小,查找最适合的范围。 这个算法的查找效率很高,但是如果使用这种查找方法要首先创建树。


算法思路:


若b是空树,则搜索失败:


若x等于b的根节点的数据域之值,则查找成功:


若x小于b的根节点的数据域之值,则搜索左子树:


查找右子树。


代码:


递归和非递归

//非递归查找算法
BSTNode *BST_Search(BiTree T,ElemType key,BSTNode *&p)
{
    //查找函数返回指向关键字值为key的结点指针,不存在则返回NULL
    p=NULL;     
    while(T!=NULL&&key!=T->data)
    {
        p=T;                          //指向被查找结点的双亲  
        if(key<T->data)
            T=T->lchild;              //查找左子树
        else
            T=T->rchild;              //查找右子树
    }
    return T;
}
//递归算法
Status Search_BST(BiTree T, int key, BiTree f, BiTree *p)
{
    //查找BST中是否存在key,f指向T双亲,其初始值为NULL
    //若查找成功,指针p指向数据元素结点,返回true;
    //若失败,p指向查找路径上访问的最后一个结点并返回false
    if(!T)
    {
        *p=f;
        return false;
    }
    else if(key==T->data)
    {                      //查找成功
        *p=T;
        return true;
    }
    else if(key<T->data)
        return Search_BST(T->lchild,key,T,p);   //递归查找左子树
    else
        return Search_BST(T->rchild,key,T,p);   //递归查找右子树
}
相关文章
|
1月前
|
搜索推荐 算法 测试技术
数据结构:一篇拿捏十大排序(超详细版)
数据结构:一篇拿捏十大排序(超详细版)
38 0
|
11月前
|
算法
五大常用算法-分治算法
五大常用算法-分治算法
43 0
|
11月前
|
供应链 算法 Java
java数据结构最经济的地下通道建设方案prim算法
MX是世界上第一大传媒娱乐企业,该公司数十年的经营历史中创作了很多经典影片,此外还经营着很多的规模十分宏大世界级的主题娱乐公园。最近MX公司刚和C国X城市达成协定,共同投资建设C国国内唯一一家主题娱乐公园。
51 0
|
存储 移动开发 算法
攻克数据结构和算法——第六天:排序
若关键字是主关键字(关键字值不重复),这无论采用何种排序方法,排出的结果都是唯一的;若关键字是次关键字(关键字值可以重复),则排出的结果可能不唯一。
162 0
攻克数据结构和算法——第六天:排序
|
数据采集 存储 算法
【查找算法】解析学习四大常用的计算机查找算法 | C++
在数据处理的过程中,能否在最短时间内去找到目的数据,是编程开发人员非常值得关心的一个问题。所谓查找,也被称为搜索,它是指从数据文件中找出满足某些条件的记录。在数据结构中描述算法时习惯用“查找”,而在搜索引擎中找信息或资料时习惯用“搜索”。我们在电话簿中查找某人的电话号码,电话簿就像是数据文件库,而姓名就是去查找电话号码的键值。我们经常使用的搜索引擎所设计的Spider程序(网页抓取程序爬虫)会主动经由网站上的超链接“爬行”到另一个网站,搜集每个网站上的信息并且收录到数据库中,这其中就涉及到了今天要讲的查找算法。
132 0
【查找算法】解析学习四大常用的计算机查找算法 | C++
|
存储 算法 搜索推荐
408王道数据结构强化——应用题(三)
408王道数据结构强化——应用题
263 1
408王道数据结构强化——应用题(三)
【夯实算法基础】 并查集
【夯实算法基础】 并查集
【夯实算法基础】 并查集
|
算法
九大查找算法,值得收藏(三)
九大查找算法,值得收藏
126 0
九大查找算法,值得收藏(三)