基础排序算法

news2024/11/25 14:53:02

插入排序(insertion sort)

插入排序每次循环将一个元素放置在适当的位置。像抓牌一样。手里的排是有序的,新拿一张牌,与手里的牌进行比较将其放在合适的位置。

插入排序要将待排序的数据分成两部分,一部分有序,另一部分待排序。默认任务数组第一个元素是有序的,然后依次取剩下的元素插入有序部分合适的位置。

算法实现

   public static void sort(int[] arr){
        int len = arr.length;
        /**
         * 将数组分成两部分
         * [0] 已排序  & [1,2,3...length-1] 待排序
         * 第一层循环将待排序元素逐个拿出与已排序部分进行插入排序
         */
        for (int i = 1; i < len; i++) {
            //先把当前循环要排序的元素从数组拿出来,空出来一个数组位置
            int val = arr[i];
            // j 当前插入位置指针
            int j = i-1;
            for (; j > 0 ; j--) {
                if(val < arr[j]){//当前插入指针位置元素比当前待排序值大,将当前位置元素后移
                    arr[j+1] = arr[j];
                }else {//当前插入指针位置元素比当前值小,好了,就应该在这里插入,插入到指针位置的后面 j+1
                    break;
                }
            }
            arr[j+1] = val;  
        }
    }

实际数据分析:

例 {2,6,5,4,8,7}

排序过程: 默认数组第一个位置2已排好,从6开始插入排序。

第一次:把6拿出来,然后6的位置可以被覆盖。

内循环:插入指针为6的下标往前一个,第一次拿出2进行比较。6 >2 。6要插入2的后面,跳出循环

第二次:拿出5 内循环:插入指针为5的下标-1, 第一次拿出6,5<6。不能在这里插入,插入点还得往前移动。这个时候要把6的位置后移一位, 因为5要插入6前面,但是现在没有位置。正好5被拿出来了,5的位置现在可以被使用。 经过一次内循环插入指针移动到了2位置,6向后移动一位。这时数组变成{2,6,6,4,8,7}。内循环第二次拿出2,5>2, ok找到插入点了,跳出内循环,5插入2的后面也就是原来6的位置。 将5放入插入位置,这时候数组变成了{2,5,6,4,8,7}。结束5的排序

第三次依次操作。

插入排序的插入理解就是将待排序的当前元素插入到有序部分合适的位置。比较前先将当前元素拿出来,在比较的过程中,如果当前元素的排序位置没找到,不需要进行交换。只需要将当前有序部分的比较元素后移一位,让出插入位置。进行下一次比较依次类推。直到找到插入位置,将当前元素放入该位置。

插入排序最好情况时间复杂度是O(n)。已经是有序的了,只一遍外循环即可。最坏情况是O(n^2)。排序算法是稳定的,因为不存在交换。也不需要开辟额外的空间。

希尔排序

希尔排序是插入排序的变种。我们知道插入排序的时间复杂度是O(n^2)。一次只能对一个元素位进行排序,并且遇到较小值在数组后部时,要进行多次比较移位才能将其放置合适位置。希尔排序能提高一定的平均排序效率。但是实际中使用的可能也不太多。

排序原理

每次按特定步长(step)将待排序分成若干组,然后每组内进行插入排序。步长取值通常为元素长度 length/2,length/(2*2)…1。最后一次步长变成1演变成完全插入排序。

代码实现

    static void sort(int[] arr){
        int len = arr.length;
        //step为步长,分组元素间的间隔
        for (int step = len/2; step >0; step /=2) {
            //每一组进行插入排序,
            for (int i = step; i < len; i++) {//外层循环每+1表示新一组元素
                int temp = arr[i];//待插入元素先拿出来
                int p = i;//当前插入指针,p-step是当前组已排序好的部分,依次拿出与当前值进行比较
                for (; p >= step ; p -= step) {//每次取同组元素,同组元素的间隔是step值
                    if(arr[p - step] > temp){
                        arr[p] = arr[p - step];
                    }else{//找到插入位置
                        break;
                    }
                }
                arr[p] = temp;//将元素插入当前位置
            }
        }
    }

实例分析:

image

冒泡排序(Bubble sort)

排序算法像冒泡一样每次遍历通过比较找到待排序元素中最大的一个,然后上浮到已排序部分。

代码实现

    static void sort(int[] arr){
        int len = arr.length;
        for (int i = 0; i < len-1; i++) {
			//len-i个元素已排好,所以内循环次数是len-i-1
            for (int j = 0; j < len-i-1; j++) {
               /**前面元素比后面元素大就进行交换,大元素后移。这样内循环结束后就找到了本次所有参与循环元素的最大值,并放到最后。
				*/
                if(arr[j] > arr[j+1]){
                    int tmp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = tmp;
                }
            }
            System.out.println(Arrays.toString(arr));;
        }
    }

优化:每次冒泡排序内循环记录是否有交换发生。如果没有交换发生,则整个数组已经是有序的了。仔细理解下这句话。代码实现

  static void sort(int[] arr){
        int len = arr.length;
        for (int i = 0; i < len-1; i++) {
            boolean swap = false;
            for (int j = 0; j < len-i-1; j++) {
                if(arr[j] > arr[j+1]){
                    int tmp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = tmp;
                    swap = true;
                }
            }
            if(!swap) break;//没有交换发生,跳出循环
            System.out.println(Arrays.toString(arr));;
        }
    }

冒泡排序时间复杂度和插入排序一样最好都是O(n),最后是O(n^2)。

选择排序

排序思想

每次从未排序的部分选择最小(或最大)的元素,放到已排序部分的末尾,直到整个数组排序完成。

代码实现

    public static void sort(int[] arr){
        int len = arr.length;
        for (int i = 0; i < len-1; i++) {
            int p = i;//记录最小位置指针,默认记录为本次循环第一个元素位置
            for (int j = i+1; j < len ; j++) {
                //如果当前数比原来标记最小数小,记录当前位置为最小数指针
                if( arr[j] < arr[p] ) p = j;
            }
            //找到本次最小数指针,如果指针不是第一个元素,将第一个元素与最小值元素进行位置交换
            if(p != i){
                int temp = arr[i];
                arr[i] = arr[p];
                arr[p] = temp;
            }
        }
    }

选择排序存在元素跨距离交换,不稳定,时间复杂度O(n^2)。理解起来和冒泡排序很像。不过中间少了很多交换。

归并排序

归并排序将待排序数组每次进行二分,直到每一组分成1个元素,则顺序也就出来了。然后再依次进行合并操作最后生成一个有序的操作。

分比较好操作每次数组进行二分即可。合就是要将分的两部分A、B依次拿出一个值(两部分当前最小的值,中间需要比较是取A部分的还是取B部分)来组合起来。因为分后的两部分已经是有序的了,所以最后两部分所有元素取完合起来的整个也是有序的。

排序过程图:

image

代码实现:

    public static void sort(int[] arr,int start,int end){
        //元素开始和结束位置相等,不能再分了,结束
        if(start >= end) return;
        //将要排序的数组进行平分
        int mid = (start + end) /2;
        sort(arr,start,mid);//前半部分进行排序
        sort(arr,mid+1,end);//后半部分进行排序
        //合并本次排序结果
        merge(arr,start,mid,end);
    }
    /**
     * 排序两部分 {start,mid} ,{mid+1,end} 都已排序完成,合并两部分
     * 合并过程:
     */
    public static void merge(int[] arr,int start,int mid,int end){
        int p1 = start;//左半部分当前下标
        int p2 = mid+1;//右半部分当前下标
        int index = start;//temp临时数组存储下标,从start开始
        /**
         * 合并已排序的两部分
         * 每次从左边部分和右边部分各拿出一个元素进行比较,小的放入数组中,
         * 然后从小的所在部分再拿出一个与上一次大的元素进行比较
         */
        while (p1 <= mid && p2 <= end){
            if(arr[p1] < arr[p2]){
                temp[index++] = arr[p1++];
            }else{
                temp[index++] = arr[p2++];
            }
        }
        /**
         * while 循环结束表示至少数组两部分有一部分取完,但是不一定两部分都取完。
         * 最后将左边部分和右边部分可能剩余的部分放入临时数组中。
         */
        while (p1 <= mid){
            temp[index++] = arr[p1++];
        }
        while (p2 <= end){
            temp[index++] = arr[p2++];
        }
        /**
         * 执行到这里 temp{start,end} 部分都已排好序,
         * 将temp{start,end}部分放入原数组中。
         */
        for (int i = start; i <= end; i++) {
            arr[i] = temp[i];
        }

    }

快速排序

算法思想:

快速排序取数组第一个元素作为基准数,然后将剩余元素与基准数进行比较,比基准数大的放在基准数左边,小的放在基准数右边,基准数在中间。然后再将基准数两个子结果进行递归按上面取基准数排序。最后整个数组变为有序的。

代码实现

    public static void sort(int arr[],int left ,int right){
        if(left >= right) return;

        int p1 = left;
        int p2 = right;
        int val = arr[left];//取数组第一个元素作为基准数
        while ( p1 < p2){
            /**
             * 从右往左找比基准数小的数
             * 找到后将其放到p1指针位置
             */
            while (p2 > p1 && arr[p2] >= val)
                p2--;
            arr[p1] = arr[p2];

            /**
             * 从左往左找找到比基准数大的数
             * 找到后将其放到p2指针位置,这个时候p2经过上面的查找已经被放到p1位置,可以覆盖
             */
            while (p1 < p2 && arr[p1] <= val)
                p1++;
            arr[p2] = arr[p1];
            //继续交叉查找
        }
        //找到中间位置了(左边的数都小于基准数,右边的数都大于基准数),将基准数放到该位置
        arr[p2] = val;
        //递归的将数组以基准数为分界点分成两部分,各自进行快速排序
        sort(arr,left,p2);
        sort(arr,p2+1,right);
    }

排序过程分析

首先取数组的第一个元素作为基准数,然后这样数组第一个位置就可以覆盖,然后从数组的末尾往前找比基准数小的数,如果找到就将其移动到原基准数的位置。这个时候刚被移动的元素位置又空出来了,这个时候在从前往后找比基准数大的,找到后在将其放到该位置。循环往复的进行以上操作。直到从后往前找的指针和从前往后找的指针位置相等,表示整个数组已经找完,将基准数放置在该位置。最后数组被分成了:[{小于基准数}{基准数}{大于基准数}]三部分。然后再对小于基准数部分和大于基准数部分进行递归的上面排序过程。直到被分元素只有一个,排序完成。

从上面的分析过程可以看出,快速排序会进行大量的跨距离移位操作,是不稳定的。平均时间复杂度是O(n*logn)

总结

排序算法时间复杂度空间复杂度适用场景
冒泡排序最好情况:O(n) 最坏情况:O(n^2) 平均情况:O(n^2)O(1)小型数据集或部分有序数据集
插入排序最好情况:O(n) ; 最坏情况:O(n^2) ; 平均情况:O(n^2)O(1)小型数据集或部分有序数据集
选择排序始终为O(n^2)O(1)小型数据集
快速排序最好情况:O(nlogn) ; 最坏情况:O(n^2) ;平均情况:O(nlogn)最好情况:O(logn) , 最坏情况:O(n)大型数据集,尤其是无序数据集
归并排序O(nlogn)O(n)大型数据集,尤其是链表结构

如果对排序稳定性有要求,可以选择插入排序、归并排序。如果数据集较小且无序,可以选择冒泡排序、插入排序或选择排序。对于大型数据集,快速排序、归并排序通常效果更好。

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

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

相关文章

ddd扬帆

简介 读取了一篇“产品代码都给你看了&#xff0c;可别再说不会DDD”较为清晰了解了&#xff1a;领域驱动设计&#xff0c;整洁架构和事件驱动架构的架构思想落地实践&#xff0c;特做记录读后感&#xff0c;可以直接跳到正文阅读原文 正文 以下正文... 构建自己的软件大厦 …

【C++】C++11——可变参数模板和emplace

可变参数模板的定义方式可变参数模板的传值计算可变参数模板参数个数参数包展开方式递归展开参数包逗号表达式展开参数包 emplace插入 可变参数模板是C11新增的最强大的特性之一&#xff0c;它对参数高度泛化&#xff0c;能够让我们创建可以接受可变参数的函数模板和类模板。 在…

(a)Spring注解式开发,注册组件的@Repository,@Service,@Controller,@Component使用及说明

注解扫描原理 通过反射机制获取注解 Target(value {ElementType.TYPE})// 设置Component注解可以出现的位置&#xff0c;以上代表表示Component注解只能用在类和接口上 Retention(value RetentionPolicy.RUNTIME)// 设置Component注解的保持性策略&#xff0c;以上代表Comp…

社区团购美团和多多买菜小程序购物车

概述 微信小程序购物车列表demo 详细 需求 显示食物名称、价格、数量。 点击相应商品增加按钮,购买数量增加1,点击食物减少按钮,购买数量减一 显示购买总数和总金额 查看当前购买的商品 效果图(数据来自本地模拟) 目录结构 实现过程 主要wxml <view classfoods>…

工具篇 | H2数据库的使用和入门

引言 1.1 H2数据库概述 1.1.1 定义和特点 H2数据库是一款以 Java编写的轻量级关系型数据库。由于其小巧、灵活并且易于集成&#xff0c;H2经常被用作开发和测试环境中的便利数据库解决方案。除此之外&#xff0c;H2也适合作为生产环境中的嵌入式数据库。它不仅支持标准的SQL…

饮料生产线Modbus协议转换网关的应用介绍

在饮料生产线设备数据采集和控制系统中&#xff0c;MODBUS网关是一种非常重要的设备。它可以将不同设备之间的通讯协议转换为统一的MODBUS协议&#xff0c;从而实现数据采集和指令下达。在本文中&#xff0c;我们将介绍如何使用MODBUS网关采集饮料生产线设备数据并下达指令。 在…

知识库搭建保姆级教程,如何从0到1完成知识库搭建

在这个信息爆炸的时代&#xff0c;如何获取、整理和应用知识成为了我们个体价值和企业核心竞争力打造的重要表现&#xff0c;搭建一个高效的知识库可以提升我们企业的竞争力&#xff0c;必要时还能快速切换赛道&#xff0c;开展一个新的领域。 今天我们将结合HelpLook 来与你一…

基于C++实现的3D野外赛车驾驶游戏源码+项目文档+汇报PPT

项目介绍&#xff1a;本项目实现了一个户外场景下的赛车游戏&#xff0c;可以通过键盘控制赛车的移动&#xff0c;视角为第二人称视角。场景中有汽车&#xff0c;建筑&#xff0c;道路&#xff0c;天空等物体&#xff0c;拥有光照和阴影的效果。通过粒子系统模拟尾气效果&#…

Kubernetes组件和架构简介

目录 一.概念简介 1.含义&#xff1a; 2.主要功能&#xff1a; 3.相关概念&#xff1a; 二.组件和架构介绍 1.master&#xff1a;集群的控制平面&#xff0c;管理集群 2.node&#xff1a;集群的数据平面&#xff0c;为容器提供工作环境 3.kubernetes简单架构图解 一.概…

Windows迁移文件的快速方法

文章目录 1.简单比较2.传输方法介绍&#xff1a;有线&#xff08;直连网络&#xff09;3.传输方法介绍&#xff1a;无线热点传输4. 共享文件夹的设置5.挂载共享文件夹 1.简单比较 方法传输速度有线传输接近900Mb无线热点传输接近500MbU盘传输基本上不超过100Mb 2.传输方法介绍…

小程序-uniapp:URL Link / 适用于在移动端 从短信、邮件、微信外网页 等场景打开小程序任意页面

一、背景介绍 小程序URL Scheme、URL Link是微信小程序后台生成的一种地址&#xff0c;适用于从短信、邮件、微信外网页 等场景打开小程序任意页面。所以&#xff0c;适用性极强。可与微信扫码携带参数跳转到小程序指定页面技术互补 若在微信外打开&#xff0c;用户可以在浏览…

【C++】C++ 类中的 this 指针用法 ③ ( 全局函数 与 成员函数 相互转化 | 有参构造函数设置默认参数值 | 返回匿名对象与返回引用 )

文章目录 一、全局函数 与 成员函数 相互转化1、成员函数转为全局函数 - 多了一个参数2、全局函数转为成员函数 - 通过 this 指针隐藏操作数 二、有参构造函数设置默认参数值三、返回匿名对象与返回引用四、完整代码示例 一、全局函数 与 成员函数 相互转化 1、成员函数转为全局…

2023-Chrome插件推荐

Chrome插件推荐 一键管理扩展 链接 https://chromewebstore.google.com/detail/lboblnfejcmcaplhnbkkfcienhlhpnni 介绍 一键开启、禁用Chrome插件。 Checker Plus for Gmail™ 链接 https://jasonsavard.com/zh-CN/Checker-Plus-for-Gmail https://chromewebstore.goo…

ElementUI之首页导航与左侧菜单

目录 一、Mock 1.1 什么是Mock.js 1.2 安装与配置 1.2.1 安装mock.js 1.2.2 引入mock.js 1.3 mock.js使用 1.3.1 定义测试数据文件 1.3.2 mock拦截Ajax请求 1.3.3 界面代码优化 二、总线 2.1 定义 2.2 类型分类 2.3 前期准备 2.4 配置组件与路由关系 2.4.1 配置…

计算机毕业设计 基于微信小程序的校园商铺系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

2023 “华为杯” 中国研究生数学建模竞赛(A题)深度剖析|数学建模完整代码+建模过程全解全析

华为杯数学建模A题 当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2021年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 让我们一起看看研赛的A题呀&#xff01; …

络安全开发和音视频开发哪个方向更有前景?

网络安全开发和音视频开发哪个方向更有前景&#xff1f; 随着互联网和移动互联网的飞速发展&#xff0c;音视频应用已经成为人们日常生活和工作中不可或缺的一部分。 从视频会议、在线教育、直播、短视频到游戏&#xff0c;音视频技术的应用场景越来越广泛&#xff0c;市场需求…

面试必杀技:Jmeter性能测试攻略大全(第二弹)

1. JMeter介绍与安装 JMeter介绍 JMeter是Apache组织开发的基于Java的压力测试工具。具有开源免费、框架灵活、多平台支持等优势。除了压力测试外&#xff0c;JMeter也可以应用的接口测试上。JMeter下载、安装及启动 下载&#xff1a; 访问JMeter官网&#xff1a;https://j…

机器学习——seaborn实用画图方法简介

0、seaborn简介: 前言:下面的总结只是介绍seaborn有哪些方法和属性,至于具体使用,通过下面给出的名称稍作查找即可。重点应该关注本文介绍的seaborn的使用方法seaborn与机器学习的关系: 知识图谱 0.1、了解即可的知识: seaborn:在matplotlib的基础上画一些更好看的图,在…

快速排序与代码

快速排序&#xff08;Quicksort&#xff09;是一种常用的排序算法&#xff0c;它基于分治的思想。 时间复杂度&#xff1a;O&#xff08;nlogn&#xff09; 空间复杂度&#xff1a;O&#xff08;logn&#xff09; 快速排序的基本思想如下&#xff1a; 选择一个元素作为基准&a…