說在前面
個人讀書筆記
快速排序
分治策略
與歸併排序算法一樣,快速排序(quicksort)算法也是分治策略的典型應用,但二者之間也有本質區別。
歸併排序的計算量主要消耗於有序子向量的歸併操作,而子向量的劃分卻幾乎不費時間。快速排序恰好相反,它可以在時間內,由子問題的解直接得到原問題的解;但爲了將原問題劃分爲兩個子問題,卻需要O(n)時間。
快速排序算法雖然能夠確保,劃分出來的子任務彼此獨立,並且其規模總和保持漸進不變,卻不能保證兩個子任務的規模大體相當——實際上,甚至有可能極不平衡。
因此,該算法並不能保證最壞情況下的O(nlogn)時間複雜度。
儘管如此,它仍然受到人們的青睞,並在實際應用中往往成爲首選的排序算法。究其原因在於,快速排序算法易於實現,代碼結構緊湊簡練,而且對於按通常規律隨機分佈的輸入序列,快速排序算法實際的平均運行時間較之同類算法更少。
快速排序算法
如上圖所示,考查任一向量區間。對於任何,以元素爲界,都可分割出前、後兩個子向量和。若中的元素均不大於,且中的元素均不小於,則元素稱作向量S的一個軸點(pivot)。
設向量經排序可轉化爲有序向量。不難看出,軸點位置必然滿足如下充要條件:
- 和的成員完全相同
- 和的成員完全相同
因此,不僅以軸點爲界,前、後子向量的排序可各自獨立地進行,而且更重要的是,一旦前、後子向量各自完成排序,即可立即(在時間內)得到整個向量的排序結果。
採用分治策略,遞歸地利用軸點的以上特性,便可完成原向量的整體排序。
快速排序算法:
可見,軸點的位置一旦確定,則只需以軸點爲界,分別遞歸地對前、後子向量實施快速排序;子向量的排序結果就地返回之後,原向量的整體排序即告完成。
算法的核心與關鍵在於:
軸點構造算法partition()應如何實現?可以達到多高的效率?
軸點構造——快速劃分算法
不妨取首元素作爲候選,將其從向量中取出並做備份,騰出的空閒單元便於其它元素的位置調整。
然後不斷試圖移動和,使之相互靠攏。
當然,整個移動過程中,需始終保證左側(右側)的元素均不大於(不小於)。
最後,當與彼此重合時,只需將原備份的回填至這一位置,則便成爲一個名副其實的軸點。
以上過程在構造出軸點的同時,也按照相對於軸點的大小關係,將原向量劃分爲左、右兩個子向量,故亦稱作快速劃分(quick partitioning)算法。
c++實現數組的快速排序:
時間複雜度
#include <iostream>
using namespace std;
template <typename T>
int __partition(T arr[], int l, int r)
{
// 第一個值拿出來, 假設爲軸點
T v = arr[l];
int j = l;
// 從第二個開始向後遍歷
for (int i = l + 1; i <= r; i++)
{
if (arr[i] < v)
{
swap(arr[j + 1], arr[i]);
j++;
}
}
swap(arr[l], arr[j]);
return j;
}
template <typename T>
void __quickSort(T arr[], int l, int r)
{
if (l>=r)
{
return;
}
int p = __partition(arr, l, r);
__quickSort(arr, l, p-1);
__quickSort(arr, p+1, r);
}
template <typename T>
void quickSort(T arr[], int n)
{
__quickSort(arr, 0, n-1);
}
int main()
{
int a[10] = {10, 9, 8, 6, 11, 5, 4, 1, 0, 3};
quickSort(a, 10);
for (int i = 0; i< 10; i++)
{
cout << a[i] << " ";
}
cout << endl;
return 0;
}
當快速排序軸點兩側數量極度不平衡的時候,算法將退化。
如上,每次都選取第一個數值爲軸點時。這樣當數組完全有序時,算法時間複雜度退化到,解決方法是在進行快速劃分算法中,每次都隨機選取一個點和第一個點交換,再將這個點作爲軸點。
但是當重複的值比較多時,由於劃分的兩部分,其中一部分是小於軸點,另一部分是大於或等於軸點,劃分的兩部分仍然可能極不平衡,改進的算法如下:
#include <iostream>
#include <stdlib.h>
using namespace std;
template <typename T>
int __partition(T arr[], int l, int r)
{
swap(arr[l], arr[rand() % (r-l+1) + l]);
// 第一個值拿出來, 假設爲軸點
T v = arr[l];
int i = l + 1, j = r;
while (true)
{
while (i <= r && arr[i] < v)
{
i++;
}
while (j >= l+1 && arr[j] > v)
{
j--;
}
if (i>j)
{
break;
}
swap(arr[i], arr[j]);
i++;
j--;
}
swap(arr[l], arr[j]);
return j;
}
template <typename T>
void __quickSort(T arr[], int l, int r)
{
if (l>=r)
{
return;
}
int p = __partition(arr, l, r);
__quickSort(arr, l, p-1);
__quickSort(arr, p+1, r);
}
template <typename T>
void quickSort(T arr[], int n)
{
srand(time(NULL));
__quickSort(arr, 0, n-1);
}
int main()
{
int a[10] = {10, 9, 8, 6, 11, 5, 4, 1, 0, 3};
quickSort(a, 10);
for (int i = 0; i< 10; i++)
{
cout << a[i] << " ";
}
cout << endl;
return 0;
}
python實現的快速排序:
# -*- coding:utf-8 -*-
import numpy as np
import time
def swap(list0, index1, index2):
b = list0[index1]
list0[index1] = list0[index2]
list0[index2] = b
def partition(list0, l, r):
swap(list0, l, np.random.randint(0, len(list0)) % (r-l+1) + l)
v = list0[l]
p = l+1
q = r
while True:
while (p <= r) and (list0[p] < v):
p = p + 1
while (q >= l+1) and (list0[q] > v):
q = q - 1
if p >= q:
break
else:
swap(list0, p, q)
p = p + 1
q = q - 1
swap(list0, l, q)
return q
def quick_sort(list0, l, r):
if l >= r:
return
p = partition(list0, l, r)
quick_sort(list0, l, p-1)
quick_sort(list0, p+1, r)
def main(list0, n):
np.random.seed(int(str(time.time()).split(".")[1]))
quick_sort(list0, 0, n-1)
print(list0)
if __name__ == "__main__":
a = [10, 9, 8, 6, 11, 5, 4, 1, 0, 3]
main(a, len(a))
三路快速排序算法
c++實現數組的三路快速排序:
#include <iostream>
#include <stdlib.h>
using namespace std;
template <typename T>
void __quickSort3Ways(T arr[], int l, int r)
{
if (l>=r)
{
return;
}
swap(arr[l], arr[rand() % (r-l+1) + l]);
T v = arr[l];
int lt = l;
int gt = r + 1;
int i = l + 1;
while (i < gt)
{
if (arr[i] < v)
{
swap(arr[i], arr[lt+1]);
i++;
lt++;
}
else if (arr[i]>v)
{
swap(arr[i], arr[gt-1]);
gt--;
}
else
{
i++;
}
}
swap(arr[l], arr[lt]);
__quickSort3Ways(arr, l, lt-1);
__quickSort3Ways(arr, gt, r);
}
template <typename T>
void quickSort3Ways(T arr[], int n)
{
srand(time(NULL));
__quickSort3Ways(arr, 0, n-1);
}
int main()
{
int a[10] = {10, 9, 8, 6, 11, 5, 4, 1, 0, 3};
quickSort3Ways(a, 10);
for (int i = 0; i< 10; i++)
{
cout << a[i] << " ";
}
cout << endl;
return 0;
}
python實現的三路快速排序:
# -*- coding:utf-8 -*-
import numpy as np
import time
def swap(list0, index1, index2):
b = list0[index1]
list0[index1] = list0[index2]
list0[index2] = b
def quick_sort(list0, l, r):
if l >= r:
return
swap(list0, l, np.random.randint(0, len(list0)) % (r - l + 1) + l)
v = list0[l]
p = l
q = r + 1
i = l + 1
while i < q:
if list0[i] < v:
swap(list0, i, p+1)
p = p + 1
i = i + 1
elif list0[i] > v:
swap(list0, i, q-1)
q = q - 1
else:
i = i + 1
swap(list0, l, p)
quick_sort(list0, l, p-1)
quick_sort(list0, q, r)
def main(list0, n):
np.random.seed(int(str(time.time()).split(".")[1]))
quick_sort(list0, 0, n-1)
print(list0)
if __name__ == "__main__":
a = [10, 9, 8, 6, 11, 5, 4, 1, 0, 3]
main(a, len(a))
求逆序對的數量——使用歸併排序
# -*- coding:utf-8 -*-
class MergeSort:
def __init__(self, list0, n):
self.list0 = list0
self.n = n
@staticmethod
def _merge(list0, l, mid, r):
global count
list1 = list0[:]
p = l
q = mid + 1
for i in range(r-l+1):
if p > mid:
list0[i+l] = list1[q]
q = q + 1
elif q > r:
list0[i+l] = list1[p]
p = p + 1
elif list1[p] <= list1[q]:
list0[i+l] = list1[p]
p = p + 1
else:
list0[i+l] = list1[q]
count = count + mid + 1 - p
q = q + 1
def _merge_sort(self, list0, l, r):
if l >= r:
return
mid = int((l+r)/2)
self._merge_sort(list0, l, mid)
self._merge_sort(list0, mid+1, r)
self._merge(list0, l, mid, r)
def merge_sort(self):
self._merge_sort(self.list0, 0, self.n - 1)
def main():
global count
count = 0
# -------------------------------------
x = 0
a = [10, 9, 8, 6, 11, 5, 4, 1, 0, 3]
for i in range(len(a)-1):
for j in range(i+1, len(a)):
if a[j] < a[i]:
x = x + 1
# -------------------------------------
b = MergeSort(a, len(a))
b.merge_sort()
print(a)
print(count, x)
if __name__ == "__main__":
main()
求第n大的值——使用快速排序
# -*- coding:utf-8 -*-
import numpy as np
import time
import sys
def swap(list0, index1, index2):
b = list0[index1]
list0[index1] = list0[index2]
list0[index2] = b
def partition(list0, l, r, th):
swap(list0, l, np.random.randint(0, len(list0)) % (r-l+1) + l)
v = list0[l]
p = l+1
q = r
while True:
while (p <= r) and (list0[p] < v):
p = p + 1
while (q >= l+1) and (list0[q] > v):
q = q - 1
if p >= q:
break
else:
swap(list0, p, q)
p = p + 1
q = q - 1
swap(list0, l, q)
return q
def quick_sort(list0, l, r, th):
if l >= r:
return
p = partition(list0, l, r, th)
if p == len(list0) - th:
print(list0[p])
sys.exit(0)
elif p > len(list0) - th:
quick_sort(list0, l, p-1, th)
else:
quick_sort(list0, p+1, r, th)
def main(list0, n, th):
np.random.seed(int(str(time.time()).split(".")[1]))
quick_sort(list0, 0, n-1, th)
# print(list0)
if __name__ == "__main__":
a = [10, 9, 8, 6, 11, 5, 4, 1, 0, 3]
# 想找第th大的數的值, th小於等於len(a) + 1
th = 3
main(a, len(a), th)
結語
如果您有修改意見或問題,歡迎留言或者通過郵箱和我聯繫。
手打很辛苦,如果我的文章對您有幫助,轉載請註明出處。