【数据结构与算法】排序

来源:互联网 发布:mac系统忘记管理员密码 编辑:程序博客网 时间:2024/04/29 03:50
/** * 一些结论: * (1)所有的简单排序,即On2的,都是稳定排序。稳定排序的特征是比较操作存在于相邻的元素。 * (2)比On2快的,除了归并,其余都不稳定,但是归并需要On空间 */public class Sort {//直接排序,最基本的思路就是在前i个元素已经排好的基础上,把第i+1个元素插入到合适的位置,但插入第i个时,缓存为t,向前遍历,只要大于就后移,直到找到第一个小于等于的元素k,然后k+1设置为tpublic void straightInsertSort(int nums[]){if (nums == null) {return;}for(int i = 1; i < nums.length; i++){int temp = nums[i];int j = i - 1;while(j >= 0 && nums[j] > temp){ //稳定nums[j + 1] = nums[j];j--;}nums[j + 1] = temp;}}//二分插入排序,在查找插入位置时使用二分法,设置low和high,直到low>high,这时要么是因为high - 1要么因为low + 1,结果真正的插入位置就是high+1处,因此high+1到i-1的所有元素后移。注意在循环体内,当中间小于t时,往左边找,大于等于往右边,这样当l==h时,右边的全大于,左边的小于等于,那么此时的元素是小于等于t的最大元素,因此在这个元素后面插入。//插入排序复杂度还是On2,因为sort分两种操作,比较和移动,比较是logn,但是移动仍然是On2public void binaryInsertSort(int[] nums){for(int i = 1; i < nums.length; i++){int temp = nums[i], low = 0, high = i - 1;while(low <= high){int m = (low + high) / 2;if (temp < nums[m]) {high = m - 1;}else {low = m + 1;}}for(int j = i ; j > high + 1; j--){nums[j] = nums[j - 1];}nums[high + 1] = temp;}}//希尔排序,思路是多次带gap的直接插入排序。最外层的while循环是控制gap的,每一次while循环都是一次带gap的插入排序,在每一次插入排序中,还是会遍历所有的元素,但是在向前插入的操作中是跳跃gap进行的。因此看上去像是有gap组子排序。//关于其的时间复杂度分析尚未得出,但是可以知道介于On和On2之间,所以是插入排序中最好的。不稳定。//关于选取increment也没有定论,但是有两条规则,第一是最后一次必须是1,第二个是相邻两次不要互为倍数。比较推荐的有2^k-1,3^k-1等。不稳定。//关于shell排序的有点有一个比较直接的分析,第一趟排序,元素基本无序,但是因为gap很大,所以操作次数很少,最后一次,基本接近有序,虽然gap是1,但操作次数也不多。public void shellSort(int[] nums){int length = nums.length;int increment = 1;while(increment * 2 + 1 < length){increment = increment * 2 + 1;}while(increment >= 1){for(int i = increment; i < length; i++){int temp = nums[i];int j = i - increment;while(j >= 0 && nums[j] > temp){nums[j + increment] = nums[j];j -= increment;}nums[j + increment] = temp;}increment = (increment - 1) / 2;}}//快速排序目前是比较快的方法,复杂度是Onlogn。其思路是一个递归,包含不断地分解过程。每一次partition都会以第一个元素为准,把low到high的元素进行移位,使得左边比key小,右边比k大。//这个partition本身就可以是一个算法题目,做法是通常选第一个为k,low和high指针分别交替靠拢,以遍历到所有之间的元素。一个移动时,另一个指向槽,最终high=low,均指向槽,把槽赋值为k即可。//一直递归下去,知道包含一个元素时返回。快排不稳定。public void Qsort(int[] nums){int low = 0, high = nums.length - 1;partition(nums, low, high);}private void partition(int[] nums, int low, int high) {if (low < high) {int key = nums[low];int l = low;int r = high;while (l < r) {while (l < r && key <= nums[r])r--;nums[l] = nums[r];while (l < r && key >= nums[l])l++;nums[r] = nums[l];}nums[r] = key;partition(nums, low, l - 1);partition(nums, l + 1, high);}}//归并排序,其思路很清晰,拿到数组以后,分为两半,各自递归调用排序子序列,然后再合并子序列。有两个注意点,在分割时,必须是[low,m]和[m + 1, high],这样如果是size为2,那么结果会被分割成两个siez为1.//第二点是,在merge操作时,要新建一个缓存,存两个序列的较小值,为了保证稳定性,如果相等,应该取前面序列的元素。所以if条件是first <= second,then choose first。//归并排序是稳定排序,复杂度nlgn。public void MergeSort(int nums[]){Msort(nums, 0, nums.length - 1);}private void Msort(int nums[], int low, int high){if (low >= high) {return;}int m = (low + high) / 2;Msort(nums, low, m);Msort(nums, m + 1, high);Merge(nums, low, m, high);}private void Merge(int nums[], int low, int m, int high){int[] cash = new int[high - low + 1];int index = 0;int first = low, second = m + 1;while(first <= m && second <= high){if (nums[first] <= nums[second]) {cash[index++] = nums[first++];}else {cash[index++] = nums[second++];}}if (first > m ) {while(second <= high){cash[index++] = nums[second++];}}if (second > high ) {while(first <= m){cash[index++] = nums[first++];}}for(int i = 0; i < cash.length; i++){nums[low + i] = cash[i];}}}

0 0