查詢

weixin_33749242發表於2017-07-02

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的計算公式為


    6380883-65c56b8d5c8b2dc5.png
    大話資料結構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
  • 待刪除節點為葉子節點→直接刪除
  • 只有左子樹或只有右子樹→刪除原有節點,並用左子樹替代
  • 左右子樹都存在→先找到左子樹中最大的值,替代該節點的值;刪除左子樹中最大節點,然後重接其父節點的左右子樹→**注意要考慮待刪除節點的左子節點就沒有右子節點的情況


    6380883-4dca9d0184ed86e3.png
    image.png

    6380883-6dcf6692aaf3176a.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);
    }
}

相關文章