資料結構——排序(C語言)
在資料結構中我們常見的排序演算法有:直接插入排序、希爾排序、選擇排序、堆排序、交換排序(氣泡排序)、快速排序、歸併排序,接下來我給大家分享一下我在寫這些程式碼時的想法(從小到大,從左到右),以及各個排序的比較
首先我們得寫一個交換函式,因為後面基本每個排序都有使用。
void Swap(int *x, int *y)//交換函式
{
int tmp = *x;
*x = *y;
*y = tmp;
}
一、直接插入排序
1、思路
插入排序顧名思義就是把資料一個一個插入到有序序列中。
2、例:陣列a[] = { 21, 25, 49, 25*, 16, 8 }
第一次插入【21】25 49 25* 16 8
第二次插入【21 25】49 25* 16 8
第三次插入【21 25 49】25* 16 8
第四次插入【21 25 25* 49】16 8
第五次插入【16 21 25 25* 49】8
第六次插入【8 16 21 25 25* 49】
3、原始碼
void InsertSort(int *a, int n)//插入排序 { assert(a); for (int i = 1; i < n; i++) { int j = i - 1; while (j >= 0) { if (a[j + 1] < a[j]) { Swap(&a[j], &a[j + 1]); j--; } else break; } } }
二、希爾排序
1、思路
希爾排序其實就是插入排序的優化,因為當原陣列時倒序時,每進行一次插入都要將前面的元素向後移動,所以我們可以進行預排序將陣列每相隔gap個數進行排序。
2、例:陣列a[] = { 21, 25, 49, 25*, 16, 8 }
gap=3:21 16 8 25* 25 49
gap=1:8 16 21 25* 25 49
3、原始碼
void ShellSort(int *a, int n)//希爾排序 { assert(a); for (int gap = n / 2; gap > 0; gap /= 2)//gap為相隔個數當相隔為1時就是直接插入排序 { for (int i = gap; i < n; i++) { int j = i; while (j - gap >= 0) { if (a[j] < a[j - gap]) Swap(&a[j], &a[j - gap]); j -= gap; } } } }
三、選擇排序
1、思路
選擇排序也顧名思義,就是選出來一個最大和最小的的元素,一個一個排下去。
2、例:陣列a[] = { 21, 25, 49, 25*, 16, 8 }
第一次排序8 【25 21 25*16】49
第二次排序8 16【21 25*】25 49
第三次排序8 16 21 25* 25 49
3、原始碼
void SelectSort(int *a, int n)//選擇排序
{
assert(a);
int min = 0, max = 0, left = 0, right = n-1;
while (left<=right)
{
for (int i = left; i<=right;i++)//選出最大數和最小數
{
if (a[min] > a[i])
min = i;
if (a[max] < a[i])
max = i;
}
Swap(&a[min], &a[left]);
if (max == left)//當最大數在左邊時,上一次的交換以將最大數換走,所以改變最大數下標
max = min;
Swap(&a[max], &a[right]);
left++;
right--;
}
}
四、堆排序
1、思路
將一位陣列按完全二叉樹的順序儲存方式儲存,而且父親結點元素值大於它的孩子結點值,升序建大堆,降序建小堆。
2、例:陣列 { 21,
25, 49,
25*, 16, 8 }
第一次向上調整21 25 49 25* 16 8
第二次向上調整49 25 21 25* 16 8
第一次向下調整49 25 21 25* 16 8交換後8 25 21 25* 16【49】
第二次向下調整25 25* 21 8 16【49】交換後16 25* 21 8【25 49】
第三次向下調整25* 16 21 8【25 49】交換後8 16 21【25* 25 49】
第四次向下調整21 16 8【25* 25 49】交換後8 16【21 25* 25 49】
第五次向下調整16 8【21 25* 25 49】交換後8【16 21 25* 25 49】
3、原始碼
void Heap(int *a, int root)
{
assert(a);
for (int parent = (root - 1) / 2; parent >= 0;parent--)
{
int child = 2 * parent + 1;
if (child != root&&a[child] < a[child + 1])//選出孩子中最大元素
++child;
if (a[parent] < a[child])
Swap(&a[parent], &a[child]);
}
}
void HeapSort(int *a, int n)//堆排序,建大堆
{
assert(a);
Heap(a, n);//向上調堆
while (n>1)
{
for (int parent = 0; parent <= (n - 1) / 2; parent++)//向下調堆
{
int child = parent * 2 + 1;
if (child < (n - 1) && a[child] < a[child + 1])
++child;
if (child < n&&a[parent] < a[child])
Swap(&a[parent], &a[child]);
}
Swap(&a[0], &a[n - 1]);//將最後一個元素和開始元素奇交換
n--;
}
}
五、交換排序(氣泡排序)
1、思路
每次交換將最大的值放置右端,然後從右向左排下去。
2、例:陣列a[] = { 21, 25, 49, 25*, 16, 8 }
第一次交換21 25 25* 16 8【49】
第二次交換21 25 16 8【25* 49】
第三次交換21 16 8【25 25* 49】
第四次交換16 8【21 25 25* 49】
第五次交換8【16 21 25 25* 49】
第六次交換【8 16 21 25 25* 49】
3、原始碼
void BubbleSort(int *a, int n)//氣泡排序
{
assert(a);
for (int i = 0; i < n; i++)
{
for (int j = 0; j + 1 < n - i; j++)
{
if (a[j]>a[j + 1])
Swap(&a[j], &a[j + 1]);
}
}
}
六、快速排序
1、思路
在陣列中選出一個標記值,比它大的排右邊,比他小的排左邊,排完後的位置就是有序後的位置。
2、例:陣列a[] = { 21, 25, 49, 25*, 16, 8 }
第一次快速排序8【25 49 25* 16 21】
第二次快速排序8【【16】21【25* 49 25】】
第三次快速排序8【【16】21【【25*】 25 【49】】】
3、原始碼
int Quick(int *a, int left, int right)
{
assert(a);
while (left < right)
{
for (; left<right; left++)//找到大的進行交換
{
if (a[left] > a[right])
{
Swap(&a[left], &a[right]);
right--;
break;
}
}
for (; left < right; right--)//找到小的進行交換
{
if (a[right] < a[left])
{
Swap(&a[left], &a[right]);
left++;
break;
}
}
}
return right;//返回標記值的有序下標
}
void QuickSort(int *a, int left, int right)//快速排序
{
assert(a);
if (left < right)
{
int div = Quick(a, left, right);//區分左右區間
QuickSort(a, left, div - 1);//處理比標記值小的
QuickSort(a, div + 1, right);//處理比標記值大的
}
}
七、歸併排序
1、思路
將陣列每次平均劃分下去,最後有序進行歸併。
2、例:陣列a[] = { 21, 25, 49, 25*, 16, 8 }
第一次劃分【21 25 49】【25* 16 8】
第二次劃分【【21】【25 49】】【【25*】【16 8】】
第三次劃分【【21】【【25】【49】】】【【25*】【【16】【8】】】
第一次歸併【【21】【25 49】】【【25*】【8 16】】
第二次歸併【21 25 49】【8 16 25*】
第三次歸併8 16 21 25 25* 49
3、原始碼
void Merge(int *a, int left, int div, int right)
{
assert(a);
int llen = div - left + 1;
int rlen = right - div;
int i = 0, j = 0, x = 0;
int *l, *r;
l = (int*)malloc(sizeof(int)*llen);
r = (int*)malloc(sizeof(int)*rlen);
for (i = 0; i < llen; i++)
l[i] = a[left + i];
for (i = 0; i < rlen; i++)
r[i] = a[div + i + 1];
i = 0;
j = 0;
while (i < llen && j < rlen)
{
if (l[i] <= r[j])
a[left + x++] = l[i++];
else
a[left + x++] = r[j++];
}
while (i < llen)
a[left + x++] = l[i++];
while (j < rlen)
a[left + x++] = r[j++];
}
void MergeSort(int *a, int left, int right)//歸併排序
{
assert(a);
int div = left + ((right - left) >> 1);
if (left < right)
{
MergeSort(a, left, div);
MergeSort(a, div + 1, right);
Merge(a, left, div, right);
}
}
八、測試程式碼
int main()
{
int a[] = { 21, 25, 49, 25, 16, 8 };
//int a[] = { 9, 17, 65, 23, 45, 78, 87, 53, 31 };
//InsertSort(a, sizeof(a) / sizeof(a[0]));
//ShellSort(a, sizeof(a) / sizeof(a[0]));
//SelectSort(a, sizeof(a) / sizeof(a[0]));
//HeapSort(a, sizeof(a) / sizeof(a[0]));
//BubbleSort(a, sizeof(a) / sizeof(a[0]));
//QuickSort(a, 0, sizeof(a) / sizeof(a[0]) - 1);
MergeSort(a, 0, sizeof(a) / sizeof(a[0]) - 1);
for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
九、各個排序比較
排序方法 | 時間複雜度 | 空間複雜度 | 穩定性 |
插入排序 | O(N^2) | O(1) | 穩定 |
希爾排序 | O(N*1.3) | O(1) | 不穩定 |
選擇排序 | O(N^2) | O(1) | 不穩定 |
堆排序 | O(N*lgN) | O(1) | 不穩定 |
氣泡排序 | O(N^2) | O(1) | 穩定 |
快速排序 | O(N*lgN) | O(lgN) | 不穩定 |
歸併排序 | O(N*lgN) | O(N) | 穩定 |