查找

first edit:20170702
last edit:20170704

一、概念

  • 查找表:同一類型數據元素的集合
  • 關鍵字:數據元素中某個數據項的值
  • 主關鍵字:可以唯一地標識一個記錄的關鍵字。如成績表中學生學號
  • 次關鍵字:可以識別多個數據元素的關鍵字。如成績表中學生總成績
  • 靜態查找表:只作查找操作
  • 動態查找表:查找過程中插入表中不存在元素,或者從表中刪除已經存在的某個元素

二、順序表查找

  • 順序查找
  1. 思想
    從表中首個元素開始逐個查找
  2. 實現
int sequentialSearch(int *a, int n, int key) {
    // a為數組,n為要查找元素的個數,key為要查找的關鍵字
    // 查找成功返回匹配元素位置,否則返回INFINITY
    for (int i = 0; i < n; i++)
        if (a[i] == key)
            return i;
    return INFINITY;
}
  1. 分析
    -- 復雜度O(n)
    -- 可以將被查找概率較大的元素放在前面,不常用記錄放在后面,這樣可以提高實際使用時的效率

三、有序表查找

  • 二分查找
  1. 思想
    在有序表中,取中間記錄作為比較對象。每次未命中比較都會將搜索范圍減半
  2. 實現
// 二分查找
int binarySearch(int *a, int n, int key) {
    // a為有序數組,n為要查找元素的個數,key為要查找的關鍵字
    // 查找成功返回匹配元素位置,否則返回INFINITY
    int low = 0, high = n - 1;
    while (low <= high) {
        int mid = (low + high) / 2;
        if (key < a[mid])
            high = mid - 1;
        else if (key > a[mid])
            low = mid + 1;
        else
            return mid;
    }
    return INFINITY;
}
  1. 分析
    -- 復雜度 O(logn)
    -- 要求查找表內元素有序
    -- 對靜態查找表,該算法較好
    -- 對于需要頻繁執行插入或刪除操作的動態查找表,維護表內元素有序的代價較高
  • 插值查找
  1. 思想
    二分查找每次從中間查找并非最優→例如,在字典中查找apple,會從靠前的位置先查找→更新mid的計算公式為


    大話數據結構p302

    →這樣能快速的使查找范圍逼近待查找值

  2. 實現
int insertSearch(int *a, int n, int key) {
    // a為有序數組,n為要查找元素的個數,key為要查找的關鍵字
    // 查找成功返回匹配元素位置,否則返回INFINITY
    int low = 0, high = n - 1;
    while (low <= high) {
        int mid = low + (high - low)*(key - a[low]) / (a[high] - a[low]);// 插值計算公式
        if (key < a[mid])
            high = mid - 1;
        else if (key > a[mid])
            low = mid + 1;
        else
            return mid;
    }
    return INFINITY;
}

3.分析
-- 復雜度仍為O(logn)
-- 但對于表長較大,且關鍵字分布較均勻的查找表來說。平均性能要優于二分查找

  • 裴波那契查找
  1. 思想
  2. 實現
  3. 分析

四、線性索引查找

  1. 概念
    • 索引:把每個關鍵字和它對應的記錄相關聯的過程
    • 線性索引:將索引項集合組織為線性結構
    • 稠密索引:在線性索引中,每個元素對應一個索引項→索引項按關鍵碼有序排列→查找快,但索引量大。
    • 分塊索引:對數據集進行分塊,使其塊間有序,塊內無序→索引項包括三個數據項:最大關鍵碼,塊中的記錄個數→指向塊首元素數據的指針→**兼顧了減少索引項數目和查找速度
    • 倒排索引:查找具有相同次關鍵字的所有記錄的記錄號→索引項包括兩個數據項:次關鍵嗎、記錄號表優點是查找快,缺點是記錄號表不定長

五、二叉排序樹

  1. 概念
    二叉排序樹/二叉查找樹,或者是一顆空樹,或者具有以下性質:
    • 若它的做子樹不為空,則左子樹上所有節點均小于它的根結構的值
    • 若右子樹不為空,則右子樹上所有節點均大于它的根節點的值
    • 左右子樹也是二叉排序樹
    • 中序遍歷二叉排序樹,得到有序序列
    • 同時兼顧插入刪除和查找操作的速度
  2. 查找操作
  • 遞歸調用查找函數,程序調用方法為SearchBST(v,key,NULL,NULL)→初始的father和result為NULL→如果是寫成BST類的方法,那么應該是返回result,father可以作為類的一個私有成員
bool SearchBST(BiTree v, int key,BiTree father, BiTree &result){
    // 查找成功→返回true,result保存指向命中節點的指針
    // 查找失敗→返回false,result保存指向查找路徑上訪問的最后一個節點
    // 如果是空樹的話,result也是空指針
    if (!v) {
        result = father;
        return false;
    }
    else if (key == v->data) {
        result = v;
        return true;
    }
    else
        SearchBST(((key < v->data ? v->lchild : v->rchild)), key, v,result);
}
  1. 插入操作
  • 先用查找操作找到合適的插入位置,注意新建分配節點
bool InsertBST(BiTree &v, int key) {
    // 當二叉樹v中不存在關鍵字key時,插入并返回true
    // 當二叉樹v中存在關鍵字key時,返回false
    BiTree p = NULL;
    if (!SearchBST(v, key, NULL, p)) { //查找不成功
        // 這里就體現出了p的作用,p為查找路徑上訪問的最后一個節點,
        // 同時,p節點肯定是有一個子節點是空的,所以key的插入后即為p的子節點
        BiTNode *s = new BiTNode;
        s->data = key;
        s->lchild = NULL; s->rchild = NULL;
        // 注意如果當且僅當v是空樹,p為空,此時新建節點為跟節點
        if (!p)
            v = s;
        else if (key < p->data)
            p->lchild = s;
        else
            p->rchild = s;
        return true;
    }
    else
        return false;
}
  1. 刪除操作
  • 使用delete(p)刪除需要刪除的節點,因為所有節點都是用new新建的
  • 考慮四種情況:
  • 不存在待刪除的節點→返回false
  • 待刪除節點為葉子節點→直接刪除
  • 只有左子樹或只有右子樹→刪除原有節點,并用左子樹替代
  • 左右子樹都存在→先找到左子樹中最大的值,替代該節點的值;刪除左子樹中最大節點,然后重接其父節點的左右子樹→**注意要考慮待刪除節點的左子節點就沒有右子節點的情況


    image.png

    待刪除節點的左子節點就沒有右子節點的情況
bool deleteBST(BiTree &v) {
    if (v->rchild == NULL) {
        BiTree q = v;
        v = v->rchild;
        delete(q);
    }
    else if (v->lchild == NULL){
        BiTree q = v;
        v = v->rchild;
        delete(q);
    }
    else {
        BiTree q = v;
        BiTree s = v->lchild; //轉左,然后向右到盡頭,這樣就是左子樹中最大的
        while (s->rchild) {
            q = s; // q指向左子樹最大節點的父節點
            s = s->rchild; // s指向左子樹中最大的節點
        }
        v->data = s->data;
        if (q != v)
            q->rchild = s->lchild;
        else // 如果v的左子節點就沒有右節點
            q->lchild = s->lchild;
        delete(s);          
    }
    return true;
}
bool DeleteBST(BiTree &v, int key) {
    if (!v) //不存在關鍵字等于key的節點
        return false;
    else {
        if (key == v->data)
            return deleteBST(v);
        else if (key < v->data)
            return DeleteBST(v->lchild, key);
        else
            return DeleteBST(v->rchild, key);
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容