1. 程式人生 > 實用技巧 >華科考研834 2019

華科考研834 2019

華中科技大學計算機考研試卷總結 2019(一) 834

  1. 空串和空白串不一樣
  2. 串是一種特殊的線性表,又叫內容受限的線性表,它的內容往往只能是一個字元或者字串。
  3. 單迴圈連結串列中設定尾指標比設定頭指標更有優勢
設尾指標rear,則訪問末尾節點就是rear;
訪問頭結點就是rear->next-next就是第一個有效節點(首節點);
rear->next是頭結點
__________________________________________________________
1. 帶尾指標的單向迴圈連結串列最後一個節點之後插入或者刪除一個節點時間複雜度O(1),在頭結點之後刪除或者刪除一個節點的時間複雜度是O(1);若是採用頭指標,對首節點進行操作的時間複雜度是O(1),但是對末尾節點進行操作,時間複雜度就是O(n);
2. 帶尾指標的雙向非迴圈連結串列,對尾結點進行刪除或者插入操作時間複雜度是O(1),但是對首節點進行刪除或者插入,它的時間複雜度就會變為O(n)
3. 帶頭指標的雙向迴圈連結串列,找到最後一個節點的直接前驅和找到首節點的直接前驅都非常容易,時間複雜度都是O(1),但是雙迴圈連結串列在使用中每個節點會有額外的空間開銷,因此效率不如帶尾指標的單迴圈連結串列,可以完成但不是最優解
4. 帶頭指標的單向迴圈連結串列,對首節點進行操作時間複雜度是O(1),但是對尾結點進行操作時時間複雜度是O(n)
____________________________________________________________
迴圈單鏈表預設是新增頭結點的,末尾節點的指標域不是null,而是指向頭結點
  1. 一維陣列的\(n\)個元素,讀取(訪問)的平均時間複雜度是\(O(1)\) ,但是插入或者刪除的時間複雜度是\(O(n)\) ;\(n\)個元素連結串列的隨機讀取時間複雜度是\(O(n)\) ,插入或者刪除的時間複雜度是\(O(1)\)

討論時間複雜度往往要考慮是讀取(訪問)的時間複雜度,還是插入或者刪除的時間複雜度,結合是順序表還是連結串列進行判定,有時候考慮綜合時間複雜度時候,讀取和插入刪除這兩部分都要考慮,但是考研試題以及大多數面試中,往往只考慮一個方面,要麼是插入刪除的時間複雜度,要麼是讀取訪問的時間複雜度,尤其是前者 前面所講的時間複雜度預設都是平均時間按複雜度

  1. 知道二叉樹的中序遍歷和前序遍歷往往可以唯一確定這顆二叉樹,方法是:前序遍歷的第一個節點是整顆二叉樹的唯一根節點,在根據這個根節點將中序遍歷序列進行左右子樹劃分,然後對左右子樹也進行類似劃分,序列分塊,找根節點
  2. rear是佇列隊尾元素指標,指標變數\(t\)指向將要入佇列的指標\(X\) ,則入隊操作是
//佇列和棧一樣,也是一個下面小,上面大的順序元素   順序表型佇列
//連結串列型佇列	就是一個帶頭結點的單向連結串列
//入佇列操作
rear->next = t;
rear = t;
//出隊操作
p = front;
front->next = front;
free(p);
  1. 有序單鏈表插入一個新的元素使得繼續有序的時間複雜度是\(O(n)\)

先找到待插入的位置,時間複雜度是O(n),再修改指標,時間複雜度是O(1)

  1. 刪除棧頂元素的指標操作
//棧也有順序結構和鏈式結構兩種
//順序棧是下面序號小,上面序號大的線性資料結構
//順序棧出棧
int x =top.data;
top--;
//順序棧進棧
top++;
top.data = x
//鏈式棧出棧   對於鏈棧,棧底是next為空的那頭元素
top = top->next

  1. 對圖的遍歷。廣度優先遍歷演算法類似樹的層次遍歷 ,深度優先遍歷演算法類似樹的先序遍歷
  2. 基數排序不進行比較,其他排序都會進行比較和交換

排序方式大的可以分為五種,插入排序、交換排序、選擇排序、歸併排序、基數排序

插入排序:直接插入排序、折半插入排序、希爾排序

​ 思想:每次將一個元素插入到前面已經排好序的序列中 這種排序演算法的特性註定會是區域性有序,即現在的有序也許會受到將來元素的插入而改變,不是正確排序的最終位置

  1. 直接插入排序

    每次從當前插入元素依次向前比較,邊比較邊後移比較時在原地排序。空間複雜度O(1),時間複雜度是\(O(n^2)\)適用於線性表和順序表。穩定排序

  2. 折半插入排序

    基於直接插入演算法。改進部分是:對於已經排好的順序元素,使用折半查詢演算法查找出待插入元素位置,再統一進行後移騰出位置。時間複雜度\(O(n^2)\) ,穩定排序

  3. 希爾排序

    適用於基本有序的情況,還是建立在基本選擇排序方法之上。先設定一個增量(通常取n/2,向下取整)。依據增量形成比較多個子對,在子對內部使用選擇排序,每一輪完成後,再取新的增量,\(di+1=di/2\) ,每一輪讓增量d為原來一半。時間複雜度\(O(n^{1.3})\) ,最壞情況下的時間複雜度是\(O(n^2)\) 這個演算法是不穩定排序,涉及到子對遠距離交換,空間複雜度是$O(1) $ 僅適用於順序表

交換排序:氣泡排序、快速排序

​ 思想:根據序列中的兩個元素比較結果,進行位置的交換 註定會是全域性有序,某一狀態的有序會一直維持到最終全部有序

  1. 氣泡排序 從後往前(或者從前往後也行),依次比較相鄰兩個元素的大小關係,進而得到位置關係,每一輪可以得出一個最終位置,會排\(n-1\)輪 穩定性:由於比較時使用的是\(>、<\) ,當兩個元素相等時不會進行交換,因此是穩定演算法。 空間複雜度\(O(1)\) ,時間複雜度\(O(n^2)\) ,最壞時間複雜度也是\(O(n^2)\)

  2. 快速排序 快速排序基於分治策略。快速排序是所有排序演算法中效能最優的排序演算法,並且每一趟排序都是全域性有序,會放到其最終位置上。空間複雜度\(O(log2n)\) ,所有考試排序中唯一有的,時間複雜度:\(O(nlog2n)\) ,最壞時間複雜度\(O(n^2)\) ,不穩定的演算法

    先隨機選一箇中心主元pivot,所有比pivot小的數放到主元左邊,所有比pivot大的數放到主元右邊,這樣得到的pivot位置是最終位置,和冒泡演算法一樣,每完成一輪比較,可以最終確定當前pivot的最終位置。這樣的的一輪叫做一次快速排序或者叫一次劃分

    為了方便起見,每次都選擇最左邊的數字作為pivot

    適用情況:每次選取的pivot可以把元素分成兩部分,此時排序演算法的速度最快,當已經是正序或者逆序就最慢 最大遞迴次數是n,最小遞迴次數是\(log2n\)

    初始時,序列最左端和最右端各有一個指標,記為\(L\)\(R\) ,一般取首元素作為pivot(實際上pivot的選取是任意的),這時左邊對應\(L\) 就是空的 \(R\) 是找比pivot小的數字,\(L\)是找比pivot大的數字,\(L\)\(R\)重合的位置就是當前pivot可以放到的位置

\(08<pivot(19)\) ,所以把08移動到\(L\)位置指向的位置;

移動完\(R\) 指標,交替移動\(L\) (交替是隻有發生事實上的移動才會有交替移動,當比較後發現不需要移動的時候,繼續移動當前指標,不需要交替移動指標)

一直重複,直至\(L\)\(R\) 重複,重複的位置就可以把當前pivot放到該處去

PS:比較以後一定要移動下標\(L\) 或者\(R\) ,至於是移動哪一個有以下規律:比較之後不發生移動的,繼續移動上次移動的下標,向中間移動一次;比較以後發生移動的,先將該元素移動到對面方指標指向的空位置處,並且交替指標(使成為對面向指標),交替指標之後向中間移動一次

\(R\)指標是把小的元素移動到左邊;\(L\)指標是把大的元素移動到右邊

選擇排序:簡單選擇排序、堆排序

選擇排序的基本思想是:每一趟在待排序元素中選擇最小的元素,作為有序序列中的元素

\(i\)趟排序在\(L[i,i+1,…n]\) 中選擇最小的元素

  1. 簡單選擇排序 時間複雜度\(O(n^2)\) 最壞情況下的時間複雜度是\(O(n^2)\) ,甚至於最好情況下的時間複雜度都是\(O(n^2)\) ,經過\(n-1\)輪選擇。 每一次的選擇都會是最終位置。是一種不穩定的排序演算法

  2. 堆排序 利用完全二叉樹的性質對數字進行排序

    適用於待排序記錄比較多的情況,雖然建堆複雜,但是畢竟效能高

    大根堆:樹以及子樹必須滿足父節點的內容大於子節點的內容

    建堆: 從已有樹的最底層觀察自底向上觀察是否滿足大根堆的要求,若有不滿足就進行heapify,使它最終調整成為大根堆,有時候上層節點調整堆會對下層原來排好的堆進行毀壞,因此建堆的時候下層的堆也要顧及得到,一直需要向下維護到葉節點才能保證成為一個完整的堆

    一般對雜亂無章的數字進行heapify要從倒數第二層往上進行,從最小的子樹進行heapify一直往上進行,讓進行heapify的子樹規模不斷擴大,一直擴充套件到整個樹都能進行heapify,這樣全部節點上的數字就會有序

    每次輸出的元素只有一個,都在堆頂(這也是為什麼堆排序歸類為選擇排序的原因),它和選擇排序非常相像,大根堆就是每次選出大根堆對頂元素輸出,即找出最大值輸出,輸出後對剩餘元素重新進行heapify,繼續找到當前節點中的最大值。過程和選擇排序依次比較得出最大元素的過程非常像


    樹節點存到陣列中,對陣列中前n個所有元素進行heapify,找到最大元素後,放到陣列最後邊;再對前\(n-1\)個元素進行heapify,找到的最大元素放到陣列中倒數第二個位置;一次重複進行heapify,知道得到每一輪堆的最大值

    堆排序的平均時間複雜度\(O(log2n)\) ,最好時間複雜度\(O(log2n)\),最壞時間複雜度\(O(log2n)\) ,空間複雜度\(O(1)\)

    比如對一個無序數字進行大根堆:

    一、 先把陣列中的元素全部填進完全二叉樹

    二、 從倒數第二層往上依次進行父節點、子節點的比較,調整成一個大頂堆

    三、 堆的重建:移出根節點之後(堆頂),從這個剩餘堆中選最後一個元素到堆頂 ,然後進行堆的調整(著重調整被影響的子樹)使成為有一個大根堆;再次輸出堆頂,繼續調整剩餘堆使得全部堆頂元素被輸出

    堆排序適合於待排記錄比較大的情況下

歸併排序

​ 思想:歸併的含義是將兩個以上的子有序表 合併成一個有序表

將$L [1…n] $ 中長度是\(h\) 的相鄰有序段合併成長度是\(2h\) 的相鄰有序段,2路歸併就是一次merge兩個段序列。排序趟數是\(m\) ,進行\(k\)路歸併,總共有\(N\) 個元素,滿足\(N = k^m\)

平均時間複雜度\(O(nlog2n)\) ,最好時間複雜度,最壞時間複雜度都是\(O(nlog2n)\) ,空間複雜度是\(O(n)\)

穩定排序演算法 且可以作為外部排序演算法

每趟歸併排序時間複雜度是\(O(n)\) 共進行了\(O(log2n)\)

基數排序

基數排序最特別,是根據一個數字的各位進行劃分class。一般我們進行的是最低位優先基數排序。基數排序是穩定的排序演算法

每次根據個位、十位、百位進行分類。一般我們都排十進位制數,基數就是10,每兩輪基數排序之間銜(''分配''收集'')接決定了無需考慮相對大小關係。或者說上一輪的收集和下一輪分配的緊密連線,決定了演算法的成功

設進行d趟分配和收集,一趟分配是\(O(n)\) ,一趟收集是\(O(r)\) 所以基數排序時間複雜度\(O(d(n+r))\)