排序算法
一、準備知識
衡量算法的好壞指標,顯然首先這個算法是正確的,除此之外,通常有三個方面的考慮:
(1)算法在執行過程中所消耗的時間;
(2)算法在執行過程中所佔資源的大小,例如,佔用內存空間的大小;
(3)算法的易理解性、易實現性和易驗證性等等。我們經常討論的時間複雜度是比較常見的評價標準之一。
• 算法的時間複雜度定義:
若有某個輔助函數f(n),當n趨向於無窮大時,如果T(n)/ f(n)的極限爲不等於零的常數,則認爲T(n)與f(n)是同量級的函數,記作:T(n) =O(f(n)),O(f(n))稱爲算法的漸進時間複雜度,簡稱時間複雜度。
• 漸進時間複雜度意義:
(1)在較複雜的算法中,進行精確分析是非常複雜的;
(2)一般來說,我們並不關心T(n)的精確度量,而只是關心其量級。T (n) = O(f (n)) 表示存在一個常數C,當n趨於正無窮大時,總有T (n) ≤ C * f(n),其意義是T(n)在n趨於正無窮大時跟f(n)基本接近,因此完全可以用f(n)來表示T(n)。
在各種不同的算法中,若算法語句的執行次數爲常數,則算法的時間複雜度爲O(1),按數量級遞增排列,常見的時間複雜度量有:
(1)O(1):常量階,運行時間爲常量
(2)O(logn):對數階,如二分搜索算法
(3)O(n):線性階,如n個數內找最大值
(4)O(nlogn):對數階,如快速排序算法
(5)O(n^2):平方階,如選擇排序,冒泡排序
(6)O(n^3):立方階,如兩個n階矩陣的乘法運算
(7)O(2^n):指數階,如n個元素集合的所有子集的算法
(8)O(n!):階乘階,如n個元素全部排列的算法
• 計算步驟
(1)找出算法中重複執行次數最多的語句的頻度來估算算法的時間複雜度;
(2)保留算法的最高次冪,忽略所有低次冪和高次冪的係數;
(3)將算法執行次數的數量級放入大Ο記號中。
二、部分排序算法的實現
排序算法的穩定性:(1)穩定:如果a原本在b前面,而a=b,排序之後a仍然在b的前面;(2)不穩定:如果a原本在b的前面,而a=b,排序之後 a 可能會出現在 b 的後面。
//變量值交換
template<class T>
void swap(T& a, T& b) {
T tmp;
tmp = a;
a = b;
b = tmp;
}
//一維數組打印
template<class T>
void printArray(T data[], int n) {
for (int i = 0; i < n; i++) {
std::cout << data[i] << " ";
}
std::cout<<std::endl;
}
1、選擇排序及其優化版本
選擇排序
//普通選擇排序
//每次調動indexOfMax(a,size)需要執行size-1次比較,因此總共的比較次數爲n-1+n-2+...+1=n(n-1)/2
//取最高次,則表示爲漸進記法爲O(n^2)
//最差時間複雜度:O(n^2)
//最優時間複雜度:O(n^2)
//平均時間複雜度:O(n^2)
//穩定性:不穩定
template<class T>
int indexOfMax(T a[], int n) {
if (n <= 0)
return -1;
int max_index=0;
for (int i = 0; i < n; i++) {
if (a[max_index] < a[i])
max_index = i;
}
return max_index;
}
template<class T>
void selectionSort(T a[], int n) {
for (int size = n; size > 1; size--) {
int j = indexOfMax(a, size);
if(j!=size-1)
swap(a[j], a[size - 1]);
}
}
//優化:即時終止選擇排序
template<class T>
void selectionSort_(T a[], int n) {
bool sorted = false;
for (int size = n; !sorted && (size > 1); size--) {
int indexOfMax = 0;
sorted = true;
for (int i = 1; i < size; i++) {
if (a[indexOfMax] <= a[i]) indexOfMax = i;
else sorted = false; //檢查有序性 false代表無序
}
if(indexOfMax!=size-1)
swap(a[size - 1], a[indexOfMax]);
}
}
2、冒泡排序及其優化版本
//冒泡排序
//在要排序的一組數中,對當前還未排好序的範圍內的全部數,自上而下對相鄰的兩個數依次進行比較和調整,
//讓較大的數往下沉,較小的往上冒。即:每當兩相鄰的數比較後發現它們的排序與排序要求相反時,就將它們互換。
//對任意數k ,函數bubble(T a[], int n) 比較n-1次,則n-1+n-2+...1=n(n-1)/2
//最差時間複雜度:O(n^2)
//最優時間複雜度:O(n^2)
//平均時間複雜度:O(n^2)
//穩定性:穩定
template<class T>
void bubble(T a[], int n) {
for (int i = 0; i < n - 1; i++) {
if (a[i] > a[i + 1]) swap(a[i], a[i + 1]);
}
}
template<class T>
void bubbleSort(T a[], int n) {
for (int i = n; i > 1; i--) {
bubble(a, i);
}
}
//優化:即時終止冒泡排序
template<class T>
bool bubble2(T a[], int n) {
bool swapped = false;
for (int i = 0; i < n - 1; i++) {
if (a[i] > a[i + 1]) {
swap(a[i], a[i + 1]);
swapped = true;
}
}
return swapped;
}
template<class T>
void bubbleSort2(T a[], int n) {
for (int i = n; i > 1 && bubble(a, i); i--);
}
3、插入排序
//插入排序
//插入排序是一種最簡單直觀的排序算法,它的工作原理是通過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。
//1)將第一待排序序列第一個元素看做一個有序序列,把第二個元素到最後一個元素當成是未排序序列。
//2)從頭到尾依次掃描未排序序列,將掃描到的每個元素插入有序序列的適當位置。(如果待插入的元素與有序序列中的某個元素相等,則將待插入元素插入到相等元素的後面。)
//最差時間複雜度:O(n^2)
//最優時間複雜度:O(n)
//平均時間複雜度:O(n ^ 2)
//穩定性:穩定
template<class T>
void insertionSort(T a[], int n){
for (int i = 1; i < n; i++) //i從index=1開始,index=0第一個元素當做最初的單元有序數組
{
int temp = a[i];
int j = i;
while (j > 0 && a[j - 1] > temp)
{
a[j] = a[j - 1];
j--;
}
a[j] = temp;
}
}
4、快速排序
/快速排序:分治法、遞歸
//1、從數列中取出一個數作爲基準數(樞軸,pivot)。
//2、將數組進行劃分(partition),將比基準數大的元素都移至樞軸右邊,將小於等於基準數的元素都移至樞軸左邊。
//3、再對左右的子區間重複第二步的劃分操作,直至每個子區間只有一個元素。
//平均時間複雜度: O(nlogn)
//最壞時間複雜度: O(n*n) 比如降序數組排成升序,反之則反之
//最好時間的複雜度: O(nlogn) 二叉樹深度logn
//穩定性:不穩定
template<class T>
int partition(T data[], int left,int right) {
int i = 0, j = 0, tmp = 0;
i = left + 1;
j = right;
tmp = data[left];
while (i <= j) {
while (data[i] < tmp)
i++;
while (data[j] > tmp)
j--;
if (i < j)
swap(data[i++], data[j--]);
else
i++;
}
swap(data[left], data[j]);
return j;
}
template<class T>
void quickSort(T data[], int left, int right) {
if (left > right)
return;
int mid = 0;
mid = partition(data, left, right);
quickSort(data, left, mid - 1);
quickSort(data, mid + 1, right);
}
5、待續…
主函數驗證:
int main() {
std::cout << "before sort : " << std::endl;
float data[10] = { 1,5,2,4,6,0,8,9,3,7 };
printArray(data, 10);
//bubleSort(data, 10);
//selectiveSort(data, 10);
//selectionSort_(data, 10);
//insertionSort(data, 10);
quickSort(data, 0, 9);
std::cout << "after sort: " << std::endl;
printArray(data, 10);
system("pause");
return 0;
}
參考:
https://blog.csdn.net/yq272393925/article/details/89146451
https://baijiahao.baidu.com/s?id=1609024533531824968&wfr=spider&for=pc
《數據結構、算法與應用》