常见查找算法实现(c++)
1 查找基本概念
查找表:由同一类型的数据元素构成的集合
关键字:数据中某个数据项的值;主关键字唯一表示记录;次关键字不唯一表示记录。
查找:就是根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录)。不存在则出现“空记录”或“空指针”。
查找表的操作方式:
- 静态查找表:只做查找操作的查找表(可以考虑对主关键字排序后,在应用折半查找)
- 动态查找表:在查找过程中同时插入查找表中不存在的数据元素,或删除元素。(考虑二叉排序树技术)
2 复杂度分析
算法 | 复杂度 |
---|---|
顺序表查找 | O ( n ) O(n) O(n) |
有序表查找 | O ( ) O() O() |
线性索引查找 | O ( ) O() O() |
二叉排序树 | O ( ) O() O() |
平衡二叉树(AVL) | O ( ) O() O() |
多路查找树(B树) | O ( ) O() O() |
散列表查找 | O ( ) O() O() |
3 代码实现
3.1 顺序表查找
顺序查找(Sequential Search)又叫线性查找,是最基本的查找技术,它的查找过程是:从表中第一个(或最后一个)记录开始,逐个进行记录的关键字和给定值比较,若某个记录的关键字和给定值相等,则查找成功,找到所查的记录;如果直到最后一个(或第一个)记录,其关键字和给定值比较都不等时,则表中没有所查的记录,查找不成功。
3.1 顺序表查找算法
#include <iostream>
using namespace std;
int sequential_search(int* a, int N, int target) //int a[];int* a;int a[4];
{
int j = -1;
for (int i = 0; i < N; i++)
{
if (a[i] == target)
{
j = i;
return j;
}
}
return j;
}
int main()
{
int a[100];
int N;
int i_target = -1;
cout << "请输入数组元素个数:" << endl;
cin >> N;
cout << "请循环输入"<< N << "个数:" << endl;
for (int i = 0; i < N; i++)
{
cin >> a[i];
}
cout << "请输入需要查找的数:" << endl;
int target;
cin >> target;
i_target = sequential_search(a, N, target);
if (i_target == -1)
{
cout << "未找到" << endl;
}
else
{
cout << "找到,下表为"<< i_target << endl;
}
system("pause");
system("cls");
return 0;
}
3.2 顺序表查找优化算法
每次循环都要对i<N进行越界判断
解决办法:设置哨兵
#include <iostream>
using namespace std;
int sequential_search(int* a, int N, int target)
{
int j = N;
while (target != a[j])
{
j--;
}
return j;
}
int main()
{
int a[100];
a[0] = 0;//预留给哨兵
int N;
int i_target = -1;
cout << "请输入数组元素个数:" << endl;
cin >> N;
cout << "请循环输入"<< N << "个数:" << endl;
for (int i = 1; i < N+1; i++)
{
cin >> a[i];
}
cout << "请输入需要查找的数:" << endl;
int target;
cin >> target;
i_target = sequential_search(a, N, target);
if (i_target == 0)
{
cout << "未找到" << endl;
}
else
{
cout << "找到,下表为"<< i_target-1 << endl;
}
system("pause");
system("cls");
return 0;
}
3.2 有序表查找
3.2.1 二分查找,其复杂度为 O ( l o g n ) O(log_n) O(logn)
使用二分查找前提是需要有序表顺序存储,对于需要频繁执行插入或删除的数据集来说,维护有序的排序会带来不小的工作量。
#include <iostream>
using namespace std;
int binary_search(int* a, int n,int target)
{
int mid, min, max;
min = 0;
max = n - 1;
while (min < max)
{
mid = (min + max) / 2;
if (target > a[mid])
{
min = mid+1;
}
else if (target > a[mid])
{
max = mid - 1;
}
else
{
return mid;
}
}
return 0;
}
int main()
{
int a[100];
int N;
int i_target = -1;
cout << "请输入数组元素个数:" << endl;
cin >> N;
cout << "请循环输入" << N << "个数:" << endl;
for (int i = 0; i < N; i++)
{
cin >> a[i];
}
cout << "请输入需要查找的数:" << endl;
int target;
cin >> target;
i_target = binary_search(a, N, target);
if (i_target == 0)
{
cout << "未找到" << endl;
}
else
{
cout << "找到,下表为" << i_target << endl;
}
system("pause");
return 0;
}
3.2.2 插值查找
mid = min + (max - min) * (target - a[min]) / (a[max] - a[min]);
优点:对于表长更大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好得多。
缺点:极端不均匀的数据,插值查找未必是合适的选择。
3.2.3 斐波那契查找
利用黄金分割原理
mid = min +F(k-1)-1;
平均情况优于折半查找,最差情况低于折半查找。
3.3 线性索引查找
很多数据集可能增长非常快,要保证记录全部是按照当中的某个关键字有序,其时间代价是非常高昂的,所以这种数据通常都是按先后顺序存储。
对于这样的查找表,为了能够快速查找到需要的数据,就需要用到索引。
数据结构的最终目的是提高数据的处理速度,索引是为了加快查找速度而设计的一种数据结构。索引就是把一个关键字与它对应的记录相关联的过程,一个索引由若干个索引项构成,每个索引项至少应包含关键字和其对应的记录在存储器中的位置等信息。索引技术是组织大型数据库以及磁盘文件的一种重要技术。
以下重点介绍三种线性索引:稠密索引、分块索引、倒排索引。
(一)稠密索引
稠密索引是指在线性索引中,将数据集中的每个记录对应一个索引项。
稠密索引要应对的可能是成千上万的数据,因此对于稠密索引这个索引表来说,索引项一定是按照关键码有序的排列。
(二)分块索引
例子:图书馆藏书。
分块有序,是把数据集的记录分成了若干块,将每块对应一个索引项,并且这些块需要满足:
(1)块内无序,即每一块内的记录不要求有序。当然,你如果能够让块内有序对查找来说更理想,不过这就要付出大量时间和空间代价,因此通常我们不要求快内有序。
(2)块间有序,例如,要求第二块所有记录的关键字均要大于第一块中所有记录的关键字,第三块所有记录的关键字均要大于第二块的所有记录关键字……因为只有块间有序,才有可能在查找时带来效率。
分块索引的索引项结构分为三个数据项:
(1)最大关键码,存储每一块中的最大关键字;
(2)存储了块中的记录个数,以便循环时用;
(3)用于指向块首数据元素的指针,便于开始对这一块中记录进行遍历。
(三)倒排索引
网页搜索一般用的就是倒排索引。
倒排索引的通用结构是:
- 次关键码(如“英文单词”)
- 记录号表(如“文章编号”)
记录号表存储具有相同次关键字的所有记录的记录号(可以是指向记录的指针或者是该记录的主关键字)
倒排索引
3.4 二叉排序树
3.5 平衡二叉树
3.6 多路查找树
3.7 散列表查找
总结
冒泡、选择、插入排序:
#include <iostream>
#include <vector>
using namespace std;
void print(vector<int> a)
{
int len = a.size();
for (int i = 0; i < len; i++)
{
cout << a[i] << " ";
}
cout << endl;
}
void swap(vector<int> &a, int i, int j)
{
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
void BubbleSort(vector<int> &a)
{
int len = a.size();
bool flag = true;
for (int i = 0; i < len - 1; i++)
{
for (int j = 1; j < len - i; j++)
{
if (a[j] < a[j - 1])
{
swap(a, j, j - 1);
flag = false;
}
}
if (flag) break;
}
}
void SelectSort(vector<int> &a)
{
int len = a.size();
for (int i = 0; i < len - 1; i++)
{
int min = i;
for (int j = i + 1; j < len; j++)
{
if (a[min] > a[j])
{
min = j;
}
}
if (min != i)
swap(a, min, i);
}
}
void InsertSort(vector<int> &a)
{
int len = a.size();
for (int i = 1; i < len; i++)
{
for (int j = i - 1; j >= 0 && a[j] > a[j + 1]; j--)
{
swap(a, j, j + 1);
}
}
}
int main(int argc, char* argv[])
{
vector<int> a;
int N;
cin >> N;
for (int i = 0; i < N; i++)
{
int cur;
cin >> cur;
a.push_back(cur);
}
//BubbleSort(a);
//print(a);
//SelectSort(a);
//print(a);
InsertSort(a);
print(a);
system("pause");
return 0;
}