1. 插入排序
插入排序分为直接插入排序,二分插入排序,希尔排序
直接插入排序在最好情况下时间复杂度为O(1),最坏情况为O(n2),稳定
二分插入排序在最好情况下时间复杂度为O(1),最坏情况为O(n2),稳定
希尔排序在最好情况下时间复杂度为O(1),最坏情况为O(n1.3),不稳定
1. 直接插入排序
思想:每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置,直到全部插入排序完为止。
关键问题:在前面已经排好序的序列中找到合适的插入位置。
#include <stdio.h>
void Print(int* arr, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
void InsertSort(int *num, int len)
{
int i, j, temp;
for (i = 1; i < len; i
++)
{
// num[i]之前的序列是有序的,num[i - 1]为有序部分的最大值
// 故只需要与有序部分的最大值进行比较,即可判断是否需要插入
if (num[i - 1] > num[i])
{
// 获取需要插入的数据
temp = num[i];
// 依次后移,查找插入位置
for ( j = i - 1; j >= 0 && num[j] > temp; j--)
{
num[j + 1] = num[j];
}
// 完成插入
num[j + 1] = temp;
}
}
}
void test()
{
int arr[] = {
12,18,9,20,13,4};
int n = sizeof(arr) / sizeof(arr[0]);
InsertSort(arr, n);
printf("排序后的数组:\n");
Print(arr,n);
}
int main()
{
test();
return 0;
}
2. 二分插入排序
二分法插入排序,简称二分排序,是在插入第i个元素时,对前面的0~i-1元素进行折半,先跟他们中间的那个元素比,如果小,则对前半再进行折半,否则对后半进行折半,直到low<high,然后再把第i个元素前1位与目标位置之间的所有元素后移,再把第i个元素放在目标位置上。
#include <stdio.h>
#define N 10
//二分插入排序 升序
void biInsertSort(int R[], int n)
{
int mid, low, high, temp;
for(int i = 0; i < n; i++)
{
low = 0;
high = i - 1;
temp = R[i];
while(low <= high)
{
mid = (low + high) / 2;
if (temp < R[mid])
high = mid - 1;
else
low = mid + 1;
}
//将0~high范围的元素右移
for(int j = i-1; j > high; j--)
R[j+1] = R[j];
R[high+1] = temp;
}
}
void main()
{
int array[N] = {
30, 40, 60, 10, 20, 50, 70, 80, 90, 100};
printf("排序前:\n");
for(int i = 0; i < N; i++)
printf("%d ", array[i]);
printf("\n");
biInsertSort(array, N);
printf("排序后:\n");
for(int i = 0; i < N; i++)
printf("%d ", array[i]);
printf("\n");
}
3. 希尔排序
#include<stdio.h>
// 实现希尔排序
void ShellSort(int arr[], int len)
{
int tmp = 0;
//gap为步长,每次减为原来的一半
for(int gap=len/2;gap>0;gap/=2)
{
//共gap个组,对每一组都执行直接插入排序
for(int i=0;i<gap;i++)
{
//组内的直接插入排序
for(int j=i+gap;j<len;j+=gap)
{
// 如果当前元素大于加上步长后的那个元素,说明交换
if(arr[j]<arr[j-gap])
{
tmp=arr[j];
int k=j-gap;
// 将大于arr[j]的值整体后移
while(k>=0 && arr[k]>tmp)
{
arr[k+gap]=arr[k];
k-=gap;
}
arr[k+gap]=tmp;
}
}
}
}
}
// 打印函数
void show(int a[], int n)
{
for (int i=0; i<n; i++)
{
printf("%d ",a[i]);
}
putchar('\n');
}
int main()
{
int a[] = {
3, 1, 5, 7, 2, 4, 9, 6, 10, 8};
int n = sizeof(a)/sizeof(int);
printf("排序前:");
show(a, n);
ShellSort(a, n);
printf("排序后:");
show(a, n);
return 0;
}
2. 交换排序
1. 冒泡排序
每一趟只能确定将一个数归位。即第一趟只能确定将末位上的数归位,第二趟只能将倒数第 2 位上的数归位,依次类推下去。如果有 n 个数进行排序,只需将 n-1 个数归位,也就是要进行 n-1 趟操作。
而 “每一趟 ” 都需要从第一位开始进行相邻的两个数的比较,将较大的数放后面,比较完毕之后向后挪一位继续比较下面两个相邻的两个数大小关系,重复此步骤,直到最后一个还没归位的数。
#include<stdio.h>
void Print(int* arr, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
void BubbleSort(int* arr, int n){
for (int i = 0; i < n-1; i++)
{
for(int j = 0; j < n-i-1; j++){
if(arr[j]>arr[j+1]){
int temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
}
void test()
{
int arr[] = {
12,18,9,20,13,4};
int n = sizeof(arr) / sizeof(arr[0]);
BubbleSort(arr, n);
Print(arr, n);
}
int main(){
test();
return 0;
}
2. 快速排序
我可以提供一个简单的快速排序算法,它是一种分治算法
它的基本思想是:首先从数列中取出一个元素,作为基准值;然后将比这个基准值小的放到它的左边,将比它大的放到它的右边;最后对左右两个子序列重复上述步骤,直到序列有序。
#include <stdio.h>
void quicksort(int *arr, int begin, int end)
{
if (begin < end)
{
int key = arr[begin];
int i = begin;
int j = end;
while (i < j)
{
while (arr[j] > key && j > i){
j--;
}
arr[i] = arr[j];
while (arr[i] < key && i < j){
i++;
}
arr[j] = arr[i];
}
arr[i] = key; // 此时,i=j
quicksort(arr, begin, i-1);
quicksort(arr, j+1, end);
}
}
void Print(int* arr, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
void test()
{
int arr[] = {
12,18,9,20,13,4};
int n = sizeof(arr) / sizeof(arr[0]);
quicksort(arr, 0, n-1);
Print(arr, n);
}
int main (void)
{
test();
return 0;
}
3.选择排序
选择排序分为直接选择排序与树形选择排序(堆排序)
直接选择排序在最好情况下时间复杂度为O(1),最坏情况为O(n2)
堆排序在最好情况下时间复杂度为O(logn),最坏情况为 O(logn2)
选择排序都是不稳定的
1. 直接选择排序
排序过程:
(1)将整个记录序列划分为有序区和无序区,初始时有序区为空,无序区含有待排序的所有记录;
(2)在无序区查找值最小的记录,将它与无序区的第一个记录交换,使得有序区扩展一个记录,同时无序区减少一个记录。
(3)不断重复步骤(2),直到无序区只剩下一个记录为止。
#include<stdio.h>
void Select(int* arr,int n){
for(int i=0;i<=n-1;i++) //进行n-1趟排序
{
int min=i; //默认第一个元素为最小值
//找出最小值下标
for(int j=i+1;j<=n-1;j++)
{
if(arr[j]<arr[min])
{
min=j;
}
}
if(min!=i)
{
int temp=arr[i];
arr[i]=arr[min];
arr[min]=temp;
}
}
}
void Print(int* arr, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
void test()
{
int arr[] = {
12,18,9,20,13,4};
int n = sizeof(arr) / sizeof(arr[0]);
Select(arr, n);
Print(arr, n);
}
int main(){
test();
return 0;
}
2. 堆排序
用大根堆,每次挑选最大记录归位。挑选最大记录采用的是筛选法。
筛选法:将一个无序序列调整为堆的过程。
堆排序使用了减治法的思想。
排序过程:
(1)将记录序列用筛选法调整为大根堆;
(2)将堆顶和堆中最后一个记录交换;
(3)不断重复步骤(1)(2),直到堆中只有一个记录为止。
#include <stdio.h>
void heapify(int arr[], int n, int i)
{
int largest = i; // 假设根节点最大
int l = 2 * i + 1; // 左子节点
int r = 2 * i + 2; // 右子节点
// 如果左子节点比根节点大,则将largest赋值为左子节点的下标
if (l < n && arr[l] > arr[largest])
{
largest = l;
}
// 如果右子节点比当前最大值还要大,则将largest赋值为右子节点的下标
if (r < n && arr[r] > arr[largest])
{
largest = r;
}
// 如果最大值的位置已经不是当前根节点的位置,则交换它们的值
if (largest != i)
{
int temp = arr[i];
arr[i] = arr[largest];
arr[largest] = temp;
// 继续递归调用堆化,直到所有子树都符合堆的要求
heapify(arr, n, largest);
}
}
void heapSort(int arr[], int n)
{
// 构建堆,从最后一个非叶子节点开始堆化
for (int i = n / 2 - 1; i >= 0; i--)
{
heapify(arr, n, i);
}
// 从堆中取出元素,并进行排序
for (int i = n - 1; i > 0; i--)
{
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// 每取出一个元素,就对堆进行一次堆化,保持堆的性质
heapify(arr, i, 0);
}
}
void Print(int* arr, int n)
{
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
}
void test()
{
int arr[] = {
12,18,9,20,13,4};
int n = sizeof(arr) / sizeof(arr[0]);
heapSort(arr, n);
printf("排序后的数组:\n");
Print(arr,n);
}
int main()
{
test();
return 0;
}
4.归并排序
1. 二路归并排序
归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
#include <stdio.h>
#include <stdlib.h>
void merge(int a[], int low, int mid, int high, int b[])//将a[low:mid]和a[mid+1:high]归并到b[low:high]
{
int i = low;
int j = mid + 1;
int k = low;
while(i <= mid && j <= high)
{
if (a[i] <= a[j])
{
b[k++] = a[i++];
}
else
{
b[k++] = a[j++];
}
}
while(i <= mid)
{
b[k++] = a[i++];
}
while(j <= high)
{
b[k++] = a[j++];
}
}
void mergesort(int a[], int length, int b[])
{
int *temp, step, i;
temp = (int *)malloc(sizeof(int) * length);
if (NULL == temp)
{
exit(-1);
}
for (step = 1; step < length; step <<= 1)
{
for (i = 0; i <= length - step; i += (step << 1))
{
merge(a, i, i + step - 1, i + (step << 1) - 1 < length - 1 ? i + (step << 1) - 1 : length - 1, temp);
}
for (int j = 0; j < length; j++)
{
a[j] = temp[j];
b[j] = a[j];
}
}
free(temp);
}
int main()
{
int a[8] = {
7, 1, 4, 9, 6, 3, 5, 0};
int b[8] = {
0};
mergesort(a, 8, b);
int i;
for(i = 0; i < 8; i++)
{
printf("%d\t", b[i]);
}
printf("\n");
return 0;
}