排序
插入排序
插入排序
思想:(从小到大)就是和之前的元素依次进行比较,如果当前值小于 前面的值,那么将当前值往前插入,然后前面的序列都依次往后移动一位。
时间复杂度:o(n²)
链表实现在插入的时候,相对于方便实现。
希尔排序(手算)
特性:不稳定,只能基于顺序表,不能链表
视频地址
每次都设置增量d,然后根据增量将 数据表分为几块,进行对比交换,建议初始d为:n/2,每次都除以2。直到d=1。
d=1时,整体基本有序,然后再进行一次插入排序,整体结束
交换排序(要会写代码)
冒泡排序
思想:相邻两个节点进行比较,根据大小进行交换位置。
特性:每一轮for排序都能确定一个指的位置
代码:
//从小到大
void bubbleSort(int array[],int length){
for (int i = 0; i < length; i++)
{
int flag = 1;//如果此次循环没有交换位置,表示数组基本已经有序了,可以直接跳出
for (int j = length-1; j > i; j--)
{
if(array[j]<array[j-1]){
int temp = array[j];
array[j] = array[j-1];
array[j-1] = temp;
flag = 0;
}
}
if(flag){
break;
}
}
toString(array,length);
}
快速排序
思想:首先选出一个枢纽(pivot)值一般为第一个,然后定义low和high,依次将数组划分为两半,左边<=枢纽值,右边>=枢纽值。 然后再依次左边继续划分,右边继续划分,类似于排序二叉树的逻辑,详情看下图和代码
当low和high相等表示左右两边已经基本一次排序完毕
特性:时间复杂度:O(n²) ,空间复杂度:O(h),不稳定
/**
* 快速排序:一阶段排序,从小到大
* return: 枢纽值
**/
int quickOnceSort(int array[],int low,int high){
int pivot = array[low];
while(low<high){
while(low<high && array[high]>pivot){
high--;
}
array[low] = array[high];
while (low<high && array[low] < pivot)
{
low++;
}
array[high] = array[low];
}
//将枢纽值进行替换到中间,按照逻辑其实使用low或者high都可以,因为low和high目前是一样的
array[low] = pivot;
return low;
}
/**
* 快速排序 :简化递归排序左右两个模块
* 其实快速排序递归操作,类似于二叉排序树结构,初次根据枢纽值,分为左右模块,然后再根据左边进行依次递归分离,右边依次分离..
**/
void quickSort(int array[],int low,int high){
if(low<high){
int pivot = quickOnceSort(array,low,high);
quickSort(array,low,pivot-1);
quickSort(array,pivot+1,high);
}
//打印
toString(array,5);
}
处理过程逻辑
1、
2、
3、
4、
选择排序
选择排序:每一趟在待排序元素中选取关键字最小(或最大)的元素加入有序子序列
简单选择排序
每次遍历最小的或者最大的放在头部,然后依次类推一直扫描n-1个元素,最后一个元素就不用扫描了
特性:不稳定,时间复杂度o(n²),空间O(n)
代码实现:
堆排序
补充: 当前节点i,左孩子 2i,右 2i+1 ,父节点i/2
物理视角:它就是一个数组
逻辑角度:它就是一个二叉树
大根堆: 就是左<中间节点,中间节点>右节点
小根堆: 就是 中间节点<左节点,中间节点<右节点
特性:建立堆o(n)时间复杂度
构成大根堆思路:从当前叶子节点开始进行处理,n/2,然后先进行判断当前 节点的左孩子和右孩子谁更大,然后再由更大的和当前节点进行比较,如果进行交换,然后再次循环查找交换后的左孩子右孩子节点再次进行对比,依次到数组结尾
大根堆构成后,然后收尾交换,然后再继续大根堆调整
堆的删除和插入操作
1、小跟堆插入:当插入一个数字默认是插入到数组的最后,然后再进行比较,直接一直上升比较它的父节点,因为左或者右节点肯定是已经调整好的。、
2、删除操作:删除分支节点,那么默认由 队尾进行代替。如删除13 那么由46进行代替,然后再进行调整。 这次调整的话则 需要节点的 左右孩子先进行对比,然后再由大的或者小的和当前节点对比调整
归并排序
归并排序:将两个或者多个有序的序列 合并为一个有序序列
结论:
1、m路归并,选出一个元素进行归并关键字对比至少m-1次
2、空间O(n) 、时间O(nlogn) 、稳定
考试一般考的都是二路归并
二路归并就是将相邻的两个 元素 认为是两个数组,然后进行排序,排序完成后进行合并成一个数组,后续也是进行对比两个相邻 ** 的数组**,然后再进行合并
基数排序(手算)
基数排序:一般适用于数据量比较比较大的情况,(如很多数据的身份证之类)根据 元素的 个十百千位数进行计算
特性:基数排序大多都是根据链式存储进行实现的,数组+队列的方式,r队列个数也就是数组
因此空间复杂度O®,时间O(d(n+r))稳定
先根据数组中元素 个位 进行排序,后序再根据十位…
第一次:个位
第二次:十位
第三次:百位
外部排序
外部排序:也就是因为数据量比较的多的情况,数据大部分都存储在磁盘上,而需求对数据进行排序,那么只能一块一块的取出部分数据读取到内存中进行排序。
外部排序:也就是基于归并排序进行实现
实例下图
一、先进行阶段性的排序(两个相邻的视为一个)
二、基本排序完成,两个相邻的数组进行 合并
第二次合并,4-4
后续依次…
最佳归并树(手算)
最佳归并树,类似于 哈夫曼树进行计算,树的权值,只不过这次的话是根据 每次(路段)【也就是数组节点】的数量作为节点数。
注意:
如果需要构造k叉 最佳归并树,而 归并段 无法构成严格的k叉归并树,那么需要进行添加 **虚段 **,然后再进行构建。
最佳归并树示例
二路归并: R1–R2–R3–R4–R5
下图R代表一个 **归并段 **
由上图可得 最佳带权路径WPL=34 也就是最佳读磁盘次数为34次
读写磁盘次数为:68
三路归并
归并段:
最佳树:
虚段
几路归并:k ;
判断是否需要增加虚段:
计算虚段示例
如:19个归并段,8路排序
①(19-1) % (8-1) = u , u=4 ,表明需要添加虚段,
②(8-1)-4 = 3 ,表示需要添加4个虚段
需要补充虚段,最佳归并路示例