选择排序
什么是选择排序?选择排序有什么特点?
描述:我们从未排序列表中不断获取最小的元素,将它放置到数组的做左侧[0]
如果数组中下标为0的元素本身就已经是最小的元素的话,name它自己与自己进行交换
为了变上述自己同自己交换的问题,我们采取使用if判断的方式进行规避
要注意的特点,整体比较的次数为数组长度-1,原因是最后的元素无序比较,就已经是最大的元素值了
图解
实现选择排序的函数
public static void select_sort(int[] arrays) {
for (int i = 0; i < arrays.length-1; i++) {//一般外层决定循环次数,循环数组长度-1次
//找一个最小元素的小标(初始值0)
int minindex = i;
for (int j = i+1; j < arrays.length; j++) {//和其后的每一个元素进行比较,选定一个最小值放在下标为0的位置
if (arrays[j]<arrays[minindex]) {//如果在数组的剩余元素中,还存在着比minindex对应的更小的元素,将minindex的值重新赋值
minindex=j;
}
}//进入后,i=0,j遍历一遍
if (minindex!=i) {//不满足此条件证明arrays[i]已经是最小的元素值了,不发生交换
System.out.println("i="+i+"\t"+"minindex="+minindex);
//元素值交换的过程
int temp = arrays[i];
arrays[i]=arrays[minindex];
arrays[minindex]=temp;
}
}
}
插入排序
以数组左边的第一个元素视为有序列表,将右侧剩余的元素进行插入操作
如果插入的第一个元素比左侧的元素值要小,name就将该元素的值放置到最左侧
图解
实现插入排序的函数
public static void insert_sort(int[] arrays){
for (int i = 1; i < arrays.length; i++) {
//第一次区分有序部分和无序部分,建钙元素下标为0的元素视为有效元素,后续元素视为无效元素
int k = i-1;//当前元素的前一个元素的下标
int temp = arrays[i];//将要比较的变量放置到临时变量中
for (;k>=0 && arrays[k]>temp; k--) {
arrays[k+1]=arrays[k];//元素后移的操作
}
arrays[k+1]=temp;//插入操作
}
}
冒泡排序
public static void maopao_sort(int[] arrays) {
boolean bo = false;
//冒泡排序的条件:3个元素,两两比较最少要比较3次
for (int i = 0; i < arrays.length; i++) {//控制比较次数
for (int j = 0; j < arrays.length-i-1; j++) {//元素两两比较
if (arrays[j]>arrays[j+1]) {
bo=true;
//两两交换
int temp = arrays[j];
arrays[j]=arrays[j+1];
arrays[j+1]=temp;
}
}
if (!bo) {
break;//退出循环
}
}
}
希尔排序
- 希尔排序的背景:
冒泡排序和插入排序的缺陷:如果数组中的第一个元素就是该数组中最大的元素,name我们要将该元素移动到数组元素的末尾处,移动小号过大 - 将数组中的元素进行分组操作,间隔相同的元素分为一组
- 逻辑意义上的分组操作:分组依据是元素之间的差值完全相同,整个数组长度的1/2,增量值:length(6)/2=3,不断为原有增量值的1/2,直至增量值为1
- 对分组模式进行局部排序,要求数组长度>=2
- 插入:数组的有序性高,已存在一个有序列表,有序序列越长,插入效率越高
- 将数组元素进行分组、局部排序后,提高了数组元素的有序性,此时再进行插入排序,则效率大大提高
实现希尔排序的函数
public static void shell_sort(int[] arrays) {
//分组操作:数组长度的1/2
int len = arrays.length;
int a = len/2;
for (int i = a; i > 0; i /= 2) {// 增量值不断为原增量的1/2
//分组动作
for (int j = i; j < arrays.length; j++) {//每插入一次,j增加1,实现一次分组内插入,i变为原来的1/2,第一次分组为两两一组
insertSort(arrays,i,j);//实现局部插入排序
}
}
}
private static void insertSort(int[] arrays, int i, int j) {
// 分组插入的动作 i-j 和 i 是一组 i-j = 0~3 为同一逻辑分组单元
int k = j-i;
int temp = arrays[j];//同一组中下标比较大的元素
for (; k >=0 && arrays[k]>temp; k-=i) {//一次位移操作后,局部组内k-步长后再判断是否需要进行唯一
arrays[k+i]=arrays[k];//唯一操作
}
//插入
arrays[k+i]=temp;//位移结束后空出的位置填入temp,如未进行位移操作则不变
}
在这里插入代码片
计数排序
什么是计数排序:
需求:有一个java数组,该数组内部的元素都是0~10的随机数,数组中总共有20个元素,如何对它进行排序?
场景:随机数组,且数组中的最大值10和最小值0相差不大的情况下
核心思想:就是要静要排序的数组的元素作为临时数组的下标,只要该元素每出现一次,就在临时数组下标对应的元素值+1
图解
实现计数排序的函数
public static void count_sort(int[] arrays) {
//真是实现计数排序逻辑
int len = arrays.length;//20
int max = arrays[0];//将数组中的第一个元素假定为最大值
for (int i = 1; i < len; i++) {//找到数组中真正的那个最大值
if (max<arrays[i]) {
max=arrays[i];//打擂思想
}
}
//创建一个下标为0~10的数组,数组长度为11
int[] nums = new int[11];
//arrays数组中的每个元素都是nums数组的下标
for (int i = 0; i < arrays.length; i++) {
nums[arrays[i]]++;//nums[9]++ 三次 nums[9] 0==>3
}
int k = 0;//备用的迭代变量
for (int i = 0; i < nums.length; i++) {//外层循环变量:索引,随机数组中的元素
for (int j = nums[i];j>0; j--) {//内层循环变量:num数组的元素,arrays数组各元素出现的次数,出现几次,则一次将出现的数字赋值给arrays[]
//k++:++是写在后面的,先操作,再加
arrays[k++]=i;//将无需数列覆盖为有序数列
}
}
}
归并排序
什么是归并排序:
1. 递归+分而治之
2. 归并就是合并的意思,合并的是谁呢?两个或多个局部有序的数组
3. 将局部有序的数组进行合并,最终为整体有序
4. 分---->递归,治---->不治而治(分到只有一个元素是,分数组有序),合---->merge阶段:left、right
5. 先局部排序,再合并
在没有做递归的情况下:left=0,right=arrays.length-1
递归:左右边界的开始,需要一个临时数组,存放已排序好的元素,middle分割边界
图解
实现归并排序的函数
public static void mergesort(int arrays[],int temp[],int left,int middle,int right) {
//i:局部数组1的下标起始位置 j:局部数组2的下标的起始位置
int i=left,j=middle+1;
//递归情况下 要不断 以左边界的位置开始 到右边界结束
for (int k =left; k<=right; k++) {
//总共会有四种情况
/**
* 为什么是小于等于:稳定排序(排序后元素的位置并没有发生改变)
* i++:将++操作写在后面 先赋值再累计
*/
if(i>middle){//如果左边的数组的元素已取完 则拼接右侧数组剩余的元素
temp[k]=arrays[j++];
}else if(j>right){ //如果右边的数组的元素已取完 则拼接左侧数组剩余的元素
temp[k]=arrays[i++];
}else if(arrays[i]<=arrays[j]){
temp[k]=arrays[i++];//把较小的元素放置到临时数组
}else{
temp[k]=arrays[j++];
}
}
//temp已被排序好了 从左边距开始 一直到右边距 对原有数组元素的值进行覆盖
for (int k = left; k <=right; k++) {
arrays[k]=temp[k];//把排序好的值覆盖到原数组
}
}
递归函数
private static void recursion(int arrays[],int temp[],int left,int right) {
//middle的值是通过自行计算的 left=right的情况下 只剩下一个了 有序 进行归并
int middle=(left+right)/2;
if(left<right){
//实现递归操作
recursion(arrays,temp,left,middle); //遍历数组左侧的值
recursion(arrays,temp,middle+1,right);//遍历右侧的值
//直到变成元素个数为1的数组的情况下 归并操作
mergesort(arrays,temp,left,middle,right);
}
}
快速排序
- 什么是快速排序:和归并排序稍微有点类似,也是在这里要获取中间值
- 基于:中轴元素,保证在中轴元素左侧所有的元素均小于中轴元素,保证在中轴元素的右侧,所有的元素均大于该中轴元素
- 由于在这里不断获取对应排序好的中轴元素–>归并排序的效果一致,递归操作
快速排序的函数实现
public static void quickSort(int[] arrays,int left,int right) {
if (left<right) {
//编写一个方法:1. 能够找到中轴元素放在合适的位置上;2. 返回中轴元素对应的下标位置
int middle = getMiddle(arrays,left,right);
//递归操作
quickSort(arrays, left, middle-1);
quickSort(arrays, middle+1, right);
}
}
private static int getMiddle(int[] arrays,int left,int right) {
//1:默认选择一个元素 作为数组中的中轴元素(左边界的第一个值)
int middle=arrays[left];
//2:从默认的中轴元素后的第一个元素开始寻找小于中轴元素的序列
int i=left+1;
//3:从数组末尾的元素寻找比中轴元素大的元素
int j=right;
// 情况:巧合(恰好选择的中轴元素是最大的)i<=j下标越界的问题
// 巧合(恰好选择的中轴元素是最小的) 中轴元素位置不动
while(true){
while(i<=j&&arrays[i]<middle) i++;
while(i<=j&&arrays[j]>middle) j--;
if(i>=j)break;
//防止的意外情况:中轴元素后的第一个元素本身就比他大 获取比它小的元素进行位置交换
int temp=arrays[i];
arrays[i]=arrays[j];
arrays[j]=temp;
}
//实现元素交换 把中轴元素middle放在该放的位置上
arrays[left]=arrays[j];
arrays[j]=middle;
return j;
}
桶排序
思想:基于计数排序,计数排序的特点是能够将数组的元素作为新数组的下标的一种排序方式
场景:如果我们的数组出现浮点数怎么办?浮点数不可能作为数组的下标,设置多个桶,每个桶都是x~y之间的一个区间值,只要我们的浮点数元素在该区间内,那么放置到桶中
桶排序的函数实现
public static void bucketSort(double[] arrays) {
//声明了最大值、最小值
double max = arrays[0];
double min = arrays[0];
for (int i = 0; i < arrays.length; i++) {
if (max<arrays[i]) {
max=arrays[i];
}
if (min>arrays[i]) {
min=arrays[i];
}
}
//获取一下两者的差值,求取步距
double step = max -min;
//声明一下桶的数量
int bucketNum=arrays.length;
//创建数据结构, 二维集合,一个ArrayList,我们把ArraysList集合中的LinkedList作为每一个桶
//声明集合中元素的个数为bucketNum
ArrayList<LinkedList<Double>> lists=new ArrayList<LinkedList<Double>>(bucketNum);
//初始化一下
for(int i = 0;i<bucketNum;i++) {
lists.add(new LinkedList<Double>());
}
//向桶中放入元素
for (int i = 0; i < arrays.length; i++) {
//随机元素:arrays[i]
//获取随机元素应该放置的下标位置
int index = (int) ((arrays[i]-min)/(step/(bucketNum-1)));
//插入操作
lists.get(index).add(arrays[i]);
}
//对桶进行局部排序: LinkedList Collections.sort()
for (int i = 0; i < lists.size(); i++) {
Collections.sort(lists.get(i));//每个桶进行局部排序
}
int k=0;
//传回原数组
for (LinkedList<Double> linkedList:lists) {
for (Double item : linkedList) {//桶中有可能放置了多个元素
//使用有序数列覆盖无序数列
arrays[k++]=item;
}
}
}
主函数
public static void main(String[] args) {
//自定义一个无序数组
int[] arrays = {8,6,5,3,7,5};
int[] arrays1 = { 9, 8, 2, 3, 5, 4, 9, 7, 1, 10, 0, 6, 9, 7, 10, 8, 3,2, 1, 4 };
double[] arrays2= {4.5,2.02,2.65,3.5,0.5};
select_sort(arrays);
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i]+" ");
}
System.out.println("\n---------------------");
insert_sort(arrays);
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i]+" ");
}
System.out.println("\n---------------------");
maopao_sort(arrays);
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i]+" ");
}
System.out.println("\n---------------------");
shell_sort(arrays);
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i]+" ");
}
System.out.println("\n---------------------");
count_sort(arrays1);
for (int i = 0; i < arrays1.length; i++) {
System.out.print(arrays1[i]+" ");
}
System.out.println("\n---------------------");
//递归
recursion(arrays, new int[arrays.length], 0, arrays.length-1);
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i]+" ");
}
System.out.println("\n---------------------");
quickSort(arrays, 0, arrays.length-1);
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i]+" ");
}
System.out.println("\n---------------------");
bucketSort(arrays2);
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i]+" ");
}
System.out.println("\n---------------------");
}