常用排序算法
排序算法大體可分爲兩種:
第一種:比較排序
主要有冒泡排序、選擇排序、插入排序、歸併排序、堆排序、快速排序等。
第二種:非比較排序
主要有計數排序、基數排序、桶排序等。
常見排序算法的性能如下:
1:冒泡排序
通過與相鄰元素的比較和交換來把小的數交換到最前面。一次遍歷把最小(或最大)的數放到最頂端,再對剩下的序列依次冒泡得到一個有序序列。
冒泡排序的時間複雜度是O(n^2),所需輔助空間0(1);
對數量比較多的元素進行排序時,效率低下。
代碼如下:
BubbleSort.cpp文件
#include <stdio.h>
class CBubbleSort{
public:
void Swap(int* pArr, int i, int j)
{
int temp = pArr[i];
pArr[i] = pArr[j];
pArr[j] = temp;
}
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
int nIndex = 0;
for (int i = 0; i < length; i++)
{
for (int j = 0; j < length - i - 1; j ++)
{
if (pArr[j] > pArr[j + 1])
Swap(pArr, j, j+1);
}
}
return;
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "BubbleSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7};
int count = sizeof(test) / sizeof(int);
CBubbleSort* pSort = new CBubbleSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
運行結果如下:
2:改進的冒泡排序
比冒泡排序的改進之處是,從低到高排序,然後從高到底排序。
改進的冒泡排序的時間複雜度是O(n^2),所需輔助空間0(1);
比冒泡排序效率稍微高點。
代碼如下:
BubbleSort2.cpp文件
#include <stdio.h>
class CBubbleSort_new{
public:
void Swap(int* pArr, int i, int j)
{
int temp = pArr[i];
pArr[i] = pArr[j];
pArr[j] = temp;
}
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
int begin = 0;
int end = length - 1;
while(begin < end)
{
// 將最大的元素放到後面
for (int i = begin; i < end; i++)
{
if (pArr[i] > pArr[i + 1])
Swap(pArr, i, i + 1);
}
end--;
// 將最小的元素放到前面
for (int i = end; i > begin; i--)
{
if (pArr[i] < pArr[i - 1])
Swap(pArr, i, i - 1);
}
begin++;
}
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "BubbleSort2.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7};
int count = sizeof(test) / sizeof(int);
CBubbleSort_new* pSort = new CBubbleSort_new();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
運行結果如下:
3:選擇排序
在未排序序列中找到最小(大)的元素,存放到有序序列的末尾;再從剩餘未排序的元素中繼續尋找最小(大)的元素,存放到有序序列的末尾;依次類推,直到所有的元素均排序完畢。
時間複雜度o(n^2),空間複雜度o(1)。
代碼如下:
SelectSort.cpp文件
#include <stdio.h>
class CSelectSort{
public:
void Swap(int* pArr, int i, int j)
{
int temp = pArr[i];
pArr[i] = pArr[j];
pArr[j] = temp;
}
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
for (int i = 0; i < length; i++)
{
int nMinIdx = i;
for (int j = i + 1; j < length; j++)
{
if (pArr[j] < pArr[nMinIdx])
nMinIdx = j;
}
if (nMinIdx != i)
Swap(pArr, nMinIdx, i );
}
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>"
#include "SelectSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7};
int count = sizeof(test) / sizeof(int);
CSelectSort* pSort = new CSelectSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
運行結果如下:
4:插入排序
從未排序的序列中選擇一個新元素,在已排序的序列中從後向前掃描,知道找到比小於或等於新元素的元素,並將新元素插入到找到的元素的後面,對其後面的元素依次後移。
時間複雜度 o(n^2),空間複雜度o(1);
代碼如下:
InsertSort.cpp文件
#include <stdio.h>
class CInsertSort{
public:
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
for (int i = 1; i < length; i++)
{
int temp = pArr[i];
int j = i;
while(j >= 1 && pArr[j - 1] > temp)
{
pArr[j] = pArr[j - 1];
j--;
}
pArr[j] = temp;
}
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "InsertSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7};
int count = sizeof(test) / sizeof(int);
CInsertSort* pSort = new CInsertSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
運行結果如下:
5:二分插入排序
是插入排序的改進。可以減少比較操作的次數。
時間複雜度o(n^2),空間複雜度o(1)。
代碼如下:
BinInsertSort.cpp文件
#include <stdio.h>
class CBinInsertSort{
public:
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
for (int i = 1; i < length; i++)
{
int nTemp = pArr[i];
int nLeft = 0;
int nRight = i - 1;
while (nLeft <= nRight)
{
int nMiddle = (nLeft + nRight) / 2;
if (pArr[nMiddle] > nTemp)
nRight = nMiddle - 1;
else
nLeft = nMiddle + 1;
}
for (int j = i; j > nLeft; j--)
pArr[j] = pArr[j - 1];
if (nLeft != i)
pArr[nLeft] = nTemp;
}
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "BinInsertSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7};
int count = sizeof(test) / sizeof(int);
CBinInsertSort* pSort = new CBinInsertSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
運行結果如下:
6:希爾排序
也叫遞減增量排序。插入排序的更高效的改進。
希爾排序是基於插入排序的以下兩點性質而提出改進方法的:
插入排序在對幾乎已經排好序的數據操作時,效率高,即可以達到線性排序的效率
但插入排序一般來說是低效的,因爲插入排序每次只能將數據移動一位
希爾排序通過將比較的全部元素分爲幾個區域來提升插入排序的性能。這樣可以讓一個元素可以一次性地朝最終位置前進一大步。然後算法再取越來越小的步長進行排序,算法的最後一步就是普通的插入排序,但是到了這步,需排序的數據幾乎是已排好的了(此時插入排序較快)。
最優時間複雜度是o(n),平均時間複雜度根據步長序列的不同而不同,空間複雜度是o(1)。
代碼如下:
ShellSort.cpp文件
#include <stdio.h>
class CShellSort{
public:
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
int nInterval = 0;
while (nInterval < length)
{
nInterval = nInterval * 3 + 1;
}
while (nInterval >= 1)
{
for (int i = 0; i < length; i++)
{
int j = i - nInterval;
int nTemp = pArr[i];
while ( j >= 0 && pArr[j] > nTemp)
{
pArr[j + nInterval] = pArr[j];
j = j - nInterval;
}
pArr[j + nInterval] = nTemp;
}
nInterval = (nInterval - 1) / 3;
}
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "ShellSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7,24,66,9,35,85,11,22};
int count = sizeof(test) / sizeof(int);
CShellSort* pSort = new CShellSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
運行結果如下:
7:歸併排序
歸併排序是建立在歸併操作上的一種有效的排序算法,該算法採用分治法的經典應用。
將已有序的子序列合併,得到完全有序的序列,即先使每個子序列有序,再使子序列段間有序。
基本思路:
1:“分解”—將序列每次折半劃分。
2:“合併”—將劃分後的序列段兩兩合併後排序。
基本步驟:
1:將數組平分爲兩個子數組。
2:遞歸調用劃分數組函數,最後每個數組只有一個元素,即爲有序的數組。
3:調用排序函數,把兩個有序的數組合併成一個有序的數組。
時間複雜度是o(nlogn),空間複雜度是o(n)
代碼如下:
MergeSort.cpp文件
#include <stdio.h>
class CMergerSort{
public:
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
int* pTemp = new int[length];
if (pTemp == NULL)
return;
MergeSort(pArr, 0, length - 1, pTemp);
delete[] pTemp;
pTemp = NULL;
}
void MergeSort(int* pArr, int nStart, int nEnd, int* pTemp)
{
if (nStart >= nEnd)
return;
int nMiddle = (nStart + nEnd) / 2;
MergeSort(pArr, nStart, nMiddle, pTemp); // 遞歸劃分左邊的數組
MergeSort(pArr, nMiddle + 1, nEnd, pTemp); // 遞歸劃分右邊的數組
Merge(pArr, nStart, nMiddle, nEnd, pTemp); // 將有序的兩個序列合併成一個
}
void Merge(int* pArr, int nStart, int nMiddle, int nEnd, int* pTemp)
{
int nFirst = nStart;
int nSecond = nMiddle + 1;
int nIndex = nStart;
if (nFirst <= nMiddle && nSecond <= nEnd)
{
if (pArr[nFirst] >= pArr[nSecond])
pTemp[nIndex++] = pArr[nSecond++];
else
pTemp[nIndex++] = pArr[nFirst++];
}
while (nFirst <= nMiddle) pTemp[nIndex++] = pArr[nFirst++];
while (nSecond <= nEnd) pTemp[nIndex++] = pArr[nSecond++];
for (int i = nStart; i <= nEnd; i++)
pArr[i] = pTemp[i];
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "MergeSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7,24,66,9,35,85,11,22};
int count = sizeof(test) / sizeof(int);
CMergerSort* pSort = new CMergerSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
運行結果如下:
9:快速排序
基本思路是使用分治法策略,確定一個基準元素,把一個序列分爲兩個子序列,小於基準的元素放在前面的序列,大於基準的元素放在後面的序列,遞歸進行此操作直到序列的大小是0或1。
時間複雜度是o(nlogn),空間複雜度一般是o(logn)。
代碼如下:
QuickSort.cpp文件
// 使用分治法實現快速排序
#include <stdio.h>
class CQuickSort{
public:
void Swap(int* pArr, int i, int j)
{
int temp = pArr[i];
pArr[i] = pArr[j];
pArr[j] = temp;
}
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
QuickSort(pArr, 0, length - 1);
}
void QuickSort(int* pArr, int left, int right)
{
int nIdx;
if (left < right)
{
nIdx = Partition(pArr, left, right);
QuickSort(pArr, left, nIdx - 1);
QuickSort(pArr, nIdx + 1, right);
}
}
int Partition(int* pArr, int left, int right) // 劃分函數
{
int nIdx = left - 1;
int nTemp = pArr[right]; // 選擇最後一個元素作爲基準
for (int i = left; i < right; i++)
{
if (pArr[i] <= nTemp) // 把小於基準的元素放在前面子數組
{
nIdx++;
Swap(pArr, nIdx, i);
}
}
int nFindIdx = nIdx + 1;
Swap(pArr, nFindIdx, right); // 將基準放在前面子數組的後面,剩下的就是大於基準的元素
return nFindIdx; // 返回基準的索引
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "QuickSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7,24,66,9,35,85,11,22};
int count = sizeof(test) / sizeof(int);
CQuickSort* pSort = new CQuickSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
運行結果如下:
10:計數排序
步驟:
1:統計數組pArr中每個A[i]出現的次數,存入C[pArr[i]];
2:從前向後,使數組C中的每個值等於其與前一項相加,這樣數組C[pArr [i]]就代表了數組pArr中小於等於pArr [i]的元素個數;
3:爲了保證穩定性,從後向前反向填充目標數組B,將數組元素pArr [i]放在數組B的第C[pArr [i]]項(即B[C[pArr [i]] - 1]),每放一個元素就將C[pArr [i]]遞減;
時間複雜度爲o(n + k),空間複雜度爲o(n + k)。
計數排序的時間複雜度和空間複雜度取決於數組A的數據範圍(等於A中元素的最大值與最小值的差加上1),因此對於數據範圍很大的數組,計數排序需要大量時間和內存。
代碼如下:
CountingSort.cpp文件
#include <string.h>
#include <stdio.h>
const int nMax = 100;
class CCountingSort{
public:
void Sort(int* pArr, int length)
{
int* C = new int[nMax];
if (C == NULL)
return;
memset(C, 0, nMax * sizeof(int));
for (int i = 0; i < length; i++)
C[pArr[i]]++;
for (int i = 1; i< nMax; i++)
C[i] = C[i] + C[i - 1];
int* B = new int[length];
if (B == NULL)
return;
for (int i = length - 1; i >= 0; i--)
{
int nTemp = pArr[i];
B[C[nTemp] - 1] = nTemp;
C[nTemp]--;
}
for (int i = 0; i < length; i++)
pArr[i] = B[i];
delete C;
C = NULL;
delete B;
B = NULL;
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "CountingSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7,24,66,9,35,85,11,22};
int count = sizeof(test) / sizeof(int);
CCountingSort* pSort = new CCountingSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
運行結果如下:
11:基數排序
基本思路:
將整形10進制按位拆分,然後從低位到高位依次比較各個位。
1:分配,從個位開始,根據位值分別放到0-9號桶中。
2:收集,將10個桶中的數據,按順序取出到數組中。
3:從個位到高位,依次重複1,2步驟。
時間複雜度爲o(n * nMaxPos),空間複雜度爲o(n * nMaxPos)
代碼如下:
RadixSort.cpp文件
#include <stdlib.h>
const int nMaxPos = 6; // 本程序中元素的最大位數
class RadixSort{
public:
int GetNumIndex(int nNum, int nPos)
{
int nTemp = 1;
for (int nIdx = 1; nIdx < nPos; nIdx++)
nTemp *= 10;
return (nNum / nTemp) % 10;
}
void Sort(int* pArr, int length)
{
int* C[10];
for (int i = 0; i < 10; i++)
{
C[i] = (int*)malloc(sizeof(int) * (length + 1));
C[i][0] = 0; // 0爲記錄這個數組的個數
}
for (int nPos = 1; nPos <= nMaxPos; nPos++) // 從第一位到最高位依次處理
{
for (int i = 0; i < length; i++) // 分配到0-9號桶中
{
int nNum = GetNumIndex(pArr[i], nPos);
int nIndex = ++C[nNum][0];
C[nNum][nIndex] = pArr[i];
}
for (int i = 0, j = 0; i < 10; i++) // 從0-9號桶收集
{
for (int k = 1; k <= C[i][0]; k++)
pArr[j++] = C[i][k];
C[i][0] = 0; // 個數復位
}
}
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "RadixSort.cpp"
using namespace std;
void main()
{
int test[]={99999, 65, 24, 47, 13, 878, 321, 5, 82222, 66, 33, 22445, 10001, 624159, 624158, 6251};
int count = sizeof(test) / sizeof(int);
RadixSort* pSort = new RadixSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
運行結果