经典排序算法
- 一张图概括十大经典排序算法:
个人理解每种排序算法应该掌握的几点
-
1. 实现思路:
-
2. 会写代码:
-
3. 时间复杂度:
- (1)时间频度 :一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。
- (2)时间复杂度 在刚才提到的时间频度中,n称为问题的规模,当n不断变化时,时间频度T(n)也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。 一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
- 简单理解时间复杂度:就是算法中语句执行的次数。
-
- 算法中语句执行次数为一个常数,即时间频度 T(n) = [常数],则时间复杂度为 O(1)。
- 对于时间频度 T(n) = 2n+10 ,T(n)=n+12,则时间复杂度为O(n)
- 对于时间频度 T(n)=n^2+3n+4 与 T(n)=4n2+2n+1,则时间复杂度为O(n2)
-
-
4. 空间复杂度:空间复杂度是指算法在计算机内执行时所需存储空间的度量。在排序算法中,则是指为了实现排序所需要的临时开辟的空间数量。
-
5. 稳定性:考察排序算法的时候有一个很重要的特性,就是算法的稳定性——假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。
1.冒泡排序
-
实现思路
-
比较相邻的元素。如果第一个比第二个大,就交换它们两个; 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数; 针对所有的元素重复以上的步骤,除了最后一个; 重复步骤1~3,直到排序完成。
-
-
示例:设想被排序的数组R[1…N]垂直竖立,将每个数据元素看作有重量的气泡,根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R,凡扫描到违反本原则的轻气泡,就使其向上"漂浮",如此反复进行,直至最后任何两个气泡都是轻者在上,重者在下为止。
-
49 13 13 13 13 13 13 13 38 49 27 27 27 27 27 27 65 38 49 38 38 38 38 38 97 65 38 49 49 49 49 49 76 97 65 49 49 49 49 49 13 76 97 65 65 65 65 65 27 27 76 97 76 76 76 76 49 49 49 76 97 97 97 97
-
-
图片理解:
-
Java代码实现:
public class BubbleSort { public static void BubbleSort(int[] arr) { int temp;//定义一个临时变量 for(int i=0;i<arr.length-1;i++){//冒泡趟数 for(int j=0;j<arr.length-i-1;j++){ if(arr[j+1]<arr[j]){ temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } public static void main(String[] args) { int arr[] = new int[]{1,6,2,2,5}; BubbleSort.BubbleSort(arr); System.out.println(Arrays.toString(arr)); } }
-
时间复杂度:假设有n个数据,寻找比较的次数
-
最好情况:O(n)
2 3 4 5 6 7 8 9 n-1
冒泡排序的优化:
public static void BubbleSort(int[] arr) { int temp;//定义一个临时变量 boolean flag = false; for(int i=0;i<arr.length-1;i++){//冒泡趟数 //n-1 for(int j=0;j<arr.length-i-1;j++){ //n-2,n-3...,1=(n-2)n/2 if(arr[j+1]<arr[j]){ temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; flag = true; } } if(!flag) { break; } } }
-
最坏情况:O(n^2)
9 8 7 6 5 4 3 2 1 n=9 开始交换: 8 7 6 5 4 3 2 1 [9] n-1 = 8 7 6 5 4 3 2 1 [8 9] n-2 = 7 。 。 。 2 1 [3 4 5 6 7 8 9] 2 1 [2 3 4 5 6 7 8 9] 1 比较次数:((n-1+1)*n)/2 = n^2 / 2
-
平均时间复杂度:O(n^2)
-
-
空间复杂度: O(1)
-
稳定性:稳定
2.选择排序
-
实现思路:
-
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。 重复第二步,直到所有元素均排序完毕。
-
-
示例:
初始关键字 [49 38 65 97 76 13 27 49] 第一趟排序后 13 [38 65 97 76 49 27 49] 第二趟排序后 13 27 [65 97 76 49 38 49] 第三趟排序后 13 27 38 [97 76 49 65 49] 第四趟排序后 13 27 38 49 [49 97 65 76] 第五趟排序后 13 27 38 49 49 [97 97 76] 第六趟排序后 13 27 38 49 49 76 [76 97] 第七趟排序后 13 27 38 49 49 76 76 [ 97] 最后排序结果 13 27 38 49 49 76 76 97
-
图片理解:
-
Java代码实现:
public class Selection { public static void main(String[] args) { int[] ins = {2,3,5,1,23,6,78,34}; int[] ins2 = sort(ins); for(int in: ins2){ System.out.println(in); } } public static int[] sort(int[] ins){ int n = ins.length-1;//经过n-1次提取最小最大值 for(int i=0; i<n; i++){//控制选择的次数 int min_index = i; for(int j=i+1; j<n; j++){ //取最小值 if(ins[j]<ins[i]){ min_index = j; } } if(min_index != i){ //交换位置 int temp = ins[i]; ins[i] = ins[min_index]; ins[min_index] = temp; } } return ins; } }
-
时间复杂度:
-
最好情况:O(n^2)
-
最坏情况:O(n^2)
-
平均时间复杂度:O(n^2)
9 8 7 6 5 4 3 2 1 n=9 [1] 8 7 6 5 4 3 2 9 n-1=8 [1 2] 7 6 5 4 3 8 9 n-2=7 ... [1 2 3 4 5 6 7] 8 9 2 [1 2 3 4 5 6 7 8] 9 1 比较次数:
-
-
稳定性:由于选择排序的方式是选择出最小的元素然后再交换,所以在交换的过程中,一个较小的元素位于两个较大的相同元素后面时,那两个相同元素中位于前面的那一个就会被交换到后面去,这时原数组的顺序就会被打乱,所以选择排序是一种不稳定排序。
1 12 12 4 5 6 7 8 第一趟: 1 12 12 3 4 5 6 7 8 第二趟: 1 3 12 12 4 5 6 7 8
3.插入排序
-
实现思路:
1.将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。 2.从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
-
示例:
每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素全部插入完为止。 [初始关键字] [49] 38 65 97 76 13 27 49 J=2(38) [38 49] 65 97 76 13 27 49 J=3(65) [38 49 65] 97 76 13 27 49 J=4(97) [38 49 65 97] 76 13 27 49 J=5(76) [38 49 65 76 97] 13 27 49 J=6(13) [13 38 49 65 76 97] 27 49 J=7(27) [13 27 38 49 65 76 97] 49 J=8(49) [13 27 38 49 49 65 76 97]
-
Java代码实现:
package work; import java.sql.Array; import java.util.Arrays; public class Test { public static void main(String[] args) { int [] a = {1,23,12,12,22,31,12,25}; insertionSort(a,a.length); System.out.println(Arrays.toString(a)); } // 插入排序,a表示数组,n表示数组大小 public static void insertionSort(int[] a, int n) { if (n <= 1) return; for (int i = 1; i < n; ++i) { int value = a[i]; int j = i - 1; // 查找插入的位置 for (; j >= 0; --j) { if (a[j] > value) { a[j+1] = a[j]; // 数据移动 } else { break; } } a[j+1] = value; // 插入数据 } } }
-
时间复杂度:
-
最好情况:O(n)
[13 27 38 49 49 65 76 97] n-1
-
最坏情况:O(n^2)
[97 87 76 65 54 43 32 21] 第一趟:1 第二趟:2 ... 第n-1趟:n-1 比较次数:n^2/2
-
平均时间复杂度:O(n^2)
-
-
稳定性:稳定