基本演算法學習(一)—— 排序
排序
一、氣泡排序
演算法思想: 在每一次對比排序中將大的數放在後面,整個排下來後,就變成有序的數列了
演算法實現:
1.(範圍為整個陣列),從前向後兩兩比較,如果前面比後面大就交換位置。第一遍後就將大的放在了最後
2.(縮小範圍),從頭再次重複步驟一,直到陣列有序為止
圖片說明:
縮小範圍繼續比較
直到最後成為了一個有序的陣列停止比較,演算法複雜度為O(
注意的地方:要注意的是for迴圈的迴圈次數,每次比較的次數為範圍的長度減一
程式碼實現:
#include "stdio.h"
#include "stdlib.h"
void bubbleSort(int *a, int len);
void swap(int *a, int *b);
void print_arr(int *a, int len);
int main()
{
int array[] = { 18, 12, 56, 34, 78, 90, 12 };
bubbleSort(array, 7);
print_arr(array,7);
system("pause");
return 0;
}
//氣泡排序
void bubbleSort(int *a, int len){
for (int i = len; i > 0; i--){
for (int j = 0; j < i-1; j++){
swap(&a[j], &a[j + 1]);
}
}
}
//交換函式
void swap(int *a, int *b){
int temp;
if (*a > *b){
temp = *a;
*a = *b;
*b = temp;
}
}
結果:
二、直接選擇排序
演算法思想:在限定的範圍內選取最小值,然後縮小範圍繼續選擇最小值
演算法實現:
1.在1~n的範圍內選擇最小值,與第1個值交換位置
2.縮小範圍在2~n上選擇最小值,與第2個值交換位置
3. 按照規律重複上述步驟,最後得到一個有序數列
圖片說明:
注意的地方:
1. 為了實現交換這一步驟,我們在還需聲名一個變數來儲存我們找到的最小值在陣列中的索引值
2. 與氣泡排序不同的是,氣泡排序在範圍內進行比較時,會進行多次交換。而直接選擇排序是在一輪搜尋後找到最小值才會進行交換。但時間複雜度仍然時
程式碼實現:
#include "stdio.h"
#include "stdlib.h"
void SelectSort(int R[], int n);
int main()
{
int array[] = { 18, 12, 56, 34, 78, 90, 12 };
SelectSort(array, 7);
print_arr(array,7);
system("pause");
return 0;
}
void SelectSort(int R[], int n){
int index = 0;
int min = 0;
for (int i = 0; i < n; i++){
min = R[i];
for (int j = i; j < n; j++){
if (R[j] < min){
min = R[j];
index = j;
}
}
swap(&R[i], &R[index]);
}
}
void swap(int *a, int *b){
int temp;
if (*a > *b){
temp = *a;
*a = *b;
*b = temp;
}
}
三、直接插入排序
演算法思想: 待排序的數字插入到已經排好序的有序數列中
(摘自白話經典部落格中的演算法步驟,後有連結)
演算法實現:
1. 初始時,a[0]自成1個有序區,無序區為a[1..n-1]。令i=1
2. 將a[i]併入當前的有序區a[0…i-1]中形成a[0…i]的有序區間。
3. i++並重復第二步直到i==n-1。排序完成。連結:這篇博文還詳細講解了直接插入排序的其他方法,可供參考
http://blog.csdn.net/morewindows/article/details/6665714
圖片描述:
程式碼實現:
#include "stdio.h"
#include "stdlib.h"
void Insertsort1(int a[], int n);
void swap(int *a, int *b);
int main()
{
int array[] = { 18, 12, 56, 34, 78, 90, 12 };
Insertsort1(array, 7);
print_arr(array,7);
system("pause");
return 0;
}
void Insertsort1(int a[], int n)
{
for (int i = 1; i < n; i++){
for (int j = i - 1; j >= 0; j--){
if (a[j] > a[j + 1]){
swap(&a[j], &a[j + 1]);
}
}
}
}
void swap(int *a, int *b){
int temp;
if (*a > *b){
temp = *a;
*a = *b;
*b = temp;
}
}
四、希爾排序
演算法思想: 是一種分組插入排序
(百度百科)
希爾排序是在直接插入排序上進行的改進,改進的原因:
1、插入排序在對幾乎已經排好序的資料操作時,效率高,即可以達到線性排序的效率。
2、但插入排序一般來說是低效的,因為插入排序每次只能將資料移動一位。
演算法實現:
1.確定縮小增量n,抽取相隔n的數為一組,進行直接插入排序
2. 減小縮小增量n的值,繼續分組排序
3. 直到增量的值變為1為止,停止排序
圖片說明:
程式碼實現:
#include "stdio.h"
#include "stdlib.h"
void Insertsort1(int a[], int n);
void swap(int *a, int *b);
int main()
{
int array[] = { 18, 12, 56, 34, 78, 90, 12 };
Insertsort1(array, 7);
print_arr(array,7);
system("pause");
return 0;
}
void shellsort3(int a[], int n)
{
int i, j, gap;
for (gap = n / 2; gap > 0; gap /= 2)
for (i = gap; i < n; i++)
for (j = i - gap; j >= 0 && a[j] > a[j + gap]; j -= gap)
swap(&a[j], &a[j + gap]);
}
void swap(int *a, int *b){
int temp;
if (*a > *b){
temp = *a;
*a = *b;
*b = temp;
}
}
五、歸併排序
演算法思想: 採用的分而治之的想法,先將大的分為小的子序列,然後再合併
演算法實現:
1.把大序列分子序列,分成單位為1為止(遞迴)
2. 建立一個輔助的空間,用來存放合併後的子序列
3.合併子序列,合併的規則是,從兩個子序列的頭部開始比較,小的放到輔助空間中。
這樣下來,演算法的時間複雜度為
注意的地方:
1.分的時候用的是遞迴的方法
2. 比較一定會有一個子序列有剩餘的情況,這個時候要將剩餘的部分新增到輔助空間上
3. 最後不要忘了將輔助空間的序列重新新增到真正的合併後的序列上
圖片描述:
(圖片來自c語言中國網)
程式碼實現:
c語言版
int main()
{
int array[] = { 18, 12, 56, 34, 78, 90, 12 };
MergeSort(array, 0, 6);
print_arr(array,7);
system("pause");
return 0;
}
//歸併排序
void Merge(int *a, int left, int mid, int right){
int left_i = left;
int right_i = mid+1;
int p = 0;
//聲名輔助空間
int *temp = (int *)malloc((right - left + 1)*sizeof(int));
while (left_i <= mid && right_i <= right){
temp[p++] = (a[left_i] <= a[right_i]) ? a[left_i++] : a[right_i++];
}
//將子序列1的剩餘部分給輔助空間
while (left_i <= mid)
{
temp[p++] = a[left_i++];
}
//將子序列2的剩餘部分給輔助空間
while (right_i <= right)
{
temp[p++] = a[right_i++];
}
for (p = 0, left_i = left; left_i <= right; p++, left_i++)
{
a[left_i] = temp[p]; //歸併完成後將結果複製回R[low..high]
}
}
//分序列
void MergeSort(int *R, int low, int high)
{
//用分治法對R[low..high]進行二路歸併排序
int mid;
if (low<high)
{
mid = (low + high) / 2;
MergeSort(R, low, mid);
MergeSort(R, mid + 1, high);
Merge(R, low, mid, high);
}
}
六、快速排序
演算法思想: 快速排序是選擇出一個標準A,然後將小於A的值放在A之前,大於A的值放在A之後.
演算法實現:
1.選擇陣列中第一個元素作為標準A,從後面開始比較
2.如果找到一個小於A的值B,就將B放在A的位置,然後從B之後開始找
3.如果找到一個大於A的值C,就將C放在最開始B的位置上,
4.依照上面的次序進行,直到從前面開始找的和從後面找的相遇,就將A放在相遇的位置.
圖片說明
等到排序一遍後,大於標準值的值就被排在了標準值的後面,小於標準值的值就被排在了標準值的前面.然後再在兩側分別執行一遍排序.最後就可以將
無序的陣列排列成從左到右的有序陣列,時間複雜度是
注意的地方:
1.我們首先要宣告一個變數來儲存我們的標準值.
2.在發現比標準大或者比標準小的時候,不是與標準值進行交換,而是填補空缺的位置上的值(填坑)
3.在填完坑後要即使移動指標,從坑後面的數開始找。
4.這裡面涉及遞迴的思想
程式碼實現:
C語言版
#include "stdio.h"
#include "stdlib.h"
void SwiftSort(int *a, int left, int right);
void print_arr(int *a, int len);
int main()
{
int array[] = { 18, 12, 56, 34, 78, 90, 12 };//給了一個初始陣列
SwiftSort(array, 0, 6);
print_arr(array,7);
system("pause");//防止在vs中執行時看不到結果就閃退
return 0;
}
void SwiftSort(int *a, int left, int right){
int i = left;
int j = right;
int save = a[left];
//如果左邊大於等於右邊說明完成一組排序
if (left < right){
while (i < j){
//去找比標準值小的數直到找到為止
while (i < j && a[j] <= save){
j--;
}
if (i < j)
a[i++] = a[j];
//去找比標準值大的數直到找到為止
while (i < j && a[i] > save) // 從左向右找第一個大於等於x的數
i++;
if (i < j)
a[j--] = a[i];
}
a[i] = save; //不要忘了把標準值放進去
SwiftSort(a, left, i - 1);
SwiftSort(a, i + 1, right);
}
}
void print_arr(int *a, int len){
for (int i = 0; i < len; i++){
printf("%i ", a[i]);
}
}
結果:
總結
1、分而治之,講整個大的排序變成每一小組的排序(希爾排序,歸併排序)
2、用對比後交換代替找到後再調整位置
上述程式碼可以在我的下載資源中找到。