- 冒泡排序
for(int j = 0; j < n - 1; j++) // 每次最大元素就像气泡一样"浮"到数组的最后
{
for (int i = 0; i < n - 1 - j; i++) // 依次比较相邻的两个元素,使较大的那个向后移
{
if (A[i] > A[i + 1]) // 如果条件改成A[i] >= A[i + 1],则变为不稳定的排序算法
{
exchange(A, i, i + 1);
}
}
}
- 选择排序
for (i = 0; i <= n - 2; i++) // 已排序序列的末尾
{
min = i;
for (j = i + 1; j <= n - 1; j++) // 未排序序列
{
if (A[j] < A[min])
// 依次找出未排序序列中的最小值,存放到已排序序列的末尾
{
min = j;
}
}
if (min != i)
{
exchange(A, min, i);
// 该操作很有可能把稳定性打乱,所以选择排序是不稳定的排序算法
}
}
- 鸡尾酒排序
int left = 0; // 初始化边界
int right = n - 1;
while (left < right)
{
for (int i = left; i < right; i++)
// 前半轮,将最大元素放到后面
if (A[i] > A[i + 1])
{
exchange(A, i, i + 1);
}
right--;
for (int i = right; i > left; i--)
// 后半轮,将最小元素放到前面
if (A[i - 1] > A[i])
{
exchange(A, i - 1, i);
}
left++;
}
- 直接插入排序
for (i = 1; i < n; i++)
{
get = A[i]; // 右手抓到一张扑克牌
j = i - 1;
// 拿在左手上的牌总是排序好的
while (j >= 0 && A[j] > get)
// 将抓到的牌与手牌从右向左进行比较
{
A[j + 1] = A[j];
// 如果该手牌比抓到的牌大,就将其右移
j--; //回到j-1的位置
}
A[j+1] = get;
// 直到该手牌比抓到的牌小(或二者相等),将抓到的牌插入到该手牌右边(相等元素的相对次序未变,所以插入排序是稳定的)
}
- 二分插入排序
for (i = 1; i < n; i++) // 类似抓扑克牌排序
{
// 右手抓到一张扑克牌
left = 0;
right = i - 1;
while (left <= right)
// 采用二分法定位新牌的位置
{
middle = (left + right) / 2;
if (A[middle] > get)
right = middle - 1;
else
left = middle + 1;
}
for (j = i - 1; j >= left; j--)
// 将欲插入新牌位置右边的牌整体向右移动一个单位
{
A[j + 1] = A[j];
}
A[left] = get;
// 将抓到的牌插入手牌
}
- 希尔排序
#include <stdio.h>
// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- 根据步长序列的不同而不同。已知最好的为O(n(logn)^2)
// 最优时间复杂度 ---- O(n)
// 平均时间复杂度 ---- 根据步长序列的不同而不同。
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 不稳定
int main()
{
int A[] = { 5, 2, 9, 4, 7, 6, 1, 3, 8 };// 从小到大希尔排序
int n = sizeof(A) / sizeof(int);
int i, j, get;
int h = 0;
while (h <= n) // 生成初始增量
{
h = 3*h + 1;
}
while (h >= 1)
{
for (i = h; i < n; i++)
{
j = i - h;
get = A[i];
while ((j >= 0) && (A[j] > get))
{
A[j + h] = A[j];
j = j - h;
}
A[j + h] = get;
}
h = (h - 1) / 3; // 递减增量
}
printf("希尔排序结果:");
for (i = 0; i < n; i++)
{
printf("%d ", A[i]);
}
printf("\n");
return 0;
}
- 归并排序
#include <stdio.h>
#include <limits.h> // 包含极限值的头文件,这里用到了无穷大INT_MAX
// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(nlogn)
// 最优时间复杂度 ---- O(nlogn)
// 平均时间复杂度 ---- O(nlogn)
// 所需辅助空间 ------ O(n)
// 稳定性 ------------ 稳定
int L[10]; // 两个子数组定义成全局变量(辅助存储空间,大小正比于元素的个数)
int R[10];
void merge(int A[], int left, int middle, int right)// 合并两个已排好序的数组A[left...middle]和A[middle+1...right]
{
int n1 = middle - left + 1; // 两个数组的大小
int n2 = right - middle;
for (int i = 0; i < n1; i++) // 把两部分分别拷贝到两个数组中
L[i] = A[left + i];
for (int j = 0; j < n2; j++)
R[j] = A[middle + j + 1];
L[n1] = INT_MAX; // 使用无穷大作为哨兵值放在子数组的末尾
R[n2] = INT_MAX; // 这样可以免去检查某个子数组是否已读完的步骤
int i = 0;
int j = 0;
for (int k = left; k <= right; k++) // 依次比较两个子数组中的值,每次取出更小的那一个放入原数组
{
if (L[i] <= R[j])
{
A[k] = L[i];
i++;
}
else
{
A[k] = R[j];
j++;
}
}
}
void mergesort_recursion(int A[], int left, int right) // 递归实现的归并排序(自顶向下)
{
int middle = (left + right) / 2;
if (left < right) // 当待排序的序列长度为1时(left == right),递归“开始回升”
{
mergesort_recursion(A, left, middle);
mergesort_recursion(A, middle + 1, right);
merge(A, left, middle, right);
}
}
void mergesort_iteration(int A[], int left, int right) // 非递归(迭代)实现的归并排序(自底向上)
{
int low, middle, high; // 子数组索引,前一个为A[low...middle],后一个子数组为A[middle+1...high]
for (int size = 1; size <= right - left; size *= 2) // 子数组的大小初始为1,每轮翻倍
{
low = left;
while (low + size - 1 <= right - 1 )// 后一个子数组存在(需要归并)
{
middle = low + size - 1;
high = middle + size;
if (high > right) // 后一个子数组大小不足size
high = right;
merge(A, low, middle, high);
low = high + 1; // 前一个子数组索引向后移动
}
}
}
int main()
{
int A1[] = { 6, 5, 3, 1, 8, 7, 2, 4 }; // 从小到大归并排序
int A2[] = { 6, 5, 3, 1, 8, 7, 2, 4 };
int n1 = sizeof(A1) / sizeof(int);
int n2 = sizeof(A2) / sizeof(int);
mergesort_recursion(A1, 0, n1 - 1); // 递归实现
mergesort_iteration(A2, 0, n2 - 1); // 非递归实现
printf("递归实现的归并排序结果:");
for (int i = 0; i < n1; i++)
{
printf("%d ",A1[i]);
}
printf("\n");
printf("非递归实现的归并排序结果:");
for (int i = 0; i < n2; i++)
{
printf("%d ", A2[i]);
}
printf("\n");
return 0;
}
- 堆排序
#include <stdio.h>
// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(nlogn)
// 最优时间复杂度 ---- O(nlogn)
// 平均时间复杂度 ---- O(nlogn)
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 不稳定
int heapsize; // 堆大小
void exchange(int A[], int i, int j) // 交换A[i]和A[j]
{
int temp = A[i];
A[i] = A[j];
A[j] = temp;
}
void heapify(int A[], int i) // 堆调整函数(这里使用的是最大堆)
{
int leftchild = 2 * i + 1; // 左孩子索引
int rightchild = 2 * i + 2; // 右孩子索引
int largest; // 选出当前结点与左右孩子之中的最大值
if (leftchild < heapsize && A[leftchild] > A[i])
largest = leftchild;
else
largest = i;
if (rightchild < heapsize && A[rightchild] > A[largest])
largest = rightchild;
if (largest != i)
{
exchange(A, i, largest); // 把当前结点和它的最大(直接)子节点进行交换
heapify(A, largest); // 递归调用,继续从当前结点向下进行堆调整
}
}
void buildheap(int A[], int n) // 建堆函数
{
heapsize = n;
for (int i = heapsize / 2 - 1; i >= 0; i--) // 对每一个非叶结点
heapify(A, i); // 不断的堆调整
}
void heapsort(int A[], int n)
{
buildheap(A, n);
for (int i = n - 1; i >= 1; i--)
{
exchange(A, 0, i); // 将堆顶元素(当前最大值)与堆的最后一个元素互换(该操作很有可能把后面元素的稳定性打乱,所以堆排序是不稳定的排序算法)
heapsize--; // 从堆中去掉最后一个元素
heapify(A, 0); // 从新的堆顶元素开始进行堆调整
}
}
int main()
{
int A[] = { 5, 2, 9, 4, 7, 6, 1, 3, 8 };// 从小到大堆排序
int n = sizeof(A) / sizeof(int);
heapsort(A, n);
printf("堆排序结果:");
for (int i = 0; i < n; i++)
{
printf("%d ", A[i]);
}
printf("\n");
return 0;
}
- 快速排序
#include <stdio.h>
// 分类 ------------ 内部比较排序
// 数据结构 --------- 数组
// 最差时间复杂度 ---- 每次选取的基准都是最大的元素(或者每次都是最小),导致每次只划分出了一个子序列,需要进行n-1次划分才能结束递归,时间复杂度为O(n^2)
// 最优时间复杂度 ---- 每次选取的基准都能使划分均匀,只需要logn次划分就能结束递归,时间复杂度为O(nlogn)
// 平均时间复杂度 ---- O(nlogn)
// 所需辅助空间 ------ O(logn)~O(n),主要是递归造成的栈空间的使用(用来保存left和right等局部变量),取决于递归树的深度
// 一般为O(logn),最差为O(n)(基本有序的情况)
// 稳定性 ---------- 不稳定
void exchange(int A[], int i, int j) // 交换A[i]和A[j]
{
int temp = A[i];
A[i] = A[j];
A[j] = temp;
}
int partition(int A[], int left, int right) // 划分函数
{
int pivot = A[right]; // 选择最后一个元素作为基准
int tail = left - 1; // tail为小于基准的子数组最后一个元素的索引
for (int i = left; i < right; i++) // 遍历基准以外的其他元素
{
if (A[i] <= pivot) // 把小于等于基准的元素放到前一个子数组中
{
tail++;
exchange(A, tail, i);
}
}
exchange(A, tail + 1, right); // 最后把基准放到前一个子数组的后边,剩下的子数组既是大于基准的子数组
// 该操作很有可能把后面元素的稳定性打乱,所以快速排序是不稳定的排序算法
return tail + 1; // 返回基准的索引
}
void quicksort(int A[], int left, int right)
{
int pivot_index; // 基准的索引
if (left < right)
{
pivot_index = partition(A, left, right);
quicksort(A, left, pivot_index-1);
quicksort(A, pivot_index+1, right);
}
}
int main()
{
int A[] = { 5, 2, 9, 4, 7, 6, 1, 3, 8 };// 从小到大快速排序
int n = sizeof(A) / sizeof(int);
quicksort(A, 0, n - 1);
printf("快速排序结果:");
for (int i = 0; i < n; i++)
{
printf("%d ",A[i]);
}
printf("\n");
return 0;
}