• 归并排序-面试例子


    小数和问题

    描述
    在一个数组中,一个数左边比它小的数的总和,叫数的小和,所有数的小和累加起来,叫数组小和。求数组小和。
    例子
    5 2 6 1 7
    小和原始的求法是:任何一个数左边比它小的数累加起来。
    5左边比它小数累加:0
    2左边比它小数累加:0
    6左边比它小数累加:5 + 2 = 7
    1左边比它小数累加:0
    7左边比它小数累加:5 + 2 + 6 + 1 = 14
    总共21。
    思路
    如果左侧某数a比右侧某数b小,则在求b的小和的时候,肯定会累加一个a,即sum+=a。
    反过来,在遍历到a的时候,如果我们知道右侧有几个数比a大,则可以提前知道会累加几个a
    使用归并排序时恰好有左右对比操作,所以使用归并排序来做
    即:
    每个数右边比它大的数的个数 * 这个数自身
    所以:
    在原来归并排序的基础上,增加一个ans用于记录结果
    在进行归并时左侧<右侧时产生小数 n * number
    小和求法还可以是:每个数右边比它大的数的个数 * 这个数自身
    
    5 2 6 1 7
    5的右边比它大的数的个数:2个(6和7),所以产生:2个 * 5 = 10
    2的右边比它大的数的个数:2个(6和7),所以产生:2个 * 2 = 4
    6的右边比它大的数的个数:1个(7),所以产生:1个 * 6 = 6
    1的右边比它大的数的个数:1个(7),所以产生:1个 * 1 = 1
    7的右边比它大的数的个数:0个,所以产生:0个 * 7 = 0
    总共21。
    
    code

    非递归

    1. public static int smallSum(int [] arr){
    2. if(arr == null || arr.length <2)
    3. return 0;
    4. int [] help = new int[arr.length];
    5. int step = 1;
    6. int N = arr.length;
    7. int L = 0;
    8. int ans = 0;
    9. while (step < N){
    10. L = 0;
    11. while (L < N){
    12. //左组最后一个数位置
    13. int m = L + step - 1;
    14. if(m >= N){
    15. break;
    16. }
    17. if(step >= N - L){
    18. break;
    19. }
    20. int R = Math.min(m+step,N-1);
    21. ans += merge(arr,L,m,R,help);
    22. L = R + 1;
    23. }
    24. if(step > N/2){
    25. break;
    26. }
    27. step <<= 1;
    28. }
    29. return ans;
    30. }
    31. public static int merge(int[] arr,int l,int m,int r,int [] help){
    32. //help index
    33. int i = 0;
    34. //p1 左侧开始index,p2 右侧开始index
    35. int p1 = l;
    36. int p2 = m+1;
    37. //结果保存
    38. int ans = 0;
    39. while (p1 <= m && p2 <= r){
    40. ans += arr[p1]1):0;
    41. help[i++] = arr[p1]
    42. }
    43. while (p1 <= m){
    44. help[i++] = arr[p1++];
    45. }
    46. while (p2 <= r){
    47. help[i++] = arr[p2++];
    48. }
    49. for (i = 0; i < r-l+1 ; i++) {
    50. arr[l+i] = help[i];
    51. }
    52. return ans;
    53. }

    递归

    1. public static int progress(int [] arr,int l,int r,int [] help){
    2. if(l == r)
    3. return 0;
    4. int m = l + ((r -l) >> 1);
    5. return progress(arr,l,m,help)
    6. + progress(arr,m+1,r,help)
    7. + merge(arr,l,m,r,help);
    8. }

    逆序对问题

    描述
    一个数组中,左边的数比右边的数大,求有多少个这样的组合

    比如  [3,1,0,4,3,1] 有7个逆序对,分别是

    (3,1),(3,0),(3,1)

    (1,0)

    (4,3),(4,1)

    (3,1)

    code
    1. //递归
    2. public static int reversePair(int [] arr){
    3. if(arr == null || arr.length <2)return 0;
    4. return progress(arr,0,arr.length -1);
    5. }
    6. public static int progress(int [] arr,int l,int r){
    7. if(l == r)return 0;
    8. int m = l + ((r-l)>>1);
    9. return progress(arr,l,m)
    10. +progress(arr,m+1,r)
    11. +merge(arr,l,m,r);
    12. }
    13. //非递归
    14. public static int reversePair2(int [] arr){
    15. if(arr == null || arr.length < 2)return 0;
    16. int ans = 0;
    17. int L = 0;
    18. int N = arr.length;
    19. int step = 1;
    20. while (step < N){
    21. L = 0;
    22. while (L < N){
    23. if(L+step >= N)break;
    24. int m = L + step - 1;
    25. if(m >= N)break;
    26. int r = Math.min(N-1,m+step);
    27. int temp = merge(arr,L,m,r);
    28. ans += temp;
    29. L = r + 1;
    30. }
    31. if(step > N/2)break;
    32. step <<= 1;
    33. }
    34. return ans;
    35. }
    36. public static int merge(int [] arr,int L,int M,int R){
    37. // 先算有多少逆序对
    38. // 和归并过程分离
    39. int res = 0;
    40. int p = M + 1;
    41. for (int i = L; i <= M; i++) {
    42. while (p <= R && arr[i] > arr[p]) {
    43. p++;
    44. }
    45. res += p - (M + 1);
    46. }
    47. // 下面完全和归并排序一样
    48. int[] help = new int[R - L + 1];
    49. int i = 0;
    50. int p1 = L;
    51. int p2 = M + 1;
    52. while (p1 <= M && p2 <= R) {
    53. help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
    54. }
    55. // 要么p1越界了,要么p2越界了
    56. while (p1 <= M) {
    57. help[i++] = arr[p1++];
    58. }
    59. while (p2 <= R) {
    60. help[i++] = arr[p2++];
    61. }
    62. for (i = 0; i < help.length; i++) {
    63. arr[L + i] = help[i];
    64. }
    65. return res;
    66. }

    左边大于右边倍数的数

    描述
    在一个数组中,
    对于每个数num,求有多少个后面的数 * 2 依然 
    
    思路
    右边有多少个数*2比左边的数小
    在归并排序过程中,分组后左侧有序,右侧有序,在进行左右侧合并时,统计验证关系【2倍关系】
    这样可以得到该左侧位置相对于右侧位置的2倍关系统计和
    code
    1. //递归
    2. public static int biggerThatRightTwice(int []arr){
    3. if(arr == null || arr.length<2){
    4. return 0;
    5. }
    6. return progress(arr,0,arr.length-1);
    7. }
    8. public static int progress(int[] arr,int l,int r){
    9. if(l == r){
    10. return 0;
    11. }
    12. int m = l + ((r-l)>>1);
    13. System.out.println("l,m,r:"+l+","+m+","+r);
    14. return progress(arr,l,m)
    15. +progress(arr,m+1,r)
    16. +merge(arr,l,m,r);
    17. }
    18. //非递归
    19. public static int biggerThatRightTwice2(int [] arr){
    20. if(arr == null || arr.length <2)return 0;
    21. int L = 0;
    22. int step = 1;
    23. int N = arr.length;
    24. int ans = 0;
    25. while (step < N){
    26. L = 0;
    27. while (L < N){
    28. if(step >= N-L)break;
    29. int m = L + step - 1;
    30. if(m >= N)break;
    31. int r = Math.min(m+step,N-1);
    32. ans += merge(arr,L,m,r);
    33. L = r + 1;
    34. }
    35. if(step > N/2)break;
    36. step <<=1;
    37. }
    38. return ans;
    39. }
    40. public static int merge(int[] arr,int l,int m,int r){
    41. //[l,m] [m+1,r]进行归并,其中[l,m],[m+1,r]分别已经有序
    42. //先计算
    43. int p1 = l,p2 = m+1;
    44. int ans = 0;
    45. //左侧遍历l
    46. while (p1 <= m){
    47. //右侧遍历
    48. while (p2 <= r){
    49. //如果左侧 > 右侧 * 2,则继续判断,知道不满足条件
    50. //当不满足条件时,则右侧从开始位置m+1到p2位置为p1满足条件的数
    51. if(arr[p1] > arr[p2] *2){
    52. p2++;
    53. }else{
    54. break;
    55. }
    56. }
    57. //p2 - (m+1) => [m+1,p2) 即从m+1到p2个元素个数,不包含p2
    58. ans += (p2 - (m+1));
    59. p1++;
    60. }
    61. //再进行归并
    62. int [] help = new int[r-l+1];
    63. int i = 0;
    64. p1 = l;
    65. p2 = m+1;
    66. while (p1<=m && p2<=r){
    67. help[i++] = arr[p1]
    68. }
    69. while (p1<=m){
    70. help[i++] = arr[p1++];
    71. }
    72. while (p2<=r){
    73. help[i++] = arr[p2++];
    74. }
    75. for(i=0;i
    76. arr[l+i] = help[i];
    77. }
    78. return ans;
    79. }

  • 相关阅读:
    K8S 多集群管理很难?试试 Karmada | K8S Internals 系列第 3 期
    自然语言处理(NLP)—— 神经网络语言处理
    如何实现ElementUI动态表头?
    表名注解/主键注解/字段注解/乐观锁注解[MyBatis-Plus系列] - 第486篇
    Opencv cuda版本在ubuntu22.04中安装办法,解决Could NOT find CUDNN的办法
    Linux简单磁盘命令
    使用python生成大量数据写入es数据库并查询操作2
    优雅关闭TCP的函数shutdown效果展示
    基于51单片机的指纹考勤器
    eyb:Redis的学习(1)
  • 原文地址:https://blog.csdn.net/hey_lie/article/details/132741871