剑指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:]
原文地址:https://www.cnblogs.com/honor260/p/14511028.html