插入、冒泡、归并、堆排序、快排总结
来源:互联网 发布:福利直播软件 编辑:程序博客网 时间:2024/05/17 06:44
1、插入排序
public static void insertionSort(int[] array) { int l = array.length; int j; for (int i = 1; i < l; i++) { int temp = array[i]; //temp <= array[j - 1] 会因为等于多一次比较 for (j = i; j > 0 && temp < array[j - 1]; j--) array[j] = array[j - 1]; array[j] = temp; } }
空间消耗 O(1) (临时保存array[i])
平均时间复杂度 O(n^2)
最好情况 O(n) (已经排序,内层for循环的检测总是立即判断不成立而终止)
最坏情况 O(n^2) (需排序的为逆序)
如果目标是把n个元素的序列升序排列,那么采用插入排序存在最好情况和最坏情况。最好情况就是,序列已经是升序排列了,在这种情况下,需要进行的比较操作需(n-1)次即可。最坏情况就是,序列是降序排列,那么此时需要进行的比较共有n(n-1)/2次。插入排序的赋值操作是比较操作的次数加上 (n-1)次。平均来说插入排序算法的时间复杂度为O(n^2)。
(可以使用二分查找法进行优化)
2、冒泡排序
冒泡排序(Bubble Sort,台湾译为:泡沫排序或气泡排序)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
可以是将最小的冒上去,或者最大的沉下去。
//改进后的,增加了一个判断标志public static void bubbleSort(int[] array) { boolean flag = true; //若flag 为false表明剩下的序列是有序的了 for (int i = 0; i < array.length && flag; i++) { flag = false; for (int j = array.length - 1; j > i; j--) { if (array[j] < array[j - 1]) { int temp = array[j]; array[j] = array[j - 1]; array[j - 1] = temp; flag = true;//表明有数据交换 } } }
空间消耗 O(1) (用于交换相邻数据)
平均时间复杂度 O(n^2)
最好情况 O(n) (改进后的,避免对已经有序的序列重复进行循环比较,未改进的为O(n^2) )
最坏情况 O(n^2) (需排序的为逆序)
当最好情况下,即需排序的数组本身是有序的,根据改进的代码。可以推断出有n-1次比较,没有数据交换。当最坏的时候,即需排序的数组本身是逆序的,此时需要比较n(n-1)/2次,并做等量数量级的记录移动。
3、归并排序
(1)递归实现
public static void mergeSort(int[] array) { int[] tempArr = new int[array.length]; mergeSort(array, tempArr, 0, array.length - 1); } private static void mergeSort(int[] array, int[] tempArr, int left, int right) { if (left < right) { int center = (left + right) / 2; //递归将左边的归并为有序 mergeSort(array, tempArr, left, center); //递归将右边的归并为有序 mergeSort(array, tempArr, center + 1, right); //将左右两个子序列归并到一起 merge(array, tempArr, left, center + 1, right); } } private static void merge(int[] array, int[] tempArr, int leftPos, int rightPos, int rightEnd) { int leftEnd = rightPos - 1, tmpPos = leftPos, num = rightEnd - leftPos + 1; while (leftPos <= leftEnd && rightPos <= rightEnd) { if (array[leftPos] < array[rightPos]) tempArr[tmpPos++] = array[leftPos++]; else tempArr[tmpPos++] = array[rightPos++];// tempArr[tmpPos++] = array[array[leftPos] < array[rightPos] ? leftPos++ : rightPos++]; } while (leftPos <= leftEnd) tempArr[tmpPos++] = array[leftPos++]; while (rightPos <= rightEnd) tempArr[tmpPos++] = array[rightPos++]; for (int i = 0; i < num; i++, rightEnd--) array[rightEnd] = tempArr[rightEnd]; }
空间消耗 O(n+log n)
平均时间复杂度 O(n log n)
最好情况 O(n log n)
最坏情况 O(n log n)
(2)非递归实现
public static void mergeSort(int[] arr) { int len = arr.length; int k = 1; while(k < len) { mergePass(arr, k, len); k *= 2; } } //mergePass方法负责将数组中的相邻的有k个元素的序列进行归并 private static void mergePass(int[] arr, int k, int n) { int i = 0; //从前往后,将2个长度为k的子序列合并为1个 //n - 2*k + 1中加 1 的原因是数组的下表是从 0 开始的 //且需要保证两两合并的序列(非落单的序列)长度为k while(i < n - 2*k + 1) { merge(arr, i, i + k-1, i + 2*k - 1); i += 2*k; } //这段代码保证了,将那些“落单的”长度不足两两merge的部分和前面merge起来。 if(i < n - k ) { merge(arr, i, i+k-1, n-1); } } //merge函数实际上是将两个有序数组合并成一个有序数组 private static void merge(int[] arr, int low, int mid, int high) { //temp数组用于暂存合并的结果 int[] temp = new int[high - low + 1]; int i = low; int j = mid+1; int k = 0; //将记录由小到大地放进temp数组 for(; i <= mid && j <= high; k++) { if(arr[i] < arr[j]) temp[k] = arr[i++]; else temp[k] = arr[j++]; } //接下来两个while循环是为了将剩余的(比另一边多出来的个数)放到temp数组中 while(i <= mid) temp[k++] = arr[i++]; while(j <= high) temp[k++] = arr[j++]; //将temp数组中的元素写入到待排数组中 for(int l = 0; l < temp.length; l++) arr[low + l] = temp[l]; }
空间消耗 O(n)
避免了递归时需要的深度为 log n 的栈空间
4、堆排序
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
在下面的篇博客中说得挺好的,具体的实现细节可以参考,但是是用JS实现代码的:
http://bubkoo.com/2014/01/14/sort-algorithm/heap-sort/
public static void heapSort(int[] arr) { //构建初始最大堆 for (int i = arr.length/2-1; i >=0; i--) { buildMaxHeap(arr,i,arr.length); } for (int i = arr.length-1; i > 0 ; i--) { //交换堆顶最大值和最后一个叶子结点 int tmp = arr[0]; arr[0] = arr[i]; arr[i] = tmp; //重新构建剩下的序列的最大堆 buildMaxHeap(arr,0,i); } } //构建最大堆 public static void buildMaxHeap(int[] array,int index,int heapSize) { int iMax, iLeft, iRight; while (true) { iMax = index; iLeft = 2 * index + 1; iRight = 2 * (index + 1); if (iLeft < heapSize && array[index] < array[iLeft]) { iMax = iLeft; } if (iRight < heapSize && array[iMax] < array[iRight]) { iMax = iRight; } if (iMax != index) { int tmp = array[iMax]; array[iMax] = array[index]; array[index] = tmp; index = iMax; } else { break; } } }
空间消耗 O(1)
平均时间复杂度 O(n log n)
最好情况 O(n log n)
最坏情况 O(n log n)
堆排序复杂度分析: 它的运行时间主要是消耗在初始构建堆和在重建堆时的反复筛选上。 在构建堆的过程中,因为我们是完全二叉树从最下层最右边的非终端结点开始构建,将它与其孩子进行比较和若有必要的互换,对于每个非终端结点来说,其实最多进行两次比较和互换操作,因此整个构建堆的时间复杂度为O(n)。 在正式排序时,第i次取堆顶记录重建堆需要用O(logi)的时间(完全二叉树的某个结点到根结点的距离为⌊log2i⌋+1),并且需要取n-1次堆顶记录,因此,重建堆的时间复杂度为O(nlogn)。 所以总体来说,堆排序的时间复杂度为O(nlogn)。由于堆排序对原始记录的排序状态并不敏感,因此它无论是最好、最坏和平均时间复杂度均为O(nlogn)。这在性能上显然要远远好过于冒泡、简单选择、直接插入的O(n2)的时间复杂度了。 空间复杂度上,它只有一个用来交换的暂存单元,也算是非常的不错。不过由于记录的比较与交换是跳跃式进行,因此堆排序也是一种不稳定的排序方法。 另外,由于初始构建堆所需的比较次数较多,因此,它并不适合待排序序列个数较少的情况。
5、快速排序
快速排序(Quicksort)是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
参考博客:http://bubkoo.com/2014/01/12/sort-algorithm/quick-sort/
public static void quickSort(int[] arr) { sort(arr,0,arr.length-1); } public static void sort(int[] array,int left,int right) { if (left > right) { return; } int storeIndex = partition(array, left, right); sort(array, left, storeIndex - 1); sort(array, storeIndex + 1, right); } private static int partition(int[] array, int left, int right) { int storeIndex = left; int pivot = array[right]; // 直接选最右边的元素为基准元素 for (int i = left; i < right; i++) { if (array[i] < pivot) { swap(array, storeIndex, i); storeIndex++; // 交换位置后,storeIndex 自增 1,代表下一个可能要交换的位置 } } swap(array, right, storeIndex); // 将基准元素放置到最后的正确位置上 //之后,以基准元素为分界点,左边是小于它的,右边是大于等于它的 return storeIndex; } private static void swap(int[] array, int i, int k) { int temp = array[i]; array[i] = array[k]; array[k] = temp; }
空间消耗 O(log n)
平均时间复杂度 O(n log n)
最好情况 O(n log n)
最坏情况 O(n^2)
最坏情况发生在每次划分过程产生的两个区间分别包含n-1个元素和1个元素的时候(设输入的表有n个元素)。最好情况为如果每次划分过程产生的区间大小都为n/2。
阅读全文
0 0
- 插入、冒泡、归并、堆排序、快排总结
- 常见排序算法总结与实现(冒泡、插入、选择、希尔、堆排序、归并、快排)
- 排序算法:快排,插入,希尔,归并,堆,选择,冒泡
- 排序算法--冒泡、插入、归并、快排
- 排序(快排,冒泡,堆排序,插入排序,归并排序,选择排序)算法Java实现
- 排序 简单排序(冒泡,插入)先进排序(快排,归并)堆排序,基数排序
- 各类排序C++实现(冒泡,选择,插入,快排,归并,堆排)
- C++实现各种基础排序(冒泡、选择、快排、插入、堆排、希尔、归并)
- 学生成绩排序(直接插入,冒泡,快排,选择,堆排,2路归并)
- 各种排序算法(冒泡、选择、快排、插入、希尔、堆排、归并、计数、基数)
- 冒泡 选择 插入 归并 快排 堆排 希尔
- 采用回调函数的内部排序算法-插入排序,希尔排序,冒泡,快排,堆排,归并排,基数排序
- 采用回调函数的内部排序算法-插入排序,希尔排序,冒泡,快排,堆排,归并排,基数排序
- C++数据结构 排序 二分 插入 冒泡 基数 归并 直选 快排 希尔 堆排序
- 链表排序(冒泡、选择、插入、快排、归并、希尔、堆排序)
- 链表排序(冒泡、选择、插入、快排、归并、希尔、堆排序)
- 八种常见排序算法:插入、冒泡、选择、希尔、归并、快排、堆排序、基数排序
- 链表排序(冒泡、选择、插入、快排、归并、希尔、堆排序)
- java代码转Smali代码
- material design 之 ripple(波纹)效果
- 162. Find Peak Element
- @RequiresPermissions 控制权限的异常处理以及Ajax方式请求时返回json
- 不规则卷积神经网络
- 插入、冒泡、归并、堆排序、快排总结
- Java学习线路
- 引用作为返回值的
- 关于EOS的部署
- opencv 实现任意角度的透视变换
- SVN的安装和关联Android Studio的重要步骤
- 安卓发布应用、更新的细节
- IPython notebook的安装及安装后网页不能打开问题
- python基础教程——dict和se