第十八章 分而治之(归并排序)

本文详细探讨了归并排序的思想、习题解答以及与其他排序算法的比较。介绍了归并排序的分治策略,包括直接归并和自然归并的实现方式,并讨论了其最优与最坏情况的时间复杂度。此外,文章还提到了使用归并排序解决找出第K小元素的问题,并分析了与其他排序算法如快速排序的优劣。最后,通过例题解析加深了对归并排序的理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思想:

习题: 

18.1 

   log2N

18.2 

  若第一次比较两边一样重说明不存在 

  多比较一次 ,第二次比较时,质量相同的一边排除,记下相同的质量即可 

18.3 

   第二章好像算过 

18.4

  因为具体实现还是一样的,只是化为小方块相乘

18.5

 当K==1 时, 就是此值 

18.6

   看总结 

18.7

 1)

  

2) 

  A的左上行,B的左上列 

18.8

   填充颜色时,用上一节的贪婪算法+

18.9

   带入公示即可 不断的乘4 一共有k个 

18.10

  满足, 因为此fx二阶导数是正的,所以是凹的,增率一定是变大的

18.11

   应该也是成立的 ,但用什么方法呢 

18.44

   合并具体实现变了,变成融合两个单链表了   

18.13-16

 17-18

     编写一个标志数组,记录相对有序的下标 ?融合合并 

Natural Merge Sort(自然归并排序) - 燃烧少年的心 - 博客园 (cnblogs.com)

  17.19-20 

      

  21:

     与上面程序一样就行了,边界改一下

  22:

     1)因为是往左边(或右边)循环到底,每一次都是一半,所以只需要logN 

      

  23: 

     当递增时,left和right都要一共遍历n个元素  一共n趟 

  24:

      分一半,必然是nlogn 

  25:

    

   部分积分 

   

        带入即可证明

  26:

      肯定好很多 ,结果大概率都是中间元素 

  27:

    srand函数生成下标值 

 28:

   可以,根据不同的规模,实际时间也不一样 

 29:

   1) 首先最小实例顺序是反的 然后反递归,顺序也是反的 

   比如   87,21,65,43

   2)  某一实例对于哪一种算法来说是最坏的呢?

   3) stable_sort()与sort()的用法区别_云守护的专栏-CSDN博客_stable_sort

   4) 看总结0  

30,31:

 32: 

     n个cn相加  线性的

 33:

    因为是中间的中间,所以轴点不会太离谱

  34:

        带进去递归即可 

  35:

      尾部递归都能换成迭代,更新边界即可 

  

  36:

     1,带入即可 

  37:

     dist函数不用sqrt 

  38:

     不能对每一个点测相邻的距离吗 O(n)

  

  39:

 1)   使K= n/2   然后调用select 寻找点 ; 其他依旧 

 2)   nlongn

 3) 不会

40: 

    n^logba  == a^k  k也就是递归的次数  

    我们猜测解是O(nlogn),我们要寻找到一个常数c,使得

         T(n)<=cnlogn<=a^logn/2+kg(n)<=a^logn+kg(n) == a^logn+logbn*g(n)<= a^k*(t(1)+g(n))

  

41:

   带入a b logba ,套表即可 

42:

  分为三种集合  a :最大候选者,b:可能最大候选者:c:不可能为最大者 

        初始a集合为n :a两元素 比较  ,一个进b 一个进c 

        最快方法: a中比较  n/2   次 b中比较  n/2-1次 一共 n-1 次 ,即为下限 

43

  

 n/2之后每一项都比n/2 大

44-45

  略,自己照着书画

46

   就是线性代数的逆序数

1) 3+1+1 =5

2) n-1+n-2+...+1 == n(n-1)/2

3)分为2种集合 a b       a:最坏序列   b:以确定最大的序列

     由于每一个相邻都需要比较 第一趟n-1次比较 入b 

    最终比较 n-1+n-2+....+1 -> Ω(n^2) 

47

  1)  n个节点 n-1条边

       n+1(加入外部节点) 到根 就有n条边 每个叶节点有两个外部节点 所以 2n 条边

      所以多2n条边

数学归纳法证明 E = I + 2n
当n为1时,I = 0; E = 2; 满足 E = I + 2n
当有n个内结点时设公式成立,则当有 n + 1个内结点时(相对于n个内结点时增加一个外结点) ,令增加了一个内结点后二叉树的高为 h(不包含外结点)、内路径长度 I(n+1) = I(n) + h , 外路径长度E(n+1) = E(n) - h + 2(h+1),又有E(n) = I(n) + 2n, 整理得 E(n+1) - h - 2 = I(n+1) - h + 2n 即有E(n+1) = I(n+1) + 2(n+1)
证毕
 

  2)

      节点相同多时,完全二叉树高度最小,所以路径最短

3)

   p+1是高度h   

   2^(p+1) 是高度为h最多 总节点数

  然后。。。不会了

4)

   E=(n+1)p-2^(p+1)+2n+2

5)

    共有2^(p+1)条外部路径 ,带入即可 

   

总结:

  

  0.排序总结: 

 

   图表 : 

     

   :可以根据n的值来选择具体排序的方法

  1 Strassen矩阵乘法

    详解矩阵乘法中的Strassen算法_u011250186的博客-CSDN博客_strassen矩阵乘法

  2 归并排序 

   分为n/k 个相对有序序列 然后归并 直至只有一个序列 

 

 当K ==2 时,次数最少 ,成为归并排序 :

 

 直接归并: 

       分为递归和迭代 

自然归并: 

     按对内数据进行划分序列 

 

#include<iostream>
using namespace std;
template<class T>
void 
merge(T *c,T *d,int startOfFirst,int endOfFirst,int endOfSecond){  // 合并两个有序序列
        int first=startOfFirst, 
            second=endOfFirst+1,
            result=startOfFirst;

    while(first<= endOfFirst && second <=endOfSecond){
         if(c[first]<=c[second])
             d[result++]=c[first++];
         else d[result++]=c[second++];

    }
     
      if(first>endOfFirst)
        for(int k=second;k<=endOfSecond;k++)
           d[result++]=c[k];
      else 
        for(int k=first;k<=endOfFirst;k++)
           d[result++]=c[k];
}
template<class T>
void 
mergePass(T *x,T* y,int n,int segmentSize){
     int start=0;
     while(start<=n-2*segmentSize){ // 循环 确定边界 使数组内所有块排序 
        merge(x,y,start,start+segmentSize-1,start+ 2*segmentSize-1);
        start=start+ 2*segmentSize;
     }

     if(start+ segmentSize<n){
          merge(x,y,start,start+segmentSize-1,n-1);
     }
      else 
        for(int j=start;j<n;j++)
          y[j]=x[j];

}
template<class T>
void 
mergeSort(T* a,int n){
  T* b=new T[n];
  int segmentSize=1; //开始序列长度为1 

  while(segmentSize<n){
      mergePass(a,b,n,segmentSize);
      segmentSize += segmentSize;
      mergePass(b,a,n,segmentSize);
      segmentSize+= segmentSize; 
  }
    delete [] b;
}

int main(){

  int element[]={-9999,88,57,999,-2,55,32,-6};
  mergeSort(element,sizeof(element)/sizeof(element[0]));
    
     for(int i=0;i<8;i++)
       cout<<element[i]<<" ";

       cout<<endl;

}

  3 快速排序 

  可以使用 三值取中 来进行快速排序,避免最坏情况

4  找出第K小的元素

      利用归并排序 

         当 a【lefrEnd】 交换到 a【j】,那么a【leftEnd】 便是第 j-leftEnd+1 小元素 并且==k

  就返回 

       如果 j-leftEnd+1<k ,那么目标就在右半边 k-j+leftEnd-1 小的元素 

       else 就在左半边 第k小元素   

   中间的中间 选择支点  

       

 

5 相距最近的点对 

        两点之间距离公示: 

                 

    我们由此出发,寻找距离最近的点对,只要从 |x-x1|  和 |y-y1|   两个方面来考虑 

       设计四个结构{

              point :数据成员为 x,y 分别代表横坐标,纵坐标 

              point1: 从point派生 :  x,y, 增添 id  定义了一个double类型的转换 ,返回横坐标x的值 用于排序 

             point2: 同上 ,返回y坐标 排序 

             pointPair :内涵 两个point类型成员(代表两点),和一个dist(两点之间距离 )

}

  

若使用直接法,计算每对点之间的距离 ,复杂度为O(n^2)

    我们设计另外一种方法

       1 划分为两边 A区和B区 ,点数大致相等 

如何划分: 

                取x横坐标 ,利用point1类型的数组进行排序 取中间,相当于一条中垂线

       2 分别寻找A区和B区内 相距最短点对(两点同区)

                利用递归算两区内最大值  a,b

                取a,b两个值的较小者记为S

       3 一个点在A 一个点在B 最短点对, 从三者中选最短即可 

             步骤:   

         1)

       因为最短长度目前已经为S,所以当离中垂线(分割线)的距离大于S时,总距离必不可能小于S(因为还要加上y的距离) 

            --》所以只要画出离中垂线 +- S 的距离区域 ,其余区域的点排除(筛选横坐标

   

 2)

       这时分为两个点的集合Ra  ,Rb  分,对于 一个集合中的点,算出距另一个集合中点的距离:

       由于S目前最短距离 ,所以Ya-Yb大于S时,总距离必不可能小于S(因为还要加上X的距离)

--》 所以只要比较距Ya  +- S 的区域 ,其余区域排除 (筛选纵坐标

具体实现 : 

   利用分而治之 ,最小实例为2 ,当小于4时直接计算相互之间的距离,>=4 分为小实例

代码:

 

 

        closestPair:

 

    计算一个点在A一个点在B点:

6. 求递归方程(套表公式法)

   

     

要点: 找到a,b 

            算logb a

            套公式计算h()

            查找到f()带入即可 

递归式求解的三种方法_l-jobs的专栏-CSDN博客_主方法求解递归式

   

7 问题的复杂度上下限

     

 状态空间方法:

决策树方法  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值