Java基础----排序算法集




一.排序概念

  1. 稳定排序:相同元素排序前后位置不变
  2. 不稳定排序:相同元素排序前后位置改变
  3. 内排序:待排序列完全放在内存中的排序
  4. 外排序:排序过程中还需要访问外存储器的排序

二.直接插入排序

1.基本思想

把n个待排序的元素看成一个有序表和一个无序表,开始时有序表中只有一个元素,无序表中含有n-1个元素,排序过程中每次从无序表中取出一个元素,将其与有序表中的元素比较,插入到有序表中的合适位置

2.直接插入排序举例

初始状态(17) 3 25 14 20 9
第一次插入(3 17) 25 14 20 9
第二次插入(3 17 25) 14 20 9
第三次插入(3 14 17 25) 20 9
第四次插入(3 4 17 20 25)9
第五次插入(3 9 14 17 20 25)

3.代码实现

public static void sort(int arr[])
    {
       int temp;
       for(int i=0;i<arr.length-1;i++)//进行n-1次插入
       {
           for(int j=i+1;j>0;j--)
               if(arr[j]<arr[j-1])//后面的元素是否小于前面的元素
               {
                   temp=arr[j];//交换
                   arr[j]=arr[j-1];
                   arr[j-1]=temp;
               }
             else//没有发生交换就说明当前有序
               break;
       }
    }
//时间复杂度O(N*N)
//稳定

三、希尔排序

1.基本思想

先将整个待排无序序列分割成若干个子序列(相隔d增量),对子序列分别进行直接插入排序,待各个子序列中的元素基本有序,再对全体元素进行一次直接插入排序,因为直接插入排序在元素基本有序的情况下,效率较高

2.希尔排序举例

3.代码实现

public static void sort(int arr[])
    {
        int n=arr.length;
        int h=1;
        while (h<n/3)
            h=3*h+1;//1 4 13 40 121...步长
        while (h>=1)
        {   
        //i=h表示第一组的最后一个元素
        //i=h+1表示第二组的最后一个元素
            for(int i=h;i<n;i++)
            {
                for(int j=i;j>=h;j-=h)//同一组前面的元素比后面的元素小,则交换
                    if(arr[j]<arr[j-h])//组内元素间隔h
                {
                    int temp=arr[j];
                    arr[j]=arr[j-h];
                    arr[j-h]=temp;
                }
            }
            h/=3;
        }

    }
    //时间复杂度大致未O(n^1.3)
    //不稳定

四、冒泡排序

1.基本思想

对待排列元素从前向后,依次比较相邻元素,若发现逆序则交换,使得较大(较小)的元素从数组前部向后部移动,如果一趟比较下来没有进行交换,则说明数组元素已经有序,每一次都能确定一个元素的最终位置

2.冒泡排序举例

初始序列3 4 1 2 5
第一趟冒泡3 1 2 4 5
第二趟冒泡1 2 3 4 5
第三趟冒泡1 2 3 4 5发现没有交换 停止排序

3.代码实现

 public static void sort(int arr[])
    {
        boolean isOrder;
        for(int i=0;i<arr.length-1;i++)//n-1趟
        {
            isOrder=true;
            for(int j=0;j<arr.length-1-i;j++)//每一趟的比较次数都在减小
            {
                if(arr[j]>arr[j+1])
                {
                    int temp=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                    isOrder=false;
                }
            }
           if(isOrder)
               break;
        }

    }
//时间复杂度O(N*N)
//稳定

五、快速排序

1. 基本思想

任取待排序列中的任意元素作为标准,一般取第一个元素,通过一次划分,将待排元素分成左右两个子序列,左边序列小于基准元素,右边序列大于基准元素,然后对两个子序列重复执行上述操作,直到每一个序列只有一个元素为止

2.快速排序举例

在这里插入图片描述

3.代码实现

//以第一个元素为基准
public static void quickSort(int arr[],int lo,int hi)
    {
        if(hi<=lo)
            return;
        int j=partition(arr,lo,hi);//j左边的元素不大于a[j],j右边的元素不小于a[j]
        quickSort(arr,lo,j-1);
        quickSort(arr,j+1,hi);
    }
public static int partition(int arr[],int lo,int hi)
    {
        int i=lo;
        int j=hi+1;
        int v=arr[lo];//切分的元素
        while (true)
        {
            //保证切分元素左边的元素都比它小,右边的元素都比它大
            while (arr[++i]<v)//查找比切分元素大的元素a
                if(i==hi)
                    break;
            while (arr[--j]>v)//查找比切分元素小的元素b
                if(j==lo)
                    break;
            if(i>=j)//左右扫描指针相等或左指针跑到右指针前面,说明扫描结束
                break;
            swap(arr,i,j);//交换前面找到的a b

        }
        swap(arr,lo,j);//最后交换切分元素,将切分元素放到指定的位置
        return j;
    }
 public static void swap(int arr[],int i,int j)
    {
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }
    //最好情况   O(nlogn)
    //最坏情况   O(n*n)
    //平均情况   O(nlogn)
    //不稳定
//以最后一个元素为基准元素
 public static void quickSort(int arr[],int lo,int hi)
    {
        if(hi<=lo)
            return;
        int j=partition(arr,lo,hi);//j左边的元素不大于a[j],j右边的元素不小于a[j]
        quickSort(arr,lo,j-1);
        quickSort(arr,j+1,hi);
    }
public static int partition(int arr[],int lo,int hi)
    {
        int i=lo-1;
        int j=hi;
        int v=arr[hi];//切分的元素
        while (true)
        {
            //保证切分元素左边的元素都比它小,右边的元素都比它大
            while (arr[++i]<v)//查找比切分元素大的元素a
                if(i==hi)
                    break;
            while (arr[--j]>v)//查找比切分元素小的元素b
                if(j==lo)
                    break;
            if(i>=j)//左右扫描指针相等或左指针跑到右指针前面,说明扫描结束
                break;
            
            swap(arr,i,j);//交换前面找到的a b

        }
      
        swap(arr,hi,i);//最后交换切分元素,将切分元素放到指定的位置
      
        return i;
    }
 public static void swap(int arr[],int i,int j)
    {
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }
}

六、简单选择排序

1. 基本原理

将待排序的元素分为已排序和未排序两组,已排序组初始为空,依次将未排序的元素中的最小值放入已排序的数组中,先找最小的加入,再找次小的加入,再找第三小的加入…

2.简单选择排序举例

在这里插入图片描述

3.代码实现

public static void sort(int arr[])
    {
        for(int i=0;i<arr.length-1;i++)
        {
            int minNum=arr[i];//假设当前的数最小
            int minIndex=i;//假设当前索引是最小数的索引
            for(int j=i+1;j<arr.length;j++)
                if(minNum>arr[j])
                {
                    minNum=arr[j];
                    minIndex=j;
                }
            if(minIndex!=i)//最小的数和刚开始假设的不是同一个数
            {
                int temp=arr[i];
                arr[i]=arr[minIndex];
                arr[minIndex]=temp;
            }
        }
    }
//时间复杂度O(N*N)
//不稳定

七、堆排序

1. 堆的概念

堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆

该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2] 图b 用来升序

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2] 图a 用来降序
在这里插入图片描述

2.堆排序基本思想

将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

  1. 将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
  2. b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端
  3. 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序

3.堆排序代码实现

public static void sort(int []arr){
        //1.构建大顶堆
        for(int i=arr.length/2-1;i>=0;i--){
            //从第一个非叶子结点从下至上,从右至左调整结构
            adjustHeap(arr,i,arr.length);
        }
        //2.调整堆结构+交换堆顶元素与末尾元素
        for(int j=arr.length-1;j>0;j--){
            swap(arr,0,j);//将堆顶元素与末尾元素进行交换
            adjustHeap(arr,0,j);//重新对堆进行调整
        }

    }

    /**
     * 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
     * @param arr
     * @param i
     * @param length
     */
    public static void adjustHeap(int []arr,int i,int length){
        int temp = arr[i];//先取出当前元素父节点i
        for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
            if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
                k++;
            }
            if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
                arr[i] = arr[k];
                i = k;
            }else{
                break;
            }
        }
        arr[i] = temp;//将temp值放到最终的位置,较小的子节点的位置
    }

    /**
     * 交换元素
     * @param arr
     * @param a
     * @param b
     */
    public static void swap(int []arr,int a ,int b){
        int temp=arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}
//时间复杂度 O(nlogn)
//不稳定

八、归并排序

1.基本思想

将两个有序表合成一个有序表,需要用一个临时数组来保存合并两个有序序列后的新序列

2. 归并排序举例

在这里插入图片描述

3.代码实现

public static void merge(int arr[],int temp[],int lo,int mid,int hi)
    {
        int i=lo;
        int j=mid+1;
        int t=0;
        while (i<=mid&&j<=hi)
        {
            if(arr[i]<arr[j])//如果左边的有序序列的当前元素小于右边有序序列的当前元素
                temp[t++]=arr[i++];
            else//如果左边的有序序列的当前元素大于右边有序序列的当前元素
                temp[t++]=arr[j++];
        }
        while (i<=mid)//左半边元素未用尽
            temp[t++]=arr[i++];
        while (j<=hi)//右半边元素未用尽
            temp[t++]=arr[j++];

        t=0;
        int k=lo;
        while (k<=hi)//将两个有序序列合并在一起的temp数组重新赋值给arr数组
            arr[k++]=temp[t++];

    }
    public static void mergeSort(int arr[],int temp[],int lo,int hi)
    {
        if(hi<=lo)
            return;
        int mid=(lo+hi)/2;
        mergeSort(arr,temp,lo,mid);//对左半部分进行排序
        mergeSort(arr,temp,mid+1,hi);//对右半部分进行排序
        merge(arr,temp,lo,mid,hi);//开始合并左半部分和右半部分
    }
}
//时间复杂度O(nlogn)
//稳定


九、基数排序

1.基本思想

将数组中的所有数按位进行分类,由于每一位数的大小都在0-9之间,因此创建下标为0~9的十个数组,根据需要对数进行存储

2.基数排序举例

11 4 12 13 10 5 21 3

先考虑个位

0号数组1号数组2号数组3号数组4号数组5号数组6号数组7号数组8号数组9号数组
101112345
2113

考虑十位

0号数组1号数组2号数组3号数组4号数组5号数组6号数组7号数组8号数组9号数组
31021
411
512
13
  • 先按个位数的大小将数组中的各个元素放入对应的桶中
  • 再按十位数的大小将数组中的各个元素放入对应的桶中,依此类推
  • 最大数字有几位,就需要进行几次排序
  • 用于处理数组元素是整数的情况
  • 如果需要处理负数需要作相应处理
  • 对于给定的n个d位数,基数是r(十位数r=10),我们使用计数排序比较元素的每一位,基数排序耗时Θ(n+r),那么基数排序的复杂度为Θ(d(n+r))。空间复杂度O(rn+r):桶的个数桶的容量+基数(桶的个数)

3.代码实现

 public static void radixSort(int arr[])
    {
        //先求出数组元素中最大数的位数
        int max=arr[0];
        for(int i=0;i<arr.length;i++)
        {
            if(arr[i]>max)
                max=arr[i];
        }

        int maxLength=(max+" ").length();//最大数字的位数,转化为字符串求长度

        int bucket[][]=new int[10][arr.length];//创建10个桶,每个桶的大小是原数组中元素的个数
     //第一维是表示数字0-9  第二维是表示相应基数的元素的个数
        int[] bucketElementCounts = new int[10];//记录每个桶中放的元素的个数

        for(int i=0,n=1;i<maxLength;i++,n*=10)//最大数字的位数几位决定了需要进行几次排序
        {
            for(int j=0;j<arr.length;j++)
            {
                int num=arr[j]/n%10;//获取当前位置的数字
                //n=1时获取个位数字   n=10时获取十位数字....
                bucket[num][bucketElementCounts[num]++]=arr[j];//将对应位置的数字放进对应的桶中
            }

            int index=0;

            for(int j=0;j<bucketElementCounts.length;j++)//j<10 0-9
            {
                if(bucketElementCounts[j]!=0)//当前桶中有元素
                {
                    for(int k=0;k<bucketElementCounts[j];k++)
                        arr[index++]=bucket[j][k];//将j号桶中的k号元素重新放入原数组中

                }
                bucketElementCounts[j]=0;//清空j号桶
            }
        }

    }

总结

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodePanda@GPF

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值