本文是小编巩固自身而作,如有错误,欢迎指出!
1.常见的排序算法
2.常见排序算法的实现
2.1插入排序
直接插⼊排序是⼀种简单的插⼊排序法,其基本思想是:把待排序的记录按其关键码值的⼤⼩逐个插 ⼊到⼀个已经排好序的有序序列中,直到所有的记录插⼊完为⽌,得到⼀个新的有序序列。
就类似于我们平时打扑克时一样,在理牌时将新拿的牌直接插入进行排序
2.1.1直接插入排序
直接插入排序的思想就是
当插⼊第 i(i>=1) 个元素时,前⾯的 array[0],array[1],…,array[i-1] 已经排好序,此时 ⽤ array[i] 的排序码与 array[i-1],array[i-2],… 的排序码顺序进⾏⽐较,找到插⼊位置 即将 array[i] 插⼊,原来位置上的元素顺序后移
代码实现如下
void insertsort(int* arr, int n)//直接插入排序
{
for (int i = 0;i < n-1;i++)
{
int end = i;
int tmp = arr[end + 1];
while (end >= 0)
{
if (arr[end] > tmp)
{
arr[end+1] = arr[end];
end--;
}
else
{
break;
}
}
arr[end + 1] = tmp;
}
}
这里代码的思想就是
在数组arr中将arr[0]到arr[end]的区间视为已经排序好的数组,将arr[end+1]视作新插入的数据与之前的数据进行比较后插入指定位置
1. 元素集合越接近有序,直接插⼊排序算法的时间效率越⾼
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
2.1.2希尔排序
希尔排序法⼜称缩⼩增量法。希尔排序法的基本思想是:先选定⼀个整数(通常是gap=n/3+1),把 待排序⽂件所有记录分成各组,所有的距离相等的记录分在同⼀组内,并对每⼀组内的记录进⾏排 序,然后gap=gap/3+1得到下⼀个整数,再将数组分成各组,进行插⼊排序,当gap=1时,就相当于直接插⼊排序。
希尔排序是在直接插入排序的情况下进行改善而成,而其为什么比直接排序更优呢,原因如下:
因为插入算法在数目及其大的情况将要将后面插入的数据与前面数据依次比较就要进行非常多次,而我们使用希尔排序就相当于将这个数组划分成若干个小数组,先将小数组进行排序,让其中的大大小关系达到近乎有序,这样我们最后就行直接插入排序,进行比较的时候就能节省很多时间。
代码实现如下
void shellsort(int* arr, int n)//希尔排序
{
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
for (int i = 0;i < n - gap;i++)//跨步长gap进行直接插入排序
{
int end = i;
int tmp = arr[end + gap];
while (end >= 0)
{
if (arr[end] > tmp)
{
arr[end + 1] = arr[end];
end -= gap;
}
else
{
break;
}
}
arr[end + gap] = tmp;
}
}
}
1. 希尔排序是对直接插⼊排序的优化。
2. 当 gap > 1 时都是预排序,⽬的是让数组更接近于有序。当 gap == 1 时,数组已经接近有序 的了,这样就会很快。这样整体⽽⾔,可以达到优化的效果。
2.2选择排序
选择排序的思想:
每⼀次从待排序的数据元素中选出最⼩(或最⼤)的⼀个元素,存放在序列的起始位置,直到全部待 排序的数据元素排完。
2.2.1直接选择排序
1. 在元素集合 array[i]--array[n-1] 中选择关键码最⼤(⼩)的数据元素
2. 若它不是这组元素中的最后⼀个(第⼀个)元素,则将它与这组元素中的最后⼀个(第⼀个)元素 交换
3. 在剩余的 array[i]--array[n-2](array[i+1]--array[n-1]) 集合中,重复上述步 骤,直到集合剩余 1 个元素
代码实现如下
void SelectSort(int* arr, int n)//直接选择排序
{
int begin = 0, end = n - 1;
while (begin < end)
{
int maxi = begin;
int mini = begin;
for (int i = begin + 1;i <= end;i++)//遍历寻找出最大,最小值
{
if (arr[i] < arr[mini])
{
mini = i;
}
if (arr[i] > arr[maxi])
{
maxi = i;
}
}
if (maxi == begin)
{
maxi = mini;
}
swap(&arr[mini], &arr[begin]);
swap(&arr[maxi], &arr[end]);
begin++;
end--;
}
}
2.2.2堆排序
在之前已经详细讲过如何实现,有需要可以看这里
2.3交换排序
交换排序基本思想:
所谓交换,就是根据序列中两个记录键值的⽐较结果来对换这两个记录在序列中的位置 交换排序的特点是:将键值较⼤的记录向序列的尾部移动,键值较⼩的记录向序列的前部移动
2.3.1冒泡排序
冒泡排序在很久之前已经讲解过了,这里不多赘述。
代码实现
void BubbleSort(int* a, int n)//冒泡排序
{
int exchange = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (a[j] > a[j + 1])
{
exchange = 1;
swap(&a[j], &a[j + 1]);
}
}
if (exchange == 0)
{
break;
}
}
}
• 时间复杂度:O(N^2 )
• 空间复杂度:O(1)
2.3.2快速排序
快速排序是Hoare于1962年提出的⼀种⼆叉树结构的交换排序⽅法,其基本思想为:任取待排序元素 序列中的某元素作为基准值,按照该排序码将待排序集合分割成两⼦序列,左⼦序列中所有元素均⼩ 于基准值,右⼦序列中所有元素均⼤于基准值,然后最左右⼦序列重复该过程,直到所有元素都排列 在相应位置上为⽌。
其主要思想就是将找到一个基准值以其为基准进行二分,然后重复这个过程。
而寻找基准值有很多种方法,但总体框架都相似
void QuickSort(int* a, int left, int right)
{
if (left >= right) {
return;
}
//_QuickSort⽤于按照基准值将区间[left,right)中的元素进⾏划分
int meet = _QuickSort(a, left, right);
QuickSort(a, left, meet - 1);
QuickSort(a, meet + 1, right);
}
2.3.2.1 hoare法找基准值
1)创建左右指针,确定基准
2)从右向左找出⽐基准值⼩的数据,从左向右找出⽐基准值⼤的数据,左右指针数据交换,进⼊下次 循环
代码实现如下
int _QuickSort(int* a, int left, int right)
{
int begin = left;
int end = right;
int keyi = left;
++left;
while (left <= right)
{
// 右边找⼩
while (left <= right && a[right] > a[keyi])
{
--right;
}
// 右边找⼩
while (left <= right && a[left] < a[keyi])
{
++left;
}
if (left <= right)
{
swap(&a[left++], &a[right--]);
}
}
swap(&a[keyi], &a[right]);
return right;
}
其主要思想就是将第一个数据设置为基准值,然后左右遍历寻找大于或者小于基准值的数,进行交换,使基准值左右两边的数都是大于或者小于基准值的数
2.3.2.3lomuto前后指针法找基准值
实现代码如下
int _QuickSort(int* a, int left, int right)
{
int prev = left, cur = left + 1;
int key = left;
while (cur <= right)
{
if (a[cur] < a[key] && ++prev != cur)
{
swap(&a[cur], &a[prev]);
}
++cur;
}
swap(&a[key], &a[prev]);
return prev;
}
其主要思想就是
定义两个指针,第一个cur指针是探路指针,其作用是寻找小于key的值,然后将其值与后面用来定位的prev指针指向的值交换,确保在prev前全是小于key的值,然后将prev的位置与key交换
2.3.2.4非递归找基准值
void Quick_SortNOR(int* arr, int left, int right)//非递归快速排序
{
ST st;
stackinit(&st);
stackpush(&st, left);
stackpush(&st, right);
{
while (!stackempty(&st))//不为空时取栈顶两个
{
int end = stacktop(&st);
stackpop(&st);
int begin = stacktop(&st);
stackpop(&st);
//[begin,end]中寻找基准值
int keyi = begin;
int prev = begin, cur = prev + 1;
while (cur <= end)
{
if (arr[cur] < arr[keyi])
{
++prev;
swap(&arr[cur], &arr[prev]);
}
++cur;
}
swap(&arr[keyi], &arr[prev]);
keyi = prev;
//此时区间[begin,keyi,end]
if (keyi + 1 < end)//右序列入栈
{
stackpush(&st, keyi + 1);
stackpush(&st, end);
}
if (keyi - 1 > begin)
{
stackpush(&st, begin);
stackpush(&st, keyi - 1);
}
}
stackdestroy(&st);
}
}
其找基准值的方法依然是使用lomuto排序法,但是二分的方法变成了利用数据结构的栈。
2.4归并排序
归并排序(MERGE-SORT)是建立在归并操作上的⼀种有效的排序算法,该算法是采⽤分治法(Divide andConquer)的⼀个⾮常典型的应⽤。将已有序的⼦序列合并,得到完全有序的序列;即先使每个 ⼦序列有序,再使⼦序列段间有序。若将两个有序表合并成⼀个有序表,称为⼆路归并。归并排序核 ⼼步骤:
void _MergeSort(int* arr, int left, int right,int* tmp)
{
if (left >= right)
{
return;
}
int mid = (left + right) / 2;
//根据mid划分左右两个序列
_MergeSort(arr, left, mid,tmp);
_MergeSort(arr, mid+1, right,tmp);
int begin1 = left, end1 = mid;
int begin2 = mid + 1, end2 = right;
//对两个区间遍历,排序,合并
int index = begin1;
while (begin1 <= end1&&begin2<=end2)
{
if (arr[begin1] < arr[begin2])
{
tmp[index++] = arr[begin1++];
}
else
{
tmp[index++] = arr[begin2++];
}
}
while(begin1 <= end1)
{
tmp[index++] = arr[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = arr[begin2++];
}
//将tmp的数据导入原数组
for (int i = left;i <= right;i++)
{
arr[i] = tmp[i];
}
}
2.5非比较排序
这里我们介绍非比较排序中的计数排序
其思想就是
1)统计相同元素出现次数
2)根据统计的结果将序列回收到原来的序列中
可能直接理解起来比较抽象,我们就看看下列代码的实现
void CountSort(int* arr, int n)//计数排序
{
//找最大最小值来确定count数组的大小
int min = arr[0], max = arr[0];
for (int i = 1;i < n;i++)
{
if (arr[i] < min)
{
min = arr[i];
}
if(arr[i]>max)
{
max = arr[i];
}
}
//max-min+1确定空间大小
int range = max - min + 1;
int* count = (int*)malloc(sizeof(int) * range);
if (count == NULL)
{
perror("malloc fail");
exit(-1);
}
memset(count, 0, sizeof(int) * range);
for (int i = 0;i < n;i++)//统计每一个数出现的次数并储存在count中
{
count[arr[i] - min]++;
}
//将count数组还原到原数组使其有序
int index = 0;
for (int i = 0;i < range;i++)
{
while (count[i]--)
{
arr[index++] = i + min;
}
}
free(count);
}
其主要思想就是
遍历整个数组寻找出 最大最小值,然后根据max与min1的差值创建一个新的数组,这个数组不是用来储存数值的,而是储存每一个数据出现的次数的根据每一个数值出现的次数,将其存在count的相应下标位置,最后将每个数依次还原到数组中去,就不用让数据间进行相互比较而实现排序
3.测试排序性能
4.完整代码实现
sort.h
#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
void insertsort(int* arr, int n);//直接插入排序
void arrprint(int* arr, int n);//数组打印
void shellsort(int* arr, int n);//希尔排序
void BubbleSort(int* a, int n);//冒泡排序
void SelectSort(int* arr, int n);//直接选择排序
void HeapSort(int* arr, int n);//堆排序
void QuickSort(int* arr, int left,int right);//快速排序
int _QuickSort(int* arr,int left,int right);//寻找基准值
int lomuto_QuickSort(int* arr, int left, int right);//lomuto寻找基准值
void Quick_SortNOR(int* arr, int left, int right);//非递归快速排序
void MergeSort(int* arr, int n);//归并排序
void CountSort(int* arr, int n);//计数排序
stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//定义栈的结构
typedef int stdatatype;
struct Stack
{
stdatatype* arr;
int top;//指向栈顶
int capacity;//栈的容量
};
typedef struct Stack ST;
void stackinit(ST* ps);//栈初始化
void stackpush(ST* ps, stdatatype x);//入栈
void stackpop(ST* ps);//出栈
bool stackempty(ST* ps);//判空
stdatatype stacktop(ST* ps);//取栈顶元素
void stackdestroy(ST* ps);//销毁栈
#define _CRT_SECURE_NO_WARNINGS
#include"Sort.h"
#include"stack.h"
void swap(int* x, int* y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}
void insertsort(int* arr, int n)//直接插入排序
{
for (int i = 0;i < n-1;i++)
{
int end = i;
int tmp = arr[end + 1];
while (end >= 0)
{
if (arr[end] > tmp)
{
arr[end+1] = arr[end];
end--;
}
else
{
break;
}
}
arr[end + 1] = tmp;
}
}
void arrprint(int* arr,int n)
{
for (int i = 0;i < n;i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void shellsort(int* arr, int n)//希尔排序
{
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
for (int i = 0;i < n - gap;i++)
{
int end = i;
int tmp = arr[end + gap];
while (end >= 0)
{
if (arr[end] > tmp)
{
arr[end + 1] = arr[end];
end -= gap;
}
else
{
break;
}
}
arr[end + gap] = tmp;
}
}
}
void BubbleSort(int* a, int n)//冒泡排序
{
int exchange = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (a[j] > a[j + 1])
{
exchange = 1;
swap(&a[j], &a[j + 1]);
}
}
if (exchange == 0)
{
break;
}
}
}
void SelectSort(int* arr, int n)//直接选择排序
{
int begin = 0, end = n - 1;
while (begin < end)
{
int maxi = begin;
int mini = begin;
for (int i = begin + 1;i <= end;i++)//遍历寻找出最大,最小值
{
if (arr[i] < arr[mini])
{
mini = i;
}
if (arr[i] > arr[maxi])
{
maxi = i;
}
}
if (maxi == begin)
{
maxi = mini;
}
swap(&arr[mini], &arr[begin]);
swap(&arr[maxi], &arr[end]);
begin++;
end--;
}
}
void AdjustDown(int* a, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
// 假设法,选出左右孩⼦中⼩的那个孩⼦
if (child + 1 < n && a[child + 1] > a[child])
{
++child;
}
if (a[child] > a[parent])
{
swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void HeapSort(int* arr, int n)//堆排序
{
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(arr, n, i);
}
// O(N*logN)
int end = n - 1;
while (end > 0)
{
swap(&arr[0], &arr[end]);
AdjustDown(arr, end, 0);
--end;
}
}
int _QuickSort(int* arr, int left, int right)
{
int keyi = left;
++left;
while (left <= right)
{
while (arr[right] > arr[keyi])
{
//right从右向左走,寻找小于基准值的
right--;
}
//left从左往右走,找大于基准值的
while (arr[left] < arr[keyi])
{
left++;
}
if (left <= right)
{
swap(&arr[left++], &arr[right--]);
}
}
swap(&arr[keyi], &arr[right]);
return right;
}
int lomuto_QuickSort(int* arr, int left, int right)
{
int keyi = left;
int prev = left, cur = prev +1;
while (cur <= right)
{
if (arr[cur] < arr[keyi])//当出现大于基准值的数跳过,将小于基准值的数与cur进行交换,最后将基准值交换
{
++prev;
swap(&arr[cur], &arr[prev]);
++cur;
}
else
{
++cur;
}
}
swap(&arr[keyi], &arr[prev]);
return prev;
}
void Quick_SortNOR(int* arr, int left, int right)//非递归快速排序
{
ST st;
stackinit(&st);
stackpush(&st, left);
stackpush(&st, right);
{
while (!stackempty(&st))//不为空时取栈顶两个
{
int end = stacktop(&st);
stackpop(&st);
int begin = stacktop(&st);
stackpop(&st);
//[begin,end]中寻找基准值
int keyi = begin;
int prev = begin, cur = prev + 1;
while (cur <= end)
{
if (arr[cur] < arr[keyi])
{
++prev;
swap(&arr[cur], &arr[prev]);
}
++cur;
}
swap(&arr[keyi], &arr[prev]);
keyi = prev;
//此时区间[begin,keyi,end]
if (keyi + 1 < end)//右序列入栈
{
stackpush(&st, keyi + 1);
stackpush(&st, end);
}
if (keyi - 1 > begin)
{
stackpush(&st, begin);
stackpush(&st, keyi - 1);
}
}
stackdestroy(&st);
}
}
void QuickSort(int* arr, int left,int right)//快速排序
{
if (left >= right)
{
return;
}
//int keyi = _QuickSort(arr,left,right);//寻找基准值,hore法
int keyi = lomuto_QuickSort(arr, left, right);//lomuto法
//left keyi right
//左序列[left,keyi-1],右序列[keyi+1,right]
QuickSort(arr, left, keyi - 1);
QuickSort(arr, keyi + 1, right);
}
void _MergeSort(int* arr, int left, int right,int* tmp)
{
if (left >= right)
{
return;
}
int mid = (left + right) / 2;
//根据mid划分左右两个序列
_MergeSort(arr, left, mid,tmp);
_MergeSort(arr, mid+1, right,tmp);
//合并两个有序序列
int begin1 = left, end1 = mid;
int begin2 = mid + 1, end2 = right;
//对两个区间遍历,排序,合并
int index = begin1;
while (begin1 <= end1&&begin2<=end2)
{
if (arr[begin1] < arr[begin2])
{
tmp[index++] = arr[begin1++];
}
else
{
tmp[index++] = arr[begin2++];
}
}
while(begin1 <= end1)
{
tmp[index++] = arr[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = arr[begin2++];
}
//将tmp的数据导入原数组
for (int i = left;i <= right;i++)
{
arr[i] = tmp[i];
}
}
void MergeSort(int* arr,int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
_MergeSort(arr, 0, n - 1, tmp);
free(tmp);
tmp = NULL;
}
void CountSort(int* arr, int n)//计数排序
{
//找最大最小值来确定count数组的大小
int min = arr[0], max = arr[0];
for (int i = 1;i < n;i++)
{
if (arr[i] < min)
{
min = arr[i];
}
if(arr[i]>max)
{
max = arr[i];
}
}
//max-min+1确定空间大小
int range = max - min + 1;
int* count = (int*)malloc(sizeof(int) * range);
if (count == NULL)
{
perror("malloc fail");
exit(-1);
}
memset(count, 0, sizeof(int) * range);
for (int i = 0;i < n;i++)//统计每一个数出现的次数并储存在count中
{
count[arr[i] - min]++;
}
//将count数组还原到原数组使其有序
int index = 0;
for (int i = 0;i < range;i++)
{
while (count[i]--)
{
arr[index++] = i + min;
}
}
free(count);
}
stack.c
#define _CRT_SECURE_NO_WARNINGS
#include"stack.h"
void stackinit(ST* ps)//栈初始化
{
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
void stackpush(ST* ps, stdatatype x)//入栈
{
if (ps->top == ps->capacity)
{
//增容
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
stdatatype* tmp = (stdatatype*)realloc(ps->arr, newcapacity*sizeof(stdatatype));
if (tmp == NULL)
{
perror("realloc fail");
exit(1);
}
ps->arr = tmp;
ps->capacity = newcapacity;
}
ps->arr[ps->top++] = x;
}
bool stackempty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
void stackpop(ST* ps)//出栈
{
assert(!stackempty(ps));//判空
--ps->top;
}
stdatatype stacktop(ST* ps)//取栈顶元素
{
assert(!stackempty(ps));
return ps->arr[ps->top - 1];
}
void stackdestroy(ST* ps)//销毁栈
{
if (ps->arr)
{
free(ps->arr);
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include"Sort.h"
void test01()
{
int a[] = { 5,4,3,7,8,6,9 };
int n = sizeof(a) / sizeof(a[0]);
printf("排序之前:");
arrprint(a, n);
insertsort(a, n);
printf("排序之后:");
arrprint(a, n);
}
void TestOP()
{
srand(time(0));
const int N = 100000;
int* a1 = (int*)malloc(sizeof(int) * N);
int* a2 = (int*)malloc(sizeof(int) * N);
int* a3 = (int*)malloc(sizeof(int) * N);
int* a4 = (int*)malloc(sizeof(int) * N);
int* a5 = (int*)malloc(sizeof(int) * N);
int* a6 = (int*)malloc(sizeof(int) * N);
int* a7 = (int*)malloc(sizeof(int) * N);
int* a8= (int*)malloc(sizeof(int) * N);
for (int i = 0; i < N; ++i)
{
a1[i] = rand();
a2[i] = a1[i];
a3[i] = a1[i];
a4[i] = a1[i];
a5[i] = a1[i];
a6[i] = a1[i];
a7[i] = a1[i];
a8[i] = a1[i];
}
int begin1 = clock();
insertsort(a1, N);
int end1 = clock();
int begin2 = clock();
shellsort(a2, N);
int end2 = clock();
int begin3 = clock();
SelectSort(a3, N);
int end3 = clock();
int begin4 = clock();
HeapSort(a4, N);
int end4 = clock();
int begin5 = clock();
QuickSort(a5, 0, N - 1);
int end5 = clock();
int begin6 = clock();
MergeSort(a6, N);
int end6 = clock();
//int begin7 = clock();
//BubbleSort(a7, N);
//int end7 = clock();
int begin8 = clock();
CountSort(a8, N);
int end8 = clock();
printf("InsertSort:%d\n", end1 - begin1);
printf("ShellSort:%d\n", end2 - begin2);
printf("SelectSort:%d\n", end3 - begin3);
printf("HeapSort:%d\n", end4 - begin4);
printf("QuickSort:%d\n", end5 - begin5);
printf("MergeSort:%d\n", end6 - begin6);
//printf("BubbleSort:%d\n", end7 - begin7);
printf("CountSsort:%d\n", end8 - begin8);
free(a1);
free(a2);
free(a3);
free(a4);
free(a5);
free(a6);
free(a7);
free(a8);
}
void test02()
{
int a[] = { 5,4,3,7,8,6,9 };
int n = sizeof(a) / sizeof(a[0]);
printf("排序之前:");
arrprint(a, n);
SelectSort(a, n);
printf("排序之后:");
arrprint(a, n);
}
void test03()
{
int a[] = { 5,4,3,7,8,6,9 };
int n = sizeof(a) / sizeof(a[0]);
printf("排序之前:");
arrprint(a, n);
QuickSort(a, 0, n - 1);
printf("排序之后:");
arrprint(a, n);
}
void test04()
{
int a[] = { 5,4,3,7,8,6,9 };
int n = sizeof(a) / sizeof(a[0]);
printf("排序之前:");
arrprint(a, n);
Quick_SortNOR(a, 0, n - 1);
printf("排序之后:");
arrprint(a, n);
}
void test05()
{
int a[] = { 5,4,3,7,8,6,9 };
int n = sizeof(a) / sizeof(a[0]);
printf("排序之前:");
arrprint(a, n);
MergeSort(a, n);
printf("排序之后:");
arrprint(a, n);
}
int main()
{
/*test01();*/
TestOP();
//test02();
//test03();
//test04();
//test05();
return 0;
}
数据结构部分的全内容就到这里了,感谢阅读!