1. 程式人生 > 實用技巧 >資料結構第八章學習小結

資料結構第八章學習小結

一、學習內容小結

排序可分為兩大類:

內部排序(Internal Sorting):待排序的記錄全部存放在計算機記憶體中進行的排序過程;

外部排序(External Sorting):待排序的記錄數量很大,記憶體不能儲存全部記錄,需要對外存進行訪問的排序過程。(現階段還沒有詳細深入的學習)

(本章學習的排序方法比較多,有點容易混淆,難記憶,花了比較多的時間在把各類方法總結成表格,看著對比比較清晰易懂一些。)

排序方法分類

排序方法

(內部排序)

概述

時間複雜度分析

空間複雜度分析

演算法特點

(優缺點)

交換類

氣泡排序

重複走訪過要排序的元素列,依次比較兩個相鄰的元素,如果順序錯誤就把他們交換過來

直到沒有相鄰元素需要交換,排序完成。

O(n^2)

O(1)

(兩元素交換時,只需要一個記錄的輔助空間)

優點:①穩定排序②可用於鏈式儲存結構

缺點:①不適用於初始記錄無序、n較大的情況

快速排序

通過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序。

O(nlog(2)(n))

最好情況:

O(log(2)(n))

最壞情況:O(n)

(遞迴,執行時需要一個棧存放資料,最大遞迴呼叫次數與遞迴樹深度一致)

優點:①適用於初始記錄無序、n較大的情況(是所有內部排序中最快的方法)

缺點:①只能用於順序儲存結構②不穩定排序

插入類

直接插入排序

(順序查詢)將一條記錄插入到已排好序的有序表中得到一個新的、記錄數量增加1的有序表。

最好情況:比較1次,不移動

最壞情況:(非遞增有序列)比較i次,移動i+1次

平均:O(n^2)

O(1)

(只需要一個記錄的輔助空間r[0])

優點:①穩定排序②演算法簡便③可用於鏈式儲存結構④適用於初始記錄基本有序(正序)

缺點:①當初始記錄基本無序、n較大時複雜度高,不適用

折半插入排序

對直接插入排序演算法的改進,排序原理同直接插入演算法。在插入到已排序的資料時採用來折半查詢(二分查詢)

O(n^2)

(與直接插入排序相比,減少了比較次數,移動次數不變)

O(1)

(只需要一個記錄的輔助空間r[0])

優點:①穩定排序②適用於初始記錄無序、n較大的情況

缺點:①只能用於順序儲存結構

希爾排序

(縮小增

量排序)

分組插入)

把記錄按下標的一定增量分組,對每組使用直接插入排序演算法排序;隨著增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,演算法便終止。

O(nlog(2)(n))~O(n^2)

(不確定)

O(1)

(只需要一個記錄的輔助空間r[0])

(要使增量序列中的值沒有除1之外的公因子,並且在最後增量值必須等於1)

優點:①適用於初始記錄無序、n較大的情況

缺點:①只能用於順序儲存結構②不穩定排序

選擇類

簡單選擇排序

先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。

O(n)

O(1)

(兩元素交換時,只需要一個記錄的輔助空間)

①穩定排序(但可能產生“不穩定現象”)②可用於鏈式儲存結構③移動記錄次數少,當每一記錄佔用空間較多時,該法比直接插入排序快

樹形選擇排序

首先對n個記錄的關鍵字進行兩兩比較,然後在n/2個較小者之間再進行兩兩比較,如此重複,直至選出最小的記錄為止。

O(nlog(2)(n))

缺點:①輔助儲存空間較多②和“最大值”進行多餘比較等缺點

堆排序

是一種樹形選擇排序,將待排序的記錄r[1,n]看作一棵完全二叉樹的順序儲存結構,利用其中雙親結點和孩子結點之間的內在關係,在當前的無序序列中選擇最大(小)記錄。

O(nlog(2)(n))

O(1)

(兩元素交換時,只需要一個記錄的輔助空間)

優點:①適用於初始記錄無序、n較大的情況,記錄較少時不適用

缺點:①只能用於順序儲存結構②不穩定排序

歸併類

2-路歸併排序

遞迴反覆將當前區間[left, right]分為兩半,對兩個子區間[left, mid]與[mid +1, right]分別遞迴進行歸併排序,然後將兩個已經有序的合併為有序序列

O(nlog(2)(n))

需要進行log(2)(n)(向上取整)躺歸併排序,每一趟O(n)

O(n)

(需要和待排序記錄個數相等的輔助儲存空間)

優點:①穩定排序②可用於鏈式儲存結構,且不需要附加儲存空間,但遞迴實現時仍要開闢相應的遞迴工作棧

至於更詳細的描述推薦參考這個網站的資料:https://www.cnblogs.com/hokky/p/8529042.html

(裡面對每一種方法都進行了非常詳細的描述,還有演算法思路的動圖jpg演示,非常清晰,簡潔易懂)

二、程式碼實戰

一開始做PTA上的作業題的時候還沒有想到原來方法挺簡單的,以為是要考察課本上的幾個演算法,然後用幾個排序方法試了挺久的。後來在網上查閱了一下才發現原來可以這麼簡單的完成=。=,還了解並學習到了桶排序這個排序方法的簡單思路。

因為每個員工的工齡在[0,50]這個區間上,所以可以開個小陣列作為桶。

 1 #include <iostream>
 2 using namespace std;
 3 
 4 int main()
 5 {
 6     int n,x;
 7     cin >> n;
 8     int count[51] = { 0 };
 9     for (int i = 0; i < n; i++)
10     {
11         cin >> x;
12         count[x]++;
13     }
14     for (int i = 0; i < 51; i++)
15         if (count[i])
16             cout << i << ":" << count[i] << endl;
17     return 0;
18 }
View Code

還有實踐1PTA排名彙總的演算法思路,總體看起來不是很困難,用了<algorithm>標頭檔案中的sort函式就比較簡便,基本就是將考生在考場內及總體分別排名一次(記得考慮同分的情況),再將總體的資料輸出。(在程式碼中也作了比較清晰的註釋,應該會比較易懂)

 1 #include <iostream>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 struct student
 7 {
 8     string id; //考號 
 9     int score; //分數 
10     int position; //考點 
11     int rank; // 所在考點排名 
12     int total_rank; //總排名 
13 }stu[30010];
14 
15 int compare(student a, student b)
16 {
17     if(a.score==b.score)
18     {
19         return a.id<b.id;
20     }
21     return a.score>b.score;
22 } 
23 
24 int main()
25 {
26     int N, K;
27     int num=0;
28     cin >> N;
29     
30     for(int i=0;i<N;i++)
31     {
32         cin >> K;
33         for(int j=num;j<num+K;j++)
34         {
35             cin >> stu[j].id >> stu[j].score;
36             stu[j].position = i+1; 
37         }
38         sort(stu+num,stu+num+K,compare); //同一考場排序 
39         
40         int rank = 1;
41         stu[num].rank = rank; //組中第一人排名為1 
42         
43         for(int t=num+1;t<K+num;t++) //確定組中排名 
44         {
45             rank++;
46             if(stu[t].score==stu[t-1].score) //同分並列 
47             {
48                 stu[t].rank = stu[t-1].rank;
49             }
50             else
51             {
52                 stu[t].rank = rank; //排名+1 
53             }
54         }
55         num = num+K; //更新stu長度 
56     }
57     
58     sort(stu,stu+num,compare);//全部考生排序 
59     
60     int rank=1;
61     stu[0].total_rank = rank;
62     for(int i=1;i<num;i++)
63     {
64         rank++;
65         if(stu[i].score ==stu[i-1].score)
66         {
67             stu[i].total_rank = stu[i-1].total_rank;
68         }
69         else
70         {
71             stu[i].total_rank = rank; 
72         }
73     }
74     
75     cout << num << endl;
76     
77     for(int i=0;i<num;i++)
78     {
79         cout << stu[i].id << " " << stu[i].total_rank << " " << stu[i].position << " " << stu[i].rank << endl;
80     } 
81     
82     return 0;
83 }
View Code

三、學習心得

本學期的內容已經到此結束了,我覺得讓我感觸很深的反而是每一次的寫部落格,每進行一章節的部落格總結,都可以從頭把內容溫習一遍,這個感覺十分好,同時也可以相互學習。本章的各種排序方法是演算法思路不算特別困難,但是打起程式碼來還是有點吃力,還有比較多的知識點要記牢、區分,希望自己在接下來的複習階段多看看程式碼,掌握思路的同時也要強化臨時打程式碼的能力。