數據結構--查找
基本概念
- 生活中處處有查找,例如:搜索引擎、大數據問題等等。
- 我們學習查找想要解決的問題:對於超大數據量,如何提高查找的效率?
- 基本概念:
- 關鍵碼:用以標識一個記錄的某個數據項。如果該關鍵碼可以唯一的標識一條記錄,則稱爲主關鍵碼,反之爲次關鍵碼。
- 查找:在具有相同類型的記錄集中找出滿足給定條件的記錄。
- 查找結果:在查找集中找到匹配的記錄,稱爲查找成功;否則查找失敗。一般情況下,查找需要返回記錄的位置。
P.S.
- 散列技術其實有很多,例如HASH哈希(題外話:在python中,字典就是一種哈希映射~欲知詳情,請查看我的另一篇筆記
),散列查找的效率是相當高的,在最近十幾年才崛起。 - 二叉排序樹與平衡二叉樹的區別?
因爲對於一組無序數據,通過二叉排序樹排序以後得到的樹有很大可能是不平衡的(左右子樹大小相差太多),而平衡二叉樹稱得上是二叉排序樹的升級版,可以解決左右子樹不平衡的問題。
**思考:**查找結構與存儲結構有什麼區別?
線性表查找
順序查找
問題: 對於亂序數據,如何快速查找出關鍵字Key是否在亂序中?若是,如何返回位置?
方法一:簡單粗暴的直接查找
int search(int a[],int n,int key)
{
for(int i=0;i<n;i++)//①
if(a[i] == key)//②
return i+1;//找到key,返回位置
return 0;//沒有找到,返回0
}
反思: 上述代碼的時間複雜度?是O(n^2),因爲有二次比較。
思考: 如何進一步提高效率?
方法二:哨兵法–用空間換時間
思想: 對於長度爲n的亂序表,另建一個長度爲n+1的表,其中a[0]做哨兵,其值賦爲key。哨兵的意義–使函數無論如何都會返回一個值,而且只需要比較一次。
int search(int a[],int n,int key)
{
a[0] = key; //哨兵
for(int i=n;a[i]!=key;i--); //從後向前查找
return i; //如果找到key,就返回位置i,沒有找到,就返回0
}
計算ASL:
- 查找不成功 ASL = n+1
- 查找成功
折半查找(敲黑板:必考題)
思考: 折半法的前提是什麼?待查找序列爲有序表。
基本思想: 先確定待查記錄所在的範圍,再用二分法逐步縮小範圍直到找到或找不到且查完整個表。
再思考: 對存放在數組中的有序表,如何快速找到Key?
注意:
- (以上題爲例)low的第一次移動要移動到 mid+1,因爲mid原來坐在的位置,數據已經比較過一次了,可以直接跳到它的下一個。(同理:high=mid-1)
- 如何判斷沒有找到key?
low > high 時就說明沒有找到。換句話說,循環條件就是 low<=high
int Search_Bin(int a[],int n,int key)
{
int low = 1;
int high = n;
while(low<=high)
{
mid = (low+high)/2;
if(key == a[mid])
return mid;
else if (key<a[mid])
high = mid-1;
else
low = mid +1;
}
return 0;
}
折半查找的性能分析
折半查找的判定樹:
- 一般情況下,表長爲n的折半查找的判定樹的深度和含有n個結點的完全二叉樹的深度相同。
所以:
索引查找(分塊查找)
- 分塊查找的性能介於順序查找和折半查找。只用於分段有序的信息表。
- 分段查找的核心思想:在建立順序表的同時,建立一個索引表。
- 可以看出索引表可以用折半查找,而基本表不可以。
- 基本思想:首先根據索引表確定待查記錄的區間,然後再確定的主表區間採用順序查找。(索引思想是當前大數據處理方面常用的思想。)
- 性能分析:
缺點:需要有輔助數組,且初始表要經過分塊排序。
三種查找方式的比較
查找方式 | 性能 | 適用條件 |
---|---|---|
順序查找 | ASL= (n+1)/2 或n+1,性能最差 | 亂序表 |
折半查找 | ASL=(2log)n 或 (2log)n+!,性能最好 | 有序表 |
分塊查找 | 性能位於前兩者中間 | 分塊有序表 |
然而這三者都只適用於靜態查找。如果我們想在查找的同時,對一些記錄進行添加、刪除操作,就要使用下面的樹表。