十大经典排序算法——java语言

article/2025/10/24 18:50:47

文章目录

  • 一、冒泡排序
  • 二、选择排序
  • 三、插入排序
  • 四、希尔排序
  • 五、归并排序
  • 六、快速排序
  • 七、堆排序
  • 八、计数排序
  • 九、桶排序
  • 十、基数排序

一、冒泡排序

概述: 冒泡排序是一种简单直观的排序算法。它重复的走访要排序的数列,一次比较两个元素,按照一定的顺序,如果顺序错误就将他们交换过来。重复进行直到没有再需要交换,也就是该数组已经排序完成。这个算法名字的又来是因为越小的元素会经交换慢慢“浮”到数列的头部。
在这里插入图片描述

算法步骤
1.比较相邻的元素。如果第一个比第二个大,就进行交换
2.对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。做完这一步之后,最后一个元素就是最大的数
3.重复1 2 步骤,除了最后一个元素。对越来越少的元素重复上面的步骤,知道没有任何一对数字需要比较

  • 当输入的数据是正序的时候排序速度最快
  • 当输入数据是反序的时候排序速度最慢
    Java代码
 public static void sort(int[] str) {if (str == null || str.length == 0) {return;}for (int i = 0; i < str.length; i++) {for (int j = 0; j < str.length - 1; j++) {if (str[j] > str[j + 1]) {int temp = str[j];str[j] = str[j + 1];str[j + 1] = temp;}}}}

时间复杂度
O(n^2)

二、选择排序

概述: 选择排序也是一种简单直观的排序算法,无论什么数据进去都是O(n^2)的时间复杂度。所以使用选择排序时,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间。
在这里插入图片描述

算法步骤
1.在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
2.从剩余未排序元素中寻找最小(大)元素,然后放到已排序序列的末尾
3.重复第二步,直到所有元素均排序完毕

Java代码

 public static void sort(int[] str){if(str==null || str.length==0){return;}for (int i = 0; i < str.length; i++) {int min = i;for (int j = i; j < str.length; j++) {if(str[min]>str[j]){min = j;}}// 将最小值放在索引为i的位置if(min!=i){int temp = str[i];str[i] = str[min];str[min] = temp;}}}

时间复杂度
O(n^2)

三、插入排序

概述: 插入排序的代码实现起来虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理是最容易理解的,插入排序的原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
在这里插入图片描述

算法步骤
将待排序序列第一个元素看成一个有序序列,将第二个元素到最后一个元素看成未排序的序列。从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面)

Java代码

public static void sort(int[] str) {for (int i = 1; i < str.length; i++) {int temp = str[i];int j = i - 1;while (j >= 0) {if (str[j] > temp) {str[j + 1] = str[j];j--;} else {break;}}str[j + 1] = temp;}
}

四、希尔排序

概述: 希尔排序也称递减增量排序算法,是插入排序的一种更高效的改进版本。但是希尔排序是不是很稳定。

  • 希尔排序是基于插入排序的一项两点性质而提出改进方法的:
    1.插入排序在对几乎已经排好序的数据操作时,效率高,既可以达到线性排序的效率;
    2 .但插入排序一般来说时低效的,因为插入排序每次只能将数据移动一位
  • 希尔排序的思想:先将整个待排序的记录序列分割成若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,在对全体记录进行依次直接插入排序。
  • 减少了交换次数
    在这里插入图片描述

算法步骤
选择一个增量序列t1,t2,…tn 其中tn=1
按照增量序列个数n,对序列进行k趟排序,每趟排序将这些子序列用插入排序进行排序知道当增量因子为1时,整个序列作为一个序列处理
Java代码

public static void sort(int[] str) {if (str == null || str.length == 0) {return;}for (int temp = str.length / 2; temp >= 1; temp /= 2) {for (int i = temp; i < str.length; i++) {int flag = str[i];int j = i - temp;while (j >= 0) {if (str[j] > flag) {str[j + temp] = str[j];j -= temp;} else {break;}}str[j + temp] = flag;}}
}

五、归并排序

概述: 归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。

  • 将序列分为两份,两个单独进行排序,然后合并

算法步骤
1.申请空间,使其大小为两个已经排序的序列之和,该空间用来存放合并后的序列
2.设定两个指针,最初位置分别为两个已经排序序列的起始位置
3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
4.重复步骤3知道某一指针到达序列尾
5.将另一序列所剩下的所有元素直接复制到合并序列尾
Java代码

public static int[] sort(int[] str) {if (str == null || str.length < 2) {return str;}int middle = str.length / 2;int[] left = Arrays.copyOfRange(str, 0, middle);int[] right = Arrays.copyOfRange(str, middle, str.length);return merge(sort(left), sort(right));}public static int[] merge(int[] left, int[] right) {int[] result = new int[left.length + right.length];int i = 0;int m = 0;int n = 0;while (m < left.length && n < right.length) {if (left[m] < right[n]) {result[i++] = left[m++];} else {result[i++] = right[n++];}}while (m < left.length) {result[i++] = left[m++];}while (n < right.length) {result[i++] = right[n++];}return result;}

六、快速排序

概述: 快速排序的最坏运行情况是O(n^2),比如说顺序数列的快排,但它的平摊期望时间是O(nlogn), 且O(nlogn)记号中隐含的常数因子很小,比复杂度稳定等于O(nlogn)的归并排序要小很多。所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。
在这里插入图片描述

算法步骤
1.从数列中挑出一个元素,称为“基准”(pivot)
2.重新排列数列,所有比基准值小的摆放在基准前面,所有元素比基准值大的把放在后面,相同的数可以放任一边。在这个分区退出之后,该基准就处于数列的中间位置,这个称为分区(partition)操作
3.递归的(recursive)把小于基准值的元素的子数列和大于基准值元素的子数列排序

Java代码

public static void sort(int[] str, int left, int right) {// 递归到底的情况if (left >= right) {return;}// 递归操作int pivot = str[left];int i = left;int j = right;while (i < j) {// 从右边开始找第一个小于pivot的元素while (i < j && str[j] > pivot) {j--;}// 替换if (i < j) {str[i] = str[j];i++;}// 从左边开始找到第一个比pivot大的元素、while (i < j && str[i] < pivot){i++;}// 替换if(i<j){str[j] = str[i];j--;}}str[i] = pivot;sort(str, left, i-1);sort(str, i+1, right);
}

七、堆排序

数据结构之堆的概念:

  • 堆逻辑上是一棵完全二叉树
  • 堆物理上是保存在数组中
  • 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆
  • 反之,则是小堆,或者小根堆,或者最小堆
  • 堆的基本作用是快速找集合中的最值

概述: 堆排序就是利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或大于)它的父结点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法:

  • 大顶堆:每个节点的值都大于或等于其子结点的值,在堆排序算法中用于升序排列
  • 小顶堆:每个结点的值都小于或等于其子结点的值,在堆排序算法中用于降序排列

在这里插入图片描述

算法步骤
1.构建一个堆(将完全二叉树整理成堆)
2.把堆首(最大值)和堆尾互换(下沉操作)
3.调用shift down方法,使除过堆尾元素的树满足最大堆的性质
4.重复步骤2,直到堆中只有一个元素
Java代码

public static void sort(int str[]) {if (str == null || str.length == 0) {return;}// 构建堆,将完全二叉树整理成堆heapify(str);// 排序for (int i = 0; i < str.length-1; i++) {// 将首位元素替换int temp = str[0];str[0] = str[i];str[i] = temp;// 整理成堆siftDown(0, str, i);}
}public static void heapify(int[] str) {// 找到最后一个元素的父亲结点int parentIndex = getParentIndex(str.length - 1);// 从最后一个元素的父亲结点开始进行下沉操作,直到根结点for (; parentIndex >= 0; parentIndex--) {siftDown(parentIndex, str, str.length);}
}// 获取堆的根结点,根结点一般都是最中间
public static int getParentIndex(int index) {if (index < 0) {throw new IllegalArgumentException("index is invalid!");}if (index == 0) { // 处理根结点return -1;}return (index - 1) / 2;
}// 根据当前数组所在的索引获取左孩子结点的索引
private static int getLeftChileIndex(int index) {return index * 2 + 1;
}// 下沉操作
private static void siftDown(int curIndex, int[] str, int length) {int leftChildIndex = getLeftChileIndex(curIndex);int changeIndex = leftChildIndex;while (leftChildIndex < length) {int rightChildIndex = leftChildIndex + 1;if (rightChildIndex < length && str[rightChildIndex] > str[leftChildIndex]) {changeIndex = rightChildIndex;}if(str[changeIndex]>str[curIndex]){// 交换操作int temp = str[curIndex];str[curIndex] = str[changeIndex];str[changeIndex] = temp;curIndex = changeIndex;leftChildIndex = getLeftChileIndex(curIndex);changeIndex = leftChildIndex;}else{break;}}
}

时间复杂度
O(nlogn)

八、计数排序

  • 不能是负数

概述: 计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
特点: 计数排序不是比较排序,排序的速度快于任何比较排序算法。由于用来计数的数组的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。

在这里插入图片描述

算法步骤
1.找出待排序的数组中最大和最小的元素
2.统计数组中每个值为i的元素出现的次数,存入数组的第i项
3.对所有的计数累加
4.反向填充目标数组

Java代码

// 辅助函数,查找数组最大值
private static int getMaxVal(int[] str) {int max = str[0];for (int i = 0; i < str.length; i++) {max = Math.max(max, str[i]);}return max;
}public static void sort(int[] str) {if (str == null || str.length == 0) {return;}// 获取数组中最大的值int maxVal = getMaxVal(str);// 创建一个数组用来计数int[] counts = new int[maxVal + 1];// 计数for (int i = 0; i < str.length; i++) {counts[str[i]]++;}// 反向填充int index = 0;for (int i = 0; i < counts.length; i++) {while (counts[i] > 0) {str[index++] = i;counts[i]--;}}
}

时间复杂度
当输入的元素是n个0到k之间的整数时,运行时间为O(n+k)

九、桶排序

概述: 桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点:

  • 在额外空间充足的情况下,尽量增大桶的数量
  • 使用映射函数能够将输入的N个数据均匀的分配到K个桶中同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要
  • 当输入的数据可以均匀的分为到每一个桶中最快
  • 当输入的数据被分配到了同一个桶中最慢
    在这里插入图片描述

算法步骤
1.创建容器(桶)
2.初始化桶
3.将数据放入桶内
4.分别对每个桶进行排序
5.将桶中的元素反向填充到数组中

Java代码

public static void sort(int[] str) {if (str == null || str.length == 0) {return;}// 创建桶List<Integer>[] buckets = new ArrayList[10];// 初始化桶for (int i = 0; i < buckets.length; i++) {buckets[i] = new ArrayList<>();}// 将数据放入桶中for (int i = 0; i < str.length; i++) {int index = str[i] / 10;buckets[index].add(str[i]);}// 分别对每个桶进行排序for (int i = 0; i < buckets.length; i++) {buckets[i].sort(null);}// 将桶中的数据反向填充到str中int index = 0;for (int i = 0; i < buckets.length; i++) {List<Integer> item = buckets[i];while (!item.isEmpty()) {str[index++] = item.remove(0);}}
}

十、基数排序

概述: 基数排序原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。基数排序的方式可以采用LSD或MSD,LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。

  • MSD:先从高位开始进行排序,在每个关键字上,可采用计数排序
  • LSD:先从地位开始进行排序,在每个关键字上,可采用桶排序
    计数排序两种方法:
    这三种排序算法都利用了桶的概念,但对桶的使用方法上由明显差异:
  • 基数排序:根据键值的每位数字来分配桶
  • 计数排序:每个桶只存储单一键值
  • 桶排序:每个桶存储一定范围的数值
  • 在这里插入图片描述

算法步骤
1.从数组中选出最大数和最大数的位数
2.根据位数进行循环
3.每次循环获取数组中每个数的最后一位,然后将其数字放入相应的桶中
4.对桶中的元素进行排序
5.然后将桶中的数反向输出到原数组中
Java代码

public static void sort(int[] str) {if (str == null || str.length == 0) {return;}// 获取最大值int maxValue = getMaxVal(str);// 获取最大位数int maxDigit = getMaxDigit(maxValue);radixSort(str, maxDigit);
}private static void radixSort(int[] str, int maxDigit) {int mod = 1;// 创建桶List<Integer>[] buckets = new ArrayList[10];// 对桶进行初始化for (int i = 0; i < buckets.length; i++) {buckets[i] = new ArrayList<>();}// 将相应的尾数的数字放入对应的桶中for (int i = 0; i < str.length; i++) {int bucketIndex = (str[i] / mod) % 10;buckets[bucketIndex].add(str[i]);}// 对桶进行排序,分别对每个桶进行排序for (int i = 0; i < buckets.length; i++) {buckets[i].sort(null);}// 将桶中的元素反向填充到str中int index = 0;for (int i = 0; i < buckets.length; i++) {while (!buckets[i].isEmpty()){str[index++] = buckets[i].remove(0);}}
}private static int getMaxDigit(int maxValue) {int max = 0;while (maxValue > 0) {max++;maxValue /= 10;}return max;
}private static int getMaxVal(int[] str) {int max = str[0];for (int i = 0; i < str.length; i++) {max = Math.max(max, str[i]);}return max;
}

http://chatgpt.dhexx.cn/article/QbgI34xa.shtml

相关文章

Java常见排序算法

目录 1、归并排序 2、堆排序 3、基数排序 4、冒泡排序 5、希尔排序 6、快速排序 7、插入排序 8、选择排序 1、归并排序 1、基本思想 归并排序&#xff08;MERGE-SORT&#xff09;是利用归并的思想实现的排序方法&#xff0c;该算法采用经典的分治&#xff08;divide-a…

java实现七种经典排序算法

简单算法&#xff1a;冒泡&#xff0c;简单选择&#xff0c;直接插入 改进算法&#xff1a;希尔&#xff0c;堆&#xff0c;归并&#xff0c;快速 直接插入排序&#xff1a;将一个记录插入到已经拍好的有序列表中&#xff0c;从而得到一个新的、记录数增加1的有序表。 冒泡排…

java实现10种排序算法

1.冒泡排序(Bubble Sort) import java.util.Arrays; //冒泡排序 public class BubbleSort_01 {public static void main(String[] args) {int a[]{3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};//记录比较次数int count0;//i0,第一轮比较for (int i 0; i < a.length-1; i) {…

SQL sever数据库触发器设计

SQL sever数据库触发器设计 一、目的: 能够理解触发器调用的机制。能够使用SQL命令创建DML触发器。能够完成触发器的修改、删除等管理任务。 二、触发器: 定义&#xff1a;触发器&#xff08; T rigger &#xff09;是 SQL server 提供给程序员和数据分析员来保证数据完整性…

MySQL——超详细数据库触发器教程

目录 一、触发器的概念 二、创建触发器 三、查看触发器 四、删除触发器 总结 一、触发器的概念 在实际开发中往往会碰到这样的情况&#xff1a; 当我们对一个表进行数据操作时&#xff0c;需要同步对其它的表执行相应的操作&#xff0c;正常情况下&#xff0c;如果我们使用…

关于数据库触发器(trigger)的简单使用操作

最近在做一些东西&#xff0c;用到关于数据库触发器的简单使用。比如当我们在做用户模块的表设计的时候&#xff0c;我们建了联用户信息表&#xff08;t_user&#xff09;和账号表&#xff08;t_account&#xff09;&#xff0c;账号表&#xff08;t_account&#xff09;用来进…

MySQL数据库触发器

MySQL 数据库中触发器是一个特殊的存储过程&#xff0c;不同的是执行存储过程要使用 CALL 语句来调用&#xff0c;而触发器的 执行不需要使用 CALL 语句来调用&#xff0c;也不需要手工启动&#xff0c;只要一个预定义的事件发生就会被 MySQL自动调用 引发触发器执行的事件一般…

mysql数据库触发器失效,mysql 的数据库触发器解决方法

mysql 的数据库触发器 我要做一个数据库触发器&#xff0c;当删除数据库中的某一张表的时候触发这个一个事件&#xff0c;删除其他表中的某一些数据。 大家给个例子 ------解决方案-------------------- MYSQL官方免费手册中已经有现成的例子了。 CREATE TABLE test1(a1 INT); …

什么是数据库触发器?

目录 什么是数据库触发器&#xff1f; 事件 AFTER触发器 INSTEAD OF触发器 特殊数据库对象 定义 用于触发器 复杂的审计 执行业务规则 派生列值 触发器很棘手&#xff01; 什么是数据库触发器&#xff1f; 数据库触发器是在数据库中发生特定操作时运行的特殊存储过…

Oracle数据库 触发器

文章目录 一、触发器的定义二、触发器的分类三、触发器的功能四、触发器的语法五、触发器的使用案例案例1&#xff1a;向emp1表中插入一条数据后输出 欢迎加入 语句案例2&#xff1a;数据校验&#xff0c;在周四这一天不允许向emp1表中插入/更新数据案例3&#xff1a;创建触发器…

数据库之触发器详解

一、触发器的概念 触发器是与表有关的数据库对象&#xff0c;在满足定义条件时触发&#xff0c;并执行触发器中定义的语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性。 举个例子&#xff0c;比如你现在有两个表【用户表】和【日志表】&#xff0c;当一个…

数据库-触发器

目录 1. 触发器概述 2. 触发器的创建 2.1 创建触发器语法 3. 查看、删除触发器 3.2 删除触发器 4. 触发器的优缺点 4.2 缺点 4.3 注意点 在实际开发中&#xff0c;我们经常会遇到这样的情况&#xff1a;有 2 个或者多个相互关联的表&#xff0c;如 商品信息 和 库存信…

触发器(数据库必学)

文章目录 概念注意 优缺点优点缺点 语法参数说明 查看触发器删除触发器实例实际应用注意重新编辑拓展不能对同一张表进行修改 概念 触发器是一种特殊类型的存储过程&#xff0c;它不同于存储过程&#xff0c;主要是通过事件触发而被执行的。而存储过程则需要主动调用其名字执行…

C语言实现字符串逆序、倒置字符串(字符串逆序问题的升级)

一.字符串逆序 问题描述&#xff1a; 输入一个字符串str&#xff0c;将其内容颠倒过来&#xff0c;并输出。 数据范围0<len(str)<10000 输入描述&#xff1a; 输入一个字符串&#xff0c;可以有空格 输出描述&#xff1a; 输出逆序的字符串 输入样例&#xff1a; …

指针实现字符串逆序

代码如下&#xff1a; #include<stdio.h> #include<string.h> void reverse(char* str) { //指针变量分别指向第一个和最后一个元素&#xff0c;借助中间变量temp进行交换。char* left str;char* right str strlen(str) - 1;while (left < right){char temp…

c语言字符串逆序输出reverse,将一个字符串逆序输出

C语言:输入一个字符串,然后逆序输出 输入一个字符串,然后逆序输出,要CSS布局HTML小编今天和大家分享主函数调用fun函数,fun的功能是逆可以将整数当做字符串(字符串长度不超过10)接收,然后反向输出字符数组元素即可。 字符串实际长度可以用strlen函数来计算。 方法程序如下…

字符串逆序输出

字符串逆置 方法1:下标法&#xff0c;定义一个i下标从头开始&#xff0c;使用strlen函数求出字符串长度(不包括\0),定义一个j下标等于字符串长度减一&#xff0c;i&#xff0c;j下标字符进行交换&#xff0c;只需要遍历字符串长度一半即可 方法2:额外创建一个数组&#xff0c…

递归实现字符串逆序

编写一个函数 reverse_string(char * string)&#xff08;递归实现&#xff09;&#xff0c;将参数字符串中的字符反向排列&#xff0c;不是逆序打印。 &#xff08;要求&#xff1a;不能使用C函数库中的字符串操作函数。&#xff09; &#xff08;在本次练习中&#xff0c;由于…

C字符串逆序、C++字符串逆序

1.C字符串逆序&#xff1a; void CReverse(char* ch) {int nLen strlen(ch) - 1;char szStr;for (int i 0; i < nLen - i; i){szStr ch[i];ch[i] ch[nLen - i];ch[nLen - i] szStr;}ch[nLen 1] 0; } 2.C字符串逆序&#xff08;利用栈的先进后出的原理&#xff09; …

字符串逆序 - 多种方法实现

字符串逆序实现方法 1. 借助额外数组2. 循环实现2.1 图解2.2 思路2.3 代码实现 3. 递归实现14. 递归实现24.1 思路 对字符串进行逆序&#xff0c;以字符串abcdef为例 1. 借助额外数组 #include <stdio.h> #include <string.h>int main() {char str[] "abcd…