常见的查找算法

670 阅读5分钟
  • 查找名词解释

    1. 查找:就是根据给定的某个值,在查找表中确定⼀个其关键字等于给定值 的数据元素
    2. 查找表:是由同⼀类型的数据元素(记录)构成的集合
    3. 关键字:是数据元素中某个数据项的值.⼜称为键值. ⽤它可 以表示⼀个数据元素,也可以标识⼀个记录的某个数据项(字段). 我们称为关键码
    4. 主关键字:若关键字可以唯⼀地标识⼀个记录, 则称此关键字为主关键字 (Primary Key)
    5. 次关键字:对于那些可以识别多个属于元素(记录)的关键字,我们称为次关键字(Secondary Key)
    6. 查找表的操作方式分类(静态/动态)
      • 静态查找表:只作查找操作的查找表;
        1. 查询某个”特定的”数据元素是否在查找表中;
        2. 检索某个"特定的"数据元素和各种属性;
      • 动态查找表:在查找过程中同时插⼊查找表中不存在的数据元素, 或者从查找表中删除已经存在的某个数据元素; 显然动态查找表的操作就是2个动
        1. 查找时插⼊数据元素;
        2. 查找时删除数据元素;
  • 顺序查找

    • 实现思路: 顺序查找是最简单基础的查找方式,又称为线性查找,其实就是从表中的第一个元素开始遍历到最后一个元素,如果某个记录的关键字和给定值相等则查找成功返回查找结果,如果遍历到最后一个元素都没有找到某个记录的关键字和给定值相等的话则返回查找失败

    • 实现代码

      //顺序查找
      //a为数组,n为查找的数组个数,key为要查找的关键字;
      //a[0]位置不存放数据
      int Sequential_Search(int *a,int n,int key){
          for(int i = 1; i <= n; i ++){
              if(a[i] == key){
                  return i;
              }
          }
          return 0;
      }
      
      //顺序查找
      //a为数组,n为查找的数组个数,key为要查找的关键字;
      //a[0]位置存放key
      //该方法可以少一个if判断
      int Sequential_Search2(int *a,int n,int key){
          a[0] = key;
          int i = n;
          while (a[i] != key) {
              i --;
          }
          return i;
      }
      
      
  • 折半查找

    • 实现思路 前提条件是传入的表中的内容是有顺序的,并且表的存储方式必须是春旭存储 现从表的中间比较,如果给定值大于表中间的值则舍弃更小的另一半,得到更大的另一半,然后继续从更大的那一半里面继续冲中间的记录开始比较重复上述操作,直到找到给定值,或者低值的索引大于高值的索引

    • 实现代码

      //3.折半查找算法
      //假设数组a,已经是有序的(从小到大)
      int Binary_Search(int *a,int n,int key){
          int low,high,mid;
          low = 1;
          high = n;
          while (low <= high) {
              mid = (low+high)/2;
              if(a[mid] > key){
                  //中间值大于key则去下半部分查找
                  high = mid - 1;
              }else if(a[mid] < key){
                  //中间值小于key则去上半部分查找
                  low = mid + 1;
              }else{
                  return mid;
              }
          }
          return 0;
      }
      
  • 插值查找

    • 实现思路: 插值查找和折半其实是很相似的无非就是mid值计算的不同,折半查找的mid = (low+high)/2 = low + (high - low)/2;而插值查找的mid = low + (key - a[low])/(a[high] - a[low])*(high-low);可以进一步的缩小key值得查找范围

    • 实现代码

      //3.插值查找算法
      //假设数组a,已经是有序的(从小到大)
      int Interpolation_Search(int *a,int n,int key){
          int low,high,mid;
          low = 1;
          high = n;
          while (low <= high) {
              mid = low + (key - a[low])/(a[high] - a[low])*(high-low);
              if(a[mid] > key){
                  //中间值大于key则去下半部分查找
                  high = mid - 1;
              }else if(a[mid] < key){
                  //中间值小于key则去上半部分查找
                  low = mid + 1;
              }else{
                  return mid;
              }
          }
          return 0;
      }
      
      
  • 斐波拉契查找

    • 实现思路

      1. 生成一个斐波拉契数列,
      2. 查找k值,F[k-1]<n<F[k]
      3. 判断如果F[k]-1 > n 则多出来的位置补齐就等于an
      4. 分别赋值low、high开始循环
      5. low = low + F[k-1]-1; 开始比较key < a[mid] 那么需要更改high值=mid-1和k值=k-1继续下一次循环,如果key > a[mid]则更改low = mid + 1和k值=k-2继续下一次循环,如果相等则需要判断mid是否大于n如果大于返回n,否则返回mid,

      注:斐波拉契查找又叫黄金分割查找,和折半查找的区别就在于折半查找每次都是冲中间开始分割,而斐波拉契查找是从数列的黄金分割点的位置开始分割

    • 实现代码

      int F[100]; /* 斐波那契数列 */
      int Fibonacci_Search(int *a,int n,int key){
          //第一步先生成斐波拉契数列
          F[0] = 0;
          F[1] = 1;
          for(int i = 2; i < MAXSIZE; i ++){
              F[i] = F[i-1] + F[i-2];
          }
          
          //第二步找到k值
          //{1,1,2,3,5,8,13}
          //F(k)-1=(F(k-1)-1)+(F(k-2)-1)+1
          int k = 0;
          while (n > F[k] - 1) {
              k++;
          }
          //n小于F[k] - 1的话则需要将数列补齐
          for(int i = n; n < F[k] - 1; i ++)
              a[i] = a[n];
          
          int low,high,mid;
          low = 1;
          high = n;
          while (low <= high) {
              
              //计算当前分隔的下标;
              mid = low+F[k-1]-1;
              
              
              if (key < a[mid]) {
                  //若查找的记录小于当前分隔记录;
                  //将最高下标调整到分隔下标mid-1处;
                  high = mid-1;
                  //斐波拉契数列下标减1位;
                  k = k-1;
                  
              }else if(key > a[mid]){
                  //若查找的记录大于当前的分隔记录;
                  //最低下标调整到分隔下标mid+1处
                  low = mid+1;
                  //斐波拉契数列下标减2位;
                  k = k-2;
                  
              }else{
                  if (mid <= n) {
                      //若相等则说明,mid即为查找的位置;
                      return mid;
                  }else
                  {
                      //若mid>n,说明是补全数值,返回n;
                      return n;
                  }
              }
          }
          return 0;
      }