搜索查找算法實現合集-經典搜索算法實現與分析:順序查找,二分查找,分塊查找;廣度優先搜索,深度優先搜索;


本博客整理了當前經典的搜索算法的實現,並進行了簡單的分析;博客中所有的代碼實現位於: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;

參考鏈接:

七大查找算法(Python)

幾種常見的搜索算法 

程序員的內功——數據結構和算法系列 

排序與搜索 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM