八大排序算法

简介: 八大排序算法

公众号merlinsea


  • 排序的基本概念


  • 排序: 排序就是将原本无序序列重新排列成有序序列的过程,这个序列的中的每一项都可以是单独的数据元素或者是一条记录(比如一个学生记录是由学号、年龄、姓名、年龄等数据元素组成)。
  • 排序的稳定性:稳定性是指待排序的序列中有两个或者两个以上相同的关键字的时,排序前后这些相等关键字的相对位置没有发生变化就是稳定排序,如果发生了相对变化,就称之为不稳定排序。 注意,这里的没有发生变化是不论初始的无序序列是如何排列的,在排序后都必须是相等关键字都不发生相对位置的变化,只要有一次相对位置发生了变化就是不稳定排序。


  • 排序算法的分类


  • 插入类排序是对新的关键字进行找位置的排序方式,找到位置以后直接进行插入即可,常见的插入类排序有直接插入排序、折半插入排序和希尔排序。
  • 选择类排序的核心是"选择",即每一趟都选出一个最大关键字或最小关键字和待排序序列的最后一个和第一个进行交换位置。
  • 交换类排序的核心是“交换”,即通过一系列交换操作,将新的关键字排到他正确的位置上,常见的交换类排序有快速排序和对排序。
  • 归并类排序是将两个或者两个以上的有序序列合并成一个新的有序序列的过程,常见的有二路归并排序算法。
  • 基数类排序不同于其他排序算法的比较和移动这两个操作,基数类排序是基于多关键字的排序思想,比如对一副去掉大小王的扑克牌进行基数排序,首先可以将牌按照花色分为4堆,然后对每一堆牌按照A-K再分为13堆,最后依次取出这13堆牌即得到有序的牌。


  • 直接插入排序


  • 直接插入排序是把无序的序列里的数据插入到有序的序列中去,在遍历无序序列的时候,首先拿无序序列里的第一个元素跟有序序列里的每一个元素比较,并且插入到合适的位置,一直到无序序列里的所有元素插入完毕为止。


public class Main {
    public static void main(String[] args) {
        int[] array = new int[]{4,2,6,1,3,8,7};
        System.out.println("排序前:");
        for(int i=0;i<array.length;i++){
            System.out.printf("%d \t",array[i]);
        }
        System.out.println();
        insertSort(array);
        System.out.println("排序后:");
        for(int i=0;i<array.length;i++){
            System.out.printf("%d \t",array[i]);
        }
        System.out.println();
    }
    // 直接插入排序
    public static void insertSort(int[] array){
        for(int i=1;i<array.length;i++){
            // 待插入待元素是array[i], array[0,i-1]
            int target = array[i];
            int j = i-1;
            for(j=i-1;j>=0;j--){
                if(target<array[j]){
                    // 保证了是个稳定排序
                    array[j+1]=array[j];
                   //array[j]= target;
                }else{
                    break;
                }
            }
            array[j+1]=target;
        }
    }


  • 希尔排序


  • 希尔排序是通过记录按下标的一定增量来进行分组,对每一组使用直接插入排序的算法进行排序;随着增量的变小,每组所包含的关键词也会越来越多,当增量变成1的时候,整个都恰好的被分成了一组,排序完后就终止了。
  • 希尔排序相当于跨步长的直接插入排序。


public class Main {  
  public static void main(String[] args) {
        int[] array = new int[]{4,2,6,1,3,8,7};
        System.out.println("排序前:");
        for(int i=0;i<array.length;i++){
            System.out.printf("%d \t",array[i]);
        }
        System.out.println();
        shellSort(array);
        System.out.println("排序后:");
        for(int i=0;i<array.length;i++){
            System.out.printf("%d \t",array[i]);
        }
        System.out.println();
    }
    // 希尔排序
    public static void shellSort(int[] array){
        int[] steps = {5,2,1};
        for(int step : steps){
            // 下面这个for循环表示步长为step所有组完成了排序,
            for(int i=0;i<step;i++){
                 // 比如步长为2, 我们下面待for循环只是完成了步长2的其中一组排序
                 for(int j=i+step;j<array.length;j=j+step){
                     // 当前待插入待元素是array[k];
                     int target = array[j];
                     for(int k=j-step;k>=i;k = k-step){
                         // 需要让target和array[k]比较
                         if(array[k]>target){
                             // 需要让target和array[k】交换位置
                             array[k+step] = array[k];
                             array[k] = target;
                         }else{
                             break;
                         }
                     }
                 }
                System.out.printf("step = %d 中的 第%d组完成了排序\n",step,i+1);
                for(int a: array){
                    System.out.printf("%d \t",a);
                }
                System.out.println();
            }
        }
    }


  • 冒泡排序


  • 冒泡排序(Bubble Sort),它是一种最为基础的交换排序。相信都喝过汽水吧,小气泡比二氧化碳要轻所有小气泡上浮。之所以叫冒泡排序,是因为这一种排序算法的每一个元素可以根据自身的大小,一点点的向着一侧来移动。


public class Main {
    public static void main(String[] args) {
        int[] array = new int[]{4,2,6,1,3,8,7};
        System.out.println("排序前:");
        for(int i=0;i<array.length;i++){
            System.out.printf("%d \t",array[i]);
        }
        System.out.println();
        bubbleSort(array);
        System.out.println("排序后:");
        for(int i=0;i<array.length;i++){
            System.out.printf("%d \t",array[i]);
        }
        System.out.println();
    }
    // 冒泡排序, 是否是稳定排序 ---》 稳定排序
    public static void bubbleSort(int[] array){
        for(int i=0;i<array.length;i++){
            boolean isSwap = false;
            for(int j=0;j<array.length-i-1;j++){
                if(array[j]>array[j+1]){
                    // 交换位置
                    int temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                    isSwap = true;
                }
            }
            if(!isSwap){
                // 我们当前轮全程没交换
                return;
            }
        }
    }
}


  • 快速排序
  • 快速排序是对冒泡排序的一种改良版,通过一趟排序,把要排序的序列分割成两个部分,一部分的所有数据要比另一部分的数据都小,然后再根据这两部分的数据来进行快速排序。以此来达到整一个数据都变成了有序序列。
  • 首先快速排序要选取一个基准值pivot,以基准值为分割,把比该基准值小的放左边,基准值大的放右边。然后得出来的序列再进行快速排序。


640.png


public class Main {
    public static void main(String[] args) {
        int[] array = new int[]{49,38,65,97,76,13,27,49};
        System.out.println("排序前:");
        for(int i=0;i<array.length;i++){
            System.out.printf("%d \t",array[i]);
        }
        System.out.println();
        quickSort(array,0,array.length-1);
        System.out.println("排序后:");
        for(int i=0;i<array.length;i++){
            System.out.printf("%d \t",array[i]);
        }
        System.out.println();
    }
    public static void quickSort(int[] arr,int left,int right){
        if(left>=right){
            //递归的终点
            return;
        }
        int pivot = arr[left];
        int l = left;
        int r = right;
        while(l<r){
            //右指针向左移动
            while(l<r && arr[r]>=pivot){
                r--;
            }
            if(l<r){
                arr[l] = arr[r];
                arr[r] = pivot;
                l++;
            }
            // pivot 位于r的位置
            //左指针向右移动
            while(l<r && arr[l]<pivot){
                l++;
            }
            if(l<r){
                arr[r]=arr[l];
                arr[l]=pivot;
                r--;
            }
            // pivo位于l位置
        }
        //当推出循环的时候,pivot位于arr[l]处
        quickSort(arr,left,l-1);
        quickSort(arr,l+1,right);
    }
}


  • 归并排序
  • 归并排序是利用了归并这种思想来实现的排序方法,这种算法采用的是经典的分治策略,将问题分成小问题然后进行求解,很像递归求解。然后将分出来的阶段再合在一起。


public class Main {
    public static void main(String[] args) {
        int[] array = new int[]{17,4,2,6,9,3,2,1};
        System.out.println("排序前:");
        for(int i=0;i<array.length;i++){
            System.out.printf("%d \t",array[i]);
        }
        System.out.println();
        mergeSort(array,0,array.length-1);
        System.out.println("排序后:");
        for(int i=0;i<array.length;i++){
            System.out.printf("%d \t",array[i]);
        }
        System.out.println();
    }
    public static void mergeSort(int[] arr,int left,int right){
        if(left>=right){
            return;
        }
        int mid = (left+right)/2;
        // 分
        mergeSort(arr,left,mid);
        mergeSort(arr,mid+1,right);
        //治
        merge(arr,left,mid,mid+1,right);
    }
    public static void merge(int[] arr,int l1,int r1,int l2,int r2){
        int len = r2-l1+1;
        // 每次归并都会开辟一个额外的temp空间,导致空间复杂度比较高
        int[] temp = new int[len];
        int i = l1;
        int j = l2;
        int idx = 0;
        while(i<=r1 && j<=r2){
            if(arr[i] < arr[j]){
                temp[idx] = arr[i];
                i++;
            }else{
                temp[idx] = arr[j];
                j++;
            }
            idx++;
        }
        //下面两个while至多执行一个while
        while(i<=r1){
            temp[idx] = arr[i];
            i++;
            idx++;
        }
        while(j<=r2){
            temp[idx] = arr[j];
            j++;
            idx++;
        }
        //将temp倒回去
        for(idx=0;idx<len;idx++){
            arr[idx+l1] = temp[idx];
        }
    }
}


  • 基数排序
  • 基数排序也称之为桶排序,其思想是基于多关键字进行排序的,其核心的思想是“分配”和“收集”操作。举个去掉了大小王的扑克牌的例子,首先将扑克牌按牌值分配到A-K的13个桶子中,然后将扑克牌收集起来;接着将收集起来的扑克牌按照花色分配到4个不同的桶中,然后将其手机起来,这样就可以得到整体上是按照花色进行排序,同一种花色按照牌的值进行排序的新牌了。


640.png


  • 基数排序的特点
  • 基数排序中的桶是一个先进先出的数据结构, 用队列来表示这个桶
  • 基数排序的第一轮分发和收集以后,输出的整体结果是按照第一轮的关键关键字【即牌值】进行排序的
  • 基数排序的第二轮分发和收集以后,输出的整体结果是按照第二轮的关键字【即花色】进行排序的,同时每一种花色内又是按照第一轮的关键字进行排序的。

  • 因此我们可以得到特点是:基数排序中,越往后的“分发”和“收集”的关键字的排序权重越高。 比如在本例子中,我们可以发现花色的排序权重要高于牌值的权重,因为最终结果是所有的红心牌都排在了方块牌的前面,在花色一样的情况下,我们才按照牌值进行排序。

640.png


public class Solution {
    public static void main(String[] args) {
        int[] array = new int[]{278,109,63,930,12,32,8,21,344};
        System.out.println("排序前");
        for(int k : array){
            System.out.printf("%d\t",k);
        }
        System.out.println();
        bucketSort(array);
        System.out.println("排序后");
        for(int k : array){
            System.out.printf("%d\t",k);
        }
        System.out.println();
    }
    // 基数排序,桶排序
    // 时间复杂度: cnt*n = n
    public static void bucketSort(int[] array){
        if(array.length <= 1){
            return;
        }
        int cnt = count(array);
        int bit = 1;
        for(int i=0;i<cnt;i++){
            // 分配
            List<Queue<Integer>> list = allocate(array,bit);
            // 收集
            collect(list,array);
            bit = bit*10;
        }
    }
    public static void collect(List<Queue<Integer>> list,int[] array){
        int idx = 0;
        for(int i=0;i<list.size();i++){
            Queue<Integer> qu = list.get(i);
            while(!qu.isEmpty()){
                int k = qu.poll();
                array[idx] = k;
                idx++;
            }
        }
        return;
    }
    public static List<Queue<Integer>> allocate(int[] array,int bit){
        List<Queue<Integer>> list = new ArrayList<>(10);
        for(int i=0;i<10;i++){
            // set 一定是对有过元素的位置进行set
           //list.set(i,new LinkedList<>());
            list.add(new LinkedList<>());
        }
        for(int k : array){
            int token = k/bit%10;
            list.get(token).offer(k);
        }
        return list;
    }
    // 统计需要进行多少次分配和收集
    private static int count(int[] array){
        int cnt = 0;
        for(int k : array){
            int curCnt = 0;
            while(k>0){
                curCnt++;
                k = k/10;
            }
            cnt = Math.max(cnt,curCnt);
        }
        return cnt;
    }
}


  • java中的sort函数使用
  • Arrays.sort()函数的的基本认识
  • 自定义类型自己实现Comparable接口
  • sort()方法传入实现了Comparator接口的示例对象
  • 注意当返回值大于0时发生交换
  • 当对数字数组进行排序时,默认是按照数字进行升序排列
  • 当对字符串数组进行排序时,默认是按照字典升序排列
  • 当对自定义类型的数组进行排序时


  • 对数字进行排序代码


public class Main {
    public static void main(String[] args) {
        int[] array = new int[]{14,3,21,17,67,55,32,12,5,7,6,44};
        System.out.println("排序前:");
        for(int i=0;i<array.length;i++){
            System.out.printf("%d \t",array[i]);
        }
        System.out.println();
        Arrays.sort(array);
        System.out.println("排序后:");
        for(int i=0;i<array.length;i++){
            System.out.printf("%d \t",array[i]);
        }
        System.out.println();
    }
}


  • 对字符串进行排序代码


public class Main2 {
    public static void main(String[] args) {
        String[] array = new String[]{"abc","abb","abca","bca","bba","abce"};
        System.out.println("排序前");
        for(String s : array){
            System.out.printf("%s \t",s);
        }
        System.out.println();
        Arrays.sort(array);
        System.out.println("排序后");
        // abb   abc   abca   abce   bba   bca   
        for(String s : array){
            System.out.printf("%s \t",s);
        }
        System.out.println();
    }
}


  • 对自定义类型进行排序代码
  • 要求对学生类进行排序,按分数从高到低输出,当分数相同时,按名字的字典顺序输出
// 分数从高到低进行排序,在分数相同的情况下按name进行排序
class Student{
    int score;
    String name;
    public Student(int score,String name){
        this.name = name;
        this.score = score;
    }
    @Override
    public String toString() {
        return "Student{" +
                "score=" + score +
                ", name='" + name + '\'' +
                '}';
    }
}
public class Solution {
    public static void main(String[] args) {
        Student[] array = {new Student(100,"lili"),new Student(119,"zhangsan"),
                new Student(88,"hong"),new Student(100,"lile"),new Student(120,"lili")};
        for(Student stu : array){
            System.out.printf("%s \t",stu);
        }
        Arrays.sort(array, new Comparator<Student>() {
            // 如果comapre返回大于0 就进行交换
            // O1在前 O2在后
            @Override
            public int compare(Student o1, Student o2) {
                if(o1.score<o2.score){
                    return o2.score - o1.score;
                }else if(o1.score == o2.score){
                    // 在分数相同的情况下,那个名字小就放前面
                    // o1.name.compareTo(o2.name)--> o1.name - o2.name >0 ---> 说明o2的name字典排序小 ---》 o2应该和o1交换位置 --》 返回一个大于0的数
                    return o1.name.compareTo(o2.name);
                }else{
                    return o2.score - o1.score;
                }
            }
        });
        System.out.println();
        System.out.println("排序后");
        for(Student stu : array){
            System.out.printf("%s \t",stu);
        }
    }
}


相关文章
|
存储 数据采集 SQL
详解数据中台的底层架构逻辑
详解数据中台的底层架构逻辑
1535 0
详解数据中台的底层架构逻辑
|
2月前
|
Ubuntu
在Ubuntu系统上设置syslog日志轮替与大小限制
请注意,在修改任何系统级别配置之前,请务必备份相应得原始档案并理解每项变更可能带来得影响。
273 2
|
4月前
|
数据安全/隐私保护 Python
大话西游自动打怪脚本,大话西游抢摊位脚本,刷图刷怪抢元宝工具
完整的游戏刷怪脚本实现,包含多模块功能(怪物生成、波次控制、掉落系统等),使用Python编写
|
8月前
|
人工智能
MIT 76页深度报告:AI加速创新马太效应,科学家产出分化加剧!缺乏判断力将被淘汰
近日,麻省理工学院(MIT)发布了一份76页的深度研究报告,探讨AI对科学发现和创新的影响。研究对象为1018名美国科学家,结果显示AI使新材料发现增加44%,专利申请增长39%,产品创新提升17%。然而,AI对高能力科学家的产出提升更显著,加剧了科学家间的分化。AI还改变了科学家的工作内容,减少了创意构思时间,增加了评估任务,导致工作满意度下降,但科学家对AI的信心增强。报告全面分析了AI带来的机遇与挑战。论文地址:https://conference.nber.org/conf_papers/f210475.pdf
337 14
|
9月前
|
自然语言处理 搜索推荐 数据管理
2025年国产CRM系统功能盘点:总有一款适合你
随着企业数字化转型加速,国产CRM系统凭借高性价比、本地化服务和灵活定制能力,成为众多企业的首选。本文盘点了全渠道CRM(如销售易、纷享销客)、销售管理型CRM(如金蝶CRM、悟空CRM)、服务管理型CRM(如Udesk、天润融通)、市场营销型CRM(如六度EC)、客户关系型CRM(如用友CRM、神州云动Cloud CC)及其他国产CRM系统(如八骏科技CRM、简道云、金蝶云之家、八百客)的功能,帮助企业根据自身需求选择最适合的CRM系统,助力业绩高质量增长。
|
存储 监控 安全
SaaS业务架构:业务能力分析
【9月更文挑战第20天】在数字化时代,软件即服务(SaaS)模式逐渐成为企业软件解决方案的首选。SaaS 业务架构设计对于提供高效、可靠的服务至关重要。其核心业务能力包括:用户管理(注册登录、角色权限)、数据管理(存储备份、安全共享)、业务流程管理(设计定制、工作流自动化)、应用集成(第三方应用、移动应用)及客户服务(支持培训、反馈改进)。通过优化这些能力,可为企业提供更高效、可靠的 SaaS 服务。
299 11
|
弹性计算 人工智能 自然语言处理
GPU实验室-通过GPU云服务器生成AI视频
自多态模型GPT-4发布后,AIGC(AI Generated Content,AI生成内容)时代正扑面而来,从单一的文字文本,演化到更丰富的图片、视频、音频、3D模型等。本文基于阿里云GPU服务器和文本生成视频模型,采用Unet3D结构,通过从纯高斯噪声视频中,迭代去噪的过程,实现文本生成视频功能。
|
XML JavaScript Java
NekoHTML 是一个基于Java的HTML扫描器和标签补全器
**NekoHTML** 是一个基于Java的HTML扫描器和标签补全器(tag balancer),由J. Andrew Clark开发。它主要用于解析HTML文档,并能够“修正”许多在编写HTML文档过程中常犯的错误,如增补缺失的父元素、自动用结束标签关闭相应的元素,以及处理不匹配的内嵌元素标签等。这使得程序能够以标准的XML接口来访问HTML文档中的信息。 ### NekoHTML的主要特点包括: 1. **错误修正**:能够自动修正HTML中的常见错误,如未闭合的标签等。 2. **DOM树生成**:将HTML源代码转化为DOM(Document Object Model)结构,便
240 1
|
存储 NoSQL 安全
GEO类型
【10月更文挑战第9天】
236 0
|
Devops 持续交付 开发者
.NET自动化之旅:是Azure DevOps还是GitHub Actions能够打造完美的CI/CD流水线?
【8月更文挑战第28天】在现代软件开发中,持续集成(CI)与持续部署(CD)是提升代码质量和加速交付的关键实践。对于 .NET 应用,Azure DevOps 和 GitHub Actions 等工具可高效构建 CI/CD 流水线,提升开发效率并确保软件稳定可靠。Azure DevOps 提供一站式全流程管理,支持 YAML 定义的自动化构建、测试和部署;GitHub Actions 则以简洁灵活著称,适用于 .NET 项目的自动化流程。选择合适的工具可显著提高开发效率并确保高质量标准。
144 0