两种O(nlogn)级别的排序,归并排序和快速排序
来源:互联网 发布:字幕下载软件 编辑:程序博客网 时间:2024/06/07 05:12
最近重新学习了算法中的排序算法,相较于几个O(n^2)级别的排序算法(冒泡排序,选择排序,插入排序,希尔排序等) O(nlogn)级别的排序算法有普遍更快的速度,相对于O(n^2)级别的排序算法来说也更难理解。下面我把我最近学习这两个排序的想法说一下。
归并排序:归并排序就是将数组不断的等分,等分到一组只有一个数,然后向上合并,例如第一次合并只有自己 就直接合并,然后就会变成两两一组 这时就有大小关系了,按照我们的规则交换之,再向上合并,最后就可以得到一个排好序的数组,大概是
private static void __mergeSort_(int[] arr, int l, int r) {//l代表left r代表right
if(l >= r){
return ;
}
int mid = (l + r) / 2; //这里是求得中间值 例如 0~8 就是4这个位置 4~8 就是6这个位置
//递归
__mergeSort_(arr, l, mid);
__mergeSort_(arr, mid + 1, r);
//调用核心
__merge(arr, l, mid, r);
}
不断的等分,然后调用比较的方法。当分成一组只有一个值的时候 就return;向上返回
快速排序:快速排序的思想就是选择一个数作为基数 去遍历数组小于的放在基数左边 大于的放在基数右边,然后根据基数的位置 递归即可求解。大概是
private static void __quickSort(int[] arr, int l, int r) {
if(l >= r){
return;
}
//取到这次基数的位置
int p = __partition(arr, l, r);
__quickSort(arr, l, p - 1);
__quickSort(arr, p + 1, r);
}
这两种算法的代码如下:
package cyd;public class NLognSort {public static void main(String[] args) {testSort(10000,0,10000);}//测试性能 public static void testSort(int size, int rangeL, int rangeR){ int[] arr = randomArr(size, rangeL, rangeR); long startTime = System.currentTimeMillis(); quickSort3Ways(arr); //boolean flag = isSortArr(arr); //判断 是否排序成功 /*if(!flag){ System.err.println("没有排序成功"); return; }*/ long endTime = System.currentTimeMillis(); System.err.println(((double)(endTime - startTime))/1000.0 + "s"); printArr(arr); }//归并排序算法public static void mergeSort(int[] arr){__mergeSort_(arr, 0, arr.length - 1); // 借用Python的规范,表示私有方法,不希望用户调用}//归并算法递归实现private static void __mergeSort_(int[] arr, int l, int r) {//l代表left r代表rightif(l >= r){return ;}//优化2 在 范围极小时,数组趋近于有序,这时可以采用插入排序 可以加快效率/*if( r - l < 15){insertSortArray(arr, l, r);return;}*/int mid = (l + r) / 2; //这里是求得中间值 例如 0~8 就是4这个位置 4~8 就是6这个位置//递归__mergeSort_(arr, l, mid); __mergeSort_(arr, mid + 1, r);//调用核心//优化1 由于归并算法的保证,左边和右边一定是有序的,如果左边大于了右边//这时我们才去对它排序,这样对于较为有序的数组排序就能够提前结束if(arr[mid] > arr[mid + 1])__merge(arr, l, mid, r);}//归并排序算法核心private static void __merge(int[] arr, int l, int mid, int r) {int[] aux = new int[r - l + 1]; //这是临时数组,大小为传入的 大小 也就是当前要归并的数组大小for(int i = l ; i <= r; i ++){aux[i - l] = arr[i]; //临时表复制}int i = l, j = mid + 1; for(int k = l; k <= r; k++){if(i > mid){ //如果 i大于 mid 说明左边的元素已经被排好但是 有右边的没有被排好,这时就要将右边的值写入arr并且游标右移arr[k] = aux[j - l];j++;}else if(j > r){ //如果 j大于 mid 说明右边边的元素已经被排好但是 有左边的没有被排好,这时就要将左边的值写入arr并且游标右移arr[k] =aux[i - l];i++;}else if( aux[i - l] < aux[j - l]){ //如果当前组左边小于右边 就将左边的值给arr 左边游标右移arr[k] = aux[i - l];i++;}else{ //剩下这种情况只能是 右边大于左边了arr[k] = aux[j - l];j++;}}}//快速排序public static void quickSort(int[] arr){__quickSort(arr , 0, arr.length - 1);}//递归实现快速排序 //每次找到当前的中间值,然后等分左右,再继续从左右依次这样做 就实现了快速排序private static void __quickSort(int[] arr, int l, int r) {if(l >= r){return;}//优化1 数据小的时候 几乎有序,使用插入排序/*if( r - l < 15){insertSortArray(arr, l, r);return;}*/int p = __partition(arr, l, r);__quickSort(arr, l, p - 1);__quickSort(arr, p + 1, r);}//快速排序核心private static int __partition(int[] arr, int l, int r) {//这是标准位置的值,最后排序完成左边的比这小右边的比这大//初始版本 取第一个元素 ,但是有一个弊端 如果 数组本身近乎有序,那么大于当前值的就会非常多,导致右侧元素多,就会导致快速排序退化成O(n^2)级别的排序//int p = arr[l];//改进 从 l 到 r 中随机取 一个数 与 起始位置交换作为起始位置,这时 就不会出现这种情况了swap(arr, l , (int) (Math.round((Math.random()*(r - l) + l))));int p = arr[l];int j = l;for(int i = l + 1; i <= r; i++){if(arr[i] < p){swap(arr, i , j + 1);j++;}}swap(arr, j , l);return j;}// 改进 快速 排序 如果 大量重复数的情况下,就会将等于标定点的元素都放在右边 这就又导致了 快排变成了 O(n^2) 级别的排序 //改进public static void quickSort2(int[] arr){__quickSort2(arr, 0, arr.length - 1);}private static void __quickSort2(int[] arr, int l, int r) {if(l >= r){return;}int p = __partition2(arr, l, r);__quickSort2(arr, l, p - 1);__quickSort2(arr, p + 1, r);}private static int __partition2(int[] arr, int l, int r) {swap(arr, l , (int) (Math.round((Math.random()*(r - l) + l))));int p = arr[l];//直接将arr分成两半,左边向右检索,右边向左检索 找到两边 左边大于 arr[l]的 和 右边小于 arr[l] 的 交换 // 最后 将标定 值 和 j最后位置的值 交换int i = l + 1;int j = r;while( true ){while(i <= r &&arr[i] < p) i++;while(j >= l + 1 && arr[j] > p) j--;if(i > j) break;swap(arr, i ,j);i++;j--;}swap(arr, j, l);return j;}//3路快排 将 数组分成 小于 标准值 等于标准值 和 大于标准值 三部分 ,然后再对 小于部分 和大于部分3路快排 等于部分就不需要再排了public static void quickSort3Ways(int[] arr){__quickSort3Way(arr, 0, arr.length - 1);}private static void __quickSort3Way(int[] arr, int l, int r) {if( l >= r){return;}//// v为pivot,初始存储在arr[l]的位置 swap(arr, l , (int) (Math.round((Math.random()*(r - l) + l))));int p = arr[l];int lt = l; // 循环过程中保持 arr[l+1...lt] < v int gt = r + 1; // 循环过程中保持 arr[gt...r] > v int i = l + 1; // 循环过程中保持 arr[lt+1...i) == vwhile( i < gt){if(arr[i] < p){ // 这里相当于 如果小于的话 直接 将 i 和 lt 都向后移一位/*swap(arr, i ,lt + 1);*/ //这步其实没有必要lt++;i++;}else if(arr[i] > p){swap(arr, i ,gt - 1);gt--;}else{i++;}}swap(arr, l, lt);__quickSort3Way(arr, l, lt - 1);__quickSort3Way(arr, gt, r);}//插入排序 从左往右 第一个数 假设已经排好,第二个数是否小于第一个数 是的话交换,第三个数是否小于 //第二个数 小于交换 并判断 第二个数(也就是之前的第三个数) 是否小于第一个数 小于交换 。。。。。 public static void insertSortArray(int[] arr) { for (int i = 1; i < arr.length; i++) { //倒着往前遍历,如果存在 小于前一个数的 交换 否则 不交换并且break ,比选择排序 快很多 不用全部循环两次 for (int j = i; j > 0 && arr[j] < arr[j - 1]; j--) { swap(arr, j, j - 1); } } } //插入排序 排序任意位置的数 public static void insertSortArray(int[] arr,int l, int r) { for (int i = r; i < arr.length; i++) { //倒着往前遍历,如果存在 小于前一个数的 交换 否则 不交换并且break ,比选择排序 快很多 不用全部循环两次 for (int j = i; j > l && arr[j] < arr[j - 1]; j--) { swap(arr, j, j - 1); } } }//输出 public static void printArr(int[] arr){ for(int i = 0; i < arr.length; i++){ if(i != arr.length - 1){ System.err.print(arr[i]+","); }else{ System.err.println(arr[i]); } } } /** * 生成随机数 * @param size * @param rangeL * @param rangeR * @return */ public static int[] randomArr(int size, int rangeL, int rangeR){ if(rangeL >= rangeR){ return null; } int arr[] = new int[size]; for(int i = 0; i < size; i++){ arr[i] = (int) (Math.round((Math.random()*(rangeR - rangeL ) + rangeL))); for(int j = 0; j <=i; j++ ){ if(i != j){ if(arr[i] == arr[j] ){ i--; break; } } } } return arr; } public static boolean isSortArr(int[] arr){ for(int i =0 ; i < arr.length - 1; i++){ if(arr[i] > arr[i + 1]){ return false; } } return true; } //交换 public static void swap(int arr[], int leftNum, int rightNum){ int temp = arr[leftNum]; arr[leftNum] = arr[rightNum]; arr[rightNum] = temp; }}
- 两种O(nlogn)级别的排序,归并排序和快速排序
- 快速排序(O(nlogn))
- Java-时间复杂度为O(nlogn)的排序算法(快速排序, 归并排序, 堆排序, 希尔排序)
- nlogn级别的排序算法(1)归并排序
- nlogn级别的排序算法(2)快速排序
- nlogn排序-归并排序
- 归并排序求逆序对 O(nlogn)
- 关于快速排序和插入排序最坏时间复杂度为O(nlogn)的算法
- nlogn排序-快速排序
- O(lgn)的三种排序,快速排序、归并排序、堆排序
- 排序算法-nlogn级别
- 归并排序{nlogn}
- 归并排序-nlogn
- 归并排序(nlogn)
- 归并排序和快速排序的比较
- 归并排序和快速排序的浅析
- 单链表的快速排序和归并排序
- 归并排序和快速排序的实现
- webView中JavaScript调用Android方法
- Java工程师成神之路
- nRF52832 — NFC脚(P9/P10)配置为普通GPIO口
- 访问网页-网络如何工作
- 用Eclipse创建一个Maven Web项目
- 两种O(nlogn)级别的排序,归并排序和快速排序
- springboot入门
- Android 手机和盒子遥控器
- 阿里云lamp、lnmp环境搭建
- java web 项目中 简单定时器实现 Timer
- mongodb lbs 学习总结 . geo 算法源码链接
- “里里外外”的ajax
- Python人工智能之图片识别,Python3一行代码实现图片文字识别
- C++文件结束符 输入两次