Java常见的排序算法

news2024/11/19 3:48:30

排序分为内部排序和外部排序(外部存储)

常见的七大排序,这些都是内部排序

在这里插入图片描述

1、插入排序:直接插入排序

1、插入排序:每次将一个待排序的记录,按其关键字的大小插入到前面已排序好的记录序列 中的适当位置,直到全部记录插入完成为止。稳定排序算法

一个排序算法的稳定性与不稳定性是通过排序后相同元素的先后顺序 来判断的。

稳定性:如果排序前后,具有相同关键字的元素的相对顺序没有改变,则排序算法被认为是稳定的。

不稳定性:如果排序前后,具有相同关键字的元素的相对顺序发生了改变,则排序算法被认为是不稳定的。

  • 从第一个元素开始,该元素可以认为已经被排序
  • 取出下一个元素,在已经排序的元素序列中从后往前扫描
  • 如果该元素(已排序)大于新元素,将该元素移到下一位置;
  • 直到找到已排序的元素小于或者等于新元素的位置
  • 将新元素插入到该位置后(这就保证了相同元素的顺序和排序前一样,所以是稳定排序
  • 重复扫描

2、代码

public class InsertSort {
/**
* <>
* @method: insertSort
* @Param: [arr]
* @Return: void
* @exception:
* @Author: fsy
* @Date: 19-5-29 下午3:50
* @description:
*
*/
public void insertSort(int[] arr){
	//需要插入的数
	int insertNum;
	for (int i=1; i <arr.length ; i++) {
		insertNum=arr[i];
		//序列元素个数
		int j=i-1;
		//将大于insertNum的元素往后移动
			while (j>=0&&arr[j]>insertNum){
				arr[j+1]=arr[j];
				j--;
				}
		//找到位置,插入当前元素
		arr[j+1]=insertNum;
		}
	}

3、复杂度分析

时间复杂度:

  • O(n²)

空间复杂度:

  • O(1)

4、总结:

插入排序所需的时间取决于输入元素的初始顺序 。对于一个很大且其中的元素已经有序(或接近有序)的数组进行排序将会比随机顺序的数据或是逆序数据进行排序要快的多

2、插入排序:希尔排序

1、希尔排序:希尔排序的本质就是分组插入排序 ,又称缩小增量法。将整个无序序列分割成若干个子序列(由相隔某个“增量”的元素组成)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序时,再对全体元素进行一次直接插入排序 。因为进行直接插入排序时元素基本有序,所以效率是很高的,因此希尔排序在时间效率上有很大提高。希尔排序是不稳定排序算法

2、代码

public void shellSort(int[] d) { //d[]为增量数组
    RecordNode temp;
    int i, j;
    System.out.println("希尔排序");
    //控制增量,增量减半,若干趟扫描
    for (int k = 0; k < d.length; k++) {
        //一趟中若干子表,每个记录在自己所属子表内进行直接插入排序
        int dk = d[k];
        for (i = dk; i < this.curlen; i++) {
            temp = r[i];
            for (j = i - dk; j >= 0 && temp.key.compareTo(r[j].key) < 0; j -= dk) {
                r[j + dk] = r[j];
            }
            r[j + dk] = temp;
        }
        System.out.print("增量dk=" + dk + "  ");
    }
}

3、复杂度分析

时间复杂度:

  • O(nlog2 n)

空间复杂度:

  • O(1)

4、总结

希尔排序更高效是因为它权衡了子数组的规模和有序性。排序之初,各个子数组都很短,排序之后子数组都是部分有序的,这两种情况都很适合插入排序

3、选择排序:简单选择排序

1、选择排序:工作原理如下。首先在未排序序列中找到最小(大)元素,存放在排序序列的初始位置。然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序列的末尾,以此类推,直到所有元素排序完毕。选择排序的主要优点是与数据移动有关,如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对n个元素的表进行排序最多n-1次交换。不稳定排序

2、代码:

public static void sort(int[] a) {
    for (int i = 0; i < a.length; i++) {
        int min = i;
        //选出之后待排序中值最小的位置
        for (int j = i + 1; j < a.length; j++) {
            if (a[j] < a[min]) {
                min = j;
            }
        }
        //最小值不等于当前值时进行交换
        if (min != i) {
            int temp = a[i];
            a[i] = a[min];
            a[min] = temp;
        }
    }
}

3、复杂度分析

时间复杂度:

  • O(n²)

空间复杂度:

  • O(1)

4、总结

选择排序非常简单和直观,但是也非常慢。无论是哪种情况,哪怕原数组已经排序完成,它也将花费将近n²/2次遍历来确认一遍。它的排序结果也是不稳定的。不耗费额外的内存空间

4、选择排序:堆排序

1、堆的定义如下:n个元素的序列(k1,k2,… ,kn)

当且仅当满足下列关系时,称之为堆

在这里插入图片描述

把此序列对应的二维数组看成是一个完全二叉树,那么堆的含义就是:完全二叉树中任何一个非叶子节点的值均不大于(或不小于)其左、右孩子节点的值 。由上述性质可知,大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。因此我们可以使用大顶堆进行升序排序,使用小顶堆进行降序排序。不稳定排序

基本思想:以大顶堆为例,堆排序的过程就是将待排序的序列构造成一个堆,选出堆中最大的移走,再把剩余的元素调整成堆,找出最大的再移走,重复直至有序

2、代码

/**
 * @param a
 */
public static void sort(int[] a) {

    for (int i = a.length - 1; i > 0; i--) {
        max_heapify(a, i);

        //堆顶元素(第一个元素)与Kn交换
        int temp = a[0];
        a[0] = a[i];
        a[i] = temp;
    }
}

/***
 *
 *  将数组堆化
 *  i = 第一个非叶子节点。
 *  从第一个非叶子节点开始即可。无需从最后一个叶子节点开始。
 *  叶子节点可以看作已符合堆要求的节点,根节点就是它自己且自己以下值为最大。
 *
 * @param a
 * @param n
 */
public static void max_heapify(int[] a, int n) {
    int child;
    for (int i = (n - 1) / 2; i >= 0; i--) {
        //左子节点位置
        child = 2 * i + 1;
        //右子节点存在且大于左子节点,child变成右子节点
        if (child != n && a[child] < a[child + 1]) {
            child++;
        }
        //交换父节点与左右子节点中的最大值
        if (a[i] < a[child]) {
            int temp = a[i];
            a[i] = a[child];
            a[child] = temp;
        }
    }
}

3、复杂度分析

时间复杂度:O(nlog₂ n)

空间复杂度:O(1)

4、总结

由于堆排序中初始化堆的过程比较次数较多,因此不太使用于小序列 。由于多次任意下标相互交换位置,相同元素之间原本相对的顺序被破坏了,是不稳定排序。

5、交换排序:冒泡排序

1、冒泡排序:是一种简单排序。重复地走访过要排序的元素,一次比较两个元素,如果他们的顺序错误就把他们交换过来,走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。只在顺序不符合大小要求时交换,所以不会破坏相同元素间的顺序,因此是稳定排序

  • 比较相邻的元素,如果第一个比第二个大,就交换他们两个
  • 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对,这步做完,最后的元素会是最大的数
  • 针对所有的元素重复以上的步骤,除了最后一个
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较

2、代码

public static void sort(int[] a) {
    //外层循环控制比较的次数
    for (int i = 0; i < a.length - 1; i++) {
      //内层循环控制到达位置
        for (int j = 0; j < a.length - i - 1; j++) {
            //前面的元素比后面大就交换
            if (a[j] > a[j + 1]) {
                int temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
            }
        }
    }
}

3、复杂度分析

时间复杂度:

  • 最好:O(n)
  • 最坏:O(n²)
  • 平均时间复杂度:O(n²)

空间复杂度:O(1)

4、总结:

是稳定的排序算法。

6、交换排序:快速排序

1、快速排序:使用分治法策略来把一个串行分为两个子串行。通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。不稳定排序算法

2、代码

public static void sortByStack(int[] a) {
    Stack<Integer> stack = new Stack<Integer>();

    //初始状态的左右指针入栈
    stack.push(0);
    stack.push(a.length - 1);
    while (!stack.isEmpty()) {
        //出栈进行划分
        int high = stack.pop();
        int low = stack.pop();

        int pivotIndex = partition(a, low, high);

        //保存中间变量
        if (pivotIndex > low) {
            stack.push(low);
            stack.push(pivotIndex - 1);
        }
        if (pivotIndex < high && pivotIndex >= 0) {
            stack.push(pivotIndex + 1);
            stack.push(high);
        }
    }
}

private static int partition(int[] a, int low, int high) {
    if (low >= high) return -1;
    int left = low;
    int right = high;
    //保存基准的值
    int pivot = a[left];
    while (left < right) {
        //从后向前找到比基准小的元素,插入到基准位置中
        while (left < right && a[right] >= pivot) {
            right--;
        }
        a[left] = a[right];
        //从前往后找到比基准大的元素
        while (left < right && a[left] <= pivot) {
            left++;
        }
        a[right] = a[left];
    }
    //放置基准值,准备分治递归快排
    a[left] = pivot;
    return left;
}


3、复杂度分析

时间复杂度:

  • 最好:O(nlog₂ n)
  • 最坏:O(n²)
  • 平均时间复杂度:O(nlog₂ n)

空间复杂度:O(1)

7、归并排序

1、归并排序:归并排序算法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的,然后再把有序子序列合并为整体有序序列。稳定排序算法

归并排序可通过两种方式实现:

  • 自上而下的递归
  • 自下而上的迭代

2、(递归的方法)代码

public class Merge {

    //归并所需的辅助数组
    private static int[] aux;

    public static void sort(int[] a) {
        //一次性分配空间
        aux = new int[a.length];
        sort(a, 0, a.length - 1);
    }

    public static void sort(int[] a, int low, int high) {
        if (low >= high) {
            return;
        }
        int mid = (low + high) / 2;
        //将左半边排序
        sort(a, low, mid);
        //将右半边排序
        sort(a, mid + 1, high);
        merge(a, low, mid, high);
    }

    /**
     * 该方法先将所有元素复制到aux[]中,然后在归并会a[]中。方法咋归并时(第二个for循环)
     * 进行了4个条件判断:
     * - 左半边用尽(取右半边的元素)
     * - 右半边用尽(取左半边的元素)
     * - 右半边的当前元素小于左半边的当前元素(取右半边的元素)
     * - 右半边的当前元素大于等于左半边的当前元素(取左半边的元素)
     */
    public static void merge(int[] a, int low, int mid, int high) {
        //将a[low..mid]和a[mid+1..high]归并
        int i = low, j = mid + 1;
        for (int k = low; k <= high; k++) {
            aux[k] = a[k];
        }

        for (int k = low; k <= high; k++) {
            if (i > mid) {
                a[k] = aux[j++];
            } else if (j > high) {
                a[k] = aux[i++];
            } else if (aux[j] < aux[i]) {
                a[k] = aux[j++];
            } else {
                a[k] = aux[i++];
            }
        }
    }

}

3、复杂度分析

时间复杂度:O(nlog₂n)

空间复杂度:O(n)

4、总结:主要缺点是所需的额外空间和N成正比(N为待排序数组的长度)

8、总结

各种排序性能对比:

排序类型平均情况最好情况最坏情况辅助空间稳定性
冒泡排序O(n²)O(n)O(n²)O(1)稳定
选择排序O(n²)O(n²)O(n²)O(1)不稳定
直接插入排序O(n²)O(n)O(n²)O(1)稳定
折半插入排序O(n²)O(n)O(n²)O(1)稳定
希尔排序O(nlog2 n)O(nlog2 n)O(nlog2 n)O(1)不稳定
归并排序O(nlog₂n)O(nlog₂n)O(nlog₂n)O(n)稳定
快速排序O(nlog₂n)O(nlog₂n)O(n²)O(nlog₂n)不稳定
堆排序O(nlog₂n)O(nlog₂n)O(nlog₂n)O(1)不稳定
计数排序O(n+k)O(n+k)O(n+k)O(k)稳定
桶排序O(n+k)O(n+k)O(n²)O(n+k)(不)稳定
基数排序O(d(n+k))O(d(n+k))O(d(n+kd))O(n+kd)稳定

稳定:冒泡排序、直接插入排序、归并排序

不稳定:选择排序、希尔排序、快速排序、堆排序

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

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

相关文章

新版-C语言学生信息管理系统

拥有基本的学生信息系统的功能, 功能点如下所示: 1.添加学生信息 2.修改学生信息 3.删除学生信息 4.查看学生信息 5.搜索学生信息 6.查看系统学生总人数 7.学生信息排序 8.保存学生信息(保存在D:/students.txt) 9.导入学生信息(导入D:/students.txt文件中的信息) 主界面 1.添加…

人工智能轨道交通行业周刊-第57期(2023.8.21-8.27)

本期关键词&#xff1a;桥梁养护、智慧天路、列车通信网络、AIGC产业报告、价值对齐、异常检测 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通RailMet…

浅谈 Java 中的 Lambda 表达式

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 Lambda 表达式是一种匿名函数&#xff0c;它可以作为参数传递给方法或存储在变量中。在 Java8 中&#xff0c;它和函数式接口一起&#xff0c;共同构建了函数式编程的框架。 什么是函数式编程 函数式编程是…

DebugInfo 模块功能系统介绍 文本上色 文本与表格对齐 分隔线 秒表计算器 语义日期

背景 今天系统性的为大家介绍一下 DebugInfo 模块。这个模块提供了一些丰富的基本功能的封装&#xff0c;希望能给有需要的人带来些许帮助。 文本上色 DebugInfo 模块引入了 colorama提供文本颜色支持。 # -*- coding:UTF-8 -*-# region 引入必要依赖 from DebugInfo.DebugI…

Modbus转Profinet网关在大型自动化仓储项目应用案例

在自动化仓储项目中&#xff0c;Modbus是一种常见的通信协议&#xff0c;用于连接各种设备&#xff0c;例如传感器、PLC和人机界面。然而&#xff0c;Modbus协议只支持串行通信&#xff0c;并且数据传输速度较慢。为了提高通信效率和整体系统性能&#xff0c;许多大型仓储项目选…

Vue的使用(2)

一个简单的Vue项目的创建 创建一个UserList.vue组件 在App.vue中使用该组件 使用组件的第一步&#xff0c;导入组件使用组件的第二部&#xff0c;申明这个组件使用组件的第三步&#xff1a;引用组件 结果&#xff1a; 事件和插值语法 <template> <div><!-- te…

五、性能测试之linux分析命令

linux分析命令 一、服务器基础知识二、linux文件结构三、linux文件权限四、linux命令1、安装应用fedora家族: 如centosdebain家族&#xff1a;如ubuntu 2、获取帮助第一种&#xff1a;command --help第二种&#xff1a;man command第三种&#xff1a;info 3、服务器性能分析基础…

VBA技术资料MF47:VBA_文件夹命令MkDir

【分享成果&#xff0c;随喜正能量】善待这漫长又短暂的岁月&#xff0c;劝君莫惜金缕衣&#xff0c;劝君惜取少年时。世事总是无常&#xff0c;人生没有如果&#xff0c;用一颗云水禅心&#xff0c;过平淡充实的日子&#xff0c;只要且行且珍惜&#xff0c;人生就会无限美好&a…

Android 编译系统(Build System)剖析

Android Build System剖析 Android预构建应用是如何制作的&#xff0c;背后的构建系统又是什么&#xff1f; 本文旨在分享关于Android构建系统以及与原始设备制造商&#xff08;OEM&#xff09;集成的知识&#xff0c;简化理解AOSP复杂机制的过程。与手动查阅各种文件及其内部…

MySQL安装记录

背景 Windows系统重装了, 想恢复一下之前的MySQL环境, 而且本地数据库也是比较常用的, 刚好本次也在安装, 做一个简单的记录. 也算是自己的学习记录输出. 遇到的问题当然也可以同时记录在这里, 方便后 续回顾. 资料包 百度网盘 // TODO 估计放了也会被CSDN屏蔽, 这里就不放…

7 集群基本测试

1. 上传小文件到集群 在hadoop路径下执行命令创建一个文件夹用于存放即将上传的文件&#xff1a; [atguiguhadoop102 ~]$ hadoop fs -mkdir /input上传&#xff1a; [atguiguhadoop102 hadoop-3.1.3]$ hadoop fs -put wcinput/work.txt /input2.上传大文件 [atguiguhadoop1…

数学分析:场论

我们之前知道的是里斯表示定理。 这里看到&#xff0c;对于多重线性映射&#xff0c;里斯表示定理会从内积变成混合积。当然我们还是只考虑三维以内的情况。 于是我们可以把不同的1形式和2形式的下标写上&#xff0c;表示他们相当于内积或者混合积对应的那个向量。 然后还差0形…

LeetCode第1~5题解

CONTENTS LeetCode 1. 两数之和&#xff08;简单&#xff09;LeetCode 2. 两数相加&#xff08;中等&#xff09;LeetCode 3. 无重复字符的最长子串&#xff08;中等&#xff09;LeetCode 4. 寻找两个正序数组的中位数&#xff08;困难&#xff09;LeetCode 5. 最长回文子串&am…

【awd系列】Bugku S3 AWD排位赛-9 pwn类型

文章目录 二进制下载检查分析运行二进制ida分析解题思路exp 二进制下载 下载地址&#xff1a;传送门 检查分析 [rootningan 3rd]# file pwn pwn: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for …

android系统启动流程之SystemServer运行过程

SystemServer进程的启动流程&#xff1a;直接看代码&#xff1a; SystemServer是Java中的一个进程&#xff0c;执行入口是SystemServer.java.main(); SystemServer.java.main();-->new SystemServer().run();-->createSystemContext();//创建系统上下文:虽然SystemServe…

Unittest 笔记:unittest拓展生成HTM报告发送邮件

HTMLTestRunner 是一个unitest拓展可以生成HTML 报告 下载地址&#xff1a;GitHub: https://github.com/defnnig/HTMLTestRunner HTMLTestRunner是一个独立的py文件&#xff0c;可以放在Lib 作为第三方模块使用或者作为项目的一部分。 方式1&#xff1a; 验证是否安装成功&…

Flutter 状态管理引子

1、为了更好地了解状态管理&#xff0c;先看看什么是状态。 在类似Flutter这样的响应式编程框架中&#xff0c;我们可以认为U相关的开发就是对数据进行封装&#xff0c;将之转换为具体的U1布局或者组件。借用Flutter官网的一张图&#xff0c;可以把我们在第二部分做的所有开发…

AliOS-Things引入

目录 一、简介 1.1 硬件抽象层 1.2 AliOS-Things内核 rhino ​编辑 1.3 AliOS-Things组件 二、如何进行AliOS-Things开发 三、安装环境 安装python pip git 修改pip镜像源 安装aos-cube 一、简介 AliOS-Things是阿里巴巴公司推出的致力于搭建云端一体化LoT软件。AliOS-…

Linux操作系统--文件与目录结构

我们初步认识了Linux操作系统,下面我们进一步看看linux的文件与目录结构。 1.文件系统和挂载点 (1).当前的操作系统中查看文件系统 位置 > 计算机 这样你就可以看见当前操作系统中的所有目录和文件。如下所示: (2).挂载点 挂载点实际上就是linux中的磁盘文件系统的入口…

快速理解 X server, DISPLAY 与 X11 Forwarding

​ X server X server是X Window System &#xff08;简称X11或者X&#xff09;系统中的显示服务器&#xff08;display server&#xff09;&#xff0c;用于监听X client发送来的图形界面显示请求&#xff0c;并且将图形界面绘制并显示在屏幕&#xff08;screen&#xff09;…