【Java数据结构】排序

【Java数据结构】排序

  • 一、排序
      • 1.1 排序的概念
      • 1.2 排序的稳定性
      • 1.3 内部排序和外部排序
        • 1.3.1 内部排序
        • 1.3.2 外部排序
  • 二、插入排序
      • 2.1 直接插入排序
      • 2.2 希尔排序
  • 三、选择排序
      • 3.1 选择排序
      • 3.2 堆排序
  • 四、交换排序
      • 4.1 冒泡排序
      • 4.2 快速排序
        • Hoare法:
        • 挖坑法:
        • 前后指针:
      • 4.3 快速排序的优化
        • 4.3.1 三数取中法
        • 4.3.2 递归到小的子区间时,使用直接插入排序
      • 4.4 非递归快速排序
  • 五、归并排序
      • 5.1 递归归并排序
      • 5.2 非递归归并排序
  • 六、总结

博客最后附有整篇博客的全部代码!!!

一、排序

1.1 排序的概念

排序是计算机科学中一个非常基础且重要的概念,它指的是将一组对象按照某种顺序排列的过程。排序算法是实现排序功能的具体方法,通过对数据进行比较、交换或移动等操作,使数据元素按照指定的顺序排列。

1.2 排序的稳定性

稳定性是一个重要的概念,它描述了排序算法是否能够保持相同元素的相对顺序不变。

排序稳定性:
一个排序算法是稳定的,如果在排序过程中,两个具有相同键值(或值)的元素在排序前后的相对顺序保持不变。换句话说,如果元素A和B在排序前满足A在B之前,并且它们的键值相同,那么排序后A仍然在B之前。

例如:
下面一组数据,里面有两个相同的值‘8’(为了展现它们的相对位置,我们将两个相同值的‘8’用不同元素表示出来)。在排序之前‘8’
在这里插入图片描述

1.3 内部排序和外部排序

1.3.1 内部排序

内部排序是指在排序过程中,所有待排序的数据都能同时存储在内存中进行处理。由于内存访问速度较快,内部排序通常效率较高,但受限于内存容量,适用于数据量较小的场景。

特点:
存储:所有数据存储在内存中。
效率:通常较快,因为内存访问速度快。
适用场景:数据量较小(通常在几万甚至几十万以内)。

1.3.2 外部排序

外部排序是指待排序的数据量过大,无法全部加载到内存中,需要借助外存(如磁盘)来完成排序的过程。外部排序通常涉及将数据分块处理,排序后再合并。

特点:
存储:数据主要存储在外存(如磁盘),内存仅用于存储部分数据。
效率:通常较慢,因为外存访问速度远低于内存。
适用场景:数据量巨大(如数百万甚至数十亿条记录),无法全部加载到内存中。

二、插入排序

2.1 直接插入排序

思想:

将待排序的元素逐个插入到已经排好序的序列中,从而逐步扩展有序序列的长度,直到所有元素都被插入,整个序列变为有序。

时间复杂度:O(N^2)。
空间复杂度:O(1)。
稳定性:稳定。

代码:

    public void insertSort(int[] array){int tmp=0;for(int i=1;i<array.length-1;i++){tmp=array[i];int j=i-1;for(;j>=0;j--){if(tmp<array[j]){array[j+1]=array[j];}else{break;}}array[j+1]=tmp;}}

2.2 希尔排序

思想:

希尔排序(Shell Sort)是插入排序的一种改进版本,通过将数组分成多个子序列进行排序,逐步缩小子序列的间隔(增量),最终使整个数组有序。
核心思想如下:
选择增量序列:初始增量通常为数组长度的一半,随后逐步减小,直到增量为1。
分组排序:按照当前增量将数组分成多个子序列,对每个子序列进行插入排序。
逐步缩小增量:重复上述过程,直到整个数组基本有序,最后使用增量为1的插入排序完成最终排序。

时间复杂度:O(n^1.3 )至 O( n^1.5)之间。
空间复杂度:O(1)。
稳定性:不稳定。

代码:

   public void shellSort(int[] array){int gap=array.length;while(gap>1){gap=gap/2;shell(array,gap);}}private void shell(int[] array, int gap) {int tmp=0;for (int i = gap; i < array.length ; i++) {tmp=array[i];int j=i-gap;for(;j>=0;j-=gap){if(tmp<array[j]){array[j+gap]=array[j];}else{break;}}array[j+gap]=tmp;}}

三、选择排序

3.1 选择排序

思想:

选择排序是一种简单直观的排序算法。它的核心思想是:

  1. 每次从未排序的部分中选择最小(或最大)的元素,并将其放到已排序部分的末尾。
  2. 重复上述过程,直到所有元素都被排序。

时间复杂度:O(N^2)。
空间复杂度:O(1)。
稳定性:不稳定。

代码:

 public void selectSort(int[] array){int minIndex=0;for (int i=0;i<array.length;i++){minIndex=i;for(int j=i+1;j<array.length;j++){if(array[minIndex]>array[j]){minIndex=j;}}int tmp=array[i];array[i]=array[minIndex];array[minIndex]=tmp;}}

延伸
思路:

定义两个变量‘minIndex’和‘maxIndex’来接收遍历完一遍数组得到的最大值和最小值下标,将得到的最大值和最小值下标分别与这组数组的最左边和最右边的值交换,以此类推。

时间复杂度:O(n²)
空间复杂度:O(1)
稳定性:不稳定

代码:

    public void selectSort2(int[] array) {int left=0;int right=array.length-1;int len=array.length;while(left<right){int minIndex=left;int maxIndex=left;for (int i = left; i < len; i++) {if(array[i]<array[minIndex]){minIndex=i;}if(array[i]>array[maxIndex]){maxIndex=i;}}swap(array,left,minIndex);left++;swap(array,right,maxIndex);right--;len--;}}public void swap(int[] array,int x,int y){int tmp=array[x];array[x]=array[y];array[y]=tmp;}

3.2 堆排序

思路:

建立大根堆,将大根堆的堆顶元素和最后一个元素进行交换,交换完后将剩下的堆重新调整,以此类推。

时间复杂度:O(N*logN)。
空间复杂度:O(1)。
稳定性:不稳定。

代码:

public void heapSort(int[] array){//创建堆createHeap(array);int end=array.length-1;while(end>0){swap(array,0,end);shiftDown(array,0,end);end--;}}public void createHeap(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 length){int child =parent*2+1;while(child<length){//如果孩子存在,找到左右孩子中较小的孩子,用child标记if (child + 1 < length && array[child] < array[child+1]) {child++;}if(array[parent]<=array[child]){swap(array,parent,child);parent=child;child=parent*2+1;}else{break;}}}

四、交换排序

4.1 冒泡排序

思想:

核心思想是通过相邻元素之间的比较和交换,逐步将最大(或最小)的元素“冒泡”到数组的末尾(或开头)。这个过程重复进行,直到整个数组有序。

时间复杂度:O(N^2) 。
空间复杂度:O(1)。
稳定性:稳定。

代码:

public void bubbleSort(int[] array){boolean flag=true;for (int i = 0; i < array.length-1; i++) {for (int j = 0;j<array.length-1-i;j++ ){if(array[j]>array[j+1]){swap(array,j,j+1);flag=false;}}if(flag){break;}}}

4.2 快速排序

思想:

核心思想是通过分区操作将数组分为两部分,使得一部分的所有元素都小于(或等于)另一部分的所有元素,然后递归地对这两部分进行排序。

时间复杂度:O(N*logN)至O(n^2)。
空间复杂度:O(logN)至O(N)。
稳定性:不稳定。

Hoare法:

思路:

选定序列第一个为基准,从后边往前找到比基准小的停下来,从前边找到比基准大的停下来,交换直到左右相遇,相遇下标的值与基准交换。

代码:

 public void quickSort(int[] array){hoareSort(array,0,array.length-1);}public void hoareSort(int[] array,int start,int end) {if(start>=end){return;}int pivot = partition(array, start, end);hoareSort(array,start,pivot-1);hoareSort(array,pivot+1,end);}private int partition(int[] array, int left, int right) {int flag=left;while(left<right){while(left<right&&array[right]>=array[flag]){right--;}while(left<right&&array[left]=<array[flag]){left++;}swap(array,left,right);}swap(array,left,flag);return left;}
挖坑法:

思路:

选定序列第一个为基准,从后往前找,找到小于基准的,将其放到基准的位置,接下来从前往后找,找到比基准大的,放到先前后面找到的比基准小的位置,以此类推。

代码:

    private int partition2(int[] array, int left, int right) {int flag=array[left];while(left<right){while(left<right&&array[right]>=flag){right--;}array[left]=array[right];while(left<right&&array[left]<=flag){left++;}array[right]=array[left];}array[left]=flag;return left;}
前后指针:

思路:

选定序列第一个为基准,定义两个prev和cur来标记下标,遍历序列,满足cur下标的值小于基准,并且prev++下标和cur下标不在同一个下标,交换prev和cur下标的值,如果不满足条件,cur++。

代码:

    private int partition3(int[] array, int left, int right) {int prev=left;int cur=prev+1;while(cur<=right){if(array[cur]<array[left]&&array[++prev]!=array[cur]){swap(array,prev,cur);}cur++;}swap(array,prev,left);return prev;}

4.3 快速排序的优化

4.3.1 三数取中法

优点:

优化性能:通过选择中值作为基准值,减少了因数据分布不均匀而导致的性能退化。
提高稳定性:在处理接近有序或完全逆序的数据时,性能更加稳定。

代码:

 public void hoareSort(int[] array,int start,int end) {if(start>=end){return;}int midIndex=getNumber(array,start,end);swap(array,start,midIndex);int pivot = partition(array, start, end);hoareSort(array,start,pivot-1);hoareSort(array,pivot+1,end);}private int getNumber(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[right]){return right;} else if (array[mid]>array[left]) {return left;}else{return mid;}}}
4.3.2 递归到小的子区间时,使用直接插入排序

优点:

因为直接插入排序的特点就是越有序越快。

代码:

    public void hoareSort(int[] array,int start,int end) {if(start>=end){return;}
//        int midIndex=getNumber(array,start,end);
//        swap(array,start,midIndex);if (end - start+1 <= 10) {insertSortRange(array,start,end);return;}int pivot = partition(array, start, end);hoareSort(array,start,pivot-1);hoareSort(array,pivot+1,end);}private void insertSortRange(int[] array, int start, int end) {int tmp=0;for(int i=start+1;i<=end;i++){tmp=array[i];int j=i-1;for(;j>=start;j--){if(tmp<array[j]){array[j+1]=array[j];}else{break;}}array[j+1]=tmp;}}

4.4 非递归快速排序

思想:

  1. 通过挖坑法先求得基准下标
  2. 通过基准下标将基准左右两边序列的start和end存进栈中,存入栈中的先后顺序要一致
  3. 通过pop()弹出start和end,再通过挖坑法求得基准下标,以此类推,当栈为空,则证明已经排序好了

代码:

    public void quickNor(int[] array,int start,int end) {Stack<Integer> stack=new Stack<>();int pivot = partition2(array, start, end);if(pivot>start+1){stack.push(start);stack.push(pivot-1);}if(pivot<end-1){stack.push(pivot+1);stack.push(end);}while(!stack.isEmpty()){end=stack.pop();start=stack.pop();pivot=partition2(array, start, end);if(pivot>start+1){stack.push(start);stack.push(pivot-1);}if(pivot<end-1){stack.push(pivot+1);stack.push(end);}}}

五、归并排序

5.1 递归归并排序

思路:

归并排序是一种分治排序算法,其核心思想是将数组分成多个小部分,分别对这些小部分进行排序,然后逐步合并这些有序部分,最终得到一个完全有序的数组。

时间复杂度:O(N*logN)。
空间复杂度:O(N)。
稳定性:稳定。

代码:

    public void mergeSort(int[] nums) {mergeSortSplit(nums, 0, nums.length - 1);}private void mergeSortSplit(int[] nums, int left, int right) {if (left >= right) {return;}//分解int mid = (left + right) / 2;mergeSortSplit(nums, left, mid-1);mergeSortSplit(nums, mid + 1, right);//合并merge(nums, left, mid, right);}private void merge(int[] nums, int left, int mid, int right) {int[] tmp = new int[right - left + 1];int k=0;int s1=left;int s2=mid + 1;while(s1<=mid&&s2<=right){if(nums[s1]<=nums[s2]){tmp[k++]=nums[s1++];}else{tmp[k++]=nums[s2++];}}while(s1<=mid){tmp[k++]=nums[s1++];}while(s2<=right){tmp[k++]=nums[s2++];}for(int i=left; i<k; i++){nums[i]=tmp[i];}}

5.2 非递归归并排序

    public void mergeSortNor(int[] array){int gap=1;while(gap<array.length){for(int i=0;i<array.length;i=i+gap*2){int left=i;int mid=left+gap-1;if(mid>=array.length){mid=array.length-1;}int right=mid+gap;if(right>=array.length){right=array.length-1;}merge(array,left,mid,right);}gap*=2;}}

六、总结

在这里插入图片描述
此篇博客的全部代码!!!

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

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

相关文章

Java数据结构 (链表反转(LinkedList----Leetcode206))

1. 链表的当前结构 每个方框代表一个节点&#xff0c;每个节点包含两个部分&#xff1a; 左侧的数字&#xff1a;节点存储的值&#xff0c;例如 45、34 等。右侧的地址&#xff08;如 0x90&#xff09;&#xff1a;表示该节点 next 指针指向的下一个节点的内存地址。 例子中&a…

Linux查看服务器的内外网地址

目录&#xff1a; 1、内网地址2、外网地址3、ping时显示地址与真实不一致 1、内网地址 ifconfig2、外网地址 curl ifconfig.me3、ping时显示地址与真实不一致 原因是dns缓存导致的&#xff0c;ping这种方法也是不准确的&#xff0c;有弊端不建议使用&#xff0c;只适用于测试…

微服务学习-服务调用组件 OpenFeign 实战

1. OpenFeign 接口方法编写规范 1.1. 在编写 OpenFeign 接口方法时&#xff0c;需要遵循以下规范 1.1.1.1. 接口中的方法必须使用 RequestMapping、GetMapping、PostMapping 等注解声明 HTTP 请求的类型。 1.1.1.2. 方法的参数可以使用 RequestParam、RequestHeader、PathVa…

基于C语言的数组从入门到精通

简介:本篇文章主要介绍了一维数组,二维数组,字符数组的定义,数组的应用,数组的核心代码解析,适用于0基础的初学者. C语言数组 1.一维数组 1.1定义 1.1.1声明 语法:数据类型 数组名[数组大小];示例:int arr[5]; 1.1.2初始化 a.静态初始化 完全初始化&#xff1a;int arr[5] {1…

音频入门(二):音频数据增强

本文介绍了一些常见的音频数据增强方法&#xff0c;并给出了代码实现。 目录 一、简介 二、代码 1. 安装必要的库 2. 代码 3. 各函数的介绍 4. 使用方法 参考&#xff1a; 一、简介 音频数据增强是机器学习和深度学习领域中用于改善模型性能和泛化能力的技术。 使用数据…

Go中new和make的区别对比

Go 中 new 和 make 的区别 在 Go 语言中&#xff0c;new 和 make 都用于分配内存&#xff0c;但它们的使用场景和行为有显著的区别。 1. new 定义 new 是 Go 语言中的一个内置函数&#xff0c;用于分配内存并返回指向该内存的指针。new 分配的内存会被初始化为零值。 作用…

消息队列篇--通信协议篇--AMOP(交换机,队列绑定,消息确认,AMOP实现实例,AMOP报文,帧,AMOP消息传递模式等)

AMQP&#xff08;Advanced Message Queuing Protocol&#xff0c;高级消息队列协议&#xff09;是一种开放的、跨平台的消息传递协议&#xff0c;旨在提供一种标准化的方式在不同的消息代理和客户端之间进行消息传递。AMQP不仅定义了消息格式和路由机制&#xff0c;还规定了如何…

LLaMA-Factory 微调LLaMA3

LoRA介绍 LoRA&#xff08;Low-Rank Adaptation&#xff09;是一种用于大模型微调的技术&#xff0c; 通过引入低秩矩阵来减少微调时的参数量。在预训练的模型中&#xff0c; LoRA通过添加两个小矩阵B和A来近似原始的大矩阵ΔW&#xff0c;从而减 少需要更新的参数数量。具体来…

【项目实战】—— 高并发内存池设计与实现

目录 一&#xff0c;项目介绍 1.1 关于高并发内存池 1.2 关于池化技术 1.3 关于malloc 二&#xff0c;定长内存池实现 2.1 实现详情 ​2.2 完整代码 三&#xff0c;高并发内存池整体设计 四&#xff0c;threadcache设计 4.1 整体设计 4.2 哈希桶映射对齐规则 4.3 …

设计模式的艺术-代理模式

结构性模式的名称、定义、学习难度和使用频率如下表所示&#xff1a; 1.如何理解代理模式 代理模式&#xff08;Proxy Pattern&#xff09;&#xff1a;给某一个对象提供一个代理&#xff0c;并由代理对象控制对原对象的引用。代理模式是一种对象结构型模式。 代理模式类型较多…

计算机网络 (54)系统安全:防火墙与入侵检测

前言 计算机网络系统安全是确保网络通信和数据不受未经授权访问、泄露、破坏或篡改的关键。防火墙和入侵检测系统&#xff08;IDS&#xff09;是维护网络系统安全的两大核心组件。 一、防火墙 定义与功能 防火墙是一种用来加强网络之间访问控制的特殊网络互联设备&#xff0c;它…

three.js+WebGL踩坑经验合集(3):THREE.Line的射线检测问题(不是阈值方面的,也不是难选中的问题)

笔者之所以要在标题里强调不是阈值方面&#xff0c;是因为网上的大多数文章提到线的射线检测问题&#xff0c;90%以上的文章都说是因为线太细所以难选中&#xff0c;然后让大家把线的阈值调大。 而本文所要探讨的问题则恰好相反&#xff0c;不是难选中&#xff0c;而是在某些角…

省市区三级联动

引言 在网页中&#xff0c;经常会遇到需要用户选择地区的场景&#xff0c;如注册表单、地址填写等。为了提供更好的用户体验&#xff0c;我们可以实现一个三级联动的地区选择器&#xff0c;让用户依次选择省份、城市和地区。 效果展示&#xff1a; 只有先选择省份后才可以选择…

快速搭建深度学习环境(Linux:miniconda+pytorch+jupyter notebook)

本文基于服务器端环境展开&#xff0c;使用的虚拟终端为Xshell。 miniconda miniconda是Anaconda的轻量版&#xff0c;仅包含Conda和Python&#xff0c;如果只做深度学习&#xff0c;可使用miniconda。 [注]&#xff1a;Anaconda、Conda与Miniconda Conda&#xff1a;创建和管…

BGP分解实验·11——路由聚合与条件性通告(3)

续接上&#xff08;2&#xff09;的实验。其拓扑如下&#xff1a; 路由聚合的负向也就是拆分&#xff0c;在有双出口的情况下&#xff0c;在多出口做流量分担是优选方法之一。 BGP可以根据指定来源而聚合路由&#xff0c;在产生该聚合路由的范围内的条目注入到本地BGP表后再向…

攻防世界easyRSA

解密脚本&#xff1a; p473398607161 q4511491 e17def extended_euclidean(a, b):if b 0:return a, 1, 0gcd, x1, y1 extended_euclidean(b, a % b)x y1y x1 - (a // b) * y1return gcd, x, ydef calculate_private_key(p, q, e):phi (p - 1) * (q - 1)gcd, x, y extend…

常见的多媒体框架(FFmpeg GStreamer DirectShow AVFoundation OpenMax)

1.FFmpeg FFmpeg是一个非常强大的开源多媒体处理框架&#xff0c;它提供了一系列用于处理音频、视频和多媒体流的工具和库。它也是最流行且应用最广泛的框架&#xff01; 官方网址&#xff1a;https://ffmpeg.org/ FFmpeg 的主要特点和功能&#xff1a; 编解码器支持: FFmpe…

.NET MAUI进行UDP通信(二)

上篇文章有写过一个简单的demo&#xff0c;本次对项目进行进一步的扩展&#xff0c;添加tabbar功能。 1.修改AppShell.xaml文件&#xff0c;如下所示&#xff1a; <?xml version"1.0" encoding"UTF-8" ?> <Shellx:Class"mauiDemo.AppShel…

计算机网络之链路层

本文章目录结构出自于《王道计算机考研 计算机网络_哔哩哔哩_bilibili》 02 数据链路层 在网上看到其他人做了详细的笔记&#xff0c;就不再多余写了&#xff0c;直接参考着学习吧。 1 详解数据链路层-数据链路层的功能【王道计算机网络笔记】_wx63088f6683f8f的技术博客_51C…

YOLOv11改进,YOLOv11检测头融合DSConv(动态蛇形卷积),并添加小目标检测层(四头检测),适合目标检测、分割等任务

前言 精确分割拓扑管状结构例如血管和道路,对各个领域至关重要,可确保下游任务的准确性和效率。然而,许多因素使任务变得复杂,包括细小脆弱的局部结构和复杂多变的全局形态。在这项工作中,注意到管状结构的特殊特征,并利用这一知识来引导 DSCNet 在三个阶段同时增强感知…