常見經典排序算法

插入排序:
算法簡介:接插入排序(Insertion Sort)的基本思想是:每次將一個待排序的記錄,按其關鍵字大小插入到前面已經排好序的子序列中的適當位置,直到全部記錄插入完成爲止。時間複雜度爲O(n^2)。 最穩定的排序算法但是效率很低
代碼實現:
void InsertSort(int *arr,int n)
{
                 for (int index = 0; index < n-1; ++index)
                {
                                 int end = index+1;
                                 int tmp = arr [end];
                                 while (end>0&&tmp<arr [end - 1])
                                {
                                                 arr[end] = arr [end - 1];
                                                end--;
                                }
                                 arr[end] = tmp;
                }
}
 
顯然當最小的數字在數組的最右端時,此時需要將整個數組進行移位,效率很低,而希爾排序可以有效的改善這個問題
希爾排序:希爾排序(Shell Sort)是插入排序的一種。也稱縮小增量排序,是直接插入排序算法的一種更高效的改進版本。希爾排序是非穩定排序算法
       先將整個待排元素序列分割成若干個子序列(由相隔某個“增量”的元素組成的)分別進行直接插入排序,然後依次縮減增量再進行排序,待整個序列中的元素基本有序(增量足夠小)時,再對全體元素進行一次直接插入排序。因爲直接插入排序在元素基本有序的情況下(接近最好情況),效率是很高的,因此希爾排序在時間效率上比前兩種方法有較大提高。
代碼實現:
void ShellSort(int *arr, int n)//希爾排序
{
                 int gap = n ;
                 while (gap>1)//由於gap=gap/3+1 最小值爲1 則在gap=1時跳出循環
                {
                                gap = gap / 3 + 1; //{ 2, 8, 9, 6, 1, 3, 4, 5, 7, 0 ,-1,-2}//注意這裏的+1  當gap=1時此時排序等同於插入排序 但是由於之前將最小的數據已經移到最左邊所以效率
//高於插入排序
                                 for (int index = 0; index <n-gap; ++index)
                                {
                                                 int end = index;
                                                 int tmp = arr [end+gap];
                                                 while (end>= 0 && arr [end]>tmp)                                                {
                                                                 arr[end+gap] = arr [end];
                                                                end -=gap;//此時插入間距爲end
                                                }
                                                 arr[end+gap] = tmp;
                                }
                }
}
附註:上面希爾排序的步長選擇都是從n/3+1開始,每次再取上一次的三分之一加1,直到最後爲1。關於希爾排序步長參見維基百科:http://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F
冒泡排序:20世紀經典算法之一,
原理是臨近的數字兩兩進行比較,按照從小到大或者從大到小的順序進行交換,這樣一趟過去後,最大或最小的數字被交換到了最後一位,再進行第二趟冒泡,由此我們可以寫出以下代碼:
void BubbleSort(int *arr, int n)
{
                 for (int i = 0; i < n; ++i)
                {
                                 for (int j = 0; j < n - i - 1; ++j)
                                {
                                                 if (arr [j]>arr[j + 1])
                                                                swap( arr[j], arr [j + 1]);
                                }
                }
}
這裏我們設立一個標記變量 flag用來標記數列中的數是否在循環結束前就已經排好序
代碼改進如下:
void BubbleSort(int *arr, int n)
{

                 bool flag = true ;
                 int k = n ;
                 while (flag)
                {
                                flag = false;
                                 for (int i = 1; i < k; ++i)
                                {
                                                 if (arr [i - 1]<arr[i])
                                                {
                                                                swap( arr[i - 1], arr [i]);
                                                                flag = true;//有發生交換 繼續冒泡 否則說明已經有序
                                                }

                                }
                                k--;
                }

}
    如果這一趟發生了交換,這flag爲rrue,則還需要繼續進行冒泡,否則說明數組已經有序,後面的不必進行下去。
那麼這裏給出這樣一種情況:(2,1,3,4,5,6,7,8,9,10)第一次循環交換之後我們會發現,後面的數組已經有序,不再需要我們進行冒泡,後面的操作都是不必要的 這裏我們只需要記錄下最後一次進行交換的位置,那麼下次遍歷只要遍歷到這個位置就可以結束。
代碼進一步優化如下:
void BubbleSort(int *arr, int n)
{

                 int flag = n ;//第一次遍歷終點爲數組尾
                 while (flag > 0)
                {
                                 int k = flag;
                                flag = 0;
                                 for (int j = 1; j < k; ++j)
                                {
                                                 if (arr [j - 1] > arr[j])
                                                {
                                                                swap( arr[j - 1], arr [j]);
                                                                flag = j;//後面的已經排序好 記錄下下一次排序的終點
                                                }
                                }
                }
}
雖然有了這麼多優化,但是冒泡排序總體還是一種效率很低的排序,數據規模過大時不適合使用這種排序


堆排序:
堆的定義:
               1.堆是一顆完全二叉樹
               2.父結點總是大於或等於(小於或等於)任何一個子節點
               3每個結點的左子樹和右子樹都是一個二叉堆
               當父結點總是大於或等於任何一個子節點值時爲最大堆。反之則爲最小堆
堆的結構示意圖如下:
存儲方式:
我們使用數組來存儲一個堆,可以看出設父節點下標值爲i 其左孩子下標值爲2*i+1,右孩子爲2*1+2;
代碼實現如下:
void AdJust_down(int *arr, int parent, int size )
{
                 int child = 2 * parent +1;
                 while (child<size )
                {
                                 if (child+1<size &&arr[child+1]> arr[child])
                                {
                                                ++child;
                                }
                                 if (arr [parent]> arr[child])
                                                 break;
                                swap( arr[parent ], arr[child]);
                                 parent = child;
                                child = 2 * parent;
                }
}
void HeapSort(int *arr,int n)
{
                 for (int i = n/2-1; i >= 0; --i)
                {
                                AdJust_down( arr, i, n );
                }
                 for (int i = n - 1; i >= 1; --i)
                {
                                swap( arr[0], arr [i]);
                                AdJust_down( arr, 0, i);
                }
}
思路分析:
1.如果要對數字進行升序,我們首先首先將數組初始化爲原始大堆
                 for (int i = n/2-1; i >= 0; --i)
                {
                                AdJust_down( arr, i, n );//從最後一個非葉子節點開始調整
                }
2.進行排序(以升序爲例)
大堆的性質爲:最大的數據一定在堆頂將堆頂和堆最後一個元素進行交換,則最大的數字此時在數字尾部,再將堆頂元素下調,且堆的大小減1,知道堆大小爲1循環結束,排序完成。
代碼如下:
                 for (int i = n - 1; i >= 1; --i)
                {
                                swap( arr[0], arr [i]);
                                AdJust_down( arr, 0, i);
                }

選擇排序
選擇排序(Selection sort)是一種簡單直觀的排序算法。它的工作原理是每一次從待排序的數據元素中選出最小(或最大)的一個元素,存放在序列的起始位置,直到全部待排序的數據元素排完。 選擇排序是不穩定的排序方法
爲了減少比較的次數,我們一次遍歷同時選出最大值和最小值,代碼實現如下:
void SelectSort(int *arr, int n)
{
                
                 int i = 0, j = n - 1;
                 int max = j;
                 int min = i;
                 int left = 0; int right = n - 1;
                 while (left<=right)
                {
                                min = left;
                                max = right; ///!!!!!!!!!!!重點
                                 for (i = left, j = right; i <= j; i++)
                                {
                                                 if (arr [min]>arr[i])
                                                                min = i;
                                                 if (arr [max] < arr[i]) ////{ 2, 9, 6, 1, 3, 4, 5, 7, 0 ,-8,1,-2}
                                                                max = i;
                                }
                                 if (left != min)
                                {
                                                swap( arr[left], arr [min]);
                                                 if (max == left)
                                                                max = min;
                                }
                                                
                                 if (right != max)
                                                swap( arr[right], arr [max]);
                                left++;
                                right--;
                }

}
這裏我們必須注意到,以升序爲例,如果一次遍歷找到的最大值剛好在數組左邊,此時肯定會先被移走,此時就最大值得下標就得更新爲轉移後的位置


快速排序:
該方法的基本思想是:
1.先從數列中取出一個數作爲key。
2.分區過程,將比這個數大的數全放到它的右邊,小於或等於它的數全放到它的左邊。
3.再對左右區間重複第二步,直到各區間只有一個數。
代碼實現如下:
int PartSort(int *arr, int left, int right)
{
                 int key = arr [right]; //{ 10,2, 8, 9, 6, 1, 3, 4, 5, 7, 0 ,-1,-2,-100};
                 int begin = left ;
                 int end = right - 1;
                 while (begin<end)
                {
                                 while (begin<end&&arr [begin]<=key)
                                {
                                                begin++;
                                }
                                 while (begin<end&&arr [end]>=key)
                                {
                                                end--;
                                }
                                 if (begin < end)
                                                swap( arr[begin], arr [end]);
                }
                 if (arr [begin]>arr[right])
                {
                                swap( arr[begin], arr [right]);
                                 return begin;
                }
                 return right ;

}
void QuickSort(int *arr, int left,int right)
{              
                 if (left >= right)
                                 return;
                 int div = PartSort(arr , left, right);
                QuickSort( arr, div + 1, right );
                QuickSort( arr, left , div - 1);

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章