zoukankan      html  css  js  c++  java
  • 剑指offer——关于排序算法的应用(一):归并排序

    对几种经典排序算法的理解

    排序算法的应用

    在之前的总结中写过关于冒泡排序和选择排序的应用,这种应用其实就是对不同排序思想的一种改造和延伸,如果理解了不同排序思想的实现方式和机制,那么在解决问题的时候也会自觉联想到排序方法。
    排序算法具体应用的时候情况多样,比如有的问题会在解决过程中先排序一下更适合解决,有的问题要求找到某种规律的数,有的问题则需要统计和顺序有关的量,或者对有某种顺序的量进行处理……排序算法的应用范围非常广。

    排序算法的分类

    我理解的排序方法其实分为三大类,一类是顺序思想下的排序思想,这种排序典型的有选择排序和冒泡排序,这种排序一般时间复杂度都会到达O(N^2);一类是插入排序,比如插入排序和希尔排序,这种排序算法也比较“实在”,特点在于其插入操作;而另一类则是分治思想下的快速排序、归并排序。
    不论什么样的排序算法,其要达到的目的终归是:给某个数找到其合适的相对位置,或者给某个相对位置找到合适的数,实现手段多种多样,比如这两种对排序的描述,就有可能让我想到快速排序(给某个数找到合适的相对位置)和选择排序(在合适的相对位置上找到合适的数)。

    遇到的题目分析:

    题目描述:

    在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数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】,0应该在0位置,但是它在7位置,因此前后做差即可。但是我忽略了快速排序在为某个数找到归属的过程中,存在其他数的交换,而这种交换非常有可能打乱这个数和它前后的数的相对顺序,不可行。比如对于[7,1,2,3,4,8,6,0,5,9,10],在对7第一次快排过程中,会对[0,1,2,3,4,5,6] 7:index=7 ,preindex=0, 和[8,9,10]进行递归,此时对7认为它构成了7-0=7个逆序对,这本来没有多大问题,但是对8和5的交换却直接让8和5可能构成的逆序对被打乱了。
    于是参考题解开始思考归并排序,用归并排序解决这个问题,本质上是用分治思想,对于【1,2,3,4,5,6,7,0】,0以前的有序部分有7个数,那么可以知道可以和0组成逆序对的数有7个,从0往前分块计算即可,对于其他可以组成逆序对的数,也可以这样统计逆序对。
    我们再来看归并排序的执行过程,对于两个有序对a,b,归并的操作是这样的:设立一个a+b=c大小的新块,然后从a、b中调数字,比如c[0]取a[0],c[1]取b[0],就这一一直把a、b有序合并为止,如果不理解文字描述可以看归并操作的实现代码:

    #larr为左边的块,rarr为右边的块
    while i<len(larr) and j<len(rarr):
            if larr[i]<rarr[j]:
                arr[k]=larr[i]
                i+=1
            else:
                arr[k]=rarr[j]
                j+=1
            k+=1
        arr[k:]=larr[i:] if i<len(larr)  else rarr[j:]
    

    归并排序中被归并的对象是有序的。
    在选择过程中,左边块中第i个数本该小于右边块中的第j个数,但是却大于了,那么就说明左边块中i后面的数一定也是大于右边块中第j个数的。我们统计左边块从第i个数起到数组结束共有多少个数就可以了。这样排序之后c块是有序的,那么到了c要被归并的时候,还可以正确统计出逆序对吗?c由无序变为有序后可以正确统计出逆序对数吗?答案是可以的,这就是分治思想的精髓,分开看并不会影响总结果。比如c和d要归并了,c中可以和d构成逆序对的数还是可以构成逆序对,而且用我们的方法不会影响计数,我们可以看一个例子:
    1:arr=[1,2,3,7,5,6,8,4]:
    2:a=[1,2,3,7] b=[5,6] e=[8] f=[4]归并ef得到d:
    3:a=[1,2,3,7] b=[5,6] d=[4,8]:
    4:归并后c=[1,2,3,5,6,7] d=[48]
    我们在看和4构成逆序对的数时,不管c中数是否有序,都不影响和4的组合个数,c不论是被分成a、b的形式,还是有序的形式,都不影响和4的逆序对组合。而84逆序对组合在归并 4 和 8时就可以统计,下面是python的实现代码,在归并排序中只需要加一个统计逆序对的实现即可:

    Python代码实现
    class Solution:
     def __init__(self):
         self.count=0
     def InversePairs(self, data):
    	   self.mergeSort(data)
    	   return self.count%1000000007
     def mergeSort(self,arr):
         #堆排序
          if len(arr)>1:
              mid=len(arr)//2
              larr=arr[:mid]
              rarr=arr[mid:]
              self.mergeSort(larr)
              self.mergeSort(rarr)
              i=0
              j=0
              k=0
              while i<len(larr) and j<len(rarr):
                  if larr[i]<=rarr[j]:
                      arr[k]=larr[i]
                      i+=1
                  else:
                      self.count+=len(larr)-i
                      arr[k]=rarr[j]
                      j+=1
                  k+=1
              arr[k:]=larr[i:] if i<len(larr) else rarr[j:]
  • 相关阅读:
    ado异常代码含义对照表及SQL Access,oracle 数据类型对照表
    关于同花顺日数据格式
    把自己以前的QQ plan贴一下
    为应用程序制作帮助文件
    [临时]单源最短路径(Dijkstra算法)
    [IDA] 分析for循环的汇编代码
    对 strlen 汇编代码的解释
    [VC6] 在对话框上实现LOGO图片的渐变性切换效果
    [C++]拼图游戏
    使用 ADO 向数据库中存储一张图片
  • 原文地址:https://www.cnblogs.com/honor260/p/14511028.html
Copyright © 2011-2022 走看看