一般來說,我們討論排序都是針對陣列結構。陣列的特點是:可以很方便地進行隨機訪問,但是增刪元素比較耗時。因此,針對陣列的排序,通常會避免元素的增刪操作,改為元素交換。同時,常採用二分的方法實現高效排序演算法。
鍊錶與陣列相反,隨機訪問非常耗時,但增刪元素很簡單。因此鍊錶的排序和陣列也會有所不同。
這篇部落格針對陣列和鍊錶的不同,分析了常用排序演算法——快速排序在陣列和鍊錶中的實現。
注:我們規定排序的語義如下:
sort(begin, end)
表示對[begin, end)
之間的元素排序,包含begin,但不包含end。
int num[n];
quick_sort(int *begin, int *end); //begin和end為指向陣列元素的指標
快速排序的思路是:以某個元素為切分點,把小於它的元素全部交換到前面,大於它的元素交換到後面,使切分點成為已排序元素,再對切分點前後進一步排序。
以首元素為切分點,**如下:
void quick_sort(int *begin, int *end)else
}partition--;
swap(*partition, *begin); //begin放在切分點位置
quick_sort(begin, partition);
quick_sort(partition + 1, end);
}
快速排序的關鍵在於切分陣列。上述**中把陣列分成4個部分:
1. begin元素
2. (begin, partition)為小於begin的元素
3. [partition, tmp)為大於等於begin的元素,
4. [tmp, end)為未排序元素
while迴圈用於處理tmp處元素,擴充套件第1和第2部分,縮減第3部分。處理方法是:若tmp小於begin元素,則tmp與partition處元素交換,再各自加1;若tmp大於等於begin元素,直接將tmp自加即可。
當tmp移動到end的時候,陣列就只剩下前3個部分。然後,partition自減1,即為小於切分元素的最後乙個元素。將partition元素和begin處元素交換,則陣列的3部分如下:
1. [begin, partition)小於切分元素
2. partition處為切分元素
3. (partition, end)大於等於切分元素
至此切分陣列工作完成,即可進行下一步的遞迴。
這裡以乙個簡單的雙向鍊錶作為示例:
class
node;
此外,設煉表的首節點為head
,尾節點為tail
,均不儲存實際資料,即,如果乙個鍊錶有三個元素1,2,3,則鍊錶的結構應為:
head node(1) node(2) node(3) tail
此處head
和tail
不儲存資料,是為了在對鍊錶進行操作時無需考慮邊界情形。
對照陣列排序的思路,可以寫出鍊錶的快速排序:
void quick_sort(node *begin, node *end)else
}quick_sort(tmp_head->next, begin);
quick_sort(begin->next, end);
}
鍊錶的快速排序從總體上和陣列的排序比較接近,也需要切分鍊錶,但切分的方式則略有不同。
鍊錶被分為以下4部分:
1. [tmp_head->next, begin)小於切分元素
2. begin處為切分元素
3. [begin->next, tmp)大於等於切分元素
4. [tmp, end)尚未處理
與陣列快速排序相同,鍊錶排序也需要借助while迴圈後移tmp節點,並擴充套件1和3部分。
通過對比可以發現,鍊錶快速排序與陣列快速排序的不同點為:
1. 陣列排序中,需要不斷修正partition的位置,而鍊錶排序中,切分點始終在begin處。
2. 陣列排序中,為避免增刪元素,需通過交換將小於切分元素的節點向前移動。而鍊錶排序中,只需要將小於begin的節點從原位置刪除,再插入到begin正前方即可。
3. 由於沒有交換操作,鍊錶的快速排序結構更加清晰。同時,鍊錶的快速排序也是穩定排序。
在陣列和鍊錶的排序中,需要充分考慮到陣列和鍊錶在隨機訪問和增刪方面的複雜度,合理編寫**。
以上陣列快速排序演算法是不穩定的,而鍊錶快速排序演算法是穩定的。但一般來說鍊錶快速排序的效率低於陣列,這是因為鍊錶的結構更為複雜,且無法有效利用程式區域性性。
陣列和鍊錶的快速排序平均時間複雜度均為o(
nlog
n),平均空間複雜度為o(
logn
) 。
以上程式以首節點為切分點,在已排序陣列或鍊錶中會遇到最壞複雜度,最壞時間複雜度為o(
n2) ,最壞空間複雜度為o(n)。
值得注意的是,陣列可以採用隨機選擇切分節點的方式使最壞情況隨機化,但鍊錶沒有隨機訪問特性,因此無法高效地隨機選擇切分節點。也就是說,鍊錶的快速排序演算法是有漏洞的。
Array和List的區別
array和list都屬於順序表。因為list是乙個鍊錶,所以我需要從第乙個元素開始逐個next到所需索引的元素。這是乙個耗時的過程。陣列必須要在初始化時分配固定的大小,比如說int a new int 3 如果我們僅僅寫int a new int 編譯器就會無情地給我們報錯。但是list由於空間不...
Array和List的異同
array和list的異同 array的建立格式是 型別 eg string array new string 5 可以在建立時限定長度,但在後面使用中不可以更改。直接賦值 不適用於二維陣列 引用賦值。只能選其一,且不能重複賦值。可以放基本型別資料和物件。list建立格式是 型別 泛型 eg lis...
Array和List的轉換
呼叫list的toarray 方法,可以直接規定引數陣列大小為1,如果不夠,方法將自動建立合適大小的陣列 listlist new arraylist list.add aaa list.add bbb string s list.toarray new string 1 for string ss...