常見查找算法實現(c++)
1 查找基本概念
查找表:由同一類型的數據元素構成的集合
關鍵字:數據中某個數據項的值;主關鍵字唯一表示記錄;次關鍵字不唯一表示記錄。
查找:就是根據給定的某個值,在查找表中確定一個其關鍵字等於給定值的數據元素(或記錄)。不存在則出現“空記錄”或“空指針”。
查找表的操作方式:
- 靜態查找表:只做查找操作的查找表(可以考慮對主關鍵字排序後,在應用折半查找)
- 動態查找表:在查找過程中同時插入查找表中不存在的數據元素,或刪除元素。(考慮二叉排序樹技術)
2 複雜度分析
算法 | 複雜度 |
---|---|
順序表查找 | |
有序表查找 | |
線性索引查找 | |
二叉排序樹 | |
平衡二叉樹(AVL) | |
多路查找樹(B樹) | |
散列表查找 |
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 二分查找,其複雜度爲
使用二分查找前提是需要有序表順序存儲,對於需要頻繁執行插入或刪除的數據集來說,維護有序的排序會帶來不小的工作量。
#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)用於指向塊首數據元素的指針,便於開始對這一塊中記錄進行遍歷。
(三)倒排索引
網頁搜索一般用的就是倒排索引。
倒排索引的通用結構是:
- 次關鍵碼(如“英文單詞”)
- 記錄號表(如“文章編號”)
記錄號表存儲具有相同次關鍵字的所有記錄的記錄號(可以是指向記錄的指針或者是該記錄的主關鍵字)
倒排索引