本博客整理了當前經典的搜索算法的實現,並進行了簡單的分析;博客中所有的代碼實現位於:https://github.com/yaowenxu/codes/tree/master/搜索算法 ; 如果代碼對您有幫助,希望能點擊star~基於推薦和鼓勵!感謝~
基本概念:
- 搜索:就是在指定結構內,找到滿足條件的關鍵字;這個結構可以是查找表結構,也可以是樹結構也可以是圖結構;
- 查找表:帶有相同種類鍵值的元素所構成的集合;
- 關鍵字:元素中的某一項數字和標識,一個數據可以有很多關鍵字,可以通過多種維度來進行搜索;
- 主鍵:可以用來唯一標識數據或者記錄的關鍵字;
- 靜態查找表:查詢某個特定的元素是否在表中;只做查找操作,查找過程中集合中的數據不發生變化;
- 動態查找表:在查找過程中,同時進行插入或者刪除數據;
- 內查找:全部過程中都在內存中進行,為內查找;
- 外查找:全部過程中需要訪問外存,為外查找;
- 性能評價:ASL(Average Search Length),平均比較長度;表示在查找過程中對關鍵字的需要執行的平均比較長度(平均比較次數),來進行評價算法性能優劣;
- 查找算法選取:判斷當前數據的組織結構,是線性表還是樹結構還是圖結構;如果是順序表,還要考慮表格中的數據是否有序;
順序查找:順序查找的思想是從左到右依次進行掃描比較,如果滿足指定的查找條件則查找成功,若到末尾還未找到,則數組中無滿足結構,適合與線性表;數據可以有序也可以無序;

#define DEBUG 1 #include <iostream> #include <cstdio> #include <vector> using namespace std; void printarray(vector<int> &arr); void seqsearch(vector<int> &arr, int v=500){ cout << "開始查找數據:" << v << endl; bool flag = true; for (int i = 0; i < arr.size(); i++) { if(arr[i] == v){ cout << "滿足索引:" << i << endl; flag = false; } } if (flag) { cout << "無滿足數據!" << endl; } } // 打印函數; void printarray(vector<int> &arr){ for(int i = 0; i < arr.size(); i++){ cout << arr[i] << " "; } cout << endl; } int main(){ vector<int> arr; freopen("in.txt", "r", stdin); // 重定向到輸入 int i = 0; int tmp; while (cin >> tmp) { arr.push_back(tmp); // cout << inarray[i] << endl; i++; } int num = i; cout << "輸入數據:" << endl; printarray(arr); cout << endl; if (DEBUG){ cout << "查找過程:" << endl; } // 順序查找 seqsearch(arr); return 0; }

bash-3.2$ c++ 順序查找.cc;./a.out 輸入數據: 1 2 3 500 9 7 6 500 1 2 查找過程: 開始查找數據:500 滿足索引:3 滿足索引:7
算法復雜度:如果只查找一個元素,第一個元素滿足情況為1;最后一個滿足情況,需要比較n次; 平均時間復雜度:ASL = (n+...+1)/n = (n+1)/2,O(n);
二分查找:二分查找又稱為折半查找;思想是將查找的關鍵字與中間記錄關鍵字進行比較,如果相等則查找結束;若不相等,比較自身和中間元素的大小,如果小於中間元素,則在左半部遞歸查找,如果大於中間元素則在右半部遞歸查找;該算法適合有序順序表的查找;二分查找使用了一種減治的思想;

#define DEBUG 1 #include <iostream> #include <cstdio> #include <vector> #include <algorithm> using namespace std; void printarray(vector<int> &arr); // 遞歸實現,找出其中一個滿足的方案;如果需要找出所有的滿足方案,只要先找出其中一個滿足方案,然后在其左右順序搜索就可以了;(因為數組是排序好的) void binsearch(vector<int> &arr, int start, int end, int v=900){ if(DEBUG){ cout << "s=" << start << " " << "e=" << end << endl; } if(end < start){ cout << "無滿足數據!" << endl; return; } int mid = (start+end)/2; if(arr[mid] == v){ cout << "滿足索引:" << mid << endl; return; } if(arr[mid] < v){ binsearch(arr, mid+1, end); } if(arr[mid] > v){ binsearch(arr, start, mid-1); } } // 非遞歸實現,非遞歸實現使用循環實現,注意循環的出口;以及索引的更新; void binsearch_2(vector<int> &arr, int v=900){ int start = 0; int end = arr.size()-1; int flag = true; //使用 do-while 來進行循環 do { int mid = (start+end)/2; if(DEBUG){ cout << "s=" << start << " " << "e=" << end << endl; } if (arr[mid]==v){ cout << "滿足索引:" << mid << endl; flag = false; break; } if (arr[mid] > v){ end = mid-1; } if (arr[mid] < v){ start = mid+1; } } while (end >= start); if(flag){ cout << "無滿足數據!" << endl; } } // 打印函數; void printarray(vector<int> &arr){ for(int i = 0; i < arr.size(); i++){ cout << arr[i] << " "; } cout << endl; } int main(){ vector<int> arr; freopen("in.txt", "r", stdin); // 重定向到輸入 int i = 0; int tmp; while (cin >> tmp) { arr.push_back(tmp); // cout << inarray[i] << endl; i++; } int num = i; sort(arr.begin(), arr.end()); cout << "輸入數據:" << endl; printarray(arr); cout << endl; if (DEBUG){ cout << "查找過程:" << endl; } // 順序查找 cout << "binsearch-遞歸:" << endl; binsearch(arr, 0, arr.size()-1); cout << "binsearch-非遞歸:" << endl; binsearch_2(arr); return 0; }

bash-3.2$ c++ 二分查找.cc; ./a.out 輸入數據: 1 2 4 5 7 8 300 900 查找過程: binsearch-遞歸: s=0 e=7 s=4 e=7 s=6 e=7 s=7 e=7 滿足索引:7 binsearch-非遞歸: s=0 e=7 s=4 e=7 s=6 e=7 s=7 e=7 滿足索引:7
平均時間復雜度:查找區間可以看作,中間為樹的根;左區間為左子樹,右區間為右子樹,整個查找過程中為二叉樹;平均查找就是從二叉樹的深度,為 O(logn);但一般數據使用二分查找,要求數據有序,如果數據無序,則先需要使用排序算法對無序數組進行排序;本實現使用的是 sort函數;當然你可以自行實現;
分塊查找:分塊查找,分塊就是把數據分為很多塊;塊內的數據是沒有順序的;塊與塊之間是有序的;左塊中的元素小於右塊中元素的最小值;分塊查找由分塊有序的線性表和有序的索引表構成;索引表記錄了每個塊的最大關鍵字和塊的起始位置;在查找過程中,先使用二分查找定位到對應的塊;然后再在塊中進行順序搜索;分塊查找支持動態數據查找;

#include <iostream> using namespace std; int seqtable[20] = {8, 14, 6, 9, 10, 22, 34, 18, 19, 31, 40, 38, 54, 66, 46, 71, 78, 68, 80, 85}; // 順序表 int indextable[4][2] = {{14, 0},{34, 5},{66, 10},{85, 15}}; // 索引表 void blocksearch1(int v = 85){ // 二分查找; int start = 0; int stop = 3; // 順序搜索的范圍,該變量由二分搜索查表確定 int s = 0; int e = 20; do{ int mid = (start+stop)/2; cout << "二分查找前:" << "start:" << start << " mid:" << mid << " stop:" << stop << " s=" << s << " e=" << e << endl; if(indextable[mid][0] < v){ start = mid+1; if (mid == 3){ cout << "無滿足數據!" << endl; return; } s = indextable[mid+1][1]; } if(indextable[mid][0] >= v){ stop = mid-1; if (mid < 3){ e = indextable[mid+1][1]; } } cout << "二分查找后:" << "start:" << start << " mid:" << mid << " stop:" << stop << " s=" << s << " e=" << e << endl; }while(stop >= start); // 順序搜索 cout << "順序搜索區間: " << "s=" << s << " e=" << e << endl; int flag = true; for (int i = s; i < e; i++){ if(seqtable[i] == v){ cout << "滿足索引:" << i << endl; flag = false; break; } } if (flag){ cout << "無滿足數據!" << endl; } } void blocksearch2(int v = 85){ // 二分查找; int start = 0; int stop = 3; // 順序搜索的范圍,該變量由二分搜索查表確定 int s = 0; int e = 20; do{ int mid = (start+stop)/2; cout << "二分查找前:" << "start:" << start << " mid:" << mid << " stop:" << stop << " s=" << s << " e=" << e << endl; if(indextable[mid][0] == v){ // 加入此判斷,可以過早終止; s = indextable[mid][1]; if (mid < 3){ e = indextable[mid+1][1]; } break; } if(indextable[mid][0] < v){ // 加入此判斷可以過早終止; start = mid+1; if (mid == 3){ cout << "無滿足數據!" << endl; return; } s = indextable[mid+1][1]; } if(indextable[mid][0] > v){ stop = mid-1; if (mid < 3){ e = indextable[mid+1][1]; } } cout << "二分查找后:" << "start:" << start << " mid:" << mid << " stop:" << stop << " s=" << s << " e=" << e << endl; }while(stop >= start); // 順序搜索 cout << "順序搜索區間: " << "s=" << s << " e=" << e << endl; int flag = true; for (int i = s; i < e; i++){ if(seqtable[i] == v){ cout << "滿足索引:" << i << endl; flag = false; break; } } if (flag){ cout << "無滿足數據!" << endl; } } int main(){ cout << "分塊查找1:" << endl; blocksearch1(); cout << endl; cout << "分塊查找2:" << endl; blocksearch2(); }

bash-3.2$ c++ 分塊查找.cc; ./a.out 分塊查找1: 二分查找前:start:0 mid:1 stop:3 s=0 e=20 二分查找后:start:2 mid:1 stop:3 s=10 e=20 二分查找前:start:2 mid:2 stop:3 s=10 e=20 二分查找后:start:3 mid:2 stop:3 s=15 e=20 二分查找前:start:3 mid:3 stop:3 s=15 e=20 二分查找后:start:3 mid:3 stop:2 s=15 e=20 順序搜索區間: s=15 e=20 滿足索引:19 分塊查找2: 二分查找前:start:0 mid:1 stop:3 s=0 e=20 二分查找后:start:2 mid:1 stop:3 s=10 e=20 二分查找前:start:2 mid:2 stop:3 s=10 e=20 二分查找后:start:3 mid:2 stop:3 s=15 e=20 二分查找前:start:3 mid:3 stop:3 s=15 e=20 順序搜索區間: s=15 e=20 滿足索引:19
算法時間復雜度:塊間使用二分查找進行查找;塊內使用順序搜索進行查找;效率介於順序查找和二分查找之間; 平均查找長度:二分查找 > 分塊查找 > 順序查找; 適用性:順序搜索無條件限制,二分查找要求數據有序;分塊查找要求分塊有序; 存儲結構:順序查找和分塊查找適用於順序表和鏈表;二分查找適用於順序表; 分塊查找優點:分塊查找效率介於順序查找和二分查找之間,可以用於動態數據查找;
廣度優先搜索(BFS):Breadth First Search; 從樹的根開始,從上打下,從左到右遍歷樹的節點;
深度優先搜索(DFS): Depth First Search; 沿着樹的深度優先遍歷樹的節點,直到到達葉子節點,再進行回溯;根絕根節點遍歷順序的不同,又分為先序,中序和后序遍歷;
關於深度優先搜索和廣度優先搜索,在經典數據結構實現與分析樹結構部分進行詳細講解;
保持更新,轉載請注明出處;更多內容請關注cnblogs.com/xuyaowen;
參考鏈接: