package com.liuxing.sort;

import com.liuxing.util.Print;

/**

  • @author liuxing007

  • @ClassName MergeSort

  • @Description 归并排序(Merge Sort)

  • 归并排序的核心思想还是蛮简单的。如果要排序一个数组,

  • 我们先把数组从中间分成前后两部分,然后对前后两部分分别排序,

  • 再将排好序的两部分合并在一起,这样整个数组就都有序了。

  • 时间复杂度:O(nlogn)

  • 空间复杂度:O(n)

  • 不是原地排序算法

  • @date 2020/9/17 15:04

*/

public class MergeSort {

public static void main(String[] args) {

int[] arr = new int[]{6,4,1,7,2,5,8,3};

int length = arr.length;

System.out.println(“排序前数组===========”);

Print.print(arr, length);

sort(arr, length);

System.out.println(“排序后数组===========”);

Print.print(arr, length);

}

/**

  • 排序算法

  • @param arr 数组

  • @param l 数组长度

*/

private static void sort(int[] arr,int l){

sortMerge(arr,0,l-1);

}

/**

  • 递归

  • @param arr 数组

  • @param p 开始位置下表

  • @param r 结束位置下表

*/

private static void sortMerge(int[] arr,int p,int r){

if(p >= r){

return ;

}

//分治的下标,这里我采用p到r的中间位置index。

int index = p + (r-p)/2;

//左侧递归

sortMerge(arr,p,index);

//右侧递归

sortMerge(arr, index + 1, r);

merge(arr, p, index,r);

System.out.println(“排序后数组===========”);

Print.print(arr, length);

}

/**

  • 合并计算

  • @param arr 原素组

  • @param l 左侧数组开始位置下标

  • @param index 左侧数组结束位置下标

  • @param r 右侧数组结束位置下标

*/

private static void merge(int[] arr, int l,int index, int r) {

//临时数组,这里可以优化,数组的频繁创建会降低程序运行的效率,

// 所以这里可以将这个临时数组改成参数传递进来,在数量较大的时候执行效率变化变焦显著

int[] temp = new int[r-l+1];

//左侧开始下标

int i= l;

//右侧开始下标

int j = index+1;

//临时数组下标

int k=0;

// 左侧数组与右侧数组进行对比,将小的元素放入临时数组中

while(i<=index && j<=r){

if(arr[i]<arr[j]){

temp[k++] = arr[i++];

}else{

temp[k++] = arr[j++];

}

}

//对比完成之后,需要把两侧数组中还没有对比的数据加入到临时数组中

//把左边剩余元素加入临时数组中

while(i<=index){

temp[k++] = arr[i++];

}

//把右边剩余元素加入临时数组中

while(j<=r){

temp[k++] = arr[j++];

}

//将临时数组的元素拷贝原数组中

for(int x=0;x<temp.length;x++){

arr[x+l] = temp[x];

}

}

}

package com.liuxing.util;

/**

  • @author liuxing007

  • @ClassName Print

  • @Description 打印

  • @date 2020/9/17 11:13

*/

public class Print {

/***

  • 打印数据

  • @param arr 数组

  • @param length 数组长度

*/

public static void print(int[] arr, int length) {

for (int i = 0; i < length; ++i) {

System.out.print(arr[i] + " ");

}

System.out.println("");

}

}

排序前数组===========

6 4 1 7 2 5 8 3

合并后的数据

4 6 1 7 2 5 8 3

合并后的数据

4 6 1 7 2 5 8

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

3

合并后的数据

1 4 6 7 2 5 8 3

合并后的数据

1 4 6 7 2 5 8 3

合并后的数据

1 4 6 7 2 5 3 8

合并后的数据

1 4 6 7 2 3 5 8

合并后的数据

1 2 3 4 5 6 7 8

排序后数组===========

1 2 3 4 5 6 7 8

Process finished with exit code 0


3.时间空间复杂度分析


我们假设对 n 个元素进行归并排序需要的时间是 T(n),那分解成两个子数组排序的时间都是 T(n/2)。我们知道,merge() 函数合并两个有序子数组的时间复杂度是 O(n)。所以,套用前面的公式,归并排序的时间复杂度的计算公式就是:

T(1) = C; n=1时,只需要常量级的执行时间,所以表示为C。

T(n) = 2*T(n/2) + n; n>1

通过这个公式,如何来求解 T(n) 呢?还不够直观?那我们再进一步分解一下计算过程。

T(n) = 2*T(n/2) + n

= 2*(2T(n/4) + n/2) + n = 4T(n/4) + 2*n

= 4*(2T(n/8) + n/4) + 2n = 8T(n/8) + 3n

= 8*(2T(n/16) + n/8) + 3n= 16T(n/16) + 4n

= 2^k * T(n/2^k) + k * n

通过这样一步一步分解推导,我们可以得到 T(n) = 2 k 2^k 2k T(n/ 2 k 2^k 2k)+kn。当 T( n / 2 k n/2^k n/2k)=T(1) 时,也就是 2 k 2^k 2k=1,我们得到 k= l o g 2 n log_2n log2​n 。我们将 k 值代入上面的公式,得到 T(n)=Cn+ l o g 2 n log_2n log2​n 。如果我们用大 O 标记法来表示的话,T(n) 就等于 O(nlogn)。所以归并排序的时间复杂度是 O(nlogn)。从我们的原理分析和伪代码可以看出,归并排序的执行效率与要排序的原始数组的有序程度无关,所以其时间复杂度是非常稳定的,不管是最好情况、最坏情况,还是平均情况,时间复杂度都是 O(nlogn)-----摘自-极客时间-数据结构与算法-王争

归并排序的时间复杂度已经很优秀了,但为什么我们在日常开发中却很少看到他的身影呢?我们先来分析一下归并排序的空间复杂度。

我们需要注意的是合并方法,这个方法中我们使用了一个临时数组用来存储数据,但是合并之后这个临时数组就会释放,又因为临时数组的最大长度不会超过原始数组长度n,所以归并排序的空间复杂度为:O(n)

为什么开发中很少人使用到归并排序呢?原因很简单,因为它不是一个原地排序算法,这个时候你可能会有疑惑了,什么是原地排序算法?简单来说:不通过其他空间来完成的排序,我们称它为原地排序算法,但归并排序很明显借用了一个临时数组,所以它不是一个原地排序算法,即使它的时间复杂都很稳定,使用的人也比较少。


三、快速排序

=====================================================================

1.什么是快速排序


如果要排序数组中下标从 p 到 r 之间的一组数据, 我们选择 p 到 r 之间的任意一个数据作为 pivot(分区点)。我们遍历 p 到 r 之间的数据,将小于 pivot 的放到左边, 将大于 pivot 的放到右边,将 pivot 放到中间。经过这一步骤之后, 数组 p 到 r 之间的数据就被分成了三个部分,前面 p 到 q-1 之间都是小于 pivot 的,中间是 pivot,后面的 q+1 到 r 之间是大于 pivot 的.根据分治、递归的处理思想,我们可以用递归排序下标从 p 到 q-1 之间的数据和下标从 q+1 到 r 之间的数据,直到区间缩小为 1,就说明所有的数据都有序了。(摘自-极客时间-数据结构与算法-王争)

快速排序的思想和归并排序有点类似,都是通过分治的思想,利用递归实现排序,只不过实现的细节有所不同,快排(快速排序)需要一个分区点,可以在数组中随便去一个元素作为分区点即可,后面就是前面讲到的概念了,归并的核心在于合并,而快排的核心在于分区点,所以我们就一起来看看在获取分区点的时候快排都干了些啥?

之前说过,快排选择一个分区点(pivot)之后,将小于分区点(pivot)的元素放左边,分区点(pivot)放中间,大于分区点(pivot)的放右边,这个一看就很好解决嘛,和归并排序一样,我先申请两个临时数组,一个存放小于分区点元素的数组,一个存放大于分区点元素的数组,这样,就能完美的解决了,非常简单,但是这个就和归并排序面临这同一样的一个问题:它不是一个原地排序算法,那如果我希望快排是一个原地排序算法呢?我们应该如何实现呢?其实也不难,我们可以参考一下选择排序:【数据结构与算法】常见的三种排序(冒泡排序、插入排序、选择排序)

我们定义一个游标 i (数组下标) 把数组a[p-(r-1)]分成两部分,A[p-(i-1)]都是小于分区点(pivot)的,我们叫他“已排序区间”。a[(i+1) - (r-1)]都是大于分区点(pivot)元素的,我们叫他“未排序区间”,只要从未排序区间去值与分区点(pivot)进行比较,如果小于分区点(pivot),那么将此元素追加到已排序区间中(a[i]),否者不需要变动。

我还是准备了一张图给大家参考,也许大家就能明白了。

这是一次分区交换的结果,当把所有分区都交换完成之后,整个数组也就有序了,既然快速排序的思想已经讲的差不多了,下面我们一起来看看代码怎么实现

快排代码实现

package com.liuxing.sort;

import com.liuxing.util.DataUtil;

import com.liuxing.util.Print;

/**

  • @author liuxing007

  • @ClassName Quicksort

  • @Description 快速排序

  • 如果要排序数组中下标从 p 到 r 之间的一组数据,

  • 我们选择 p 到 r 之间的任意一个数据作为 pivot(分区点)。

  • 我们遍历 p 到 r 之间的数据,将小于 pivot 的放到左边,

  • 将大于 pivot 的放到右边,将 pivot 放到中间。经过这一步骤之后,

  • 数组 p 到 r 之间的数据就被分成了三个部分,

  • 前面 p 到 q-1 之间都是小于 pivot 的,中间是 pivot,

  • 后面的 q+1 到 r 之间是大于 pivot 的.

  • 根据分治、递归的处理思想,

  • 我们可以用递归排序下标从 p 到 q-1 之间的数据和下标从 q+1 到 r 之间的数据,

  • 直到区间缩小为 1,就说明所有的数据都有序了(摘自-极客时间-数据结构与算法-王争)

  • 时间复杂度:O(nlogn)

  • 空间复杂度:O(1)

  • 原地排序算法,但不是稳点排序算法

  • @date 2020/9/18 10:22

*/

public class Quicksort {

public static void main(String[] args) {

int[] arr = new int[]{6, 5, 4, 3, 2, 1};

// int[] arr = DataUtil.createIntArrData();

int length = arr.length;

System.out.println(“排序前数组===========”);

Print.print(arr, length);

sort(arr, length);

System.out.println(“排序后数组===========”);

Print.print(arr, length);

}

/**

  • 排序算法

  • @param arr 数组

  • @param l 数组长度

*/

private static void sort(int[] arr,int l){

sortRec(arr,0,l-1);

}

/**

  • 递归

  • @param arr

  • @param p

  • @param r

*/

private static void sortRec(int[] arr, int p, int r) {

//递归终止条件

if (p >= r){

return;

}

//获取分区点

int q = partition(arr, p, r);

入秋的第一篇数据结构算法:看看归并与快排的风采,三面蚂蚁金服成功拿到offer相关推荐

  1. (十)更快的排序算法(归并、快排、基数)

    目标 1) 使用下列方法将一个数组按升序排序:归并排序.快速排序和基数排序 2) 评估排序的效率,讨论不同的方法的相对效率 目录 9.1 归并排序 9.1.1 归并数组 9.1.2 递归归并排序 9. ...

  2. 常见经典排序算法学习总结(插入、shell、冒泡、选择、归并、快排等)

    博主在学习过程中深感基础的重要,经典排序算法是数据结构与算法学习过程中重要的一环,这里对笔试面试最常涉及到的7种排序算法(包括插入排序.希尔排序.选择排序.冒泡排序.快速排序.堆排序.归并排序)进行了 ...

  3. 排序(一)归并、快排、优先队列等(图文具体解释)

    排序(一) 0基础排序算法 选择排序 思想:首先,找到数组中最小的那个元素.其次,将它和数组的第一个元素交换位置.再次.在剩下的元素中找到最小的元素.将它与数组的第二个元素交换位置. 如此往复,直到将 ...

  4. 三路快排算法加强版(三路快排的再次改进)

    :不要忘记初心哈 :) 理论依据 快排算法的缺陷及其逐一改进 三路快排尽可能三等份划分区间 通过待排元素的区间长度划分? 通过待排元素的最值之差划分? 直接使用待排元素的最大值划分? 实验数据 大范围 ...

  5. 惊雷算法下如何使用快排快速上首页?

    SEO的小朋友们,应该都正面临这样的问题,站内文章词穷,站外链接受到各种限制.这个时候,很多站长都面临着一个尴尬的局面,排名禁锢在前五页,始终无法再往上提升.一个网站想要好的排名永远离不开三种东西:页 ...

  6. 算法介绍--- 二分法,冒泡排序,快排

    目录 二分法 冒泡排序 快速排序 二分法 二分法是一种在有序数组中查找某一特定元素的搜索算法. 搜索过程从数组的中间元素开始: 如果中间元素正好是要查找的元素,则搜索过程结束; 如果某一特定元素大于或 ...

  7. 排序算法:冒泡和快排 摘自网络

    冒泡排序: 首先我们自己来设计一下"冒泡排序",这种排序很现实的例子就是: 我抓一把沙仍进水里,那么沙子会立马沉入水底, 沙子上的灰尘会因为惯性暂时沉入水底,但是又会立马像气泡一样 ...

  8. 八大排序算法之快速排序(下篇)(快排的优化+非递归快排的实现)

    目录 一.前言 1.快速排序的实现: 快速排序的单趟排序(排升序)(快慢指针法实现):​ 2.未经优化的快排的缺陷 二.快速排序的优化 1.三数取中优化 优化思路: 2. 小区间插入排序优化 小区间插 ...

  9. 第一篇 数据结构、设计模式与手写代码

    目录 1.怎么理解时间复杂度和空间复杂度? 2.数组和链表结构简单对比? 3.怎么遍历一个树 4.冒泡排序(Bubble Sort) 5.快速排序(Quick Sort) 6.二分查找(Binary ...

  10. 【排序算法复习备忘】冒泡、选择、插入、归并、快排、堆排序

    冒泡排序,最简单的排序,基本思想遍历数组n次,每一次选出当前最大(小)的元素,以近似起泡的方式将最值慢慢挪到边界,完成排序.时间复杂度 O(n^2)下面以升序为列 void bubbleSort(ve ...

最新文章

  1. 生物信息学、系统生物学 期刊 会议
  2. torch view view_as
  3. 51 nod 1427 文明 (并查集 + 树的直径)
  4. Apache httpd Server 配置正向代理
  5. 让开发自动化: 利用 Ivy 管理依赖项
  6. ZOJ - 3228 Searching the String(AC自动机求不重复子串出现次数)
  7. c#语言中的变量名,在C#中创建动态变量名
  8. vue2 父子组件传参 回调函数使用
  9. multisim中轻触开关在哪_关于轻触开关的原理、区别、寿命以及接线方式的介绍...
  10. 计算机中submit commit区别
  11. [开发手记] 在WinForm应用程序中打开Url
  12. 蓝牙iBeacon工作原理
  13. 4g/5g信号远程遥控小车解决方案二
  14. 小米计算机视觉算法工程师面试题
  15. scss动态生成类名、样式值
  16. oracle 表分区
  17. 第三章 培养工作兴趣
  18. 超详细的fiddler教程,从小白到精通(六)❤️
  19. 原生JS总结-- JS 绑定点击事件
  20. 初学者如何选择开发语言

热门文章

  1. 数据库中反引号的作用
  2. acfun怎么下载视频
  3. 斐讯w2换表盘_【斐讯W2智能手表使用感受】表盘|屏幕|GPS|电量_摘要频道_什么值得买...
  4. 还在用git commit -m 吗?Git 提交规范和自动产生changelog手摸手教程。
  5. CPP QT实现excel的冻结窗格
  6. 如何制作六一儿童节答题测试H5页面?
  7. G盘格式化了,要怎样恢复文件
  8. unity 常见的置灰处理
  9. matlab的from模块_Matlab中SIMULINK的模块库以及比较常用的模块
  10. 计算机保研面试题-数据结构