摘要:在上面的三种排序中,它们的效率为用大表示法来表示都是,但实际上按比较的次数和交换的次数来考虑,插入排序效率高于选择排序,选择排序效率高于冒泡排序。大表示法常见的基于的走势图如下图所示
大O表示法初体验
身在斯洛文尼亚的阿拉里克得到斯提里科被杀的消息后,仰天大笑:“终于没有人能阻止我去罗马了。”
当他手下的将军问:“不知大王打算走哪条路去罗马?”
西哥特王哈哈大笑,说出了那句千古名言:All roads lead to Rome
条条大路通罗马,这句著名的英语谚语告诉人们,达到同一目的可以有多种不同的方法和途径。
在编程中同样如此,同样一个编程问题,十个程序员可能会写出十种程序,算法各不相同,确实是条条大路通罗马。
但程序员对算法的效率可不会像西哥特王那般豪放和豁达,程序员对于算法的效率十分在乎,尽管算法无论效率高低,均能解决问题,但效率高的算法,除了能解决问题,还可以在问题规模变大变复杂时,高效地解决问题。
于是,我们面临一个问题,那就是如何评价,以及从什么角度去评价一个算法。
通常,我们可能说,这个算法比那个算法更快一点,但这个比较是没有意义的。当算法要处理的数据项数量不同时,谁快谁慢都要重新评价。
评价算法时,应该结合数据量来评价,即当数据量增大时,算法所消耗的时间变化趋势。
在计算机世界中,这种粗略的评价方式被称为大O表示法
简单排序 冒泡排序冒泡排序先从数组最左边开始,比较第1个和第2个元素的值,值比较高的往数组的高位排,然后依次比较第2和第3个元素,值比较大的往高位排,一直比较到倒数第2个和倒数第1个元素,这称为第一趟排序,这一趟就能确定数组中值最大的那个元素,并把这个最大的元素排到数组的最高位。
依次类推,第二趟排序会确定数组中第二大的元素,并把它排在最大的元素前边。
假设数组有n个元素,那么经过n-1趟排序,数组的元素就是有序的。
因为每一趟中,最大的元素就像水泡一样,冒到了数组的高位,冒泡排序因此得名。
/** * * * @author beanlam * @date 2016年3月9日 下午11:26:20 * @version 1.0 * */ public class SimpleSort { public static void bubbleSort(final int[] array) { if (array == null || array.length == 0) { throw new IllegalArgumentException("array"); } int length = array.length; for (int outLoop = length - 1; outLoop > 0; outLoop-- ) { for (int innerLoop = 0; innerLoop < outLoop; innerLoop++) { if (array[innerLoop] > array[innerLoop + 1]) { swap(array, innerLoop, innerLoop + 1); } } } } private static void swap(final int[] array, final int left, final int right) { int temp = array[left]; array[left] = array[right]; array[right] = temp; } }选择排序
选择排序的过程是从左向右扫描数组,并从中找出最小值的元素,把它放在左边已知的最小位置上,比如第一趟扫描,找出最小的元素后,将该元素放到数组的下标0处。第二趟扫描从下标1开始扫描,找出最小元素后,放到下标1处。总共需要扫描n-1次,就能使该数组处于有序状态。
/** * * * @author beanlam * @date 2016年3月9日 下午11:26:20 * @version 1.0 * */ public class SimpleSort { public static void selectionSort(final int[] array) { if (array == null || array.length == 0) { throw new IllegalArgumentException("array"); } int length = array.length; int minIndex; for (int outLoop = 0; outLoop < length - 1; outLoop++) { minIndex = outLoop; for (int innerLoop = outLoop + 1;innerLoop < length; innerLoop++) { if (array[innerLoop] < array[minIndex]) { minIndex = innerLoop; } } swap(array, outLoop, minIndex); } } private static void swap(final int[] array, final int left, final int right) { int temp = array[left]; array[left] = array[right]; array[right] = temp; } }插入排序
插入排序的精髓在于先令局部有序,先令左边一部分数据有序,然后这部分有序的元素的下一位再与这些有序的元素比较,寻找合适自己站立的位置,插队排进去,插队也意味着右边的有序元素要挪动身子。
一下提供基于for循环和while循环的两种插入排序实现方式:
/** * * * @author beanlam * @date 2016年3月9日 下午11:26:20 * @version 1.0 * */ public class SimpleSort { public static void insertionSort(final int[] array) { if (array == null || array.length == 0) { throw new IllegalArgumentException("array"); } int length = array.length; for (int outLoop = 1; outLoop < length; outLoop++) { int temp = array[outLoop]; for (int innerLoop = outLoop - 1; innerLoop >= 0; innerLoop--) { if (array[innerLoop] > temp) { array[innerLoop + 1] = array[innerLoop]; if (innerLoop == 0) { array[innerLoop] = temp; } } else { array[innerLoop + 1] = temp; break; } } } } public static void insertionSort1(final int[] array) { if (array == null || array.length == 0) { throw new IllegalArgumentException("array"); } int length = array.length; int temp; int innerLoop; for (int outLoop = 1; outLoop < length; outLoop++) { temp = array[outLoop]; innerLoop = outLoop; while (innerLoop > 0 && array[innerLoop - 1] >= temp) { array[innerLoop] = array[innerLoop-1]; --innerLoop; } array[innerLoop] = temp; } } private static void swap(final int[] array, final int left, final int right) { int temp = array[left]; array[left] = array[right]; array[right] = temp; } }三种排序的效率比较
假设需要比较的数组中有N个元素,
冒泡排序中,需要扫描N-1趟,每扫描一趟就要多次对两个元素做比较,并且在必要时需要对两个元素做位置的交换,由于数据是随机的,所以平均下来,一趟中大概有一半被扫描的数据需要作位置的交换。
第1趟需要N-1次比较,第2趟需要N-2次比较,以此类推,总共需要N(N-1)/2趟比较,而元素的交换次数平均下来需要做NN/4次。
选择排序和冒泡排序进行了相同次数的比较N*(N-1)/2,但每一趟只有一次交换,甚至没有任何交换。因此,选择排序比冒泡排序更有效率,因为它减少了很多交换。
插入排序却又要比选择排序更有效率一点,因为第1趟排序中,它最多比较1次,第2趟排序中,最多比较2次, 依次类推,最后一趟,最多比较N-1次,
平均只有全体数据的一半被比较,因此比较的次数为N*(N-1)/4,与冒泡和选择排序不同的是,插入排序不需要交换数据,只是把一个值赋给数组的某一个下标,赋值的速度比交换数据的速度要快很多,因此插入排序比选择排序和冒泡排序更有效率。
大O表示法只是一个粗略的估算值,它关注与随着数据量N的增大,算法速度的变化。
对于数组某个下标的赋值,算法消耗的时间是个常数K
对于线性的查找,算法的消耗时间与N成正比。
对于二分查找,算法消耗时间与log(N)成正比。
大O表示法通常会忽略常数,因为它关注的是算法的消耗时间随着N的变化而怎么变化。常数通常与处理器芯片或者编译器有关。
在上面的三种排序中,它们的效率为用大O表示法来表示都是O(N^2),但实际上按比较的次数和交换的次数来考虑,插入排序效率高于选择排序,选择排序效率高于冒泡排序。
大O表示法常见的基于N的走势图如下图所示:
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/65597.html
摘要:很早就学习了堆排序但当时没有用代码实现,现在再去想实现已经忘光光啦,于是就去网上搜了一番,发现没有一篇我能认真看完的文章,没办法就是没耐心,就是笨呗。。。 很早就学习了堆排序但当时没有用代码实现,现在再去想实现已经忘光光啦┑( ̄Д  ̄)┍,于是就去网上搜了一番,发现没有一篇我能认真看完的文章,没办法就是没耐心,就是笨呗。。。好了,言归正传= ̄ω ̄= 了解概念 首先明白什么是堆,什么是完...
摘要:泛化泛化数据集实验泛化过拟合的风险泛化泛化能力是指机器学习算法对新鲜样本的适应能力。机器学习的基本冲突是适当拟合我们的数据,但也要尽可能简单地拟合数据。使用测试集再次检查该模型。特征通常在正常范围内,其中第百分位数的值约为。 泛化&泛化数据集&实验 泛化 (Generalization):过拟合的风险 泛化:泛化能力(generalization ability)是指机器学习算法对新...
摘要:三性能优化处理做工具类的项目,性能是非常大的挑战,我总结了以下几个常见的性能优化点数据缓存。防抖,节流,事件委托内存释放。 内容大纲: 1、功能介绍 2、技术架构 3、性能优化 4、细节分享 5、开源说明 一、项目功能介绍 很久没写过技术类的文章了,这次给大家分享一个近期的项目,采用react+mobx+jquery构建的大型工具类项目。查看项目网址。 如果用过易企秀,maka或者...
摘要:三性能优化处理做工具类的项目,性能是非常大的挑战,我总结了以下几个常见的性能优化点数据缓存。防抖,节流,事件委托内存释放。 内容大纲: 1、功能介绍 2、技术架构 3、性能优化 4、细节分享 5、开源说明 一、项目功能介绍 很久没写过技术类的文章了,这次给大家分享一个近期的项目,采用react+mobx+jquery构建的大型工具类项目。查看项目网址。 如果用过易企秀,maka或者...
阅读 3185·2021-11-23 09:51
阅读 3540·2021-11-09 09:46
阅读 3558·2021-11-09 09:45
阅读 2919·2019-08-29 17:31
阅读 1798·2019-08-26 13:39
阅读 2690·2019-08-26 12:12
阅读 3567·2019-08-26 12:08
阅读 2175·2019-08-26 11:31