Java算法(五)-------------------八大排序

目录

一、交换排序

(一)冒泡排序

(二)快速排序

二、插入排序

 

(一)直接插入排序

(二)希尔排序

三、选择排序

(一)简单选择排序

(二)堆排序

四、归并排序

五、基数排序


今天,我来与大家分享最常用的八大排序!

首先,分别有以下排序

一、交换排序

(一)冒泡排序

 八大排序中的冒泡排序:
 .逻辑上比较简单
 .比较相邻两个元素的大小,如果左边的元素大于邮编的元素,则交换两个元素
 .交换规则:比较一轮后会把最大的数据放到最后的位置,下一轮会把第二大的元素放到右边第二个位置.....
需要判断的是:
1.什么时候交换位置(当左边元素大于右边元素时交换位置)
2.需要比较多少轮(数据长度-1)
3.每一轮比较多少次(和轮数n有关  递减的关系 数据长度-n-1)
4.时间复杂度:平均情况O(n^2) 最好情况:O(n)  最坏情况:O(n^2) 稳定性:是稳定排序

代码实现:

import java.util.Arrays;

public class Bubbing {
    public static void main(String[] args) {
        int arr[]={2,3,6,7,0,9};
        //确定比较的轮数
        for (int i=0;i<arr.length-1;i++){
            //确定每一轮比较多少次
            for(int j=0;j<arr.length-i-1;j++){
                if(arr[j]>arr[j+1]){
                    int tem=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=tem;
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }
}

(二)快速排序

交换排序中的快速排序(快排)
例如,我们先定义一个数组{5,3,1,2,8,9,4}
1.首先我们在序列当中找到一个基准数,一般那第一个数据(元素)作为基准数
2.遍历这个数组将小于基准数的放到基准数的左边,大于基准数的放到基准数右边
3.得到的类似的数组{3,1,2,4,5,8,9}
4.在初始状态下,5在第一个位置,现在5再中间的某个位置
5.确定用递归的方式来实现快速排序
6.怎么实现比对这个逻辑,需要用到两个哨兵(i,j)
7.i,j哨兵分别j向左走,i向右走,直到i==j时停止,将基准数与i位置上的数进行交换
8.如果i!=j 并且i<j,我们要交换i j两个位置上的数
9.将基准数的两边看成是两个数组,这两个数组还需要重复第一次的操作---递归
10.快速排序时间复杂度:平均情况:O(nlog2n) 最好情况O(nlog2n) 最坏情况O(nlog2n) 是不稳定排序

代码如下:


import java.util.Arrays;

public class QuickSort {
    public static void main(String[] args) {
        int[] arr={3,1,2,4,5,8,9};
        quickSort(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));

    }
    //参数:需要对那个数组进行排序,还需要两个哨兵,left right
    public static void quickSort(int[] arr,int left,int right){
        //出口:当left大于right时结束
        if(left>right){
            return;
        }
        //定义一个变量来存储基准数
        int base=arr[left];
        //i,j充当现有数组哨兵
        int i=left;
        int j=right;
        while(i!=j){ //一共分了多少次的子集合,一旦这个循环停了,说明该换基准数了。
            //让哨兵往左走,当i==j的时候结束,一定要先走j(先走右边)
            while(arr[j]>=base&&i<j){
                j--;
            }
            //然后再让i哨兵向右走
            while(arr[i]<=base&&i<j){
                i++;
            }
            //当i,j都停了,说明i所在位置的的数据大于基准数,j所在位置的数小于基准数,交换这两个数
            if(i<j){
                int tem=arr[i];
                arr[i]=arr[j];
                arr[j]=tem;
            }

        }
        //当外层while循环结束说明我们该把基准数放到中间位置了
        arr[left]=arr[i];
        arr[i]=base;

        //用递归重复上面的循环
        quickSort(arr,left,i-1);
        quickSort(arr,i+1,right);
    }
}

二、插入排序

(一)直接插入排序

插入排序:直接插入排序
核心思想:将数组中的所有元素依次跟前面的有序元素相比较,如果待排序的元素比正在比较的元素小,那么这个正在排序的元素往前移。
      排序的过程:将序列中的第一个元素当做有序的序列。
                然后依次拿后面的元素(待排序的元素)和前面已经排序好的元素序列进行比较,
                如果待排序元素大于正在比较的元素,则不动。
                如果待排序元素小于正在比较的元素,那么正在比较的这个元素小猴哦移动一位。
                直到待排序元素遇到比自己小的元素时,将待排序的元素放到正在比较的元素的后面。

代码如下:


import java.util.Arrays;

public class DirectInsert {
    public static void main(String[] args) {
        int[] arr={3,1,2,4,5,8,9};
        for(int i=1;i< arr.length;i++){//拿到待排序的元素;
           int tem=arr[i];//待排序元素
            int j;
            for(j=i-1;j>=0&&arr[j]>tem;j--){//每一个待排序元素和前面有序序列比较的过程(次数)
                //将arr[j]向后移动一位
                arr[j+1]=arr[j];
            }
            arr[j+1]=tem;
        }
        System.out.println(Arrays.toString(arr));
    }
}

(二)希尔排序

  希尔排序:是插入排序的一种,又称为“缩小增量排序”,是直接插入排序的一种更高效的改进版本。
希尔排序是非稳定性排序算法。
  该方法因D.L.Shell于1959年提出而得名。
  直接插入排序的升级版
哪些地方做了优化:
 希尔排序在排序前先将序列分成几个序列,再对这几个子序列分别进行插入排序
 再将整个序列分成几个子序列(比第一次分得子序列少) 再对每一个子序列进行插入排序
 直到不能再将整个序列拆分成子序列为止。
增量:每次拆分成子序列的个数,这个增量是依次减小的
          例如:序列长度为7 第一次的增量是7/2=3 第二次的增量是3/2=1
第一次将序列分成(序列长度/2)个子序列
第二次将序列分成(前一次增量/2)个子序列
...........................
时间复杂度:平均时间复杂度:O(n^1.3)  最好情况:O(n)  最坏情况:O(n^2)
是一种不稳定的排序

代码如下:


import java.util.Arrays;

public class ShellSort {
    public static void main(String[] args) {
        int[] arr={12,23,2,5,47,45,96,100,120,19,23,14};
        int gap=arr.length/2;
        while (gap!=0){//将数组分几次 增量的个数
        //将子序列进行插入排序
        for(int i=gap;i<arr.length;i++){//i表示带插入元素的小标(待排序的元素)
            int value=arr[i];//拿到具体的待排序的元素
            int j;
            for(j=i-gap;j>=0&&arr[j]>value;j-=gap){
                //将元素向后移动一个增量位;
                arr[j+gap]=arr[j];
            }
            arr[j+gap]=value;
        }
        //变换增量
        gap=gap/2;


        }
        System.out.println(Arrays.toString(arr));
    }
}

三、选择排序

(一)简单选择排序

 选择排序--直接选择排序(简单选择排序)
 核心:比较+置换(交换)
 始终拿序列当中最小的那个元素和其他的元素进行比较,最终将最小的元素插入到适当位置
 假设第一个是最小的元素
 时间复杂度:平均时间复杂度:O(n^2)  最好情况:O(n^2)  最坏情况:O(n^2)
 是一种不稳定的排序

代码如下:

import java.util.Arrays;

public class SimpleChoice {
    public static void main(String[] args) {
        int[] arr={3,10,15,24,35,36,47,58,67,77,87,96};
        for(int i=0;i<arr.length;i++){          //拿到待排序元素 从下标为0的地方开始拿
           //核心的地方始终拿比较小的那个元素进行比较
            int index=i;//找第三方变量来存储两个元素比较后,小的那个元素的下标(i表示的是拿到的最小元素的下标)
            for(int j=i+1;j<arr.length;j++){//表示比较的次数
                if(arr[j]<arr[index]){
                    index=j;
                }
            }
            //最小的元素下标拿到了(index)
            //和上一次最小的元素后面的元素进行交换(最小下标对应的元素和拿到的元素进行交换)
            int tem=arr[index];
            arr[index]=arr[i];
            arr[i]=tem;
        }
        System.out.println(Arrays.toString(arr));
    }
}

(二)堆排序

堆排序:大顶堆(升序排序)和小顶堆(降序排序)  我们以大顶堆为例来进行堆排序的分享
        堆:近似于完全二叉树
 大顶堆:每一个结点都大于子节点
 小顶堆:每一个结点都小于子节点

 将序列构建成大顶堆(小顶堆)
 从最后一个非叶子结点开始构建(叶子结点:没有子节点的结点)
 大顶堆构建完以后开始排序
       将大顶堆的根元素与最后一个结点调换位置
       然后将最后一个结点以外的其他节点在构建大顶堆
       然后再将根元素与最后一个结点调换位置,重复操作,最终形成了排序


 公式:大顶堆的每个节点与子节点的关系: 每个结点的左节点公式arr[i]>=arr[2*i+1]
                                     每个结点的右节点公式arr[i]>=arr[2*i+2]
      小顶堆的每个结点与子节点的公式: 每个结点的左节点公式:arr[i]<=arr[2*i+1]
                                     每个结点的右节点公式:arr[i]<=arr[2*i+2]
 代码部分要先构建大顶堆

代码如下:


import java.util.Arrays;

public class MaxHeap {
    public static void main(String[] args) {
        int[] arr={3,9,0,2,1,4,5};
        //最后一个飞叶子节点
        int starIndex=(arr.length-1)/2;
        //构建大顶堆
        for(int i=starIndex;i>=0;i--) {
            toMaxHeap(arr, arr.length, starIndex);
        }

        //构建完大顶堆之后我们需要将根元素有最后一个元素调换位置

        for(int i=arr.length-1;i>0;i--){
            //将根元素与i对应的元素进行调换
            int tem=arr[0];
            arr[0]=arr[i];
            arr[i]=tem;
            //交换完以后,我们还需要将剩余元素调整成大顶堆
            toMaxHeap(arr,i,0);
            System.out.println(Arrays.toString(arr));
        }
    }

    //构建大顶堆的方法
    public static void toMaxHeap(int[] arr,int size,int index){
        //arr表示对哪个数组进行大顶堆构建 size表示调整元素的个数 index表示从哪里开始调整
        //获取左右子节点的索引
        int leftNode=index*2+1;
        int rightNode=index*2+2;
        //拿到最大结点所对应的那个索引
        //假设 传进来的当前下标就是最大元素的下标
        int maxIndex=index;
        if(arr[leftNode]>arr[maxIndex]&&leftNode<size){
            maxIndex=leftNode;
        }
        if(arr[rightNode]>arr[maxIndex]&&rightNode<size){
            maxIndex=rightNode;
        }
        //下标比较完以后我们需要调换
        //需要判断上面的两个if语句是否执行 判断maxIndex是否等于index
        if(maxIndex!=index){
            int tem=arr[maxIndex];
            arr[maxIndex]=arr[index];
            arr[index]=tem;
            //调换完以后可能会影响其他节点 违反了大顶堆
        }

        toMaxHeap(arr,size,maxIndex);
    }
}

四、归并排序

归并排序:是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法)将问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案修补”在一起,即分而治之。基本思想
1.把数组从中间划分成两个子数组;
2.一直递归地把子数组划分成更小的子数组,直到子数组里面只有一个元素
3.依次按照递归的返回顺序,不断地合并排好序的子数组,直到最后把整个数组的顺序排好。

代码如下:

import java.util.Arrays;

public class Guibing {
    public static void main(String[] args) {
//        int[] arr={1,4,7,8,2,5,6,10};
        int[] arr={12,4234,5,7,3,764,7,3,56,8};
        //拆分
        chaifen(arr,0,arr.length-1);

        //归并
//        Sort.guibing(arr,0,(arr.length/2)-1,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }
    //拆分的方法
    public static void chaifen(int[] arr,int starIndex,int endIndex){
        //定义一个中间量
        int centerIndex=(starIndex+endIndex)/2;
        if(starIndex<endIndex){
            //拆分整个数据的左边
            chaifen(arr,starIndex,centerIndex);
            //拆分整个数据的右边
            chaifen(arr,centerIndex+1,endIndex);
            guibing(arr,starIndex,centerIndex,endIndex);
        }
    }





    //归并排序的方法
    /*
    * arr:原数据
    * 以下三个参数来将元数据分割成两个子数组
    * starIndex:从哪里开始比较合并
    * centerIndex:元数据的中间位置
    * endIndex:元数据比较到哪里结束
    *
    * */
    public static void guibing(int[] arr,int starIndex,int centerIndex,int endIndex){
        int[] temarr=new int[endIndex-starIndex+1];
       //i表示左边子数组的开始下标
        int i=starIndex;
        //j表示右边子数组开始的下标
        int j=centerIndex+1;
        //定义一个中间数组的下标,以0开始
        int index=0;
        while(i<centerIndex&&j<=endIndex){
            if(arr[i]<arr[j]){
                temarr[index]=arr[i];
                i++;

            }
            else{
                arr[index]=arr[j];
                j++;

            }
            index++;
        }
        //如果左边子数组或右边子数组有剩余元素我们还要放到中间数组里
        while(i<=centerIndex){
            temarr[index]=arr[i];
            i++;
            index++;
        }
        while(j<=centerIndex){
            temarr[index]=arr[j];
            j++;
            index++;
        }
        //将中间数组里面的元素放入导原数组中
        for(int k=0;k<temarr.length;k++){
            arr[k+starIndex]=temarr[k];
        }
    }
}

五、基数排序

基数排序是桶排序的扩展。它的基本思想是: 将整数按位数切割成不同的数字,然后按每个位数分别比较。 具体做法是: 将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

代码如下:.


import java.util.Arrays;

public class JiShu {
    public static void main(String[] args) {
        int[] arr={3,5,2,14,35,657,23,545,7,45};
//        //先获取做大的数
//        int max=getMax(arr);
//        System.out.println(max);
        //调用基数排序方法
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }
    //获取数据中最大的数:
    public static int getMax(int[] arr){
        int max=arr[0];
        for(int i=1;i<arr.length;i++){
            if(arr[i]>max){
                max=arr[i];
            }
        }
        return max;
    }
    public static void sort(int[] arr){

        //定义一个二维数组 来存储10个桶
        int[][] temArr=new int[10][arr.length];
        //获取数组中的最大数,目的是算出一共多少位,根据多少位来决定需要向桶里放多少次拿多少次
        int max=getMax(arr);
        //转换成字符串,求长度
        int[] count=new int[10];
        int length=String.valueOf(max).length();
        for(int i=0,n=1;i<length;i++,n=n*10){
            //循环几次来向桶里放多少次
            for(int j=0;j<arr.length;j++){//拿到每一个原数组里面的数据
                //获取每个原数组的每个数据的每一位
                int ys=arr[j]/n%10;
                //往桶里面放
                temArr[ys][count[ys]++]=arr[j];
            }
            int index=0;
            //取出桶里面的元素放回原数组中
            for(int k=0;k<count.length;k++){//从哪些桶里面取数据
                if(count[k]!=0){
                    for(int h=0;h<count[k];h++){
                        arr[index]=temArr[k][h];
                        index++;
                    }
                    count[k]=0;
                }
            }
        }
    }
}

最后,我来给大家总结一下这八种排序的时间以及空间复杂度。

OK! 今天的分享就到这里 谢谢大家。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值