目录
一. 排序的概念及应用
1.1 排序的概念
1.2 常见的排序算法
二. 常见排序算法的实现(从小到大排序)
2.1 插入排序
2.1.1基本思想:
2.1.2 直接插入排序
2.1.3 希尔排序( 缩小增量排序)
2.2 选择排序
2.2.1基本思想:
2.2.2 直接选择排序:
2.2.3 堆排序
一. 排序的概念及应用
1.1 排序的概念
排序 :所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
稳定性 :假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j] ,且 r[i]在r[j] 之前,而在排序后的序列中, r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
内部排序 :数据元素全部放在内存中的排序。
外部排序 :数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序
1.2 常见的排序算法
二. 常见排序算法的实现(从小到大排序)
2.1 插入排序
2.1.1基本思想:
直接插入排序是一种简单的插入排序法,其基本思想是:
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到 一个新的有序序列 。实际中我们玩扑克牌时,就用了插入排序的思想
2.1.2 直接插入排序
当插入第 i(i>=1) 个元素时,前面的 array[0],array[1],…,array[i-1] 已经排好序,此时用 array[i] 的排序码与 array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将 array[i] 插入,原来位置上的元素顺序后移
思路:
- 从第二个元素i = 2时开始插入, 将第i个元素放在tmp中, 设j = i-1
- 如果小标为j的值大于tmp, 说明tmp要插在j前面, 所以arr[j+1]= arr[j], 将j所指的值向后移动, 给i让出位置, j继续向前移动
- 但如果j所指的值小于tmp, 则说明j的值的位置不需要变化, 说明tmp找到了自己的位置, 直接将arr[j+1] = tmp , 因为前面的数据已经有序, 所以直接跳出循环即可, 前面的元素不用比较, 一定是小于tmp的
- 如果循环走完了, 即j = -1了, 说明tmp是最小的一个数据, 直接放在数组的最前面即可, 前面已经腾出了位置,即 arr[j+1] = tmp
- 继续循环上述步骤, 指到将数组的元素全部插入
代码:public static void insertSort(int[] arr){for (int i = 1; i <arr.length ; i++) {int tmp = arr[i];int j = i-1;for ( ; j >= 0; j--) {if(arr[j] > tmp){arr[j+1] = arr[j];}else{//arr[j+1] = tmp;break;}}arr[j+1] = tmp;}}
直接插入排序的特性总结:
- 元素集合越接近有序,直接插入排序算法的时间效率越高
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
- 稳定性:稳定
2.1.3 希尔排序( 缩小增量排序)
希尔排序法又称缩小增量法。希尔排序法的基本思想是: 先选定一个整数gap,把待排序文件中所有记录分成多个组, 所有距离为gap的记录分在同一组内,并对每一组内的记录进行排序。然后,缩小gap的值,重复上述分组和排序的工作。当到达gap =1 时,所有记录在统一组内排好序 。
思路:
- 首先我们要分组gap, 第一次分组, 分成数组长度的一半, 接下来分组的方式有很多, 可以gap = gap/2, 可以gap = gap/3 +1 , 等等, 现在没有任何理论可以证明哪种分组最好, 这里我们使用gap= gap/2
- 分组分到所有的数据为一组, 即gap = 1时,停止
- 每一次分组, 我们都要对组内的元素进行排序, 我们选择的方法时直接插入排序, 可参考上述
- 但需要注意的是, 如何找到组内的元素, 我们可以将i设成gap, 很巧妙的是, i此时正好是每个组内的第二个元素, 那么前面的元素, 即 j 就是i-gap, j的移动单位就为gap, i每次向后走1, 可以到下一个组进行排序, 当i走到数组的最后时, 每个组内的元素都是有序的
代码:public static void shellSort(int[] arr){int gap = arr.length/2;while(gap!= 1){gap = gap/2;shell(arr, gap);}}private static void shell(int[] arr,int gap){for (int i = gap; i < arr.length; i++) {int tmp = arr[i];int j = i-gap;for (; j>=0; j-=gap) {if(arr[j] > tmp){arr[j+gap] = arr[j];}else{break;}}arr[j+gap] = tmp;}}
希尔排序的特性总结:
- 希尔排序是对直接插入排序的优化。
- 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
- 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定
- 稳定性:不稳定
2.2 选择排序
2.2.1基本思想:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
2.2.2 直接选择排序:
在元素集合 array[i]--array[n-1] 中选择关键码最大 ( 小 ) 的数据元素, 若它不是这组元素中的最后一个( 第一个 ) 元素,则将它与这组元素中的最后一个(第一个)元素交换在剩余的array[i]--array[n-2] ( array[i+1]--array[n-1] )集合中,重复上述步骤,直到集合剩余 1 个元素
思路1:
- 设i=0, 将i存放在minIndex里, 假设i是最小值的下标
- 遍历i后面的元素, 如果有比minIndex下标的值小的数据, 则更改minIndex的值, 即minIndex = j
- 遍历完成后, minIndex里面存放的是最小数据的值, 接着交换i和minIndex下标的值
注意: 此时不能写arr[i] = arr[minIndex], 如果这样写, 假设i下标存放的不是最小值, 那么用minIndex下标的值将i下标的值覆盖, 则会丢失i下标的值
4. 接着i++, 循环上述步骤, 直到遍历完整个数组
代码:public static void selectSort(int[] arr){for (int i = 0; i < arr.length; i++) {int minIndex = i;for (int j = i+1; j < arr.length; j++) {if(arr[j] < arr[minIndex]){minIndex = j;}}swap(arr,i,minIndex);}}private static void swap(int[] arr,int i,int j){int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}
思路2:
- 相当于思路1的优化, 遍历一遍数组可以同时找出最小值和最大值
- left 和 right相当于思路1中i的作用
- i 相当于思路1中j的作用, 用来遍历数组(注意:此时要从left开始遍历, 而不是left+1, 因为有可能left值就是最大值, 如果从left+1开始, 则最大值找不到)
- minIndex 和 maxIndex作用同样是存放最小值下标和最大值下标
代码:
public static void selectSort2(int[] arr){int left = 0;int right = arr.length -1;while(left< right){for (int i = left; i <= right ; i++) {int minIndex = left;int maxIndex = right;if(arr[i] < arr[minIndex]){minIndex = i;}if(arr[i] > arr[maxIndex]){maxIndex = i;}swap(arr,left,minIndex);swap(arr,right,maxIndex);left++;right--;}}}private static void swap(int[] arr,int i,int j){int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}
【 直接选择排序的特性总结 】
- 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
- 稳定性:不稳定
2.2.3 堆排序
堆排序 (Heapsort) 是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。
思路:
- 建立大根堆
- 交换堆顶元素和最后一个元素end, 这样就找到的最大的数放在最后
- 向下调整成大根堆, 但不算最后一个结点, 因为最后一个结点已经在应该的位置, 这样堆顶元素又变成了除最后一个元素外最大的元素
- 重复前两个步骤, 交换倒数第二个元素end--和堆顶元素, 这样就找到了倒数第二大的元素, 以此类推, 直到end==0时, 说明已经全部有序, 输出即可
代码:public static void heapSort(int[] arr){createHeap(arr);int end = arr.length-1;while(end > 0){swap(arr,0,end);siftDown(arr,0,end);end--;}}private static void createHeap(int[] arr){for(int parent = (arr.length-1-1)/2;parent >0;parent--){siftDown(arr,parent,arr.length);}}private static void siftDown(int[] arr, int parent, int len){int child = (parent*2)+1;while(child < len){if(child +1 < len && arr[child+1]>arr[child]){child = child+1;}if(arr[child] > arr[parent]){swap(arr,child,parent);parent = child;child = (parent*2)+1;}else{break;}}}
【堆 排序的特性总结 】
- 堆排序使用堆来选数,效率就高了很多。
- 时间复杂度:O(N*logN)
- 空间复杂度:O(1)
- 稳定性:不稳定
未完待续...