JAVA-数据结构-排序

1.直接插入排序

1.原理:和玩扑克牌一样,从左边第二个牌开始,选中这个,和前面的所有牌比较,插在合适的位置

public static void insertsort(int[] arr){//直接插入排序
        for (int i = 1; i < arr.length; i++) {//此循环用来从1开始确定牌的位置
            int j = i-1;//得到j前面一个元素
            int tmp = arr[i];//将i处元素取出来,用来作比较
            for (; j >=0 ; j--) {//此循环用来和i前面的比较,将i放在正确的位置
                if(arr[j] > tmp){
                    arr[j+1] = arr[j];//若i前面的比i大则将前面的值赋值给后面
                }else{
//                    arr[j+1] = tmp;
                    break;//比前面都要大,没有比较的必要
                }
            }
            arr[j+1] = tmp;//j走完上面循环为-1,上面走完一次循环j=0时为空,
        }
    }

直接插入排序特点

1. 元素集合越接近有序,直接插入排序算法的时间效率越高
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1),它是一种稳定的排序算法
4. 稳定性:稳定

2.希尔排序

希尔排序是直接插入排序的Promax版本,将直接插入排序分为n份,比较完n份,排序即成功

思想:先选定一个整数,把待排序文件中所有记录分成多个组,
所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达
=1时,所有记录在统一组内排好序。

public static void shellSort(int[] array){//希尔排序是直接插入排序的优化
        int gap = array.length;
        while(gap > 0){//将每组距离最终干为1,即可成功
            gap /= 2;//得到每组的距离
            shell(array,gap);
        }
    }
    public static 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--) {
                if(array[j] > tmp){
                    array[j+gap] = array[j];
                }else{
                    array[j+gap] = tmp;
                    break;
                }
            }
            array[j+gap] = tmp;
        }
    }

3.选择排序

基本思想:一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元
素排完 。

 public static void selsctSort1(int[] array){//选择排序基础版
        for (int i = 0; i < array.length; i++) {
            int minIndex = i;//每循环一次确定一个从左往右的最小值
            int j = i+1;
            for (; j <array.length; j++) {
                if(array[minIndex] > array[j]){
                    minIndex = j;//将minTndex和j的下标先进行交换,找到最小的和其交换
                }//从一次次循环中得出在[i,length)的最小值
            }
            swap(array,minIndex,i);
        }
    }

    public static void selsctSort(int[] array){//选择排序promax版
         int left = 0;
         int right = array.length-1;
         while (left < right){
             int minIndex = left;
             int maxIndex = left;//统一从没排序的起点开始
             for (int i = left+1; i < array.length; i++) {
                 if(array[maxIndex] < array[i]){
                     maxIndex = i;
                 }
                 if(array[minIndex] > array[i]){
                     minIndex = i;
                 }
             }
             swap(array,left,minIndex);
             swap(array,right,maxIndex);
             left++;
             right--;
         }
    }

    public static void swap(int[]array ,int minIndex,int j){
        int tmp = array[j];
        array[j] = array[minIndex];
        array[minIndex] = tmp;
    }
}

总结:
1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:不稳定

4. 堆排序

 降序建大堆,升序建小堆

详情思路请看之前写的堆部分

https://mp.csdn.net/mp_blog/creation/editor/139502440

 /**
     * 堆排序
     * 时间复杂度:O(n*logN)
     * 空间复杂度:O(1)
     * 稳定性:不稳定
     * @param array
     */
    public static void heapSort(int[] array) {
        createHeap(array);
        int end = array.length-1;
        while (end > 0) {
            swap(array,0,end);
            siftDown(array,0,end);
            end--;
        }
    }

    private static void createHeap(int[] array) {
        for (int parent = (array.length-1-1)/2; parent >= 0; parent--) {
            siftDown(array,parent,array.length);
        }

    }

    /**
     *
     * @param array
     * @param parent 每棵子树调整的根节点
     * @param length 每棵子树调整的结束节点
     */
    private static void siftDown(int[] array, int parent, int length) {
        int child = 2 * parent + 1;
        while (child < length) {
            if(child + 1 < length && array[child] < array[child+1]) {
                child++;
            }
            if(array[child] > array[parent]) {
                swap(array, parent, child);
                parent = child;
                child = 2*parent+1;
            }else {
                break;
            }
        }
    }

6.快速排序

思想:任取待排序元素序列中的某元
素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有
元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

翻译过来实现过程就是取第一个数,分别在头和尾定义一个指针 ,头指针找比第一个数大的(需求就是把小的放在左边所以需要找大的和右边大的交换),尾指针找比第一个小的,两个都找到后,进行交换,确保左边的都是小于或等于第一个数的,右边都是大于或等于第一个数的,直到两个指针位置相等,然后再交换第一个与它们的位置,通过递归将它们分为一个个更小的然后即可完成

快速排序Hoare法实现过程

问题:为什么先从后面开始

如果先从前面开始则会造成最后汇合点大于key.

递归法

通过left和right的不断变化,直到left与right相遇为止,当不断分为很多的left和right后,

public class Sort {
    public static void swap(int[]array ,int minIndex,int j){
        int tmp = array[j];
        array[j] = array[minIndex];
        array[minIndex] = tmp;
    }
    public static void qsort(int[] array){
        int left = 0,right = array.length-1;
        parttrtions(array,left,right);
    }
    private static void parttrtions(int[]array,int left,int right){
        if(left>=right){
            return;
        }
        int start = parttrtion(array,left,right);
        parttrtions(array,left,start-1);
        parttrtions(array,start+1,right);
    }
    private static int parttrtion(int[] array,int left,int right){//Hoare版
        int privt = array[left];
        int end = right,start = left;
        while(start<end){
            while(start<end&& array[end] >= privt) {//从右往左直到找到比array[start](privt)小的放在
                end--;
            }

            while(start<end&&array[start] <= privt){//跳出小循环说明找到了比privt大的数
                start++;
            }
            swap(array,left,start);
        }
        return start;
    }
    private static int parttrtion1(int[] array,int left,int right){//挖坑法
        int privt = array[left];
        int end = right,start = left;
        while(start<end){
            while(start<end&& array[end] >= privt) {//从右往左直到找到比array[start](privt)小的放在
                end--;
            }
            array[start] = array[end];//将end下标的值来补充start的空缺
            while(start<end&&array[start] <= privt){//跳出小循环说明找到了比privt大的数
                start++;
            }
            array[end] = privt;//将start下标的值来补充end的空缺
        }
        return start;
    }
}

 快排的优化:

1.三数取中法:如果是单一的数则可能造成,单支树的产生,最高造成N(O*2),所以可以提前将中间的值给到start,这样能极大减少计算量

private static int getMiddleNum(int[] array,int left,int right) {
        int mid = (left+right)/2;
        if(array[left] < array[right]) {
            if(array[mid] < array[left]) {
                return left;
            }else if(array[mid] > array[right]) {
                return right;
            }else {
                return mid;
            }
        }else {
            if(array[mid] > array[left]) {
                return left;
            }else if(array[mid] < array[right]) {
                return right;
            }else {
                return mid;
            }
        }
    }

非递归法

 利用栈后进先出不断变化left和right的值

public static void qsort1(int[] array){
        int left = 0,right = array.length-1;
        int par = parttrtion(array,left,right);
        Stack<Integer> stack = new Stack<>();
        if(par>left+1){
            stack.push(left);
            stack.push(par-1);
        }
        if(par < right-1){
            stack.push(par+1);
            stack.push(right);
        }
        while(!stack.empty()){
            right = stack.pop();
            left = stack.pop();
            par = parttrtion(array,left,right);
            if(par>left+1){
                stack.push(left);
                stack.push(par-1);
            }
            if(par < right-1){
                stack.push(par+1);
                stack.push(right);
            }
        }

    }

总结: 

 1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(logN)
4. 稳定性:不稳定

7.归并排序

思想:先将数组分成很多小份,然后再进行排序,然后把排序完的数组重新整和,最终得到一个有序数组。

递归法

1.首先将大数组分为小数组,把大问题拆分为小问题,1.找到最底层的两个数组,2.排序

 public void mergeSortTmp(int[]a,int left,int right){//将数组先分开再合并
        if(left>=right){
            return;
        }
        int mid = (left+right)/2;
        mergeSortTmp(a,left,mid);//分成很多份,找到最左边
        mergeSortTmp(a,mid+1,right);//上层递归完成,找到对应要排序的另一个数组
        merge(a,mid,left,right);//两个都找到后,进行排序操作
    }

2.然后利用两个数组组合排序的方法,定义两个指针,取到小的添加到新的数组

private void merge(int[]a,int mid,int left,int right){
        int [] array = new int[right-left+1];
        int set = 0;
        int s1 = left;
        int e1 = mid;
        int s2 = mid+1;
        int e2 = right;
        while(s1<e1&&s2<e2){
            if(a[s1] < a[s2]){
                array[set++] = a[s1++];
            }
            else {
                array[set++] = a[s2++];
            }
        }
        while (s1 <= mid) {
            array[set++] = array[s1++];
        }
        while (s2 <= right) {
            array[set++] = array[s2++];
        }
        for (int i = 0; i < array.length; i++) {
            a[i+left] = array[i];//分组后每一小组的下标并不是零
        }
    }

非递归法

思路:我们这个排序的核心就是1.一步步得到最小数组 2.一步步将两个小数组合并起来直到得到 大数组

所以可以在循环里嵌套循环,外面决定数组长度,里面循环将小数组排序合并,外循环设置每个小数组的相隔距离

 public void mergrSort1(int[] array){
        int left = 0,right = array.length-1;
        int gap = 1;//gap负责将数组分为array.length/2
        while(gap<array.length){
            for (int i = 0; i < array.length; i+=2*gap) {//得到小一号的数组并且进行排序合并,里面for循环是为了得到最多组数组
                left = i;
                int mid = left+gap-1;
                right = mid+gap;
                if(mid >= array.length) mid=array.length-1;
                if(right>=array.length) right = array.length-1;
                merge(array,mid,left,right);
            }
                gap *= 2;//世界线收束,每次小数组排序好后,再将更大数组排序
            }
        }
    }

总结:1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(N)
4. 稳定性:稳定

8.计数排序

思路:将原数组遍历一遍,得到原数组的最大值和最小值,将最大值和最小值相减,得到它们的取值范围,创建一个新数组,然后将对应的值给到,和其相对相等的下标,(比如数组的值为1给到开辟数组的1下标,)然后遍历新数组,赋值给原数组

/**
     * 计数排序:
     * 时间复杂度:O(范围 + n )
     *       范围越大  越慢
     * 空间复杂度:O(范围)
     * 稳定性:
     * @param array
     */
    public static void countSort(int[] array) {
        //1. 找最大值 和 最小值 来确定 计数数组的大小
        int maxVal = array[0];
        int minVal = 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 len = maxVal - minVal + 1;
        int[] count = new int[len];

        //2. 遍历原来的数组array把 每个元素 放到对应的计数数组当中 进行计数
        for (int i = 0; i < array.length; i++) {
            int index = array[i];
            count[index-minVal]++;
        }
        //3.依次 遍历计数数组 O(范围)
        int index = 0;
        for (int i = 0; i < count.length; i++) {
            while (count[i] != 0) {
                array[index] = i+minVal;
                index++;
                count[i]--;
            }
        }
    }

总结 

1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
2. 时间复杂度:O(MAX(N,范围))
3. 空间复杂度:O(范围)
4. 稳定性:稳定

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

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

相关文章

LOID:有效提升遮挡条件下的车道检测精度

1.论文信息 论文标题&#xff1a;LOID: Lane Occlusion Inpainting and Detection for Enhanced Autonomous Driving Systems 作者&#xff1a;Aayush Agrawal, Ashmitha Jaysi Sivakumar, Ibrahim Kaif∗, Chayan Banerjee† 作者单位&#xff1a;印度马德拉斯印度理工学院&…

Web安全 - 路径穿越(Path Traversal)

文章目录 OWASP 2023 TOP 10导图定义路径穿越的原理常见攻击目标防御措施输入验证和清理避免直接拼接用户输入最小化权限日志监控 ExampleCode漏洞代码&#xff1a;路径穿越攻击案例漏洞说明修复后的安全代码代码分析 其他不同文件系统下的路径穿越特性Windows系统类Unix系统&a…

【C++】基于红黑树封装set和map

&#x1f680;个人主页&#xff1a;小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言一、更高维度的泛型二、模版参数三、比较逻辑的重写四、迭代器4.1 const迭代器4.2 重载4.3 - -重载 五、完整代…

为什么很多人宁愿加钱买港版,也不愿买国行 iPhone 16

最近的 iPhone 16 市场&#xff0c;真的是倒反天罡&#xff0c;攻守异形啊。 过去&#xff0c;港版 iPhone 都是性价比的次选&#xff0c;便宜个 10% 都得考虑考虑。但今年&#xff0c;港版 iPhone 16 的价格&#xff0c;反而比国行还贵。 比如&#xff0c;闲鱼上某个卖家&am…

[红队apt]文件捆绑攻击流程

免责声明:本文用于了解攻击者攻击手法&#xff0c;切勿用于不法用途 前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文整理黑客通过文件捆绑进行攻击的流程思路 文件捆绑原理 废话只多说这一句。 1.exe和2.exe被你捆绑为3.exe。 那么你点击了3.exe就等于点…

kafka消息队列核心内容及常见问题

目录 1. 使用消息队列的目的&#xff08;优点与缺点&#xff09; 2. 常见各消息队列对比 3. kafka介绍 3.1 kafka简介 3.2 kafka特点 3.3 kafka系统架构 3.4 设置数据可靠性 3.4.1 Topic 分区副本 3.4.2 消息确认机制 4. 常见问题&#xff08;面试题&#xff09; 4.…

Acwing 排序

1.快速排序 主要思想&#xff1a;基于分治思想。通过选择一个基准元素&#xff0c;将数组分为两部分&#xff0c;左边部分元素都小于等于基准&#xff0c;右边部分元素都大于等于基准。然后对这两部分分别递归地进行排序。 分区逻辑&#xff1a;双指针算法 左指针i从左往右找…

《RabbitMQ篇》消息应答和发布确认

消息应答 消息应答机制&#xff1a;消费者接收信息并处理完之后&#xff0c;告诉rabbitmq该信息已经处理&#xff0c;rabbitmq可以把该信息删除了. 消息自动重新入队&#xff1a;如果处理某个消息的消费者异常关闭了&#xff0c;没有发送ACK确认&#xff0c;rabbitmq会将其重…

金九银十软件测试面试题(800道)

今年你的目标是拿下大厂offer&#xff1f;还是多少万年薪&#xff1f;其实这些都离不开日积月累的过程。 为此我特意整理出一份&#xff08;超详细笔记/面试题&#xff09;它几乎涵盖了所有的测试开发技术栈&#xff0c;非常珍贵&#xff0c;人手一份 肝完进大厂 妥妥的&#…

postgresql 安装

一、下载 PostgreSQL: File Browser 下载地址 PostgreSQL: File Browser 上传到服务器,并解压 二、安装依赖 yum install -y perl-ExtUtils-Embed readline-devel zlib-devel pam-devel libxml2-devel libxslt-devel openldap-devel 创建postgresql 和目录 useradd …

Java-基础

1. 导入模块不能纯粹的复制粘贴&#xff0c;要从new里导入&#xff0c;因为前者建立不了关联 2. 数组 String[] name{"张三","李四","王五"};int[] numsnew int[]{1,2,3};//二维String[][] names{{"张三","李四"},{"…

39 C 语言枚举类型、枚举常量、枚举变量、枚举的遍历、枚举数组、枚举与 switch

目录 1 什么是枚举 2 定义枚举类型 2.1 语法格式 2.2 枚举元素的特点 2.3 案例演示 3 枚举变量 3.1 什么是枚举变量 3.2 定义枚举变量的多种方式 3.3 案例演示 1&#xff1a;标准版枚举类型 3.4 案例演示 2&#xff1a;简化版枚举类型 3.5 案例演示 3&#xff1a;匿…

uniapp学习(005-2 详解Part.2)

零基础入门uniapp Vue3组合式API版本到咸虾米壁纸项目实战&#xff0c;开发打包微信小程序、抖音小程序、H5、安卓APP客户端等 总时长 23:40:00 共116P 此文章包含第41p-第p51的内容 文章目录 mainifest.json文件配置获取微信小程序appid注册微信小程序微信小程序控制台图形界…

22. 括号生成【回溯】

文章目录 22. 括号生成解题思路Go代码 22. 括号生成 22. 括号生成 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;["((()))",&quo…

node.js服务器基础

node.js的事件循环 node.js是基于事件驱动的&#xff0c;通常在代码中注册想要等待的事件&#xff0c;设定好回调函数&#xff0c;当事件触发的时候就会调用回调函数。如果node.js没有要处理的事件了&#xff0c;那整个就结束了;事件里面可以继续插入事件&#xff0c;如果有事…

手撕数据结构 —— 带头双向循环链表(C语言讲解)

目录 0.前言 1.什么是带头双向循环链表 理解带头 ​编辑 理解双向 理解循环 2.带头双向循环链表的实现 List.h文件中接口总览 具体实现 结点的定义 申请结点 初始化 打印链表 尾插 尾删 头插 头删 ​编辑​编辑 获取大小 查找 在指定位置前插入 ​编辑…

数据结构--线性表双向链表的实现

目录 思路设计 总体思维导图 插入部分 头插法尾插法 任意位置插入 删除部分 头结点 尾节点 中间节点 只有头结点且删除的就是头结点 ​编辑 清空链表部分 遍历清空链表的所有节点 不遍历清空 各部分代码 Main部分 MyListedList部分 IndexOutOfException部分 …

YOLO11改进 | 注意力机制 | 结合静态和动态上下文信息的注意力机制

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 上下文Transformer&#xff08;CoT&…

WPF中的布局

布局原则 1、不显式设置元素大小。 2、不使用绝对定位。 元素应该根据容器的内容来进行排列。绝对定位在开发前期会带来一些便捷&#xff0c;但扩展性比较差。一旦显示器尺寸或分辨率发生改变&#xff0c;界面的显示效果可能会达不到预期的效果。 3、布局容器可以嵌套使用 常…

【Axure原型分享】标签管理列表

今天和大家分享通过标签管理列表的原型模板&#xff0c;包括增删改查搜索筛选排序分页翻页等效果&#xff0c;这个模板是用中继器制作的&#xff0c;所以使用也很方便&#xff0c;初始数据我们只要在中继器表格里填写即可&#xff0c;具体效果可以观看下方视频或者打开预览地址…