基本排序算法

目录

一,插入排序

二,希尔排序

三,选择排序

四,冒泡排序

五,快排

5.1 Hoare法

5.2 挖坑法

5.3 指针法

 5.4 非递归写法

六,归并排序

6.1 递归 

6.2 非递归


一,插入排序

基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。

    /**
     * 直接插入排序
     * 时间复杂度:O(n^2)
     * 空间复杂度:O(1)
     * 稳定性:稳定
     */
    public void insertSort(int[] arr){
        for (int i = 1; i < arr.length; i++) {
            int tmp = arr[i];//要插入的元素
            int j = i-1;
            for (; j >= 0; j--) {
                if(arr[j] > tmp){//判断是否插入
                    arr[j+1] = arr[j];
                }else{
                    break;
                }
            }
            arr[j+1] = tmp;
        }
    }

二,希尔排序

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

    /**
     * 希尔排序 - 直接排序的优化版
     * 时间复杂度:O(n^1.3)~O(1^1.5)
     * 空间复杂度:O(1)
     * 稳点性:不稳定
     */
    public void shellSort(int[] arr){
        int gap = arr.length;
        while(gap > 1){
            gap /= 2;
            for (int i = gap; i < arr.length; i++) {
                int tmp = arr[i];
                int j = i - gap;
                for (; j >= 0; j -= gap) {
                    if(arr[j] > tmp){
                        arr[j+gap] = arr[j];
                    }else{
                        break;
                    }
                }
                arr[j+gap] = tmp;
            }
        }
    }

三,选择排序

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

    /**
     * 选择排序
     * 时间复杂度:O(n^2)
     * 空间复杂度:O(1)
     * 稳定性:不稳定
     */
    public void selectSort(int[] arr){
        for (int i = 0; i < arr.length; i++) {
            int minIndex = i;
            for (int j = i+1; j < arr.length; j++) {
                if(arr[minIndex] > arr[j]){
                    minIndex = j;
                }
            }
            swap(arr,minIndex,i);
        }
    }
    public void swap(int[] arr, int i, int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

四,冒泡排序

基本思想:根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

    /**
     * 冒泡排序
     * 时间复杂度:O(n^2)
     * 空间复杂度:O(1)
     * 稳定性:稳定
     */
    public void bubbleSort(int[] arr){
        for (int i = 0; i < arr.length-1; i++) {
            boolean flg = true;
            for (int j = 0; j < arr.length-1-i; j++) {
                if(arr[j] > arr[j+1]){
                    int tmp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = tmp;
                    flg = false;
                }
            }
            if(flg){
                break;
            }
        }
    }

五,快排

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

5.1 Hoare法

 从整体看,这就像一颗二叉树,所以我们可以用类似二叉树遍历的递归来实现,代码如下:

    /**
     * 快排
     * 时间复杂度:O(nlogN)
     * 空间复杂度:O(logN)
     * 稳定性:不稳定
     */    
    public void quickSort(int[] arr, int left, int right){
        if(left > right) return;//递归终止条件
        int div = partition(arr, left, right);//得到基准值下标
        quickSort(arr,left,div-1);//递归基准值前面的值
        quickSort(arr,div+1,right);//递归基准值后面的值
    }
    public int partition(int[] arr, int left, int right){
        int tmp = left;
        int key = arr[left];
        while(left < right){
            //注意这里只能先遍历右边,否则基准值的前面就会存在 >基准值的值,
            //后面就会存在 <基准值的值
            // && = 号必须有,不然如果基准和R相同,就会出现死循环
            while(left < right && arr[right] >= key){
                right--;
            }
            while(left < right && arr[left] <= key){
                left++;
            }
            swap(arr,left,right);
        }
        swap(arr,tmp,left);
        return left;//or right
    }

5.2 挖坑法

与Hoare法类似,只不过它把基准的初始位置当作一个坑,查找右边,将右边的值赋给坑,将右边变成坑,再查找左边,将左边的值赋给坑,将左边变成坑,重复以上操作直到 L== R,将arr[L] = key。再往下面递归,这里就不画图讲解了,直接上代码:

    public int partition(int[] arr, int left, int right){
        int tmp = arr[left];
        while(left < right){
            while(left < right && arr[right] >= tmp){//=必须有 , 必须是right先走
                right--;
            }
            arr[left] = arr[right];
            while(left < right && arr[left] <= tmp){
                left++;
            }
            arr[right] = arr[left];
        }
        arr[left] = tmp;
        return left;
    }

5.3 指针法

它的主要思想没变,还是找到基准值的下标位置,将其分成两份,依次类推,但是它寻找基准的方法很神奇,先看代码:

    public int partition(int[] arr, int left, int right){
        int prev = left;
        int cur = left + 1;
        while(cur <= right){
            if(arr[cur] < arr[left] && (++prev) != cur ){
                swap(arr,prev,cur);
            }
            cur++;
        }
        swap(arr,left,prev);
        return prev;
    }

 5.4 非递归写法

    public void quickSortNor(int[] arr, int left, int right){
        Stack<Integer> ret = new Stack<>();
        ret.push(left);
        ret.push(right);
        while(!ret.empty()){
            right = ret.pop();
            left = ret.pop();
            int div = partition(arr,left,right);//找到基准的下标
            if(left + 1 < div){//基准左边有2+的元素
                ret.push(left);
                ret.push(div-1);
            }
            if(right-1 > div){//基准右边有2+的元素
                ret.push(div+1);
                ret.push(right);
            }
        }
    }

六,归并排序

基本思路:将一组数据分成等长的两份,再将每份分成等长的两份,直到每份数据的长度都为一,然后再逆推回去,每次逆推都要进行一次排序,直到变成一份。如图:

6.1 递归 

可以通过子问题的思路来理解代码:先将前面的一半排序,再将后面的一半排序,最后将整体排序,它们的每一部分都可是这样操作,所以可以使用递归解决。

    /**
     * 归并排序
     * 时间复杂度:O(nlogn)
     * 空间复杂度:O(n)
     * 稳定
     */
    public void mergeSort(int[] arr, int left, int right){
        if(left >= right) return;
        int mid = left + (right - left)/2;
        mergeSort(arr,left,mid);// 前 n/2 排序
        mergeSort(arr,mid+1,right);// 后 n/2 排序
        merge(arr,left,right,mid);// 整体排序
    }
    public void merge(int[] arr, int left, int right, int mid){
        int s1 = left;
        int s2 = mid+1;
        int k = 0;
        int[] tmp = new int[right-left+1];
        while(s1 <= mid && s2 <= right){
            if(arr[s1] <= arr[s2]){
                tmp[k++] = arr[s1++];
            }else{
                tmp[k++] = arr[s2++];
            }
        }
        while(s1 <= mid){
            tmp[k++] = arr[s1++];
        }
        while (s2 <= right){
            tmp[k++] = arr[s2++];
        }
        for (int i = 0; i < tmp.length; i++) {
            arr[i+left] = tmp[i];
        }
    }

6.2 非递归

思路:直接将其分成一个一组,然后再两两组合,直到合成一体,就只有上面那张图的下半部分:

    public void mergeSortNor(int[] arr){
        int gap = 1;
        while(gap < arr.length){
            for (int i = 0; i < arr.length; i+=2*gap) {
                int left = i;//相邻两段子数组的开始和末位下标 [left,mid] [mid+1,right]
                int mid = left + gap -1;
                int right = mid + gap;
                if(mid >= arr.length){//说明只有前面一段数组
                    mid = arr.length - 1;
                }
                if(right >= arr.length){//说明后面的子数组数量少
                    right = arr.length-1;
                }
                merge(arr,left,right,mid);
            }
            gap *= 2;
        }
    }

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

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

相关文章

CorelDraw怎么做立体字效果?CorelDraw制作漂亮的3d立体字教程

1、打开软件CorelDRAW 2019&#xff0c;用文本工具写上我们所需要的大标题。建议字体选用比较粗的适合做标题的字体。 2、给字填充颜色&#xff0c;此时填充的颜色就是以后立体字正面的颜色。我填充了红色&#xff0c;并加上了灰色的描边。 3、选中文本&#xff0c;单击界面左侧…

superset为何无法上传excel,csv等外部文件

superset为何无法上传excel&#xff0c;csv等外部文件 这是由于没有打开数据库的上传外部文件的权限 1.打开数据库连接设置&#xff0c;选择Allow file uploads to database 2.发现这里的上传链接都可以使用

c++ 类

类的引入 c 语言的结构体只能定义变量 但是 c的结构体除了定义变量之外&#xff0c;还可以定义函数。 感受感受&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1//我们声明一个结构体 struct Stack {// c可以把函数写在结构体中//叫成员函数:// 如下&#xff1a;//c的写法&am…

股票回购不积极,遭分析师看空,汽车之家财务前景黯淡

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 第一季度财报后股价表现不佳 汽车之家&#xff08;ATHM&#xff09;于2023年5月11日公布了2023年第一季度业财报绩。 猛兽财经通过查询财报得知&#xff0c;汽车之家第一季度的实际营收为2.21亿美元&#xff0c;正常每股收…

uniapp实现预约时间选择弹窗组件

做了个组件&#xff0c;实现出当日预约时间组件&#xff0c;效果图如下 废话不多说&#xff0c;直接上代码&#xff0c;代码简单&#xff0c;参数自己任意改 <template><view class"inventory"><u-popup :show"show" :round"10"…

开源快速开发平台:做好数据管理,实现流程化办公!

做好数据管理&#xff0c;可以提升企业的办公协作效率&#xff0c;实现数字化转型。开源快速开发平台是深受企业喜爱的低代码开发平台&#xff0c;拥有多项典型功能&#xff0c;是可以打造自主可控快速开发平台&#xff0c;实现一对一框架定制的软件平台。在快节奏的社会中&…

Docker的安装与部署

Docker 基本概念介绍 通俗理解&#xff1a;镜像是类&#xff0c;容器是对象实例 仓库 应用商店、镜像 下载的应用安装程序、容器 应用程序 镜像(Image) 这里面保存了应用和需要的依赖环境 为什么需要多个镜像&#xff1f;当开发、构建和运行容器化应用程序时&#xff0c;我们…

redis集群设置

先下载redis数据库可以在一台机器上设置redis集群高可用 cd /etc/redis/ mkdir -p redis-cluster/redis600{1..6} for i in {1..6} do cp /opt/redis-5.0.7/redis.conf /etc/redis/redis-cluster/redis600$i cp /opt/redis-5.0.7/src/redis-cli /opt/redis-5.0.7/src/redis-s…

二叉搜索树的本质

引言 打算写写树形数据结构&#xff1a;二叉查找树、红黑树、跳表和 B 树。这些数据结构都是为了解决同一个基本问题&#xff1a;如何快速地对一个大集合执行增删改查。 本篇是第一篇&#xff0c;讲讲搜索树的基础&#xff1a;二叉搜索树。 基本问题 如何在一千万个手机号中…

UniSSOView 任意命令执行复现

免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危害国家安全、荣誉和利益,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失,均由使…

动态内存管理面试题

动态内存管理面试题 文章目录 动态内存管理面试题一、第一题此代码存在的问题运行结果分析原因修改 二、第二题此代码存在的问题运行结果分析原因修改 一、第一题 代码如下&#xff08;示例&#xff09;&#xff1a; #include<stdio.h> #include<string.h> #incl…

Android开发之Fragment动态添加与管理

文章目录 主界面布局资源两个工具Fragment主程序 主界面布局资源 在activity_main.xml中&#xff0c;声明两个按钮备用&#xff0c;再加入一个帧布局&#xff0c;待会儿用来展示Fragment。 <?xml version"1.0" encoding"utf-8"?> <LinearLayo…

如何选择台式还是便携式多参数水质检测仪呢

选择台式还是便携式多参数水质检测仪主要取决于具体的使用需求和场景。 1.便携式多参数水质检测仪适用于需要在不同地点进行水质检测的情况&#xff0c;例如户外采样、实地调查等。它具有小巧轻便的特点&#xff0c;方便携带和操作&#xff0c;适合需要频繁移动或需要灵活使用的…

Go语言进阶语法八万字详解,通俗易懂

文章目录 File文件操作FileInfo接口权限打开模式File操作文件读取 I/O操作io包 文件复制io包下的Read()和Write()io包下的Copy()ioutil包总结 断点续传Seeker接口断点续传 bufio包bufio包原理Reader对象Writer对象 bufio包bufio.Readerbufio.Writer ioutil包ioutil包的方法示例…

微信小程序 样式和全局配置

WXSS wxss 把屏幕分为750个物理像素&#xff0c;大屏大&#xff0c;小屏小&#xff0c;随着设备不一致自动适配 推荐使用iPhone6作为标准&#xff0c;1个rpx 0.5个px&#xff0c;把px乘以2就是rpx的参数 import 导入外部样式表 import /common/common.wxss 样式 权重一…

InnoDB数据存储结构

一. InnoDB的数据存储结构&#xff1a;页 索引是在存储引擎中实现的&#xff0c;MySQL服务器上的存储引擎负责对表中数据的读取和写入工作。不同存储引擎中存放的格式一般不同的&#xff0c;甚至有的存储引擎比如Memory都不用磁盘来存储数据&#xff0c;这里讲讲InooDB存储引擎…

【C++】多态,虚函数表相关问题解决

文章目录 多态概念及其触发条件重写和协变&#xff08;考点1&#xff09;&#xff08;考点2&#xff09; 虚函数表及其位置&#xff08;考点3&#xff09; 多继承中的虚函数表 多态概念及其触发条件 多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态。具体点就是去完成…

Flutter Widget Life Cycle 组件生命周期

Flutter Widget Life Cycle 组件生命周期 视频 前言 了解 widget 生命周期&#xff0c;对我们开发组件还是很重要的。 今天会把无状态、有状态组件的几个生命周期函数一起过下。 原文 https://ducafecat.com/blog/flutter-widget-life-cycle 参考 https://api.flutter.dev/f…

[深度学习实战]基于PyTorch的深度学习实战(下)[Mnist手写数字图像识别]

目录 一、前言二、Mnist手写数字图像识别2.1 加载数据2.1.1 下载地址2.1.2 用 numpy 读取 mnist.npz 2.2 定义卷积模型2.3 开始训练2.4 完整代码2.5 验证结果2.6 修改参数 三、后记 PyTorch——开源的Python机器学习库 一、前言 首先感谢所有点开本文的朋友们&#xff01;基于P…

Linux:shell命令运行原理和权限的概念

文章目录 shell和kernelshell的概念和原理Linux的权限文件的权限文件的类型文件的权限管理权限的实战应用 shell和kernel 从狭义上来讲&#xff0c;Linux是一个操作系统&#xff0c;我们叫它叫kernel&#xff0c;意思是核心&#xff0c;核心的意思顾名思义&#xff0c;就是最关…