算法学习-排序算法
数据结构-算法复杂度
时间复杂度
事件频度
一个算法花费的时间与算法种语句的执行次数成正比,哪个算法种语句执行次数多,它花费时间就多。
一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)
eg:计算1-100的和
方法一:for循环 T(n) = n + 1
方法二:直接计算 T(n) = 1
时间复杂度
- 一般情况下,算法中的基本操作语句的重复执行次数时问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n) / f(n) 的极限值为不等于0的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n))为算法的渐进时间复杂度,简称时间复杂度
- T(n)不同,但时间复杂度可能相同
- 计算时间复杂度的方法
- 用常数1代替运行时间中的所有加法常数
- 修改后的运行次数函数中,只保留最高阶项
- 去除最高阶项的系数
常见的时间复杂度
- 常数阶O(1)(无论执行多少行,只要没有循环等复杂结构,就是常数阶)
- 对数阶O(log2n)(while(i < n) { i *= 2})
- 线性阶O(n)(for(int i = 0; i < =n; i++))
- 线性对数阶O(nlog2n)(前两个结合)
- 平方阶O(n^2)
- 立方阶O(n^3)
- k次方阶O(n^K)
- 指数阶O(2^n)
说明:常见的算法时间复杂度由小到大如上述,从上到下,随着n的增加,时间复杂度不断增大,算法的执行效率越低
平均时间复杂度和最坏时间复杂度
- 平均时间复杂度是指等概率下的运行时间
- 最坏情况,一般讨论的时间复杂度都是最坏的时间复杂度,因为这是上限,不会比它更长
- 平均复杂度和最坏复杂度是否一致,与算法有关
空间复杂度
基本介绍
- 类似于时间复杂度,一个算法的空间复杂度定义为该算法所耗费的存储空间,它也是问题规模n的函数
- 空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度。随n的增大而增大,当n较大时,将占用较多的存储单元,例如快速排序与归并排序
- 在做算法分析时,主要考虑时间复杂度,一些缓存产品和算法,本质上就是用空间换时间
排序算法介绍
基本介绍
排序也称为排序算法(Sort Algorithm),排序是将一组数据,依指定的顺序进行排列的过程。
排序的分类
内部排序
指将需要处理的所有数据都加载到内部存储器中进行排序
外部排序
数据量过大,无法全部加载到内存中,需要借助外部存储进行排序
常见的排序算法分类
常用的排序算法对比
排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 排序方式 | 稳定性 |
---|---|---|---|---|---|---|
冒泡排序 | O(n^2) | O(n) | O(n^2) | O(1) | 不占用额外内存 | 稳定 |
选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不占用额外内存 | 不稳定 |
插入排序 | O(n^2) | O(n) | O(n^2) | O(1) | 不占用额外内存 | 稳定 |
序 | O(n log n) | O(n log2 n) | O(n log2 n) | O(1) | 不占用额外内存 | 不稳定 |
排序 | O(n log n) | O(n log n) | O(n log n) | O(n) | 占用额外内存 | 稳定 |
序 | O(n log n) | O(n log n) | O(n^2) | O(log n) | 不占用额外内存 | 不稳定 |
序 | O(n log n) | O(n log n) | O(n log n) | O(1) | 不占用额外内存 | 不稳定 |
序 | O(n + k) | O(n + k) | O(n + k) | O(K) | 占用额外内存 | 稳定 |
序 | O(n + k) | O(n + k) | O(n^2) | O(n + k) | 占用额外内存 | 稳定 |
序 | O(n * k) | O(n * k) | O(n * k) | O(n + k) | 占用额外内存 | 稳定 |
- 稳定是指:若a原本在b前面,经过排序后a仍然在b前面
- 内排序:所有操作在内存中完成
- 外排序:由于数据过大,因此把数据放在磁盘中,排序通过磁盘和内存的数据传输才能进行
- k:桶的个数
排序算法-冒泡排序
基本介绍
冒泡排序(Bubble Sorting)的基本思想是:通过对待排序顺序从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就像水中的气泡一样逐渐向上冒
因为排序的过程中,各个元素不断接近自己的位置,若一趟比较下来没有进行过交换,则说明排序有序,因此要在排序中设置一个标志 flag 判断元素是否进行过交换,若没有,说明排序完毕,从而减少不必要的比较
代码实现
public static void bubbleSort(int[] arr){ //冒泡排序的时间复杂度O(n^2) int temp = 0; //临时变量 boolean flag = false; //标识变量,表示是否进行过交换 for(int i = 0; i < arr.length - 1; i++) { //共进行n-1躺排序 for(int j = 0; j < arr.length - 1 - i; j++) { //共比较 n - 1 - i 次比较 //若前面的数大于后面的数,则交换 if(arr[j] > arr[j + 1]) { flag = true; //若交换,则 将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让其进行下次判断 } } }
public static void main(String[] args) { int[] arr = {3, 9, -1, 10, -2}; System.out.println("排序前:"); System.out.println(Arrays.toString(arr)); bubbleSort(arr); System.out.println("排序后:"); System.out.println(Arrays.toString(arr)); }
测试冒泡排序的速度,给80000个数据
public static void main(String[] args) { int[] arr = new int[80000]; for(int i = 0; i < 80000; i++) { arr[i] = (int)(Math.random()* 8000000); //生成一个[0, 8000000)的数 } Date date1 = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date1Str = simpleDateFormat.format(date1); System.out.println("排序前的时间是 :" + date1Str); bubbleSort(arr); Date date2 = new Date(); String date2Str = simpleDateFormat.format(date2); System.out.println("排序后的时间是 :" + date2Str); }
排序八万个数,大约花费时间 13 秒
排序算法-选择排序
基本介绍
选择排序(select sorting)也属于内部排序,是从要排序的数据中,按指定的规则选出来某一元素,再按规定交换位置后达到排序的目的
排序思想
第一次从arr[0] ~ arr[n - 1]中选取最小值,与arr[0] 交换
第二次从arr[1] ~ arr[n - 1]中选取最小值,与arr[1] 交换
第i次...
第n - 1次从arr[n - 2] ~ arr[n - 1]中选取最小值,与arr[n - 2] 交换
总共通过 n - 1 次,得到一个从小到大的有序序列
代码实现
- 10 34 19 100 80 进行排序
package cn.imut.sort; import java.util.Arrays; //选择排序 public class SelectSort { public static void main(String[] args) { int[] arr = {101, 34, 119, 1, -1, 90, 123}; System.out.println("排序前"); System.out.println(Arrays.toString(arr)); selectSort(arr); System.out.println("排序后"); System.out.println(Arrays.toString(arr)); } public static void selectSort(int[] arr) { for(int i = 0; i < arr.length - 1; i++) { int minIndex = i; //最小值的索引,假定为0 int min = arr[i]; //假定第一个数就是最小值 for(int j = i + 1; j < arr.length; j++) { if(min > arr[j]) { //说明假定的最小值并不是最小的 min = arr[j]; //重置最小值 minIndex = j; //重置最小值下标 } } //将最小值,放在arr[0],进行交换 if(minIndex != i) { //i与假设最小值下标是重合的,只有交换才有区别 arr[minIndex] = arr[i]; arr[i] = min; } } } }
对选择排序进行速度测试
public static void main(String[] args) { int[] arr = new int[80000]; for(int i = 0; i < 80000; i++) { arr[i] = (int)(Math.random() * 8000000); //生成一个[0, 8000000)的数 } Date date1 = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date1Str = simpleDateFormat.format(date1); System.out.println("排序前的时间是 :" + date1Str); selectSort(arr); Date date2 = new Date(); String date2Str = simpleDateFormat.format(date2); System.out.println("排序后的时间是 :" + date2Str); }
结论:选择排序八万个数字排序只需3秒
排序算法-插入排序
基本介绍
插入排序属于内部排序,是对要排序的元素以插入的方式寻找该元素的适当位置,以达到排序的目的
算法思想
把n个待排序的元素看成一个有序表和一个无序表
开始时,有序表只含有一个元素,无序表中有n-1个元素
排序过程中每次从无序表取出第一个元素,把它的排序码依次和有序表元素的排序码进行比较
将它插入到有序表中的适当位置,使之称为新的有序表
代码实现
package cn.imut.sort; import com.sun.org.apache.xpath.internal.SourceTree; import java.util.Arrays; //插入排序 public class InsertSort { public static void main(String[] args) { int[] arr = {101, 34, 119, 1}; insertSort(arr); } //插入排序 public static void insertSort(int[] arr) { for(int i = 1; i < arr.length; i++) { int insertVal = arr[i]; //要插入的数(第二个列表的首位) int insertIndex = i - 1; //要插入数的前一个数的下标(第一个列表的末尾的编号) //注:第一个列表是有序的 //insertIndex > 0 保证 插入的位置不越界 //insertVal < arr[insertIndex] 说明 要插入的数还没有找到插入的位置,否则它正常加入到最后即可 //接着将 arr[insertIndex] 后移 while(insertIndex >= 0 && insertVal < arr[insertIndex]) { arr[insertIndex + 1] = arr[insertIndex]; insertIndex--; } //退出while循环时,说明插入的位置找到,insertIndex + 1 arr[insertIndex + 1] = insertVal; System.out.println("第" + i + "轮插入"); System.out.println(Arrays.toString(arr)); } } }
插入排序速度测试
public static void main(String[] args) { int[] arr = new int[80000]; for(int i = 0; i < 80000; i++) { arr[i] = (int)(Math.random() * 8000000); //生成一个[0,8000000) 数 } Date date1 = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format = simpleDateFormat.format(date1); System.out.println("排序前的时间是:" + format); insertSort(arr); Date date2 = new Date(); String format1 = simpleDateFormat.format(date2); System.out.println("排序后的时间是:" + format1); }
插入排序测试八万个数据,只需1秒
相关推荐
要知道时间复杂度只是描述一个增长趋势,复杂度为O的排序算法执行时间不一定比复杂度为O长,因为在计算O时省略了系数、常数、低阶。实际上,在对小规模数据进行排序时,n2的值实际比 knlogn+c还要小。