Java 7大排序

🐵本篇文章将对数据结构中7大排序的知识进行讲解


一、插入排序

有一组待排序的数据array,以升序为例,从第二个数据开始(用tmp表示)依次遍历整组数据,每遍历到一个数据都再从tmp的前一个数据开始(下标用j表示)从后往前依次和其进行比较,如果tmp比它小,则令array[j + 1] = array[j];

1.1 实例讲解

第一趟:


第二趟:

第三趟和第四躺:

1.2 代码实现

    public 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 (tmp < array[j]) {
                    array[j + 1] = array[j]; //将tmp移动到当前数据顺序的最小位置处,此步操作相当于给tmp腾位置
                } else {
                    break;
                }
            }
            array[j + 1] = tmp;
        }
    }

在该排序算法中,当tmp前面出现比其小的元素时,则再往前的数据也一定比tmp小,所以插入排序是元素越有序,其效率越快的排序算法

时间复杂度:O(N²)

空间复杂度:O(1)

稳定

二、希尔排序

希尔排序是对直接插入排序的优化,它会将一组数据进行分组,然后针对每一组进行直接插入排序,那么该如何进行分组:定义一个gap,代表同一组数据的间隔,比如由一组数据:6,5,4,3,2,1;gap = 2,则6,4,2为一组,5,3,1为一组。在gap = 2的情况下的每一组数据排序完毕后,要缩小gap并再进行分组,然后再对每一组进行插入排序,随着gap的减小,该组数据会变得越来越有序,当gap = 1时,此时数据已经接近有序了,所以效率会非常快 

2.1 实例讲解

第一躺:


第二趟:


第三趟:

2.2 代码实现

    public void shellSort(int[] array) {
        int gap = array.length;
        while(gap > 1) { //当gap = 1时分组结束
            gap = gap / 2;
            shell(array, gap);
        }
    }

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

希尔排序不稳定

三、选择排序

选择排序较为简单,这里直接讲实例

3.1 实例讲解

第一躺:


第二趟:


第三趟:

第四躺和第五躺也都是如此排序的,由于数据已经有序,这里就不再演示

3.2 代码实现

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

    private void swap(int[] array, int minIndex, int i) {
        int tmp = array[minIndex];
        array[minIndex] = array[i];
        array[i] = array[minIndex];
    }

选择排序的效率不是很高,日常开发使用较少

时间复杂度:O(N²)

空间复杂度:O(1)

不稳定

四、堆排序

在上篇文章:Java优先级队列(堆)中进行了讲解,这里只给出代码:

4.1 代码实现

    public void createHeap(int[] array) { //创建大根堆
        int usedSize = array.length;
        for (int parent = (usedSize - 1 - 1) / 2; parent >= 0; parent--) {
            siftDown(array, parent, usedSize);

        }
    }

    private void siftDown(int[] array, int parent, int end) { //向下调整
        int child = 2 * parent + 1;
        while(child < end) {
            if (child + 1 < end && array[child] < array[child + 1]) {
                child++;
            }

            if (array[parent] < array[child]) {
                swap(array, parent, child);
                parent = child;
                child = 2 * parent + 1;
            } else {
                break;
            }
        }
    }

    private void swap(int[] array, int i, int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }


    public void heapSort(int[] array) { //堆排序
        createHeap(array);
        int end = array.length - 1;
        while(end > 0) {
            swap(array, 0, end);
            siftDown(array, 0, end - 1);
            end--;
        }
    }


堆排序:

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

空间复杂度:O(1)

不稳定

五、冒泡排序

冒泡排序在C语言阶段也进行了详细讲解,这里也只给出代码:

5.1 代码实现

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

冒泡排序

时间复杂度:O(N²)

空间复杂度:O(1)

稳定

六、快速排序

6.1 实例讲解

以最左边的数作为基准,先从数组的最右边开始遍历,当找到比基准小的数时停止,然后从数组的最左边开始遍历,当找到比基准大的数时停止,这时将 l 和 r 所对应的值进行交换,之后重复上述过程直到 left 和 right 相遇,相遇的下标定义为pivot,最后将pivot下标的值和tmp进行交换

此时6的左边都是比其小的数,6的右边都是比其大的数;之后分别对6左边的数据和右边的数据进行重复的操作

之后再对这两组数据的pivot的两边进行重复操作,由此可以联想到使用递归,类似于二叉树

6.2 代码实现

    public void quickSort(int[] array) {
        quick(array, 0, array.length - 1);
    }


    private void quick(int[] array, int start, int end) {

        int pivot = partition(array, start, end); //通过paratition方法得到 left 和 right 相遇的下标 (paratition后续再实现)

        quick(array, start, pivot - 1); //递归 pivot 的左边

        quick(array, pivot + 1, end); //递归 pivot 的右边
    }



上述的 quick 方法中还缺少递归结束的条件,第一种不难想到就是left 和 right相遇时

第二种情况如下图:

上图的下一步是r = pivot - 1;开始递归pivot的左边,但其左边并没有数据,所以当left > right时结束递归

    public void quickSort(int[] array) {
        quick(array, 0, array.length - 1);
    }

    private void quick(int[] array, int start, int end) {
        if (start >= end) {
            return;
        }

        int pivot = partition1(array, start, end);

        quick(array, start, pivot - 1);

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

    private int partition(int[] array, int left, int right) { //确定pivot
        int tmp = array[left]; //基准
        int i = left;

        while(left < right) {
            while(left < right && array[right] >= tmp) {
                right--;
            }

            while(left < right && array[left] <= tmp) {
                left++;
            }

            swap(array, left, right);
        }
        swap(array, i, right);
        return left;
    }

上述的 partition 确定pivot的下标被称为Hoare法,接下来再介绍一种 “挖坑法”

仍然是先从右边开始遍历,找到比tmp小的数则放在空出来的位置,此时right下标的位置就空出来了,然后从左边开始遍历找到比tmp大的数则放在空出来的位置,重复上述过程

// 挖坑法
    private int partition(int[] array, int left, int right) {
        int tmp = array[left];
        while(left < right) {
            while(left < right && array[right] >= tmp) {
                right--;
            }
            array[left] = array[right];

            while(left < right && array[left] <= tmp) {
                left++;
            }
            array[right] = array[left];
        }

        array[left] = tmp;
        return left;
    }

6.3 快速排序的优化

一组数据在较为理想的情况下,每次找到的基准元素都可以将这组数据分为大致相等的两部分,此时的快速排序算法的时间复杂度为 O(nlogn) ,但是也会存在一些极端的情况:每次找到的基准元素都是这组数据的最大值或最小值,此时会出现"单分支"的情况,时间复杂度为O(n^2)

6.3.1 三数取中法

改优化方法主要针对趋于有序的待排数组(升序或逆序),比如有这样一组数据:1,2,3,4,5在每一次取基准元素之前,分别取该数组的第一个数,最后一个数和中间的数,取这三个数的中间大的数和第一个数进行交换,交换完后上述数组就会变成:3,2,1,4,5,这样就是上述提到的较为理想的情况

    private static void quick(int[] array, int start, int end) {
        if (start >= end) {
            return;
        }


        //如果待排数组趋于有序,则采用三数取中法进行优化
        int index = middleNum(array, start, end);
        swap (array, start, index);

        int pivot = partition(array, start, end);

        quick (array, start, pivot - 1);

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

6.3.2 递归到小的子区间时,进行直接插入排序

之前有说道:待排数据的有序性越强,直接插入排序的效率越高,所以可以考虑当快排的递归深度较深或者说递归到的子区间较小时,采用直接插入排序,这样也可以提升快速排序的效率

    private static void quick(int[] array, int start, int end) {
        if (start >= end) {
            return;
        }

        //如果区间较小,则使用这种优化
        if (end - start + 1 <= 10) {
            insertSort(array, start, end);
            return;
        }

        int pivot = partition(array, start, end);

        quick (array, start, pivot - 1);

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


    public static void insertSort(int[] array, int start, int end) { //这里不能只传数组,因为并不是对整个数组进行插入排序,而是某一个子区间进行直接插入排序
        for (int i = start + 1; i <= end; i++) { //由于只是对特定的区间进行插入排序,所以这里要限定空间
            int tmp = array[i];
            int j = i - 1;
            for (; j >= start; j--) { // >=start
                if (array[j] > tmp) {
                    array[j + 1] = array[j];
                } else {
                    break;
                }
            }
            array[j + 1] = tmp;
        }
    }

快速排序时间复杂度:最好:O(N*logN),最坏:O(N²),平均:O(N*logN)

空间复杂度:O(logN)

不稳定

七、归并排序

7.1 实例讲解

归并排序是先将待排数组递归的进行两两分组,直到每组只有一个元素,之后两两递归的进行有序合并

7.2 代码实现

先进行分解

    public static void mergeSort (int[] array) {
        //将待排数组进行分解
        mergeFunc(array, 0, array.length - 1);
    }

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

        int mid = left + ((right - left) >> 1); //得到改组数据的中间下标

        //分别分解数组的左边和右边
        mergeFunc (array, left, mid);

        mergeFunc (array, mid + 1, right);

        //将分解后的数组进行 二路归并
        merge (array, left, mid, right);

    }

之后进行合并,以下面这一组为例:

将上面这两组数据进行有序合并,可以给这两组数据的第一个元素和最后一个元素的下标分别定义为s1,e1,s2,e2;之后再创建一个数组tmpArr,每次比较s1和s2的值,并将较小的值放在tmpArr中,(如果s1的值较小则s1++,反之s2++),然后将tmpArr中的数据再拷贝到原数组中

    private static void merge (int[] array, int left, int mid, int right) {
        int s1 = left;
        int e1 = mid;
        int s2 = mid + 1;
        int e2 = right;

        int[] tmpArr = new int[right - left + 1];
        int k = 0;
        //1.保证两个表都有数据
        while (s1 <= e1 && s2 <= e2) {
            if (array[s1] < array[s2]) {
                tmpArr[k++] = array[s1++];
            } else {
                tmpArr[k++] = array[s2++];
            }
        }

        //2.上个循环走完之后,可能还有一个表的数据没有全部放到tmpArr中
        while (s1 <= e1) {
            tmpArr[k++] = array[s1++];
        }

        while (s2 <= e2) {
            tmpArr[k++] = array[s2++];
        }

        //3.将tmpArr中的数据拷贝回原数组中
        for (int i = 0; i < k; i++) {
            array[i + left] = tmpArr[i]; //array[i + left]是因为合并的两组数据不一定是原数组的0下标开始
        }
    }

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

空间复杂度:O(N)

不稳定


🙉本篇文章到此结束

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ASP.NET学生信息管理系统

摘 要 本文介绍了在ASP.net环境下采用“自上而下地总体规划&#xff0c;自下而上地应用开发”的策略开发一个管理信息系统的过程。通过分析某一学校学生管理的不足&#xff0c;创建了一套行之有效的计算机管理学生的方案。文章介绍了学生管理信息系统的系统分析部分&#xff0c…

终端安全管理防护软件排行榜2024(四大终端监控软件推荐)

你的企业存在这些问题吗&#xff1f; 数字化转型的深入和远程办公模式的普及&#xff0c;企业对终端安全管理的需求日益凸显。 确保终端设备的安全性不仅关乎数据保护、业务连续性&#xff0c;更直接影响企业的声誉与合规性。 2024年终端安全防护软件排行榜&#xff0c;有谁荣…

搞嵌入式需要什么条件?

嵌入式系统是一种将计算机系统嵌入到物理设备中的技术&#xff0c;是现代电子技术的重要组成部分&#xff0c;具有广泛的应用领域。 那么&#xff0c;搞嵌入式需要什么条件呢&#xff1f; 嵌入式可以简单分为硬件与软件两个方向。做嵌入式软件需要对语言有一定的基础&#xf…

使用in运算符检查状态活动

在具有并行状态分解的Stateflow图表中&#xff0c;子状态可以同时处于活动状态。如果检查状态活动&#xff0c;则可以在两个平行状态下同步子状态。 例如&#xff0c;此图表有两个平行的状态&#xff1a;Place和Tracker。Tracker中的转换会在适当的位置检查状态活动&#xff0c…

服务器远程桌面局域网连接不上的解决方法

在企业网络环境中&#xff0c;服务器远程桌面局域网连接不上是一个常见且棘手的问题。这种问题可能导致工作效率下降&#xff0c;甚至影响业务运营。因此&#xff0c;我们需要采取专业的方法来解决这一问题。 服务器远程桌面局域网连接不上的解决方法&#xff1a; 1、确保服务器…

弱监督语义分割-对CAM的生成过程进行改进1

一、仿射变换图像结合正则项优化CAM生成 论文&#xff1a;Self-supervised Equivariant Attention Mechanism for Weakly Supervised Semantic Segmentation &#xff08;CVPR,2020&#xff09; 1.SEAM方法 孪生网络架构&#xff08;Siamese Network Architecture&#xff09…

【CTF Web】XCTF GFSJ0478 cookie Writeup(HTTP协议+信息收集+Cookie)

cookie X老师告诉小宁他在cookie里放了些东西&#xff0c;小宁疑惑地想&#xff1a;‘这是夹心饼干的意思吗&#xff1f;’ 解法 按 F12&#xff0c;点击网络。 刷新页面。查看请求头中的 Cookie。 look-herecookie.php访问&#xff1a; http://61.147.171.105:53668/cookie.…

智慧互联,统信UOS V20桌面专业版(1070)解锁办公新模式丨年度更新

从小屏到大屏 突破&#xff0c;就在方寸之间 从人机到智脑 融合&#xff0c;旨在新质生产力 统信UOS一直致力于将先进科技与用户场景相结合&#xff0c;不断提升用户的工作效率和生产力。在最新发布的统信UOS V20桌面专业版&#xff08;1070&#xff09;版本中&#xff0c;我们…

MySQL指令

MySQL指令 1.数据库管理 查看已有的数据库(文件夹) show databases;创建数据库(文件夹) create database 数据库名字; #可能有汉字&#xff0c;编码方式可能不适用&#xff0c;产生乱码create database 数据库名字 DEFAULT CHARSET utf8 COLLATE utf8_general_ci ; #使用utf8…

Scala编程入门:从零开始的完整教程

目录 引言环境准备创建第一个Scala项目基本语法高阶概念进阶资源结语 引言 Scala是一种强大的、静态类型的、多范式编程语言&#xff0c;它结合了面向对象和函数式编程的特点。本教程将指导您如何从零开始学习Scala&#xff0c;并搭建一个简单的开发环境。让我们开始探索Scala…

2024数维杯数学建模B题完整论文讲解(含每一问python代码+结果+可视化图)

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2024数维杯数学建模挑战赛生物质和煤共热解问题的研究完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文。 B题论…

详解drop,delete,truncate区别

在SQL中&#xff0c;"DROP"、"DELETE"和"TRUNCATE"是用于删除数据的不同命令&#xff0c;它们之间有一些重要的区别&#xff1a; DROP&#xff1a; DROP用于删除数据库对象&#xff0c;例如删除表、视图、索引、触发器等。使用DROP删除的对象将…

C++相关概念和易错语法(11)(npos、string的基本使用)

本文主要是分享一些基础的用法和接口&#xff0c;不会涉及迭代器版本&#xff0c;也没有底层概念&#xff0c;主要是保证简单入门和使用。 1.npos string本质上是一个类&#xff0c;里面包含了上百个成员函数&#xff0c;在调用这个头文件后&#xff0c;我们要知道整个类都被…

unity制作app(5)--发送数据给数据库

这个之前做过&#xff0c;先不做照片的。下一节再做带照片的。 第一步 收集数据 1.先做一个AppModel结构体&#xff0c;这个结构体需要单做的。 using System; using System.Collections.Generic; using System.Linq; using System.Text; //using Assets.Model; public clas…

LangChain连接国内大模型测试|智谱ai、讯飞星火、通义千问

智谱AI 配置参考 https://python.langchain.com/v0.1/docs/integrations/chat/zhipuai/ZHIPUAI_API_KEY从https://open.bigmodel.cn/获取 from langchain_community.chat_models import ChatZhipuAI from langchain_core.messages import AIMessage, HumanMessage, SystemMes…

Lambda表达式Stream流-函数式编程入门教程

目录 函数式编程思想 Lambda 表达式 Stream流 常用的Stream中的API 创建Stream 第一种是直接使用.Stream()的方式来创建 第二种采用.of()方式创建 具体来看看每一个API是怎么用的 concat max min findFirst findAny count peek forEach forEachOrdered skip d…

oracle 9i 行头带有scn的表

oracle 9i 行头带有scn的表 conn scott/tiger drop table t1; drop table t2; create table t1(c varchar2(5)); create table t2(c varchar2(6)) ROWDEPENDENCIES; --t2表每行都有scn,会增加六个字节的开销 alter table t1 pctfree 0; alter table t2 pctfree 0; insert in…

Array.map解析

map方法会创建一个新数组。该方法会循环数组中的每个值&#xff0c;如果仅仅是想循环数组不需要返回值使用数组的forEach方法就可以。原数组中的每个元素都调用一次提供的函数后的返回值组成。Array.map 它接收一个函数 这个函数可以接收三个参数 数组的每个值item 这个值的索引…

2016-2021年全国范围的2.5m分辨率的建筑屋顶数据

一、论文介绍 摘要&#xff1a;大规模且多年的建筑屋顶面积&#xff08;BRA&#xff09;地图对于解决政策决策和可持续发展至关重要。此外&#xff0c;作为人类活动的细粒度指标&#xff0c;BRA可以为城市规划和能源模型提供帮助&#xff0c;为人类福祉带来好处。然而&#xf…

【html项目】教你制作地表最强王者辅助网站装逼神器

今天如何教你们写教你们如何写一个 仿王者荣耀修改器装逼神器 效果图 html: <body><header> <a href"//pvp.qq.com/" class"qqq" title"王者荣耀" onclick"PTTSendClick(link,logo,logo);">王者荣耀</a>&l…