这里只是列出了常用的几种排序算法例如:冒泡排序、直接插入排序、简单选择排序、希尔排序、堆排序、归并排序、快速排序。下面将给出各种排序算法的基本思想以及实现代码。下面这两个自定义头文件在后面的实现过程中要用到,先给出来
fatal.h定义错误处理
#include <stdio.h>
#include <stdlib.h>
#define FatalError(str) fprintf(stderr, "%s\n", str), exit(1)
mysort.h函数声明
typedef int ElementType;
#ifndef MYSORT_H
#define MYSORT_H
void swap(ElementType *lhs, ElementType *rhs);//实现数据交换
void bubble_sort(ElementType *arr, int n);//冒泡排序
void selected_sort(ElementType *arr, int n);//简单选择排序
void insert_sort(ElementType *arr, int n);//直接插入排序
void shell_sort(ElementType *arr, int n);//希尔排序
void heap_sort(ElementType *arr, int n);//堆排序
void merge_sort(ElementType *arr, int n);//归并排序递归实现
void merge_sort2(ElementType *arr, int n);//归并排序非递归实现
void quick_sort(ElementType *arr, int n);//快速排序
#endif
1.冒泡排序(BubbleSort)
基本思想:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。
void bubble_sort(ElementType *arr, int n)//冒泡排序
{
int i, j, swapped;
swapped = 1;
for(i = 0; i < n-1 && swapped; i++)
{
swapped = 0;
for(j = n-1; j > i; j--)
{
if(arr[j-1] > arr[j])
{
swap(arr + j - 1, arr + j);
swapped = 1;
}
}
}
}
2. 简单选择排序(Simple Selection Sort)
基本思想:通过n-i次关键字的比较从n-i+1个记录中选出关键字最小的记录,并和第i个记录交换
void selected_sort(ElementType *arr, int n)//简单选择排序
{
int i, j, k;
for(i = 0; i < n-1; i++)
{
k = i;
for(j = i+1; j < n; j++)
if(arr[j] < arr[k])
k = j;
swap(arr+k, arr+i);
}
}
3. 直接插入排序 (Straight Insertion Sort)
基本思想:将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增一的有序表。
void insert_sort(ElementType *arr, int n)//插入排序
{
int i, j;
ElementType tmp;
for(i = 1; i < n; i++)
{
tmp = arr[i];
for(j = i; j > 0 && tmp < arr[j -1]; j--)
arr[j] = arr[j-1];
arr[j] = tmp;
}
}
4.希尔排序(Shell Sort)
基本思想:先将整个待排序记录序列分割成若干个子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,在对全体记录进行一次直接插入排序。
void shell_sort(ElementType *arr, int n)//希尔排序
{
int i, j, increment;
ElementType tmp;
for(increment = n/2; increment > 0; increment /= 2)
{
for(i = increment; i < n; i++)
{
tmp = arr[i];
for(j = i; j >= increment && tmp < arr[j - increment]; j -= increment)
arr[j] = arr[j - increment];
arr[j] = tmp;
}
}
}
5. 堆排序(Heap Sort)
基本思想:将待排序的序列构造成一个大根堆。此时,整个序列的最大值就是堆顶的根节点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造一个堆,这样就会得到n个元素中次大的值。如此反复执行,便能得到一个有序序列。
#define LeftChild(i) (2*(i)+1)
static void percdown(ElementType *arr, int i, int n)//下滤
{
int child;
ElementType tmp;
for(tmp = arr[i]; LeftChild(i) < n; i = child)
{
child = LeftChild(i);
if(child != n - 1 && arr[child + 1] > arr[child])
child++;
if(tmp < arr[child])
arr[i] = arr[child];
else
break;
}
arr[i] = tmp;
}
void heap_sort(ElementType *arr, int n)//堆排序
{
int i;
for(i = n/2; i >= 0; i--)//构建堆
percdown(arr, i, n);
for(i = n-1; i > 0; i--)//删除堆顶元素
{
swap(arr, arr+i);
percdown(arr, 0, i);
}
}
6. 归并排序(Merge Sort)
基本思想:假设初始序列中含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到⌈n/2⌉(⌈x⌉表示不小于x的最小整数)个长度为2或1的有序子序列;再两两归并,……,如此反复,直到得到一个长度为n的有序序列为止。
static void merge(ElementType *arr, ElementType *tmp, int left, int center, int right)//合并
{
int i, rpos, cnt;
i = left;
rpos = center + 1;
cnt = right - left + 1;
while(left <= center && rpos <= right)
{
if(arr[left] <= arr[rpos])
tmp[i++] = arr[left++];
else
tmp[i++] = arr[rpos++];
}
while(left <= center)
tmp[i++] = arr[left++];
while(rpos <= right)
tmp[i++] = arr[rpos++];
for(i = 0; i < cnt; i++, right--)
arr[right] = tmp[right];
}
static void msort(ElementType *arr, ElementType *tmp, int left, int right)
{
int center;
if(left < right)
{
center = (left + right) / 2;
msort(arr, tmp, left, center);
msort(arr, tmp, center+1, right);
merge(arr, tmp, left, center, right);
}
}
void merge_sort(ElementType *arr, int n)//归并排序
{
ElementType *tmp;
tmp = (ElementType *)malloc(sizeof(ElementType) * n);
if(tmp == NULL)
{
FatalError("Out of space");
return;
}
msort(arr, tmp, 0, n-1);
free(tmp);
}
归并非递归实现
#define MIN(a,b) ((a)<(b)?(a):(b))
void merge_sort2(ElementType *arr, int n)//非递归实现
{
int left, center, right, size;
ElementType *tmp;
tmp = (ElementType *)malloc(sizeof(ElementType) * n);
if(tmp == NULL)
{
FatalError("Out of space");
return;
}
for(size = 1; size < n; size *= 2)
{
left = 0;
while(left + size < n)
{
center = left + size - 1;
right = MIN(n - 1, center + size);
merge(arr, tmp, left, center, right);
left = right+1;
}
}
free(tmp);
}
7. 快速排序
基本思想:通过一趟排序将待排序记录分割成独立的两个部分,其中一部分记录的关键字均比另一部分关键字小,则可以对这两部分记录继续进行排序,以达到整个序列有序的目的。
//递归
int partition(int *arr, int left, int right)
{
int pivot = arr[left];
while(left < right)
{
while(left < right && arr[right] >= pivot)
--right;
arr[left] = arr[right];
while(left < right && arr[left] <= pivot)
++left;
arr[right] = arr[left];
}
arr[left] = pivot;
return left;
}
int quickSort(int *arr, int left, int right)
{
if(left < right)
{
int mid = partition(arr, left, right);
quickSort(arr, left, mid - 1);
quickSort(arr, mid + 1, right);
}
}
//非递归
int partition(int *arr, int left, int right)
{
int pivot = arr[left];
while(left < right)
{
while(left < right && arr[right] >= pivot)
--right;
arr[left] = arr[right];
while(left < right && arr[left] <= pivot)
++left;
arr[right] = arr[left];
}
arr[left] = pivot;
return left;
}
void quickSort(int *arr, int left, int right)
{
if(left < right)
{
stack<int> s;
int mid = partition(arr, left, right);
if(left < mid -1)
{
s.push(left);
s.push(mid - 1);
}
if(mid + 1 < right)
{
s.push(mid + 1);
s.push(right);
}
while(!s.empty())
{
right = s.top();
s.pop();
left = s.top();
s.pop();
mid = partition(arr, left, right);
if(left < mid - 1)
{
s.push(left);
s.push(mid - 1);
}
if(mid + 1 < right)
{
s.push(mid + 1);
s.push(right);
}
}
}
}
以上排序时间复杂度、空间复杂度总结
排序算法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
冒泡排序 | O(n2) | O(n) | O(n2) | O(1) | 稳定 |
直接选择排序 | O(n2) | O(n2) | O(n2) | O(1) | 不稳定 |
直接插入排序 | O(n2) | O(n) | O(n2) | O(1) | 稳定 |
希尔排序 | O(nlogn)~O(n2) | O(n1.3) | O(n2) | O(1) | 不稳定 |
堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
快速排序 | O(nlogn) | O(nlogn) | O(n2) | O(logn)~O(n) | 不稳定 |