数据结构之八大排序总结
1、排序的概念:排序就是将一组数据按照一定的顺序,递增或递减排列起来。
2、排序的稳定性:对于两个关键字相等的记录,它们在序列中的相对位置,在排序之前和经过排序之后,没有改变。
3、内部排序与外部排序:内部排序指的是排序时需要将全部数据加载到内存中;外部排序是指所要排序的数据太大了不能同时在内存中进行。
4、八大排序:插入排序、希尔排序、选择排序、快速排序、冒泡排序、堆排序、归并排序、计数排序、基数排序
5、各种排序的实现及总结:
5.1、插入排序(直接插入排序与希尔排序):每步将一个待排序的纪录,按其关键码值的大小插入前面已经排序的元素序列中适当位置上,直到全部插入完为止。(类似于打扑克)
5.1.1、算法的实现及总结:
直接插入排序算法:将数组中的所有元素依次跟前面已经排好的元素相比较,如果选择的元素比已排序的元素小,则交换,直到全部元素都比较过。
因此,从上面的描述中我们可以发现,直接插入排序可以用两个循环完成:第一层循环:遍历待比较的所有数组元素
第二层循环:将本轮选择的元素(selected)与已经排好序的元素(ordered)相比较。
如果:selected > ordered,那么将二者交换void Inser_sort(int* array, int size){// 找待插入元素位置for (int i = 0; i < size; i++){int key = array[i];int end = i - 1;// 搬移元素 while (end >= 0 && key < array[end]){array[end + 1] = array[end];end--;}array[end + 1] = key;}
}// 直接插入排序的算法特点:
// 时间复杂度:O(N^2)
// 空间复杂度:O(1) 未借助辅助空间
// 稳定性:稳定 (根据是否间隔元素来判断)
// 应用场景:元素有限切接近有序,越有序排序效率越高// 希尔排序(缩小增量排序)
// 先选定一个整数,把待排序文件中所有记录分成若干组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。
// 然后,取,重复上述分组和排序的工作。当到达 = 1时,所有记录在统一组内排好序。void Shell_sort(int* array, int size){int gap;int i;int key; int end;for (gap = size / 2; gap > 0; gap /= 2){for (i = gap; i < size; i++){end = i - gap; // 每次排序的最后一个元素位置key = array[i]; // 每次排序的最后一个元素 while (end >= 0 && key < array[end]){array[end + gap] = array[end];end -= gap;}array[end + gap] = key;}}}// 希尔排序算法总结
// 时间复杂度:平均:O(N^1.3-N^2) 最坏:O(N^2)
// 空间复杂度:O(1)
// 稳定性: 不稳定
// 应用场景:当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就
// 会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
5.2、选择排序(直接选择排序与堆排序):每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的 数据元素排完 。
5.2.1、算法的实现及总结:
// 选择排序
// 每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。// 直接选择排序:
// 在元素集合array[i]--array[n-1]中选择关键码最大(小)的数据元素
// 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
// 在剩余的array[i]--array[n - 2](array[i + 1]--array[n - 1])集合中,重复上述步骤,直到集合剩余1个元素void Select_sort(int* array, int size){for (int i = 0; i<size; i++){int maxpos = i;for (int j = i; j < size; j++){if (array[maxpos]<array[j]){maxpos = j;}if (maxpos != i){swap(&array[maxpos], &array[i]);}}}}// 直接选择算法总结
// 时间复杂度:O(N^2)
// 空间复杂度:O(1)
// 稳定性:不稳定
// 应用场景:直接选择排序由于效率不高,在实际中很少用// 堆排序
// 利用堆得删除操作来实现序列的排序,升序用大堆,降序用小堆
// 1、建堆
// 找最后一个非叶子结点,不断的调整,直到最后一个父节点// 调整过程:
// 拿当前父节点与左右孩子比较大小,然后交换// 2、排序
// 拿堆顶元素与最后一个元素进行交换,交换后最后一个元素不参与调整
// 将对顶元素作为调整起点做向下调整
// 重复以上步骤// 堆调整,以大堆为例:void HeapAdjust(int* array, int size, int last){int parent = size;// 默认为左孩子int child = parent * 2 + 1;while (child <= last){// 开始比较if (child + 1 < last && array[child] < array[child + 1]){child++;}if (array[parent]>array[child]){return;}else{swap(&array[parent], &array[child]);parent = child;child = parent * 2 + 1;}}
}// 堆排序:以升序为例
void Heap_sort(int* array,int size){// 建堆for (int i = size / 2 - 1; i >= 0; i--){HeapAdjust(array, size, i);}// 排序for (int i = size - 1; i >= 0; i--){swap(&array[0], &array[i]);HeapAdjust(array, i, 0);}
}// 堆排序算法总结
// 时间复杂度:O(N*logN)
// 空间复杂度:O(1)
// 稳定性:不稳定
// 应用场景:数据量小时,效率较高
5.3、交换排序(冒泡排序与快速排序):所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排 序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
5.3.1、算法实现及总结:
// 冒泡排序:
// 相邻两个元素进行比较后交换void Bubble_sort(int* array, int size){for (int i = 0; i < size; i++){int Mark = 0; for (int j = 1; j < size; j++){if (array[j - 1]>array[j]){swap(&array[j - 1], &array[j]);Mark = j + 1;}}// 一趟比较完if (Mark == 0){break;}i = size - 1 - Mark;}
}// 冒泡排序算法总结:
// 时间复杂度:O(N^2)
// 空间复杂度:O(1)
// 稳定性:稳定
// 应用场景:有序数列冒泡排序最快// 快速排序
// 区间分割法
// 取一个基准值
// 将剩余元素与基准值进行比较分组int Partition(int* array, int left, int right){int begin = left;int end = right;int key = array[right];while (begin < end){while (begin < end && array[begin] <= key){begin++;}while (begin < end && array[end] >= key){end--;}swap(&array[begin], &array[end]);}swap(&array[begin], &array[right-1]);return begin;}void Quick_sort(int* array, int left, int right){if (right - left > 1){int div = Partition(array, left, right);Quick_sort(array, left, div-1);Quick_sort(array, div+1, right);}
}// 快速排序的非递归实现
// 需要先实现一个栈
void QuickSortNonR(int* a, int left, int right)
{Stack st;StackInit(&st);StackPush(&st, left);StackPush(&st, right);while (StackEmpty(&st) != 0){int end = StackTop(&st);StackPop(&st);int begin = StackTop(&st);StackPop(&st)int div = PartSort1(a, begin, end);if (begin < div - 1){StackPush(&st, begin);StackPush(&st, div - 1);}if (div + 1 < end){StackPush(&st, div + 1);StackPush(&st, end);}}
}// 快速排序算法总结
// 时间复杂度:O(N*longN)
// 空间复杂度:O(longN)
// 稳定性:不稳定
// 应用场景:由于综合性能较好,所以在现实中使用较多
5.4、归并排序: 归并排序是将两个有序的数组归并成一个更大的有序数组。要将一个数组排序,可以先(递归的)将他分成两半分别排序,让后将结果归并起来。它能够保证将任意长度为N的数组排序所需时间和NlogN成正比;它的主要缺点就是所需的额外空间和N成正比。
5.4.1、算法的实现及总结:
int MergData(int* array, int left, int mid, int right, int* temp){int begin1 = left, end1 = mid;int begin2 = mid + 1, end2 = right;int i = 0;// 均分序列while (begin1 <= end1 && begin2 <= end2){if (array[begin1] < array[begin2]){temp[i] = array[begin1];begin1++;}else{temp[i] = array[begin2];begin2++;}}while (begin1 <= end1){temp[i++] = array[begin1];begin1++;}while (begin2 <= end2){temp[i++] = array[begin2];begin2++;}}void _Mergsort(int* array, int left,int right,int* temp){if (right - left > 1){// 均分序列int mid = left + ((right - left) >> 1);// 递归左右_Mergsort(array, left, mid, temp);_Mergsort(array, mid, right, temp);// 归并数据到临时空间MergData(array, left, mid, right, temp);// 拷贝临时空间的元素到原空间中memcpy(array + left, temp + left, sizeof(array)/sizeof(array[0]));}}void Mergsort(int* array, int left ,int right){int* temp = (int*)malloc(sizeof(int)*array[0]);if (temp == NULL){return;}_Mergsort(array, left,right,temp);free(temp);}// 归并排序算法总结
// 时间复杂度:O(N*logN)
// 空间复杂度:O(N)
// 稳定性:稳定
// 应用场景:数据量较大且要求排序稳定时
// 优化措施:由于使用递归,递归深度太深容易造成内存溢出,所以可使用非递归版本归并排序
5.5、计数排序:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。
操作步骤:
1. 统计相同元素出现次数
2. 根据统计的结果将序列回收到原来的序列中
5.5.1、算法的实现及总结:
void countsort(int* array, int size){int minval = array[0];int maxval = array[0];int i = 0;int index = 0;// 找区间for (i = 0; i < size; i++){if (array[i] < minval){minval = array[i];}if (array[i] > maxval){maxval = array[i];}}int range = maxval - minval + 1;// 给空间int* temp = (int*)malloc(sizeof(int)*size);if (temp == NULL){assert(0);return;}memset(temp, 0, sizeof(int)* range);// 统计每个数据出现次数for (i = 0; i < size; i++){temp[array[i]] = minval++;}// 回收数据for (i = 0; i < range; i++){while (temp[i]--){array[index++] = i + minval;}}free(temp);
}void print(int* array, int size){for (int i = 0; i < size; i++){printf("%d ");}printf("\n");
}// 应用场景:计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
// 时间复杂度:O(MAX(N, 范围))
// 空间复杂度:O(范围)
// 稳定性:稳定
5.6、八大算法的比较:
数据结构之八大排序总结相关推荐
- 数据结构算法---八大排序
目录 冒泡排序 插入排序 希尔排序 选择排序 堆排序 计数排序 归并排序 快速排序 源码 一般使用的八大排序算法是:插入排序.选择排序.冒泡排序.希尔排序.归并排序.快速排序.堆排序.计数排序. ...
- 两万字搞定《数据结构》 八大排序 必读(建议收藏)
前言:本章将介绍常见八大排序包括如下直接插入排序.希尔排序.选择排序.堆排序.冒泡排序.快排.归并排序以及计数排序(基数排序和桶排序面试基本不涉及,本文忽略了,有兴趣的读者可以自行补充),本章内容是重 ...
- 数据结构进阶 八大排序算法详解
数据结构就是定义出某种结构:像数组结构.链表结构.树形结构等,实现数据结构就是我们主动去管理增删查改的实现函数 排序的概念 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列 ...
- 【数据结构】八大排序
文章目录 一.排序的基础知识 1.排序的概念 2.常见算法排序概览 3.排序的应用 二.八大排序介绍 1.直接插入排序 直接插入排序特性总结 2.希尔排序 希尔排序特性总结 3.选择排序 选择排序特性 ...
- 《数据结构》八大排序(详细图文分析讲解)
目录 排序 排序的应用 排序简介 排序的分类 排序算法的好坏评判 冒泡排序法 思路分析 代码实现 选择排序法 思路分析 代码实现 插入排序 思路分析 代码实现 希尔排序 思路分析 代码演示 归并排序法 ...
- 【数据结构】 八大排序实现简析+复杂度及稳定性分析
概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,这里八大排序就是内部排序,指直接插入,希尔,选择,堆排,冒泡,快排,归并,计数. 下面让我们来共同学习这八大排序吧!
- Java数据结构之八大排序算法
目录 一.排序算法的介绍 1.排序算法 2.算法时间的频度 时间频度 3.时间复杂度 4.常见的时间复杂度 5.平均时间复杂度和最坏时间复杂度 6.空间复杂度 二.冒泡排序 1.基本介绍 2.模拟冒泡 ...
- 数据结构之八大排序——希尔排序
直接插入排序算法的时间复杂度为O(),如果待排序的序列为"正序"时,时间复杂度为O(n),但是如果比较大的数字在第一个位置,那么进行排序的话需要一直移动到最后一位,比较适合基本有序 ...
- 【数据结构】八大排序(超详解+附动图+源码)
目录 前言 常见排序算法的实现 1.插入排序 2.希尔排序 3.选择排序 4.堆排序 5.冒泡排序 6.快速排序 6.1 hoare版本 6.2挖坑法 6.3前后指针法 6.4快速排序优化 6.5快速 ...
最新文章
- Timer 和TimerTask 的定时任务入门
- 如何通过编程方式添加Native Client服务器别名
- 返回值带头信息 php_PHP注释标记的整理
- php读写分离数据不能同步,thinkphp 下数据库读写分离代码剖析
- 比起掉头发,我更怕掉队
- python bootstrap 4_Python3.4+Django1.9+Bootstrap3
- java对象底层原存储结构图解_图解图库JanusGraph系列-一文知晓“图数据“底层存储结构...
- php set medias,manage-medias.php
- python编辑图像_在python中创建图像编辑应用程序
- oracle会计事件,新的一年打开会计期发生错误
- cocos2d-x 是男人就下100层 附源码
- 《矩阵与变换》教学中的几个“务必”
- window64位版本的secoclient工具下载(secoclient-win-64-7.0.2.26.exe)
- 16种常用的数据分析方法-聚类分析
- 杭州电子科技大学acm---2008
- java 中 isb啥意思_isb官网-新版下载
- 2021年美容师(中级)新版试题及美容师(中级)免费试题
- Java面试之多线程:Java创建多线程为什么只有一种方式?
- 三款好用的语音转文字软件,你知道几个?
- mysql数据库数据恢复