排序算法(Java版)

目录

  • 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)稳定

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

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

相关文章

深度学习常用优化算法笔记介绍,各种梯度下降法详细介绍

优化算法 mini-batch梯度下降法 当一个数据集其数据量非常大的时候&#xff0c;比如上百万上千万的数据集&#xff0c;如果采用普通的梯度下降法&#xff0c;那么运算速度会非常慢&#xff0c;因为如果使用梯度下降法在每一次迭代的时候&#xff0c;都需要将这整个上百万的数…

基于边缘智能网关的工业燃气管网监测应用

随着城市化和工业化的飞速发展&#xff0c;燃气的使用量和应用范围持续增加&#xff0c;燃气管网作为承载燃气输送的设施&#xff0c;安全问题至关重要。一旦燃气管网发生泄漏事故&#xff0c;极易引发起火、爆炸等&#xff0c;从而酿成人员伤亡及财产损失的恶性事故。 得益于物…

流量分析利器arkime的学习之路(三)---结合Suricata攻击检测

1、基础 Arkime安装部分参考《流量分析利器arkime的学习之路&#xff08;一&#xff09;—安装部署》 在此基础上安装suricata软件并配置。 2、安装suricata yum install suricate 可能依赖的文件包括libyaml&#xff0c;PyYAML&#xff0c;这些可能在之前安装arkime或者其他…

Vue接收后端POST、GET返回的zip文件流打开报异常

近期接到一个小任务是将内容导出为 Zip 文件流的行式给前端 Vue 供用户下载&#xff1b;过程中发现一个问题打开 zip 文件报异常&#xff0c;如下&#xff1a; 首先后端这块单独在服务端请求是落盘的文件是正常的&#xff1b;因此后端的这块的逻辑没有问题&#xff1b;但中间前…

微服务拆分

目录 前言&#xff1a; 逻辑视图架构风格 一、分层式架构风格 二、六边形架构 如何定义微服务架构 微服务的拆分 业务能力进行服务拆分 子域进行服务拆分 拆分的原则 单一职责 闭包原则 前言&#xff1a; 我们在软件开发的时候一直在谈论架构&#xff0c;那么什么是…

线程池复习

手写线程池 - C语言版 | 爱编程的大丙 (subingwen.cn) 1. 线程池原理 我们使用线程的时候就去创建一个线程&#xff0c;这样实现起来非常简便&#xff0c;但是就会有一个问题&#xff1a;如果并发的线程数量很多&#xff0c;并且每个线程都是执行一个时间很短的任务就结束了&…

8款好用的电脑监控软件分享丨好资源不私藏!

电脑已经成为我们日常生活和工作的重要工具。随之而来的是&#xff0c;电脑监控的需求也逐渐增加。为了帮助大家更好地管理和监控电脑使用情况&#xff0c;本文将为您推荐8款好用的电脑监控软件。这些软件功能强大&#xff0c;易于使用&#xff0c;适用于各种场景&#xff0c;让…

哪些博客类型是最受欢迎的?

在创建博客时&#xff0c;您可能会想到的最常见的问题之一是哪些是最受欢迎的博客类型&#xff1f;有许多不同类型的博客涉及广泛的主题&#xff0c;兴趣和受众。对于一个成功的博客&#xff0c;你需要提前计划并选择适合你的利基市场。在本文中&#xff0c;我们将分享您可以立…

数字工厂管理系统如何实现生产过程透明化

随着科技的飞速发展&#xff0c;数字化转型已成为制造业不可逆转的趋势。数字工厂管理系统作为实现生产自动化、智能化的重要工具&#xff0c;其在提升生产效率、降低运营成本、优化资源配置等方面的作用日益凸显。其中&#xff0c;实现生产过程的透明化是数字工厂管理系统的重…

东莞厂家环保水空调的应用场所

环保水空调&#xff08;也被称为蒸发式冷风机、水冷式环保空调等&#xff09;由于其独特的节能环保特性&#xff0c;适用于多种需要降温和通风的场所。以下是一些常见的应用场所&#xff1a; 工业厂房&#xff1a;工业厂房通常对温度、湿度和空气质量有较高要求。环保水空调不…

【C++】匿名对象超详细详解(什么是匿名对象?对象可以是哪些类型呢?)

目录 一、前言 二、匿名对象的概念详解 &#x1f95d; 语法结构 &#x1f34d;概念理解 三、匿名对象的对象类型 四、匿名对象的使用 &#x1f347;简单场景的使用 &#x1f349;复杂场景的使用 五、总结 六、共勉 一、前言 在C中&#xff0c;匿名对象&#xff08;Ano…

如何提高日语听力?日语学习日语培训柯桥小语种学校

每次一说起练日语听力&#xff0c;总离不开一个词&#xff0c;那就是“磨耳朵”。 可是&#xff0c;“磨耳朵”真的有用吗&#xff1f; 在讨论这个问题之前&#xff0c;我们需要先知道&#xff1a;什么是“磨耳朵”&#xff1f; 所谓的“磨耳朵”&#xff0c;其实就是让我们的耳…

C语言(操作符)2

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸各位能阅读我的文章&#xff0c;诚请评论指点&#xff0c;关注收藏&#xff0c;欢迎欢迎~~ &#x1f4a5;个人主页&#xff1a;小羊在奋斗 &#x1f4a5;所属专栏&#xff1a;C语言 本系列文章为个人学习笔记&#x…

【JUC】并发编程 AQS,ReentryLock,CyclicBarrier,CountDownLatch 原理总结

AQS AQS是什么&#xff1f;重写AQS就能实现锁的效果&#xff1f; AQS是一个抽象类&#xff0c;是一个并发包的基础组件&#xff0c;用来实现各种锁&#xff0c;同步组件的工具&#xff08;通过volatile cas进行实现&#xff09;。它包含了共享成员变量state、等待队列、条件…

6层板学习笔记1

说明:笔记基于6层全志H3消费电子0.65MM间距BGA 目的:掌握各类接口的布局思路和布线,掌握DDR高速存储设计 1、网表的导入是原理图的元件电气连接关系,位号,封装,名称等参数信息的总和 2、原理图文件包含(历史版本记录,功能总框图,电源树,GPIO分配,DDR功能,CPU,US…

【跨境商家必读】TikTok Shop商城运营全指南

随着社交媒体和电子商务之间界限的日益模糊&#xff0c;一种全新的购物平台——TikTok商城&#xff0c;正在迅速成为全球跨境商家们关注的焦点。在这个竞争激烈的TikTok跨境电商领域中&#xff0c;了解如何有效利用TikTok Shop的各项功能&#xff0c;理解其独特的运营模式&…

OpenAI 高管:一年后,你会觉得现在的 ChatGPT 像笑话一样糟糕|TodayAI

OpenAI 的首席运营官 Brad Lightcap 表示&#xff0c;一年后&#xff0c;你会觉得现在的 ChatGPT 像笑话一样糟糕。未来的 ChatGPT 版本将会有重大升级。他还讨论了 AI 取代人类工作和对电网的压力的可能性。 虽然我们不知道 OpenAI 何时会推出 GPT-5&#xff0c;但公司高管已…

视频怎么去水印?这三款工具助你轻松搞定

在视频处理的过程中&#xff0c;水印常常成为我们的一大难题。它不仅影响了视频的美观度&#xff0c;还可能涉及版权问题。那么&#xff0c;如何高效去除视频中的水印呢&#xff1f;接下来&#xff0c;我将为大家推荐三款国内外备受好评的视频去水印工具&#xff1a;水印云、In…

【Linux】基础命令:进程、网络

systemctl命令 控制内置服务 systemctl start | stop | status | enable | disable 服务名 start | stop开启关闭&#xff0c;status状态&#xff0c;enable | disable开启关闭开机自启 date命令 查看系统时间 date [-d] [格式化字符串] date -d “1 day” %Y-%m-%d 修改时区…

数字电商人才孵化基地授牌仪式在天府锋巢直播产业基地隆重举行!

2024年4月25日&#xff0c;数字电商人才孵化基地授牌仪式在天府锋巢直播产业基地隆重举行。此次仪式不仅标志着德商锋巢与天府新区信息技术职业学院的紧密合作正式启动&#xff0c;更意味着双方在数字电商领域的人才培养和产业发展上迈出了坚实的步伐。 仪式现场&#xff0c;德…