排序算法(Java版)

news2024/12/28 19:14:58

目录

  • 1、直接插入排序
  • 2、希尔排序
  • 3、直接选择排序
  • 4、堆排序
  • 5、冒泡排序
  • 6、快速排序
    • 6.1 递归实现
    • 6.2 非递归实现
  • 7、归并排序
    • 7.1 递归实现
    • 7.2 非递归实现
  • 8、性能分析

今天我们学习一种算法:排序算法(本文的排序默认是从小到大顺序)!!!

1、直接插入排序

算法原理: 每次将无序序列中的第一个插入到有序序列当中,使有序序列仍为有序,第一趟排序默认第一个元素是有序的,类比于生活中的摸牌,每次将新的排插入已有的牌当中。直接插入排序的算法原理很简单,我们只需要找到每个元素该插入到哪个位置即可。
在这里插入图片描述

代码实现:

    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 (array[j] > tmp) {
                    array[j + 1] = array[j];
                } else {
                    array[j + 1] = tmp;
                    break;
                }
            }
            array[j + 1] = tmp;
        }
    }

代码图解:
在这里插入图片描述

2、希尔排序

算法原理: 希尔排序又称缩小增量排序,原理是先选定一个数作为分组的组数,将数组进行分组,接着分别对每个组进行排序,每组排序好之后,缩小分组的组数,重复上述步骤,直到组数为1。对每个组进行排序,我们使用插入排序的方法进行排序。
在这里插入图片描述

代码实现:

    public void ShellSort(int[] array) {
        int gap = array.length;
        //分成gap组,对每一组进行插入排序
        while (gap > 1) {
            gap /= 2;
            shell(array, gap);
        }
    }
	//对每组进行插入排序
    public 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 (array[j] > tmp) {
                    array[j + gap] = array[j];
                } else {
                    array[j + gap] = tmp;
                    break;
                }
            }
            array[j + gap] = tmp;
        }
    }

3、直接选择排序

算法原理: 每次待排序序列中选择最小的元素和待排序的第一个元素交换
代码实现:

    public 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;
                }
            }
            //交换minIndex下标和i下标的值
            int tmp = array[minIndex];
        	array[minIndex] = array[i];
        	array[i] = tmp;
        }
    }

4、堆排序

算法原理: 堆排序是借用堆这种数据结构来实现的一种排序算法,如果升排序,建立大根堆;如果排降序,建立小根堆 。建堆之后:
1、交换0下标元素和最后一个元素的值
2、然后重新将数组进行向下调整为大根堆
重复这两个步骤,直到全部有序
在这里插入图片描述

代码实现:

    public void HeapSort(int[] array) {
        //先创建大堆
        createBigHeap(array);
        int end = array.length - 1;
        while (end >= 0) {
        	//交换
            int tmp = array[0];
        	array[0] = array[end];
        	array[end] = tmp;
        	
            ShiftDown(array, 0, end);
            end--;
        }
    }
    
    public void createBigHeap(int[] array) {
        for (int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--) {
            ShiftDown(array, parent, array.length);
        }
    }
    
    public void ShiftDown(int[] array, int parent, int end) {
        int child = parent * 2 + 1;
        while (child < end) {
			if (child + 1 < end && array[child] < array[child + 1]) {
            	child++;
        	}
            if (array[child] > array[parent]) {
            	//交换
                int tmp = array[parent];
        		array[parent] = array[child];
	        	array[child] = tmp;
	        	
                parent = child;
                child = parent * 2 + 1;
            } else {
                break;
            }
        }
    }    

5、冒泡排序

算法原理: 遍历数组,每次比较相邻两个元素的大小,如果大的数字在前则交换两个元素的位置,这样就完成了一趟冒泡排序,此时最大的数到了最后,然后对前n-1个数进行相同的操作,直到有序。
代码实现:

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

问题:如果遍历一遍数组已经有序了,就不用再继续比较下去了,因此对上面代码进行优化
优化后:

    public void BubbleSort(int[] array) {
        boolean flg = false;
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = i; j < array.length - i - 1; j++) {
                if (array[j] > array[j + 1]) {
                	//交换
                    int tmp = array[j];
	        		array[j] = array[j+1];
		        	array[j+1] = tmp;
                    flg = true;
                }
            }
            if (!flg) {
                break;
            }
        }
    }

6、快速排序

算法原理: 快速排序的基本思想就是:选定一个基准,通过一趟快速排序之后,能把数据分割为两部分,左边部分比基准的值小,右边的部分比基准的值大,接着再按照这个方法分别对基准左边部分和右边部分进行递归,重复这个步骤直到整个序列都有序。快速排序的最重要部分就是如何将序列分割成两部分,常见的分割方法有hoare法和挖坑法
Hoare法分割: 先选定一个基准(默认是第一个元素),定义left、right下标,left从序列最右边开始找比基准小的值(升序排序),找到之后停下来,接着让left从最左边开始找比基准大的值,找到之后停下来,将找到的这两个值交换,当left和right相遇时(left=right),交换基准的值和left/right下标的值,这样left/right下标左边的元素全都比left/right下标的值小,右边的元素都比它大,这样就分割好了。
图解:
在这里插入图片描述

挖坑法:
和Hoare法的区别是:挖坑法是边找边交换,如图
在这里插入图片描述

6.1 递归实现

代码实现:

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

    public void quick(int[] arr, int left, int right) {
    	//递归结束的条件
        if (left >= right) {
            return;
        }
        //进行分割
        int pio = partition(arr, left, right);
        quick(arr, 0, pio - 1);
        quick(arr, pio + 1, right);
    }

hoare法分割

    public int partition(int[] arr, int left, int right) {

        int tmp = arr[left];
        int i = left;
        while (left < right) {
            while (left < right && arr[right] >= tmp) {
                right--;
            }
            while (left < right && arr[left] <= tmp) {
                left++;
            }
            //交换
			int tmp = array[right];
	        array[right] = array[left];
		    array[left] = tmp;
        }
        //交换
		int tmp = array[i];
	    array[i] = array[left];
		array[left] = tmp;
        return left;
    }

挖坑法分割

    public int partition(int[] arr, int left, int right) {
        int tmp = arr[left];
        while (left < right) {
            while (left < right && arr[right] >= tmp) {
                right--;
            }
            arr[left] = arr[right];
            while (left < right && arr[left] <= tmp) {
                left++;
            }
	        array[right] = array[left];
        }
        arr[left] = tmp;
        return left;
    }

优化: 如果待排序序列是:1、2、3、4、5这种有序的序列,假如还是取第一个元素为基准,就会出现左边没有小于基准的值,如何让每次分割都是均匀分割?方法很简单,取序列最左边、最右边和中间位置的三个元素的中位数作为基准,再进行Hoare法或者挖坑法分割,此时每次都能均匀分割,如图
在这里插入图片描述

优化后:

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

    public void quick(int[] arr, int left, int right) {
        //递归
        if (left >= right) {
            return;
        }
        //中位数的值作为基准
        int midIndex = midThreeIndex(arr, left, right);
        //交换
        int tmp = arr[left];
        arr[left] = arr[midIndex];
        arr[midIndex] = tmp;    
        int pio = partition(arr, left, right);
        quick(arr, 0, pio - 1);
        quick(arr, pio + 1, right);
    }
    public int partition(int[] arr, int left, int right) {
        int tmp = arr[left];
        int i = left;
        while (left < right) {
            while (left < right && arr[right] >= tmp) {
                right--;
            }
            while (left < right && arr[left] <= tmp) {
                left++;
            }
            swap(arr, right, left);
        }
        swap(arr, i, left);
        return left;
    }        

6.2 非递归实现

原理: 利用栈这个数据结构来实现。首先先对序列进行一次分割(Hoare法或者挖坑法都可以),将基准左边部分的left、right下标入栈,再将右边部分的left、right下标入栈,然后出栈两个元素作为新的left、right来进行分割,重复上述步骤,直到栈为空
在这里插入图片描述

代码实现:

    public void QuickSortNoRecursion(int[] arr) {
        Stack<Integer> stack = new Stack<>();
        int left = 0;
        int right = arr.length - 1;
        int pio = partition(arr, left, right);
        if (pio > left + 1) {
            stack.push(left);
            stack.push(pio - 1);
        }
        if (pio < right - 1) {
            stack.push(pio + 1);
            stack.push(right);
        }
        while (!stack.isEmpty()) {
            right = stack.pop();
            left = stack.pop();
            pio = partition(arr, left, right);
            if (pio > left + 1) {
                stack.push(left);
                stack.push(pio - 1);
            }
            if (pio < right - 1) {
                stack.push(pio + 1);
                stack.push(right);
            }
        }
    }

7、归并排序

原理: 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;归并排序的思想是:先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
在这里插入图片描述
在这里插入图片描述

7.1 递归实现

递归思路: 先将序列进行分解,直到分解为单个元素为一组,然后再进行合并。合并:开辟新的数组,新的数组存储的是合并之后且有序的子序列,再开辟的新数组的元素拷贝回原数组

    public void mergeSort(int[] arr) {
        merge(arr, 0, arr.length - 1);
    }

    public void merge(int[] arr, int left, int right) {
        if (left >= right) {
            return;
        }
        int mid = (left + right) / 2;
        //分解
        merge(arr, left, mid);
        merge(arr, mid + 1, right);
        //合并
        mergeFun(arr, left, mid, right);
    }

    //合并
    public void mergeFun(int[] arr, int left,
                          int mid, int right) {
        int s1 = left;
        int e1 = mid;
        int s2 = mid + 1;
        int e2 = right;
        int k = 0;
        int[] tmp = new int[right - left + 1];//开辟新的数组
        while (s1 <= e1 && s2 <= e2) {
            if (arr[s1] < arr[s2]) {
                tmp[k++] = arr[s1++];
            } else {
                tmp[k++] = arr[s2++];
            }
        }

        while (s1 <= e1) {
            tmp[k++] = arr[s1++];
        }
        while (s2 <= e2) {
            tmp[k++] = arr[s2++];
        }
        //此时tmp有序了,拷回到原数组

        for (int i = 0; i < k; i++) {
            arr[left + i] = tmp[i];
        }
    }

7.2 非递归实现

非递归省去了分解的步骤,直接对数组进行合并

    //非递归
    public void mergeSortN(int[] arr) {
        mergeN(arr);
    }

    //没有分解的过程
    private void mergeN(int[] arr) {
        int gap = 1;
        while (gap <= arr.length) {
            for (int i = 0; i < arr.length; i = i + 2 * gap) {
                int mid = i + gap - 1;
                if (mid >= arr.length) {
                    mid = arr.length - 1;
                }
                int right = mid + gap;
                if (right >= arr.length) {
                    right = arr.length - 1;
                }
                mergeFun(arr, i, mid, right);
            }
            gap *= 2;
        }
    }
    public void mergeFun(int[] arr, int left,
                          int mid, int right) {
        int s1 = left;
        int e1 = mid;
        int s2 = mid + 1;
        int e2 = right;
        int k = 0;
        int[] tmp = new int[right - left + 1];
        while (s1 <= e1 && s2 <= e2) {
            if (arr[s1] < arr[s2]) {
                tmp[k++] = arr[s1++];
            } else {
                tmp[k++] = arr[s2++];
            }
        }

        while (s1 <= e1) {
            tmp[k++] = arr[s1++];
        }
        while (s2 <= e2) {
            tmp[k++] = arr[s2++];
        }
        //此时tmp有序了,拷回到原数组

        for (int i = 0; i < k; i++) {
            arr[left + i] = tmp[i];
        }
    }

8、性能分析

性能包括:时间复杂度、空间复杂度、稳定性

排序算法平均时间复杂度空间复杂度稳定性
插入排序O(n^2)O(1)稳定
希尔排序O(和增量有关)O(1)不稳定
选择排序O(n^2)O(1)不稳定
堆排序O(n*logn)O(1)不稳定
冒泡排序O(n^2)O(1)稳定
快速排序O(n*logn)O(logn)不稳定
归并排序O(n*logn)O(n)稳定

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

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

相关文章

【mysql篇】执行delete删除大量数据后,磁盘未清空,为什么?

目录 迁移脚本删除数据以及备份数据 解决方法OPTIMIZE TABLE二进制日志按月生成数据 最近某个项目虽说用户量不大&#xff0c;但是&#xff0c;单表的数据量越来越大&#xff0c;mysql一般单表超过千万级别后&#xff0c;性能直线下降&#xff0c;所以利用shardingphere按月做了…

ISO14229 -1 UDS诊断服务记录-001:0x34\0x36\0x37\0x31\0x19\0x14服务报文格式介绍

目录 1、34服务-请求下载 1.1、诊断请求格式 1.2、正响应格式 1.3、负响应格式 1.4、工程应用分析 2、36服务-传输数据 2.1、请求报文格式 2.2、正响应格式 2.3、负响应NRC 3、37服务-退出传输 3.1、报文格式 3.2、正响应格式 3.3、负响应NRC 4、31服务-例程控制 …

从零开始的软件测试学习之旅(八)jmeter线程组参数化及函数学习

jmeter线程组参数化及函数学习 Jmeter基础基本使用流程组件与元件 线程组线程的执行方式Jmeter组件执行顺序 常见属性设置查看结果数的作用域举例 Jmeter参数化实现方式1.用户定义参数2.用户参数3.函数4.csv数据文件设置 每日复习 Jmeter基础 基本使用流程 启动项目案例 启动…

Ubuntu22.04下安装kafka_2.11-0.10.1.0并运行简单实例

目录 一、版本信息 二、安装Kafka 1.将Kafka安装包移到下载目录中 2.下载Spark并确保hadoop用户对Spark目录有操作权限 三、启动Kafka并测试Kafka是否正常工作 1.启动Kafka 2.测试Kafka是否正常工作 一、版本信息 虚拟机产品&#xff1a;VMware Workstation 17 Pro 虚…

电脑那些可以升级的基本配置

一. 中央处理器&#xff08;CPU&#xff09;&#xff1a;&#xff08;若不是焊点的可以升级&#xff09; 1、一句话简介&#xff1a; 这是电脑的心脏&#xff0c;决定了电脑的处理能力。常见的品牌有Intel和AMD。 2、换CPU指南&#xff1a; 1) 处理器品牌&#xff1a; - 主要…

RT-IoT2022 数据集-扩展数据(自制方法)

数据集官网Discover datasets around the world!https://archive.ics.uci.edu/dataset/942/rt-iot2022RT-IoT2022 是源自实时物联网基础设施的专有数据集&#xff0c;作为集成了各种物联网设备和复杂网络攻击方法的综合资源而引入。该数据集包含正常和对抗性网络行为&#xff0…

使用Docker安装Yapi接口管理工具

简介&#xff1a; YAPI 是由去哪儿网移动架构组开发的一款可视化接口管理工具。它具有可视化管理、高效易用、功能强大等特点。它提供了便捷的接口创建、发布和维护方式&#xff0c;开发人员可以通过简单的操作实现接口管理。 YAPI 还支持类似 postman 的接口调试&#xff0c;对…

CSS---Emmet(二)

一、Emmet语法 Emmet语法是一种用于快速编写HTML和CSS的缩写技术。它允许开发者通过简洁的表达式快速生成复杂的代码结构&#xff0c;极大地提高了编码效率。使用Emmet&#xff0c;你只需要写出一些简短的缩写符号和操作符&#xff0c;然后通过快捷键&#xff08;通常是Tab键&…

Android 10.0 Launcher3定制folder文件夹2x2布局之一xml文件配置和解析相关属性

1.前言 在10.0的系统rom产品定制化开发中,在对Launcher3的folder文件夹功能定制中,要求folder文件夹跨行显示,就是 2x2布局显示,默认的都是占1格的,现在要求占4格显示,系统默认是不支持显示4格的,所以接下来需要分析相关的 功能,然后来实现这个功能 2.Launcher3定制fo…

《构建高效审批系统:架构设计与实践》

在现代企业管理中&#xff0c;审批系统扮演着至关重要的角色&#xff0c;它不仅能够规范业务流程&#xff0c;提高工作效率&#xff0c;还能够增强企业的管理控制力和信息化水平。本文将探讨如何设计和构建一套高效的审批系统架构&#xff0c;以满足企业日常审批需求&#xff0…

【Python】在Windows Server上部署Flask后端服务器

想要在Windows Server上部署flask应用&#xff0c;当然不能只下一个anaconda配完环境之后直接启动py文件&#xff0c;这样的话后台会有一段警告&#xff1a; * Serving Flask app app* Debug mode: off WARNING: This is a development server. Do not use it in a production …

UE4_Water插件_Buoyancy组件使用

water插件提供了一个浮力Actor蓝图类。 需要注意的几个问题&#xff1a; 1、StaticMesh需要替换根组件。 2、需要模拟物理设置质量。 3、需要添加浮力组件&#xff0c;设置浮力点&#xff0c;应用水中牵引力。 4、最重要的是需要激活——自动启用。 5、调水波长的地方 双击图片…

Linux 基础命令、性能监控

一、Linux 基础命令 grep&#xff1a;在文件中执行关键词搜索&#xff0c;并显示匹配的结果。 -c 仅显示找到的行数 -i 忽略大小写 -n 显示行号 -v 反向选择: 仅列出没有关键词的行 (invert) -r 递归搜索文件目录 -C n 打印匹配行的前后 n 行grep login user.cpp # 在…

Meta更低的训练成本取得更好的性能: 多token预测(Multi-Token Prediction)

Meta提出了一种透过多token预测(Multi-token Prediction)来训练更好、更快的大型语言模型的方法。这篇论文的重点如下: 训练语言模型同时预测多个未来的token,可以提高样本效率(sample efficiency)。 在推论阶段,使用多token预测可以达到最高3倍的加速。 论文的主要贡献包括: …

并发问题系统学习(更新中)

进程、线程 进程&#xff1a;进程是代码在数据集合上的一次运行活动&#xff0c;是系统进行资源分配和调度的基本单位。可以理解为一个java应用。 线程&#xff1a;线程是进程的一个执行路径&#xff0c;一个进程中至少有一个线程&#xff0c;进程中的多个线程共享进程的资源。…

CleanMyMac X 4.15.3 版本发布

CleanMyMac X 4.15.3 版本发布&#xff0c;一款苹果 macOS 系统好用的伴侣软件&#xff0c;其包含 1.一键深度清理。2.系统垃圾专清。3.大/旧文件专清。4.系统提速。5.性能悬浮窗。6.恶意软件防护。7.隐私保护。8.软件卸载器。9.软件更新器等 9 大功能&#xff0c;为您的苹果电…

VSCode-vue3.0-安装与配置-export default简单例子

文章目录 1.下载VSCode2.修改语言为中文3.辅助插件列表4.vue3模板文件简单例子5.总结 1.下载VSCode 从官网下载VSCode&#xff0c;并按下一步安装成功。 2.修改语言为中文 点击确认修改&#xff0c;如下图所示&#xff1a; 或者打开命令面板&#xff1a;输入Configure Displ…

如何快速学习VCU电控开发

本课程基于实际项目案例和岗位需求技能制定教学大纲&#xff0c;以任务驱动方式引导学员&#xff0c;让学员快速掌握VCU开发知识。首先从VCU开发必备知识点和MATLAB/Simulink软件建模工具的使用入手&#xff0c;夯实学员基础。再通过策略设计、模型搭建和测试标定来指导学员完成…

关闭vscode保存自动格式化的功能

1 首先打开设置 搜索&#xff1a;editor.formatOnSave 取消勾选框 2 再打开 settings.json 搜索 editor 找到 settings.json 设置&#xff1a; "editor.formatOnSave": false

基于opencv的车辆统计

车辆统计&#xff09; 一、项目背景二、整体流程三、常用滤波器的特点四、背景减除五、形态学开运算闭运算 六、项目完整代码七、参考资料 一、项目背景 检测并识别视频中来往车辆的数量 最终效果图&#xff1a; 二、整体流程 加载视频图像预处理&#xff08;去噪、背景减除…