排序算法基础(冒泡排序,选择排序,插入排序)

    科技2025-10-08  2

    排序算法的介绍

    排序也称排序算法(Sort Algorithm),排序是将一组数据,依指定的顺序进行排列的过程。

    排序的分类:

    内部排序: 指将需要处理的所有数据都加载到内部存储器(内存)中进行排序。外部排序法: 数据量过大,无法全部加载到内存中,需要借助外部存储(文件等)进行排序。常见的排序算法分类(见下图): 其中直接插入排序,简单选择排序,冒泡排序最常见基数排序也是桶排序的一种

    冒泡排序

    基本介绍

    冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。

    优化:

    因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志flag 判断元素是否进行过交换。从而减少不必要的比较。(这里说的优化,可以在冒泡排序写好后,在进行)

    演示冒泡过程的例子(图解)

    小结上面的图解过程:

    1.一共进行数组的大小-1 次大的循环

    每一趟排序的次数在逐渐的减少如果我们发现在某趟排序中,没有发生一次交换, 可以提前结束冒泡排序。这个就是优化

    交换条件: if (arr[j] > arr[j + 1])

    交换过程:

    把a[j]保存起来:temp = arr[j];j+1位置向左移:arr[j] = arr[j + 1];j+1位置的元素变成:arr[j + 1]= temp;

    优化 if (!flag) { // 在一趟排序中,一次交换都没有发生过 break; } else { flag = false; // 重置flag!!!, 进行下次判断 }

    代码实现

    import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; public class BubbleSort { public static void main(String[] args) { // int arr[] = {3, 9, -1, 10, 20}; // // System.out.println("排序前"); // System.out.println(Arrays.toString(arr)); //为了容量理解,我们把冒泡排序的演变过程,给大家展示 //测试一下冒泡排序的速度O(n^2), 给80000个数据,测试 //创建要给80000个的随机的数组 int[] arr = new int[80000]; for(int i =0; i < 80000;i++) { arr[i] = (int)(Math.random() * 8000000); //生成一个[0, 8000000) 数 } Date data1 = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date1Str = simpleDateFormat.format(data1); System.out.println("排序前的时间是=" + date1Str); //测试冒泡排序 bubbleSort(arr); Date data2 = new Date(); String date2Str = simpleDateFormat.format(data2); System.out.println("排序后的时间是=" + date2Str); //System.out.println("排序后"); //System.out.println(Arrays.toString(arr)); /* // 第二趟排序,就是将第二大的数排在倒数第二位 for (int j = 0; j < arr.length - 1 - 1 ; j++) { // 如果前面的数比后面的数大,则交换 if (arr[j] > arr[j + 1]) { temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } System.out.println("第二趟排序后的数组"); System.out.println(Arrays.toString(arr)); // 第三趟排序,就是将第三大的数排在倒数第三位 for (int j = 0; j < arr.length - 1 - 2; j++) { // 如果前面的数比后面的数大,则交换 if (arr[j] > arr[j + 1]) { temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } System.out.println("第三趟排序后的数组"); System.out.println(Arrays.toString(arr)); // 第四趟排序,就是将第4大的数排在倒数第4位 for (int j = 0; j < arr.length - 1 - 3; j++) { // 如果前面的数比后面的数大,则交换 if (arr[j] > arr[j + 1]) { temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } System.out.println("第四趟排序后的数组"); System.out.println(Arrays.toString(arr)); */ } // 将前面额冒泡排序算法,封装成一个方法 public static void bubbleSort(int[] arr) { // 冒泡排序 的时间复杂度 O(n^2), 自己写出 int temp = 0; // 临时变量 boolean flag = false; // 标识变量,表示是否进行过交换 for (int i = 0; i < arr.length - 1; i++) { for (int j = 0; j < arr.length - 1 - i; j++) { //因为比较的是j和j+1所以j<arr.length-1,j< arr.length则溢出 // 如果前面的数比后面的数大,则交换 if (arr[j] > arr[j + 1]) { flag = true; temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } //System.out.println("第" + (i + 1) + "趟排序后的数组"); //System.out.println(Arrays.toString(arr)); if (!flag) { // 在一趟排序中,一次交换都没有发生过 break; } else { flag = false; // 重置flag!!!, 进行下次判断 } } } }

    问题:

    问题一:第一次是j < arr.length - 1的原因 for (int j = 0; j < arr.length - 1 - i; j++) { //因为比较的是j和j+1所以j<arr.length-1,j< arr.length则溢出 问题二:为什么是 j < arr.length - 1 - i最后为什么减i 第一趟排序,就是将第一大的数排在倒数第一位 第二趟排序,就是将第二大的数排在倒数第二位 第一趟后最后一位确定了不用再遍历到最后一位了,可以减少循环次数。

    选择排序

    基本介绍

    选择式排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的。

    选择排序思想:

    选择排序(select sorting)也是一种简单的排序方法。它的基本思想是: 第一次从arr[0]~arr[n-1]中选取最小值,与arr[0]交换, 第二次从arr[1]~arr[n-1]中选取最小值,与arr[1]交换, 第三次从arr[2]~arr[n-1]中选取最小值,与arr[2]交换,…, 第i 次从arr[i-1]~arr[n-1]中选取最小值,与arr[i-1]交换,…, 第n-1 次从arr[n-2]~arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1 次,得到一个按排序码从小到大排列的有序序列。

    选择排序思路分析图:

    对一个数组的选择排序再进行讲解

    原始的数组 : 101, 34, 119, 1 第一轮排序 : 1, 34, 119, 101 第二轮排序 : 1, 34, 119, 101 第三轮排序 : 1, 34, 101, 119 说明:

    选择排序一共有 数组大小 - 1 轮排序每1轮排序,又是一个循环, 循环的规则(代码) 2.1先假定当前这个数是最小数 2.2 然后和后面的每个数进行比较,如果发现有比当前数更小的数,就重新确定最小数,并得到下标 2.3 当遍历到数组的最后时,就得到本轮最小数和下标 2.4 交换 [ 交换到数组-1第一个数组]

    简单的说:

    假如:int [] arr = {101, 34, 119, 1};

    先定义:arr[i]是需要交换的指针min指向最小的数和指针的下标minIndex先假设最小的就是arr[i],min指arr[i],最后一次不用指,因为已经是最后一个了。 for (int i = 0; i < arr.length - 1; i++) { int minIndex = i; int min = arr[i]; }又一个for循环:j也就是min的下一个,i+1,他就会在剩下的i+1至arr.length中找最小的重置min,minIndex(假如是最后一个则,minlndex=4,min=1) for (int j = i + 1; j < arr.length; j++) { if (min > arr[j]) { // 说明假定的最小值,并不是最小 min = arr[j]; // 重置min minIndex = j; // 重置minIndex }交换: 3.1: arr[minIndex] = arr[i];(第一个换到最后一个去即数组变成:{1,34,119,101} 3.2 : arr[i] = min;(第一个是min=1)优化: 假如minlndex本来就是第一个了就不用交换了所以不是i才进行交换 if (minIndex != i) { arr[minIndex] = arr[i]; arr[i] = min; }

    选择排序应用实例:

    有一群牛, 颜值分别是101, 34, 119, 1 请使用选择排序从低到高进行排序[101, 34, 119, 1]

    代码实现

    import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; //选择排序 public class SelectSort { public static void main(String[] args) { //int [] arr = {101, 34, 119, 1, -1, 90, 123}; //创建要给80000个的随机的数组 int[] arr = new int[80000]; for (int i = 0; i < 80000; i++) { arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数 } System.out.println("排序前"); //System.out.println(Arrays.toString(arr)); Date data1 = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date1Str = simpleDateFormat.format(data1); System.out.println("排序前的时间是=" + date1Str); selectSort(arr); Date data2 = new Date(); String date2Str = simpleDateFormat.format(data2); System.out.println("排序前的时间是=" + date2Str); //System.out.println("排序后"); //System.out.println(Arrays.toString(arr)); } //选择排序 public static void selectSort(int[] arr) { //在推导的过程,我们发现了规律,因此,可以使用for来解决 //选择排序时间复杂度是 O(n^2) for (int i = 0; i < arr.length - 1; i++) { int minIndex = i; int min = arr[i]; for (int j = i + 1; j < arr.length; j++) { if (min > arr[j]) { // 说明假定的最小值,并不是最小 min = arr[j]; // 重置min minIndex = j; // 重置minIndex } } // 将最小值,放在arr[0], 即交换 if (minIndex != i) { arr[minIndex] = arr[i]; arr[i] = min; } //System.out.println("第"+(i+1)+"轮后~~"); //System.out.println(Arrays.toString(arr));// 1, 34, 119, 101 } /* //使用逐步推导的方式来,讲解选择排序 //第1轮 //原始的数组 : 101, 34, 119, 1 //第一轮排序 : 1, 34, 119, 101 //算法 先简单--》 做复杂, 就是可以把一个复杂的算法,拆分成简单的问题-》逐步解决 //第1轮 int minIndex = 0; int min = arr[0]; for(int j = 0 + 1; j < arr.length; j++) { if (min > arr[j]) { //说明假定的最小值,并不是最小 min = arr[j]; //重置min minIndex = j; //重置minIndex } } //将最小值,放在arr[0], 即交换 if(minIndex != 0) { arr[minIndex] = arr[0]; arr[0] = min; } System.out.println("第1轮后~~"); System.out.println(Arrays.toString(arr));// 1, 34, 119, 101 //第2轮 minIndex = 1; min = arr[1]; for (int j = 1 + 1; j < arr.length; j++) { if (min > arr[j]) { // 说明假定的最小值,并不是最小 min = arr[j]; // 重置min minIndex = j; // 重置minIndex } } // 将最小值,放在arr[0], 即交换 if(minIndex != 1) { arr[minIndex] = arr[1]; arr[1] = min; } System.out.println("第2轮后~~"); System.out.println(Arrays.toString(arr));// 1, 34, 119, 101 //第3轮 minIndex = 2; min = arr[2]; for (int j = 2 + 1; j < arr.length; j++) { if (min > arr[j]) { // 说明假定的最小值,并不是最小 min = arr[j]; // 重置min minIndex = j; // 重置minIndex } } // 将最小值,放在arr[0], 即交换 if (minIndex != 2) { arr[minIndex] = arr[2]; arr[2] = min; } System.out.println("第3轮后~~"); System.out.println(Arrays.toString(arr));// 1, 34, 101, 119 */ } }

    问题:

    如果改为降序 if (min > arr[j])改为if (min <carr[j])即可

    上面的选择排序的时间比冒泡排序短 虽然他们时间复杂度一样但是选择排序for循环里面的语句少很多,而且是if语句,很多情况下不用执行

    插入排序

    插入排序法介绍:

    插入式排序属于内部排序法,是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的。

    插入排序法思想:

    插入排序(Insertion Sorting)的基本思想是:把n 个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1 个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

    插入排序思路图:

    思路分析

    标准分析. 有序表在左边,无序表在右边。 insertVal (temp)是无序表的第一个指针指向a[i] insertIndex(j)有序表倒数第一个指针的下标

    第一次: temp指针在第二个也就是arr[1]这个位置, j为0,arr[j]看成一个有序表循环(找不到插入位置)j指的位置大于temp: while( j一定要>=0,temp< arr[j],j- -(有序表内查找)); arr[j + 1] = arr[j](意思就是有序列表数组的元素向右移动)当找到位置的时候插入:j指的位置小于temp了,所以j后面一位就可以是temp了: arr[j+ 1] = temp;优化:要赋值插入的数字本来就在j后面一位即if(insertIndex + 1 = i),不用赋值了直接下轮循环如果不是 if(insertIndex + 1 != i) { arr[insertIndex + 1] = insertVal; }

    插入排序法应用实例:

    有一群小牛, 考试成绩分别是101, 34, 119, 1 请从小到大排序

    代码实现:

    import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; public class InsertSort { public static void main(String[] args) { //int[] arr = {101, 34, 119, 1, -1, 89}; // 创建要给80000个的随机的数组 int[] arr = new int[80000]; for (int i = 0; i < 80000; i++) { arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数 } System.out.println("插入排序前"); Date data1 = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date1Str = simpleDateFormat.format(data1); System.out.println("排序前的时间是=" + date1Str); insertSort(arr); //调用插入排序算法 Date data2 = new Date(); String date2Str = simpleDateFormat.format(data2); System.out.println("排序前的时间是=" + date2Str); //System.out.println(Arrays.toString(arr)); } //插入排序 public static void insertSort(int[] arr) { int insertVal = 0; int insertIndex = 0; //使用for循环来把代码简化 for(int i = 1; i < arr.length; i++) { //定义待插入的数 insertVal = arr[i]; insertIndex = i - 1; // 即arr[1]的前面这个数的下标 // 给insertVal 找到插入的位置 // 说明 // 1. insertIndex >= 0 保证在给insertVal 找插入位置,不越界 // 2. insertVal < arr[insertIndex] 待插入的数,还没有找到插入位置 // 3. 就需要将 arr[insertIndex] 后移 while (insertIndex >= 0 && insertVal < arr[insertIndex]) { arr[insertIndex + 1] = arr[insertIndex];// arr[insertIndex] insertIndex--; } // 当退出while循环时,说明插入的位置找到, insertIndex + 1 // 举例:理解不了,我们一会 debug //这里我们判断是否需要赋值 if(insertIndex + 1 != i) { arr[insertIndex + 1] = insertVal; } //System.out.println("第"+i+"轮插入"); //System.out.println(Arrays.toString(arr)); } /* //使用逐步推导的方式来讲解,便利理解 //第1轮 {101, 34, 119, 1}; => {34, 101, 119, 1} //{101, 34, 119, 1}; => {101,101,119,1} //定义待插入的数 int insertVal = arr[1]; int insertIndex = 1 - 1; //即arr[1]的前面这个数的下标 //给insertVal 找到插入的位置 //说明 //1. insertIndex >= 0 保证在给insertVal 找插入位置,不越界 //2. insertVal < arr[insertIndex] 待插入的数,还没有找到插入位置 //3. 就需要将 arr[insertIndex] 后移 while(insertIndex >= 0 && insertVal < arr[insertIndex] ) { arr[insertIndex + 1] = arr[insertIndex];// arr[insertIndex] insertIndex--; } //当退出while循环时,说明插入的位置找到, insertIndex + 1 //举例:理解不了,我们一会 debug arr[insertIndex + 1] = insertVal; System.out.println("第1轮插入"); System.out.println(Arrays.toString(arr)); //第2轮 insertVal = arr[2]; insertIndex = 2 - 1; while(insertIndex >= 0 && insertVal < arr[insertIndex] ) { arr[insertIndex + 1] = arr[insertIndex];// arr[insertIndex] insertIndex--; } arr[insertIndex + 1] = insertVal; System.out.println("第2轮插入"); System.out.println(Arrays.toString(arr)); //第3轮 insertVal = arr[3]; insertIndex = 3 - 1; while (insertIndex >= 0 && insertVal < arr[insertIndex]) { arr[insertIndex + 1] = arr[insertIndex];// arr[insertIndex] insertIndex--; } arr[insertIndex + 1] = insertVal; System.out.println("第3轮插入"); System.out.println(Arrays.toString(arr)); */ } }

    for循环版本

    void StraightSort(int *arr,int len) { int tmp; int i; int j; for (i = 1;i < len;i++) { tmp = arr[i]; for (j = i - 1;j >= 0 && arr[j] > tmp;j--) { arr[j + 1] = arr[j]; } arr[j + 1] = tmp; } }

    问题:

    降序的时候 while (insertIndex >= 0 && insertVal < arr[insertIndex])改为while (insertIndex >= 0 && insertVal >arr[insertIndex])

    insertIndex一定要>=0 一定要在insertIndex一定要>=0的情况下减减,不然要插入的数字在第一个的时候也就是arr[0]就会出错,因为insertIndex会一直减下去

    注意:

    for (int j = 0; j < arr.length-1; j++)中 j < arr.length-1代表arr[j]倒数第二位 for (int j = 0; j < arr.length; j++)中 j < arr.length代表arr[j]倒数第一位

    Processed: 0.011, SQL: 8