排序(java)

news2025/5/25 16:03:57

一.概念

排序:对一组数据进行从小到大/从大到小的排序

稳定性:即使进行排序相对位置也不受影响如:

如果再排序后 L  在  i   的前面则稳定性差,像图中这样就是稳定性好。 

二.常见的排序

 三.常见算法的实现

1.插入排序

1.1 直接插入排序

时间复杂度

 最好情况:数据完全有序的时候O(N)

最坏的情况:数据完全逆序的时候O(N^2)

当前数据越有序越快

空间复杂度:O(1)

稳定性:稳定

 public static void insertSort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            int tmp = array[i];
            int j = i - 1;
            for (; j >= 0; j--) {
                if (array[j] > tmp) {
                    array[j + 1] = array[j];
                } else {
                    break;
                }
            }
            array[j + 1] = tmp;
        }
    }

该排序就是从第二个数据开始进行比较,以第4个数据进行举例,假设第三个数据大于第四个数据则第一个数据的值就改为第三个数据,第二个数据不大于第四个数据循环结束,当前第二个数据后面的数据就是当时第四个数据的值。

1.2 希尔排序

public static void shellSort(int[] array) {
        int gap = array.length;
        while (gap > 1) {
            gap /= 2;
            shell(array, gap);
        }
    }

    private static void shell(int[] array, int gap) {
        for (int i = gap; i < array.length; i += gap) {
            int tmp = array[i];
            int j = i - gap;
            for (; j >= 0; j -= gap) {
                if (array[j] > tmp) {
                    array[j + gap] = array[j];
                } else {
                    break;
                }
            }
            array[j + gap] = tmp;
        }
    }

                                                                                                           c                      ,

 通过对数据分成多个组,随着组数越来越多,组的大小越来越小,数据也越归于有序,通常gap大小都是gap/=2,这种方法是对于插入算法的优化,但是因为gap不固定所以时间复杂度不固定。

稳定性:不稳定

2.选择排序

基本思想:每次都从元素中找一个最大/最小的值,放在初始/末尾位置。

2.1直接选择排序

方法一:

 public static void selectSort(int[] array) {

        for (int i = 0; i < array.length; i++) {
            int minIndex = i;
            for (int j = i + 1; j < array.length ; j++){
                if (array[j] < array[minIndex]) {
                    minIndex = j;
                }
        }
            swap(array, minIndex, i);
    }
}

方法一就是找到一个最小的值放在初始位置。 

方法二:

  public static void selsectSort2(int[] array){
        int left = 0;
        int right = array.length - 1;
       while (left < right){
           int minIndex = left;
           int maxIndex = left;
            for (int j = left + 1; j <= right; j++) {
                if (array[minIndex] > array[j]){
                    minIndex = j;
                }
                if (array[maxIndex] < array[j]) {
                    maxIndex = j;
                }
            }
            swap(array,left,minIndex);
            if(maxIndex == left){
                maxIndex =minIndex;
            }
            swap(array,right,maxIndex);
            left++;
            right--;
        }
    }

方法二就是找到最大值和最小值,然后进行交换,需要注意的是我们在进行最小值交换的时候。如果与最小值交换的元素刚好就是最大值,我们就需要进行判断一下。 然后将最大值的下标更新一下。

1.时间复杂度: O(N^2)

2.空间复杂度:O(1)

3.稳定性:不稳定

2.2堆排序

在使用堆时,我们已经知道可以根据创建一个大根堆来实现对于元素的排序,这种方法是因为大根堆的根结点是最大的,所以我们可以将它和最后一个交换位置,然后对排序元素个数减一后进行向下排序,此时可以确定的是最后一个元素是最大的。

 public static void heapSort(int[] array){
        creatBigHeap(array);
        int end = array.length - 1;
        while(end > 0){
            swap(array,0 ,end);
            end--;
            siftDown(0,array,end);
        }
    }

    public static void creatBigHeap(int[] arrray){
        for (int parent = (arrray.length - 1 - 1)/2; parent >= 0 ; parent--) {
            siftDown(parent, arrray,arrray.length);
        }
    }

    private static void siftDown(int parent,int[] array,int end) {
        int child = 2*parent + 1;
        while(child < end){
            if(child + 1 < end && array[child] < array[child+1]){
                child++;
            }
            if(array[child] > array[parent]){
                swap(array,child,parent);
                parent = child;
                child = parent*2 + 1;
            }else{
                break;
            }
        }
    }

时间复杂度:O(n*logN)

空间复杂度:O(1)

稳定性:不稳定的

3.交换排序

概念:通过比较大小来交换元素,元素大的向后走,元素小的向前走。

3.1冒泡排序

冒泡排序就是通过遍历来进行前后比较,来进行排序,通过这种方法,数组的末尾总是当前的最大值,所以我们在进行遍历比较是时,应该减去我们我们进行整体排序的次数,之所以给外循环的上限减去一,是因为我们在对n个数据进行在整体排序时,我们只需要排序n-1次就好,因为最后一个数据应该也是有序,没必要再进行排序。

时间复杂度:O(N^2)

空间复杂度: O(1)

稳定性:稳定

public static void popSort(int[] array){
       boolean flag = false;
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = 0; j < array.length - 1 -i ; j++) {
                if (array[j] > array[j+1]) {
                    swap(array, j, j + 1);
                    flag = true;
                }
            }
            if (flag == false){
                break;
            }
        }
    }

3.2快速排序

基本思想就是取一个基准值,将当前数据分为大于基准值和小于基准值两个部分,然后再对这两个部分进行同样的操作,直到所有的元素都有序为止。

最好情况:O(N*logN)       满二叉树/完全二叉树

最坏情况:O(N^2)       单分支的树

空间复杂度:

最好情况:O(logN)       满二叉树/完全二叉树

最坏情况:O(N)            单分支的树

稳定性:不稳定              

3.2.1递归版
 private static void quick(int[] array,int start,int end) {
        if(start >= end){
            return;
        }
        int pivot = partition3(array,start,end);

        quick(array,start,pivot - 1);

        quick(array,pivot+1,end);
    }
3.2.1.1 Hoare版
  private static int parttionHoare(int[] array,int left,int right){
        int key = array[left];
         int i = left;
        while(left < right){
            while(left < right && array[right] >= key){
                right--;
            }
            while(left < right && array[left] <= key){
                left++;
            }
            swap(array,left,right);
        }
        swap(array,left,i);
        return left;
    }

Hoare版的快速排序就是从两边进行调整,左边一碰到大于基准的值就停下来,右边碰到小于基准的值就停下来,然后进行交换,就这样进行下去知道left >= right,最后我们left所停留的位置就是基准应该再的位置,但是基准开始被设为就是left,所以我们提前存的left就有了价值了,让他俩交换,就完成了一次快排。

3.2.1.2挖坑法
 private static int parttion2(int[] array,int left,int right){
       int key = array[left];
       while(left < right){
           while(left < right && array[right] >= key){
               right--;
           }
           array[left] = array[right];
           while(left < right && array[left] <= key){
               left++;
           }
           array[right] = array[left];
       }
      array[left] = key;
       return left;
   }

挖坑法就是先挖个坑让右边小于基准的值给填进去,这样右边就也有个坑了,然后再在左边找,找到一个大于基准的数,填满右边的坑,之后肯定有一个坑是空的,这就是我们要找的基准位置。 

3.2.1.3 双指针法
 private static int partition3(int[] array, int left, int right) {
      int prev = left;
      int pcur = left + 1;
      while(pcur <= right){
          if (array[left] > array[pcur] && array[++prev] != array[pcur]){
              swap(array,prev,pcur);
          }
          pcur++;
      }
      swap(array,left,prev);
      return prev;
    }

我们会以左边第一个元素为基准,如果碰到比基准小的值,我们会用用pcur来进行记录,而prev移动的条件就是当pcur遇到小的值时,而prev的下一个元素也必然时大于基准的,因为当前这个元素不大于基准pcur不能走。我们来画图举例:

3.2.2 非递归
public static void quickSortNor(int[] array){
        Stack<Integer> stack = new Stack<>();
        int left = 0;
        int right = array.length - 1;
        int pivot  = parttionHoare(array,left,right);
        if(pivot - 1 > left) {
            stack.push(left);
            stack.push(pivot - 1);
        }
        if(pivot + 1 < right) {
            stack.push(pivot+1);
            stack.push(right);
        }
        while(!stack.isEmpty()){
            right = stack.pop();
            left = stack.pop();
           pivot = parttionHoare(array,left,right);
            if(pivot - 1 > left){
                stack.push(left);
                stack.push(pivot - 1);
            }
            if(pivot + 1 < right){
                stack.push(pivot + 1);
                stack.push(right);
            }
        }
    }

非递归使用了栈,首先将元素分为大于基准和小于基准两组,然后重复进行操作,通过HOare操作来对数组进行排序。 

 快速排序优化
 private static void insertSortRange(int[] array,int begain,int end) {
       for (int i = begain + 1; i <= end; i++) {
           int tmp = array[i];
           int j = i - 1;
           for (; j >= begain; j--) {
               if (array[j] > tmp) {
                   array[j + 1] = array[j];
               } else {
                   break;
               }
           }
           array[j + 1] = tmp;
       }
   }

    private static void quick(int[] array,int start,int end) {
       if(start >= end){
           return;
       }
       if (end - start + 1 <= 15){
           //插入排序
           insertSortRange(array,start,end);
           return;
       }

       //三数取中
       int index =  midOfThree(array,start,end);

       swap(array,index,start);
       int pivot = parttionHoare(array,start,end);

       quick(array,start,pivot - 1);

       quick(array,pivot+1,end);
   }

当我们遇到小的区间可以用插入排序来解决,在选择基准时我们可以尽量选择取中间值来进行排序,所以我们又有了一个方法就是三数取中。

4.归并排序

基本思想:将一组数据分成两组,然后对分开的组在进行分组,直到分为一个组,然后进行合并,在合并过程中进行排序,开始时一 一排序,然后是二二,知道全部组都合并为一个组。

归并排序总结

1.归并排序缺点在于时间复杂度过高,因为占用外部空间来排序很多。 

2.时间复杂度:O(N*logN)

3.空间复杂度:O(N)

4.稳定性:稳定

代码实现

 public static void mergeSort(int[] array){
       mergeSortFunc(array,0,array.length - 1);
   }

    private static void mergeSortFunc(int[] array, int left, int right) {
       if(left >= right){
           return ;
       }

       int mid = (left + right) / 2;
        mergeSortFunc(array,left,mid);
        mergeSortFunc(array,mid + 1,right);
        merge(array,left,right,mid);
    }

    private static void merge(int[] array, int left, int right, int mid) {
      int s1 = left;
      int s2 = mid + 1;
      int[] tmpArr = new int[right - left + 1];
      int k = 0;

      while(s1 <= mid && s2 <= right){
          if (array[s2] <= array[s1]){
              tmpArr[k++] = array[s2++];
          }else{
              tmpArr[k++] = array[s1++];
          }
      }

        while (s1 <= mid){
            tmpArr[k++] = array[s1++];
        }
        while(s2 <= right){
            tmpArr[k++] = array[s2++];
        }
        for (int i = 0; i < tmpArr.length; i++) {
            array[i + left ] = tmpArr[i];
        }
    }

非递归实现

 public static void mergeSortNor(int[] array){
       int gap = 1;
       while(gap < array.length ){
           for (int i = 0; i < array.length; i += 2*gap ) {
               int left = i;
               int mid = left + gap - 1;
               int right = mid + gap;
               if (mid >= array.length){
                   mid = array.length - 1;
               }
               if (right >= array.length){
                   right = array.length - 1;
               }
               merge(array,left,right,mid);
           }
           gap*=2;
       }
    }

在这七大排序中稳定的只有归并排序,冒泡排序,插入排序,其中归并排序,快速排序,插入排序应当作为重点。

其他排序

计数排序

 public static void countSort(int[] array){
       int minVal = array[0];
       int maxVal = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] < minVal){
                minVal = array[i];
            }
            if (array[i] > maxVal){
                maxVal = array[i];
            }
        }

        int[] count = new int[maxVal - minVal + 1];


        for (int i = 0; i < array.length; i++) {
            count[array[i] - minVal]++;
        }

        int index = 0;
        for (int i = 0; i < count.length; i++) {
            while(count[i] > 0){
                array[index] = i+minVal;
                index++;
                count[i]--;
            }
        }
    }

通过对相同元素出现的次数来进行排序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2336520.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【HDFS入门】HDFS副本策略:深入浅出副本机制

目录 1 HDFS副本机制概述 2 HDFS副本放置策略 3 副本策略的优势 4 副本因子配置 5 副本管理流程 6 最佳实践与调优 7 总结 1 HDFS副本机制概述 Hadoop分布式文件系统(HDFS)的核心设计原则之一就是通过数据冗余来保证可靠性&#xff0c;而这一功能正是通过副本策略实现的…

智能 GitHub Copilot 副驾驶® 更新升级!

智能 GitHub Copilot 副驾驶 迎来重大升级&#xff01;现在&#xff0c;所有 VS Code 用户都能体验支持 Multi-Context Protocol&#xff08;MCP&#xff09;的全新 Agent Mode。此外&#xff0c;微软还推出了智能 GitHub Copilot 副驾驶 Pro 订阅计划&#xff0c;提供更强大的…

【今日三题】添加字符(暴力枚举) / 数组变换(位运算) / 装箱问题(01背包)

⭐️个人主页&#xff1a;小羊 ⭐️所属专栏&#xff1a;每日两三题 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 添加字符(暴力枚举)数组变换(位运算)装箱问题(01背包) 添加字符(暴力枚举) 添加字符 当在A的开头或结尾添加字符直到和B长度…

Linux——消息队列

目录 一、消息队列的定义 二、相关函数 2.1 msgget 函数 2.2 msgsnd 函数 2.3 msgrcv 函数 2.4 msgctl 函数 三、消息队列的操作 3.1 创建消息队列 3.2 获取消息队列并发送消息 3.3 从消息队列接收消息recv 四、 删除消息队列 4.1 ipcrm 4.2 msgctl函数 一、消息…

领慧立芯LHE7909可兼容替代TI的ADS1299

LHE7909是一款由领慧立芯&#xff08;Legendsemi&#xff09;推出的24位高精度Δ-Σ模数转换器&#xff08;ADC&#xff09;&#xff0c;主要面向医疗电子和生物电势测量应用&#xff0c;如脑电图&#xff08;EEG&#xff09;、心电图&#xff08;ECG&#xff09;等设备。以下是…

MongoDB简单用法

图片中 MongoDB Compass 中显示了默认的三个数据库&#xff1a; adminconfiglocal 如果在 .env 文件中配置的是&#xff1a; MONGODB_URImongodb://admin:passwordlocalhost:27017/ MONGODB_NAMERAGSAAS&#x1f4a1; 一、为什么 Compass 里没有 RAGSAAS 数据库&#xff1f;…

uniapp-商城-26-vuex 使用流程

为了能在所有的页面都实现状态管理,我们按照前面讲的页面进行状态获取,然后再进行页面设置和布局,那就是重复工作,vuex 就会解决这样的问题,如同类、高度提炼的接口来帮助我们实现这些重复工作的管理。避免一直在造一样的轮子。 https://vuex.vuejs.org/zh/#%E4%BB%80%E4…

UDP概念特点+编程流程

UDP概念编程流程 目录 一、UDP基本概念 1.1 概念 1.2 特点 1.2.1 无连接性&#xff1a; 1.2.2 不可靠性 1.2.3 面向报文 二、UDP编程流程 2.1 客户端 cli.c 2.2 服务端ser.c 一、UDP基本概念 1.1 概念 UDP 即用户数据报协议&#xff08;User Datagram Protocol &…

Flutter项目之设置页

目录&#xff1a; 1、实现效果图2、实现流程2.1、引入依赖2.2、封装弹窗工具类2.3、设置页2.4、路由中注册设置页面 1、实现效果图 2、实现流程 2.1、引入依赖 2.2、封装弹窗工具类 import package:fluttertoast/fluttertoast.dart;class CommontToast {static showToast(Str…

通过GO后端项目实践理解DDD架构

最近在工作过程中重构的项目要求使用DDD架构&#xff0c;在网上查询资料发现教程五花八门&#xff0c;并且大部分内容都是长篇的概念讲解&#xff0c;晦涩难懂&#xff0c;笔者看了一些github上入门的使用DDD的GO项目&#xff0c;并结合自己开发中的经验&#xff0c;谈谈自己对…

天线静电防护:NRESDTLC5V0D8B

一. 物联网天线的使用环境 1.1 联网天线广泛应用于智能家居领域&#xff0c;比如智能门锁、智能摄像头等设备中&#xff0c;通过天线实现设备与家庭网络的连接&#xff0c;用户可以远程控制和监控家居设备。以智能摄像头为例&#xff0c;它通过天线将拍摄的画面实时传输到用户…

【Linux 并发与竞争】

【Linux 并发与竞争】 Linux是一个多任务操作系统&#xff0c;肯定会存在多个任务共同操作同一段内存或者设备的情况&#xff0c;多个任务甚至中断都能访问的资源叫做共享资源&#xff0c;就和共享单车一样。在驱动开发中要注意对共享资源的保护&#xff0c;也就是要处理对共享…

实用类题目

1. 密码强度检测 题目描述&#xff1a;生活中&#xff0c;为保证账户安全&#xff0c;密码需要有一定强度。编写一个方法&#xff0c;接收一个字符串作为密码&#xff0c;判断其是否符合以下强度要求&#xff1a;长度至少为 8 位&#xff0c;包含至少一个大写字母、一个小写字…

STM32F103C8T6-基于FreeRTOS系统实现步进电机控制

引言 上一篇文章讲述了如何使用蓝牙连接stm32进行数据收发控制步进电机&#xff0c;这篇在之前的基础上通过移植操作系统&#xff08;FreeRTOS或者其他的也可以&#xff0c;原理操作都类似&#xff09;实现步进电机控制。 上篇博客指路&#xff1a;STM32蓝牙连接Android实现云…

macOS安装java

一、下载 官网Java Downloads | Oracle 安装载java8,下载对应的JDK Java Downloads | Oracle 二、双击安装 安装 完成 三、查看安装位置 打开终端窗口&#xff0c;执行命令&#xff1a; /usr/libexec/java_home -V /Library/Java/JavaVirtualMachines/jdk-1.8.jdk/Content…

zkmall模块商城:B2C 场景下 Vue3 前端性能优化的广度探索与实践

ZKmall作为面向B2C场景的模块化电商平台&#xff0c;其前端性能优化在Vue3框架下的实践融合了架构设计、渲染机制与业务特性&#xff0c;形成了一套多维度的优化体系。以下从技术实现与业务适配两个维度展开分析&#xff1a; 一、Vue3响应式系统深度适配 ​Proxy驱动的精准更新…

【Netty篇】Future Promise 详解

目录 一、 Netty Future 与 Promise —— 异步世界的“信使”与“传话筒”&#x1f680;1、 理解 Netty Future2、 理解 Netty Promise 二、 代码案例解读&#x1f4bb;例1&#xff1a;同步处理任务成功&#x1f44d;例2&#xff1a;异步处理任务成功&#x1f4f2;例3&#xff…

视频分析设备平台EasyCVR安防视频管理系统,打造电石生产智能视频监控新体系

一、背景介绍 电石生产中的出炉工序是整个生产流程中最为繁重且危险的环节。在开堵炉眼的过程中&#xff0c;电石极易发生飞溅现象&#xff0c;尤其在进行吹氧操作时&#xff0c;人员灼伤的风险极高。鉴于此&#xff0c;该工序正逐步由传统的人工操作模式向智能化方向转变。然…

从 PyTorch 到 ONNX:深度学习模型导出全解析

在模型训练完毕后&#xff0c;我们通常希望将其部署到推理平台中&#xff0c;比如 TensorRT、ONNX Runtime 或移动端框架。而 ONNX&#xff08;Open Neural Network Exchange&#xff09;正是 PyTorch 与这些平台之间的桥梁。 本文将以一个图像去噪模型 SimpleDenoiser 为例&a…

Android 应用添加Tile到SystemUI QuickSettings

安卓源码里有谷歌给的关于 Tile 的说明。 frameworks/base/packages/SystemUI/docs/qs-tiles.md SystemUI QuickSettings 简称QS&#xff0c;指的是 下拉菜单里的区域。区域里的一个选项就是一个 Tile 。 下图是 frameworks/base/packages/SystemUI/docs/ 里的附图示例&#…