静态查找——CQU

一、查找的基本概念

查找(Searching):就是根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素( 或记录)。

查找表(Search Table):是由同一类型的数据元素(或记录)构成的集合。

关键字(Key):数据元素中唯一标识该元素的某个数据项的值,使用基于关键字的查找,查找结果应该是唯一的。例如,在由一个学生元素构成的数据集合中,学生元素中“学号”这一数据项的值唯一地标识一名学生。

静态查找表(Static Search Table):只作查找操作的查找表。

  • 主要操作
  1. 查询某个“特定的”数据元素是否在查找表中。
  2. 检索某个“特定的”数据元素和各种属性。

平均查找长度:在查找过程中,一次查找的长度是指需要比较的关键字次数,而平均查找长度,则是所有查找过程中进行关键字的比较次数的平均值,其数学定义为
A S L = ∑ i = 1 n P i C i ASL=\sum^n_{i=1}P_iC_i ASL=i=1nPiCi
这个式子中,n是查找表的长度; P i P_i Pi是查找第 i i i个数据元素的概率,一般认为每个数据元素的查找概率相等,即 P i = 1 n P_i=\frac{1}n Pi=n1; C i C_i Ci是找到第 i i i个数据元素所需进行的比较次数。平均查找长度是衡量查找算法效率的最主要的指标。

二、顺序表查找

1.定义

顺序查找(Sequential Search) 又叫线性查找,是最基本的查找技术,作为一种最直观的查找方法,其基本思想是从线性表的一端开始,逐个检查关键字是否满足给定的条件。若查找到某个元素的关键字满足给定条件,则查找成功,返回该元素在线性表中的位置;若已经查找到表的另一端,但还没有查找到符合给定条件的元素,则返回查找失败的信息。

2.算法

代码如下:
平均查找长度:在查找过程中,一次查找的长度是指需要比较的关键字次数,而平均查找长度,则是所有查找过程中进行关键字的比较次数的平均值,其数学定义为
A S L = ∑ i = 1 n P i C i ASL=\sum^n_{i=1}P_iC_i ASL=i=1nPiCi
这个式子中,n是查找表的长度; P i P_i Pi是查找第 i i i个数据元素的概率,一般认为每个数据元素的查找概率相等,即 P i = 1 n P_i=\frac{1}n Pi=n1; C i C_i Ci是找到第 i i i个数据元素所需进行的比较次数。平均查找长度是衡量查找算法效率的最主要的指标。

二、顺序表查找

1.定义

顺序查找(Sequential Search) 又叫线性查找,是最基本的查找技术,作为一种最直观的查找方法,其基本思想是从线性表的一端开始,逐个检查关键字是否满足给定的条件。若查找到某个元素的关键字满足给定条件,则查找成功,返回该元素在线性表中的位置;若已经查找到表的另一端,但还没有查找到符合给定条件的元素,则返回查找失败的信息。

2.算法

代码如下:

/*有哨兵顺序查找*/
int Sequential_Search(int *a, int n, int key){
	int i;
	a[0] = key;	//设置a[0]为关键字,称之为“哨兵”
	i = n;	//循环从数组尾部开始
	while(a[i] != key){
		i--;
	}
	return i;	//返回0则说明查找失败
}

三、顺序查找的应用

1.查找最大值和最小值

查找顺序表中的最大值和最小值,比较次数不超过 3 2 n \frac32 n 23n

算法1:朴素查找法——比较次数:2(n-1)

//返回一个数组,数组第一个元素为最大值,第二个元素为最小值
int res[2];
int* findmaxandmin(int arr[],int n){
 int max=arr[0];
 int min=arr[0];
 for(int i=1;i<n;i++){
     if(max<arr[i]) max=arr[i];
     if(min>arr[i]) min=arr[i];
 }

 res[0]=max;
 res[1]=min;
 return res;
}

算法2:快速查找法——比较次数 3 2 n \frac32 n 23n

先比较两者,再用较小值和较大值分别与最小值和最大值比较

//返回一个数组,数组第一个元素为最大值,第二个元素为最小值
int res2[2];
int* findmaxandmin2(int arr[],int n){
    int max=arr[0];
    int min=arr[0];
    int k=n%2;//n是奇数,k从1开始,否则从0开始
    while (k<n)
    {
        if(arr[k]<arr[k+1]){
            if(min>arr[k]) min=arr[k];
            if(max<arr[k+1]) max=arr[k+1];
        }
        else{
            if(min>arr[k+1]) min=arr[k+1];
            if(max<arr[k]) max=arr[k];
        }
        k=k+2; //每次同时比较两个元素
    }
    res2[0]=max;
    res2[1]=min;
    return res2;
}
2.查找区间内所有质数

查找正整数区间[1,n](n>1)内所有质数。

(1).试除法 ——时间复杂度 O ( n n ) O(n\sqrt n) O(nn )

代码如下:

#include <iostream>
#include <cmath>
using namespace std;

// 判断一个数是否是质数的函数
bool isPrime(int num) {
  // 1和负数都不是质数
  if (num <= 1) return false;
  // 2是唯一的偶数质数
  if (num == 2) return true;
  // 其他偶数都不是质数
  if (num % 2 == 0) return false;
  // 从3开始,每次加2,只检查奇数因子
  for (int i = 3; i <= sqrt(num); i += 2) {
    // 如果有任何因子可以整除,就不是质数
    if (num % i == 0) return false;
  }
  // 没有找到任何因子,就是质数
  return true;
}

int main() {
  // 输入n的值
  int n;
  cout << "请输入n的值:";
  cin >> n;
  // 遍历区间[1,n],输出所有质数
  cout << "区间[1," << n << "]内的所有质数如下:" << endl;
  for (int i = 1; i <= n; i++) {
    if (isPrime(i)) {
      cout << i << " ";
    }
  }
  cout << endl;
  return 0;
}

(2)埃氏筛选法——时间复杂度:O(nloglog(n))

代码如下:

#include <iostream>
#include <vector>
using namespace std;

// 用埃式筛选法查找正整数区间[1,n]内所有质数的函数
vector<int> eratosthenes(int n) {
  vector<int> primes; // 存储找到的素数
  vector<bool> is_prime(n + 1, true); // 标记每个数是否是素数,初始都为true
  is_prime[0] = is_prime[1] = false; // 0和1不是素数,标记为false
  for (int i = 2; i <= n; i++) {
    if (is_prime[i]) { // 如果i是素数,就把它加入到primes中
      primes.push_back(i);
      // 用i的倍数从i*i开始筛掉所有能被i整除的数,因为小于i*i的合数已经被更小的素数筛掉了
      for (int j = i * i; j <= n; j += i) {
        is_prime[j] = false;
      }
    }
  }
  return primes; // 返回素数的向量
}

int main() {
  // 输入n的值
  int n;
  cout << "请输入n的值:";
  cin >> n;
  // 调用eratosthenes函数,得到区间[1,n]内的所有素数
  vector<int> primes = eratosthenes(n);
  // 输出素数
  cout << "区间[1," << n << "]内的所有素数如下:" << endl;
  for (int p : primes) {
    cout << p << " ";
  }
  cout << endl;
  return 0;
}

(3)合数限定法——时间复杂度:O(n)

代码:略

在这里插入图片描述

(4)欧拉筛选法——时间复杂度:O(n)

代码如下:

#include <iostream>
#include <vector>
using namespace std;

// 用欧拉筛选法查找正整数区间[1,n]内所有质数的函数
vector<int> euler(int n) {
  vector<int> primes; // 存储找到的素数
  vector<bool> is_prime(n + 1, true); // 标记每个数是否是素数,初始都为true
  is_prime[0] = is_prime[1] = false; // 0和1不是素数,标记为false
  for (int i = 2; i <= n; i++) { // i从2循环到n(外层循环)
    if (is_prime[i]) primes.push_back(i); // 如果i没有被前面的数筛掉,则i是素数,加入到primes中
    for (int j = 0; j < primes.size() && i * primes[j] <= n; j++) { // 筛掉i的素数倍,即i的primes[j]倍
      // j循环枚举现在已经筛出的素数(内层循环)
      is_prime[i * primes[j]] = false; // 倍数标记为合数,也就是i用primes[j]把i * primes[j]筛掉了
      if (i % primes[j] == 0) break; // 最神奇的一句话,如果i整除primes[j],退出循环
      // 这样可以保证线性的时间复杂度
    }
  }
  return primes; // 返回素数的向量
}

int main() {
  // 输入n的值
  int n;
  cout << "请输入n的值:";
  cin >> n;
  // 调用euler函数,得到区间[1,n]内的所有素数
  vector<int> primes = euler(n);
  // 输出素数
  cout << "区间[1," << n << "]内的所有素数如下:" << endl;
  for (int p : primes) {
    cout << p << " ";
  }
  cout << endl;
  return 0;
}

四、有序表查找

1.折半查找

折半查找(Binary Search)技术,又称为二分查找。它的前提是线性表中的记录必须是关键码有序(通常从小到大有序),线性表必须采用顺序存储。折半查找的基本思想是:在有序表中,取中间记录作为比较对象,若给定值与中间记录的关键字相等,则查找成功;若给定值小于中间记录的关键字,则在中间记录的左半区继续查找;若给定值大于中间记录的关键字,则在中间记录的右半区继续查找。不断重复上述过程,直到查找成功,或所有查找区域无记录,查找失败为止。

代码如下:

//实现方法1
int BinarySearch1(int arr[],int left,int right,int key){
    int low=left;
    int high=right;
    int mid;
    while(low<=high){
        mid = (low + high)/2;	//取中间位置
		if(arr[mid] == key){
			return mid;	//查找成功返回所在位置
		}else if(arr[mid] > key){
			high = mid - 1;	//从前半部分继续查找
		}else{
			low = mid + 1;	//从后半部分继续查找
		}
	}
	return -1;	//查找失败,返回-1
}
//实现方法2
int BinarySearch2(int arr[],int left,int right,int key){
    int low=left-1;
    int high=right+1;
    int mid;
    while(high>low+1){
        mid = (low + high)/2;	//取中间位置
		if(arr[mid] == key){
			return mid;	//查找成功返回所在位置
		}else if(arr[mid] > key){
			high = mid ;	//从前半部分继续查找
		}else{
			low = mid ;	//从后半部分继续查找
		}
	}
	return -1;	//查找失败,返回-1    
}
2.二分查找的应用

在这里插入图片描述

1.区间查询

代码如下:

int SearchinInterval(int arr[],int left,int right,int key){
    int low=left-1;
    int high=right+1;
    int mid;
    while(high>low+1){
        mid = (low + high)/2;	//取中间位置
		if(arr[mid] >= key){
			high = mid ;	
		}else{
			low = mid ;	//从后半部分继续查找
		}
	}
	return high;//或者 low;	// high :>=key的最小值位置 low:<key的最大值位置   
}
2.快速求幂

给定正整数a和n,求 a n a^n an的值

法一:直接迭代: a n = a n − 1 ∗ a a^n=a^n-1*a an=an1a ——时间复杂度O(n)

法二:二分递归: a n = a n 2 ∗ a n 2 ∗ a n % 2 a^n=a^{\frac n2}*a^{\frac n2}*a^{n\%2} an=a2na2nan%2——时间复杂度O(log(n))

代码如下:

int Power(int x,int n){
    if(n==1){
        return x;
    }
    int pow=Power(x,n/2);
    pow=pow*pow;
    if(n%2==1){
        pow=pow*x;
    }
    return pow;
}
3.快速查找

查找未排序序列 < a 1 , a 2 . … , a n > <a_1,a_2.\dots,a_n> <a1,a2.,an>中的第k小元素

法一: 循环k次选择排序或冒泡排序 ——时间复杂度O(kn)

法二:快速或递归排序——时间复杂度:O(nlogn)[排序]+O(1)[找第k小元素]

法三: 快速建最小堆+k次出堆——时间复杂度:O(n)+O(klogn)

法四:利用快速排序的partition过程,进行二分——时间复杂度:O(n)

法四,代码如下:(注意k输入时要-1)

void swap(int* a,int* b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}
int Partition(int* a,int begin,int end)
{
    
    int L = begin,R = end,key = begin;//定义L,R,key下标
    while(L < R)
    {
        while(a[R] >= a[key] && L < R)//右边先走,右边大于等于key就一直走下去,否则停下来,加上判断L < R,防止越界
        {
            R--;
        }
        while(a[L] <= a[key] && L < R)//同理
        {
            L++; 
        }
        swap(&a[L],&a[R]);//都停下来后交换
    }
    swap(&a[key],&a[R]);//最后key值和相遇点交换
    return R;
}

int QuickSearch(int arr[],int left,int right, int k){
    if(left<right){
        int m=Partition(arr,left,right);
        if(m==k) return arr[m];
        if(m<k) {
            return QuickSearch(arr,m+1,right,k);
        }

        else{
            return QuickSearch(arr,left,m-1,k);
        }
    }
    return arr[left];
}

索引表查找

在这里插入图片描述

  • 10
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Haru_Yuki

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值