剑指Offer——归并排序思想应用

举报
SHQ5785 发表于 2020/12/29 23:35:49 2020/12/29
【摘要】 剑指Offer——归并排序思想应用 前言     在学习排序算法时,初识归并排序,从其代码量上感觉这个排序怎么这么难啊。其实归并排序的思想很简单:将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。归并排序是建立在归并操作上的一种有效排序算法。该算法是采用分治法(Divi...

剑指Offer——归并排序思想应用

前言

    在学习排序算法时,初识归并排序,从其代码量上感觉这个排序怎么这么难啊。其实归并排序的思想很简单:将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。归并排序是建立在归并操作上的一种有效排序算法。该算法是采用分治法Divide and Conquer)的一个非常典型的应用。下面结合一编程实例进行系统学习。

   时间复杂度:O(nlogn)

   空间复杂度:O(n)

   稳定性:稳定

数组中的逆序对

     题目描述

   在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

     输入描述:

     题目保证输入的数组中没有的相同的数字

     数据范围:

     对于%50的数据,size<=10^4

     对于%75的数据,size<=10^5

      对于%100的数据,size<=2*10^5

     输入例子:

     1,2,3,4,5,6,7,0

     输出例子:

     7

    这里的解题思路是:先把数组分隔成子数组,统计出字数组内部逆序对的数目;然后再统计出相邻两个子数组之间的逆序对数目。在统计逆序对过程中,还需要对数组进行排序。不难发现,这个排序过程实际上就是归并排序。

    归并排序中用到了递归的写法,其实自己对递归并不感冒。递归方式虽使代码看起来更加整洁简练,但是由于其使用到了栈,而栈的内存大小是一定的,故当递归深度过于大时,就会出现栈溢出StackOverflow的异常情况。递归的方式可由非递归即循环方式替代。


  
  1. package cn.edu.ujn.offersword;
  2. public class C5_36_InversePairs {
  3. /**
  4. * @date 2016-09-16
  5. * @number 03
  6. * @author SHQ
  7. */
  8. public static void main(String[] args) {
  9. int [] array = {1,2,3,4,5,6,7,0};
  10. System.out.println(InversePairs(array));
  11. }
  12. private static int InversePairs(int [] array) {
  13. if(array == null || array.length == 0)
  14. {
  15. return 0;
  16. }
  17. int len = array.length;
  18. int[] copy = new int[len]; // 设置一辅助数组,用于存放排序结果
  19. for(int i = 0; i < len; i++)
  20. {
  21. copy[i] = array[i];
  22. }
  23. int count = InversePairsCore(array, copy, 0, len-1);
  24. return count;
  25. }
  26. private static int InversePairsCore(int[] array, int[] copy,int low, int high)
  27. {
  28. if(low == high)
  29. {
  30. return 0;
  31. }
  32. int mid = (low + high) >> 1;//使用左移运算符 >>,运算速度高于除法运算符
  33. int leftCount = InversePairsCore(array, copy, low, mid) % 1000000007;
  34. int rightCount = InversePairsCore(array, copy, mid+1, high)% 1000000007;
  35. int count = 0;
  36. int i = mid;
  37. int j = high;
  38. int locCopy = high;
  39. while(i >= low && j > mid)
  40. {
  41. if(array[i] > array[j])
  42. {
  43. count += j - mid;
  44. copy[locCopy--] = array[i--];
  45. if(count >= 1000000007) // 数值过大求余
  46. {
  47. count %= 1000000007;
  48. }
  49. }
  50. else
  51. {
  52. copy[locCopy--] = array[j--];
  53. }
  54. }
  55. for(; i >= low; i--)
  56. {
  57. copy[locCopy--] = array[i];
  58. }
  59. for(; j > mid; j--)
  60. {
  61. copy[locCopy--] = array[j];
  62. }
  63. for(int s = low; s <= high; s++)
  64. {
  65. array[s] = copy[s];
  66. }
  67. return (leftCount + rightCount + count) % 1000000007;
  68. }
  69. }

归并排序原始算法


  
  1. package cn.edu.ujn.offersword;
  2. import java.util.Arrays;
  3. public class MergeSort {
  4. /**
  5. * 归并排序
  6. * 简介:将两个(或两个以上)有序表合并成一个新的有序表 即把待排序序列分为若干个子序列,每个子序列是有序的。
  7. * 然后再把有序子序列合并为整体有序序列
  8. * 时间复杂度为O(nlogn)
  9. * 空间复杂度为O(n)
  10. * 稳定排序方式
  11. * @param nums 待排序数组
  12. * @return 输出有序数组
  13. */
  14. public static int[] sort(int[] nums, int low, int high) {
  15. int mid = (low + high) / 2;
  16. if (low < high) {
  17. // 左边
  18. sort(nums, low, mid);
  19. // 右边
  20. sort(nums, mid + 1, high);
  21. // 左右归并
  22. merge(nums, low, mid, high);
  23. }
  24. return nums;
  25. }
  26. public static void merge(int[] nums, int low, int mid, int high) {
  27. int[] temp = new int[high - low + 1];
  28. int i = low;// 左指针
  29. int j = mid + 1;// 右指针
  30. int k = 0;
  31. // 把较小的数先移到新数组中
  32. while (i <= mid && j <= high) {
  33. if (nums[i] < nums[j]) {
  34. temp[k++] = nums[i++];
  35. } else {
  36. temp[k++] = nums[j++];
  37. }
  38. }
  39. // 把左边剩余的数移入数组
  40. while (i <= mid) {
  41. temp[k++] = nums[i++];
  42. }
  43. // 把右边边剩余的数移入数组
  44. while (j <= high) {
  45. temp[k++] = nums[j++];
  46. }
  47. // 把新数组中的数覆盖nums数组
  48. for (int k2 = 0; k2 < temp.length; k2++) {
  49. nums[k2 + low] = temp[k2];
  50. }
  51. }
  52. // 归并排序的实现
  53. public static void main(String[] args) {
  54. int[] nums = { 2, 7, 8, 3, 1, 6, 9, 0, 5, 4 };
  55. MergeSort.sort(nums, 0, nums.length-1);
  56. System.out.println(Arrays.toString(nums));
  57. }
  58. }

  在求链表长度时,不要出现以下语句。会抛出空指针异常。


  
  1. int len1 = 0;
  2. int len2 = getLength(pHead2);
  3. int difLen = 0;
  4. // pHeadTmp1指向较长链表
  5. ListNode pHeadLong = pHead1;
  6. while(pHeadLong != null){
  7. len1++;
  8. pHeadLong = pHeadLong.next;
  9. }
  10. // pHeadTmp2指向较短链表
  11. ListNode pHeadShort = pHead2;
  12. if(len1 > len2){
  13. difLen = len1 - len2;
  14. }else{
  15. // 此处会抛异常(在本地无异常)
  16. pHeadLong = pHead2;
  17. pHeadShort = pHead1;
  18. difLen = len2 - len1;
  19. }

  相应的,可单独实现一函数计算链表长度,如下:


  
  1. private static int getLength(ListNode pHead){
  2. int len = 0;
  3. while(pHead != null){
  4. len++;
  5. pHead = pHead.next;
  6. }
  7. return len;
  8. }

美文美图

 



文章来源: shq5785.blog.csdn.net,作者:No Silver Bullet,版权归原作者所有,如需转载,请联系作者。

原文链接:shq5785.blog.csdn.net/article/details/52561990

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。