排序這個東西是我們經常要用到的,下面談一談我對排序的理解。
(1)插入排序:插入排序是指先將元素的第一個數當做一個有序的表,然後把第二個元素與第一個元素之間進行比較,插入最開始有序表中,從第二個元素開始,每一個元素都與前面的有序表比較,並加入這個有序表。最後得到一個完全有序的序列。
因爲在比較過程中,如果待插入的元素如果與有序表中的某個元素相比,是相等的。那麼這個元素就插入在後面,所以插入排序是穩定的。時間複雜度:O(n^2),其他插入排序:2分插入
執行的效果如下圖:
// 插入排序
var arr = [1, 4, 5, 7, 3, 2];
function insertSort(arr, length) {
var temp;
for (var i= 0; i < length; i++) {
if (arr[i]>arr[i+1]) {
temp = arr[i+1];
for (var j=i;j>=0&&temp<arr[j];j--){
console.log(123)
arr[j+1]=arr[j];
}
arr[j+1]=temp
}
}
return arr;
}
console.log(insertSort(arr, arr.length))
(2)希爾排序:希爾排序是插入排序的改進,希爾排序按照一個給定的2k,k,k/2...1這樣的數來將一堆需要排序的數劃分成幾個子序列,進行插入排序,然後再將一個基本有序的序列,再進行直接插入排序。
比如說給定一個序列 1 7 6 5 9 2 0 以2劃分子序列是 1 5 0,7 9,6 2,對這三個子序列分別進行插入排序,最後再進行直接插入排序。因爲在子序列的直接插入過程中,很可能會造成一個數據與另一個子序列中的數據相同,但是卻改變了位置,所以希爾排序是一個不穩定的排序。
// 插入排序
void ShellSort(int arr[], int length)
{
int increasement = length;
int i, j, k;
do
{
// 確定分組的增量
increasement = increasement / 3 + 1;
for (i = 0; i < increasement; i++)
{
for (j = i + increasement; j < length; j += increasement)
{
if (arr[j] < arr[j - increasement])
{
int temp = arr[j];
for (k = j - increasement; k >= 0 && temp < arr[k]; k -= increasement)
{
arr[k + increasement] = arr[k];
}
arr[k + increasement] = temp;
}
}
}
} while (increasement > 1);
}
(3)堆排序:堆其實就是一個完全二叉樹,而且必須滿足根節點必須大於等於子節點或者小於等於孩子節點。
堆排序分成兩個過程:
第一個過程是構建最大堆或者最小堆。一個排序後祖先節點就是最大的數或者最小的數。
第二個過程就是排序了,將第一個過程得到祖先節點存入數組中,然後將最後一個葉子節點換到第一個過程得到的祖先節點上,再進行構造最大堆或者最小堆,依次這樣直到剩下最後一個葉子節點。
堆排序是不穩定的排序
(4)選擇排序:選擇排序是指在一個給定的序列中,如果是從小到大排序,首先選擇一個最小的數與第一個數交換,然後從第二個數到最後一個數中選擇一個最小的數與第二個數交換,依次進行,直到n-1個元素與第n個元素比較。
選擇排序是不穩定的排序。
// 選擇排序
var arr = [1, 4, 5, 7, 3, 2];
function selectionSort(arr, length) {
var temp;
for (var i= 0; i < length; i++) {
for (var j = i; j < length - 1; j++) {
if (arr[j] > arr[j+1]) {
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
if (arr[j] >= arr[i]) {
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
console.log(arr[j])
}
return arr;
}
console.log(selectionSort(arr, arr.length))
(5)交換排------冒泡排序:這個是我第一個接觸的排序,主要思想是第一個數依次與第二-最後一個數進行比較,然後進行交互,依次進行,直到n-1個數與第n個數進行比較。
冒泡排序是穩定的排序。
(6)快速排序:這個排序是目前非常火的一個排序,當序列是分佈隨機的,用快速排序的平均時間是最快的。當排序的數特別大,比如從10000個數中選取最小的十個時用堆排序。
快速排序的思想是給定一個序列,給定兩個遊標i,j分別指向序列的頭和尾(假設以升序排列)。(1)讓i和j指向的數進行比較,如果不發生交互,j向後走1,如果一直不發生交換,則j與i重合,則這一趟排序將待排序的序列分成了左右兩個部分----i後面的元素都比i大,如果發生交換,則i+1,然後與j比較,如果又發生交換,j-1,直到i,j重合,一趟排序完畢。
快速排序是不穩定的排序。
void QuickSort(int arr[], int start, int end)
{
if (start >= end)
return;
int i = start;
int j = end;
// 基準數
int baseval = arr[start];
while (i < j)
{
// 從右向左找比基準數小的數
while (i < j && arr[j] >= baseval)
{
j--;
}
if (i < j)
{
arr[i] = arr[j];
i++;
}
// 從左向右找比基準數大的數
while (i < j && arr[i] < baseval)
{
i++;
}
if (i < j)
{
arr[j] = arr[i];
j--;
}
}
// 把基準數放到i的位置
arr[i] = baseval;
// 遞歸
QuickSort(arr, start, i - 1);
QuickSort(arr, i + 1, end);
}
(7)歸併排序:把一個序列分成兩個或兩個以上的多個子序列,分別將各個子序列排列有序,然後合併成一個有序序列。
void MergeSort(int arr[], int start, int end, int * temp)
{
if (start >= end)
return;
int mid = (start + end) / 2;
MergeSort(arr, start, mid, temp);
MergeSort(arr, mid + 1, end, temp);
// 合併兩個有序序列
int length = 0; // 表示輔助空間有多少個元素
int i_start = start;
int i_end = mid;
int j_start = mid + 1;
int j_end = end;
while (i_start <= i_end && j_start <= j_end)
{
if (arr[i_start] < arr[j_start])
{
temp[length] = arr[i_start];
length++;
i_start++;
}
else
{
temp[length] = arr[j_start];
length++;
j_start++;
}
}
while (i_start <= i_end)
{
temp[length] = arr[i_start];
i_start++;
length++;
}
while (j_start <= j_end)
{
temp[length] = arr[j_start];
length++;
j_start++;
}
// 把輔助空間的數據放到原空間
for (int i = 0; i < length; i++)
{
arr[start + i] = temp[i];
}
}
(8)冒泡排序
首先從數組的第一個元素開始到數組最後一個元素爲止,對數組中相鄰的兩個元素進行比較,如果位於數組左端的元素大於數組右端的元素,則交換這兩個元素在數組中的位置,此時數組最右端的元素即爲該數組中所有元素的最大值。接着對該數組剩下的n-1個元素進行冒泡排序,直到整個數組有序排列。算法的時間複雜度爲O(n^2)。
附上冒泡排序JavaScript實現方法:
// 冒泡排序
var arr = [1, 4, 5, 7, 3, 2];
function bubbleSort(arr, length) {
var temp;
for (var i= 0; i < length; i++) {
var flag = 0;
for (var j = 0; j < length - i - 1; j++) {
if (arr[j] > arr[j+1]) {
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
flag = 1;
}
}
if (!flag) {
break;
}
}
return arr;
}
console.log(bubbleSort(arr, arr.length))