概述
排序算法小结
定义
排序算法的核心是比较和移动,排序算法分类为内部排序和外部排序,区别的要点是排序过程是否需要外部的内存交换过程;也可以按照算法的思路分为比较排序和非比较排序;
排序算法的稳定性,若排序对象中存在多个关键字相同的记录,经过排序后,相同关键字的记录之间的相对次序保持不变,则该排序方法是稳定的,若次序发生变化(哪怕只有两条记录之间),则该排序方法是不稳定的;
时间复杂度,一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得T(n)/f(n)的极限值(当n趋近于无穷大时)为不等于零的常数,记T(n)=O(f(n))T(n),O(f(n))为算法的渐进时间复杂度,简称时间复杂度;
空间复杂度,空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度,它是问题规模n
n的函数,记做S(n)=O(f(n))。比如直接插入排序的时间复杂度是O(n2),空间复杂度是O(1)。而一般的递归算法就要有O(n)的空间复杂度了,因为每次递归都要存储返回信息,需要辅助空间的大小随着n的增大线性增大。
1、冒泡排序
基本原理:
N个元素,只剩一个首元素的时候,不需要比较,总共需要循环比对N-1次,每一次选出其中最大或者最小的数冒出到尾端;
在每一个循环 i 中,从当前循环的第一元素开始比较到最后一个,因此共有N-1-i次内部循环,内部进行比较操作,符合条件的两数,则移动双方位置。
改进:
1、如果某次冒泡不存在数据交换,则说明已经排序好了,可以直接退出排序
2、头尾进行冒泡,每次把最大的沉底,最小的浮上去,两边往中间靠1
~~鸡尾酒排序与冒泡排序~~ 的不同处在于排序时是以首尾双向在序列中进行排序。
先对数组从左到右进行升序的冒泡排序;
再对数组进行从右到左的降序的冒泡排序;
以此类推,持续的、依次的改变冒泡的方向,并不断缩小没有排序的数组范围。
2、直接插入排序
基本原理:
将第1和第2元素排好序,然后将第3个元素插入到已经排好序的元素中,依次类推,直到轮询到最后一个元素。
for (int i = 1; i < n; i++)//从第二个元素开始,依次与前一个元素比大小
{
if (a[i] < a[i - 1])//若前面的元素大于后面的元素,则寻找后面元素本应正确的位置
{
temp = a[i];
for (j = i - 1; j >= 0 && a[j] > temp; j--)
{
a[j + 1] = a[j];
}
a[j + 1] = temp;//将无序元素放入到正确的位置
}
}
3、希尔排序
基本原理:
插入排序每次只能操作一个元素,效率低。元素个数N,取奇数k=N/2,将下标差值为k的数分为一组(一组元素个数看总元素个数决定),在组内构成有序序列,再取k=k/2,将下标差值为k的数分为一组,构成有序序列,直到k=1,然后再进行直接插入排序。
设置一个最小增量dk,刚开始dk设置为n/2。进行插入排序,随后再让dk=dk/2,再进行插入排序,直到dk为1时完成最后一次插入排序,此时数组完成排序。
例如,现在要对序列{ 1,8,3,9,5,7 }进行希尔排序
第一步,选择增量为3,进行排序:
则产生三个分组(括号为数组元素下标)
{ 1(0),9(3) }
{ 8(1),5(4) }
{ 3(2),7(5) }
然后对该所有分组进行直接插入排序,得到新的分组(注意:此时下标是不会变的,只是用直接插入排序进行数组排序)
{ 1(0),9(3) }
{ 5(1),8(4) }
{ 3(2),7(5) }
因此按照下标,我们可以写出第一趟按照增量为3的希尔排序得到的序列:{ 1,5,3,9,8,7 }
第二步,选择增量为1,进行排序
这一步,直接按照直接插入排序的方法,进行直接插入排序,得到有序序列:{ 1,3,5,7,8,9 }
// 初始时从dk开始增长,每次比较步长为dk
void Insrtsort(int *a, int n,int dk) {
for (int i = dk; i < n; ++i) {
int temp = a[i];
for(j = i-dk; j >=0 && a[j] > temp; j=j-dk)
{
a[j+dk] = a]j];
}
a[j+dk] = tmp; // 插入tmp
}
}
}
void ShellSort(int *a, int n) {
int dk = n / 2; // 设置初始dk
while (dk >= 1) {
Insrtsort(a, n, dk);
dk /= 2;
}
}
4、简单选择排序
基本原理:
每次循环选出当前未排序的列表中最小的数,因此每次排序完后的,分为有序组和无序组,无序组的容量会逐渐减少,外部循环N-1次。每一次循环内,并不是比较大小出现冲突就换,而是记录下min的下标位置,一次循环完后才移动一次位置。
for (int i = 0; i < n; i++)
{
int key = i; // 临时变量用于存放数组最小值的位置
for (int j = i + 1; j < n; j++) {
if (a[j] < a[key]) {
key = j; // 记录数组最小值位置
}
}
if (key != i)
{
int tmp = a[key];
a[key] = a[i];
a[i] = tmp; // 交换最小值
}
}
5、堆排序
推薦博文:
堆排序詳解
圖解堆排序過程
詳細的遞歸代碼
【基本原理】
先把数组构造成一个大顶堆(父亲节点大于其子节点),然后把堆顶(数组最大值,数组第一个元素)和数组最后一个元素交换,这样就把最大值放到了数组最后边。把数组长度n-1,再进行构造堆,把剩余的第二大值放到堆顶,输出堆顶(放到剩余未排序数组最后面)。依次类推,直至数组排序完成。
堆分为大根堆和小根堆 | 完全二叉树 |
---|---|
大根堆 | 任意一个父节点的值大于等于其子节点的值【用于升序排列】 |
小根堆 | 任意一个父节点的值小于等于其子节点的值【用于降序排列】 |
【大根堆】
以升序排序为例,利用大根堆的性质(堆顶元素最大)不断输出最大元素,直到堆中没有元素
1.构建大根堆
2.输出堆顶元素与堆底元素交换
3.将堆低元素放一个到堆顶,再重新构造成大根堆,再输出堆顶元素,以此类推
【代码思路】
○ ○ ○ ○ ○ ○ ○
根 左 右
1、若根的索引为0开始,最远的非叶子节点(最后的父节点)按照层次排列:i = n/2 -1 2、(n为数目,i为索引)——其左孩纸(2i+1) 右孩子(2i+2)
若根的索引为1开始,则最远的非叶子节点(最后的父节点)按照层次排列:i = n/2
——其左孩纸(2i) 右孩子(2i+1)
#include <stdio.h>
#include <stdlib.h>
void swap(int* a, int* b)
{
int temp = *b;
*b = *a;
*a = temp;
}
//從頂到尾迭代實現每一個父節點和子結點的比較和交換
void max_heapify(int arr[], int start, int end)
{
//建立父节点指标和子节点指标
int dad = start;
int son = dad * 2 + 1;
while (son <= end) //若子节点指标在范围内才做比较
{
if (son + 1 <= end && arr[son] < arr[son + 1])
//先比较两个子节点大小,选择最大的
son++;
if (arr[dad] > arr[son]) //如果父节点大於子节点代表调整完毕,直接跳出函数
return;
else //否则交换父子内容再继续子节点和孙节点比较
{
swap(&arr[dad], &arr[son]);
dad = son;
son = dad * 2 + 1;
}
}
}
void heap_sort(int arr[], int len)
{
int i;
//初始化,i从最後一个父节点开始调整
//建立一個初始化的大頂堆
for (i = len / 2 - 1; i >= 0; i--)
//迭代實現堆的構建
max_heapify(arr, i, len - 1);
//先将第一个元素和已排好元素前一位做交换,再重新调整,直到排序完毕
for (i = len - 1; i > 0; i--)
{
swap(&arr[0], &arr[i]);
max_heapify(arr, 0, i - 1);
}
}
int main() {
int arr[] = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };
int len = (int) sizeof(arr) / sizeof(*arr);
heap_sort(arr, len);
int i;
for (i = 0; i < len; i++)
printf("%d ", arr[i]);
printf("n");
return 0;
}
6、快速排序
基本原理:
选择一个基准元素,比基准元素小的放基准元素的前面,比基准元素大的放基准元素的后面,这种动作叫分区,每次分区都把一个数列分成了两部分,每次分区都使得一个数字有序,然后将基准元素前面部分和后面部分继续分区,一直分区直到分区的区间中只有一个元素的时候。
快速排序的一趟算法思想是:
1.设置两个变量 i , j ,令 i = 0 ;j = n - 1 ;
2.以第一个数组元素作为关键数据,赋值给key,即 key = A[0];
3.从 j 开始向前搜索,即由后开始向前搜索(j- -),找到第一个小于key的值A[j],将A[j]和A[i]互换;
4.从 i 开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的值A[i],将A[i]和A[j]互换;
5.重复第3、4步,直到 i = j 结束。
領域:
原本在正確位置的元素是不會移動,每一次前後的指針相同時,表示一次排序完成,接着以該位置分爲前後兩個部分,重複操作。
//快速排序
void QuickSort(int a[], int left, int right)
{
if (left >= right) {
return;
}
// 定义两个标识
int i = left;
int j = right;
int key = a[left];//将序列的第一个元素设为基数
int temp;
while (i < j)
{
while (i < j && a[j] >= key)
{
j--;
}
if (i != j)//如果i j没有相遇,交换位置
{
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
while (i < j && a[i] <= key)
{
i++;
}
if (i != j)//如果i j没有相遇,交换位置
{
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
else
{
printArr(a, 8);//打印数组
}
}
QuickSort(a, left, i - 1);
QuickSort(a, i + 1, right);
}
7、归并排序
基本原理:分治思想
将一个无序的数列一直一分为二,直到分到序列中只有一个数的时候,这个序列肯定是有序的,因为只有一个数,然后将两个只含有一个数字的序列合并为含有两个数字的有序序列,这样一直进行下去,最后就变成了一个大的有序数列
// 合并两个已排好序的数组
void Merge(int a[], int left, int mid, int right)
{
int len = right - left + 1; // 数组的长度
int *temp = new int[len]; // 分配个临时数组
int k = 0;
int i = left; // 前一数组的起始元素
int j = mid + 1; // 后一数组的起始元素
while (i <= mid && j <= right)
{
// 选择较小的存入临时数组
temp[k++] = a[i] <= a[j] ? a[i++] : a[j++];
}
while (i <= mid)
{
temp[k++] = a[i++];
}
while (j <= right)
{
temp[k++] = a[j++];
}
for (int k = 0; k < len; k++)
{
a[left++] = temp[k];
}
}
// 递归实现的归并排序
void MergeSort(int a[], int left, int right)
{
if (left == right) //結束的標誌
return;
int mid = (left + right) / 2;
MergeSort(a, left, mid);
MergeSort(a, mid + 1, right);
Merge(a, left, mid, right);
}
8、桶排序
桶排序算法详解
1、注意桶范围的选取、排序数值的最小值和最大值的差、或者将原数按照相同的过程进行处理
2、注意相同数的排序
#include <stdio.h>
int main()
{
int book[1001],i,j,t,n;
for(i=0;i<=1000;i++)
book[i]=0;
scanf("%d",&n);//输入一个数n,表示接下来有n个数
for(i=1;i<=n;i++)//循环读入n个数,并进行桶排序
{
scanf("%d",&t); //把每一个数读到变量t中
book[t]++; //进行计数,对编号为t的桶放一个小旗子
}
for(i=1000;i>=0;i--) //依次判断编号1000~0的桶
for(j=1;j<=book[i];j++) //出现了几次就将桶的编号打印几次
printf("%d ",i);
getchar();getchar();
return 0;
}
最后
以上就是粗心小伙为你收集整理的C语言8大经典排序算法(详细测试核心代码)排序算法小结的全部内容,希望文章能够帮你解决C语言8大经典排序算法(详细测试核心代码)排序算法小结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复