算法分析与设计——分治策略求逆序对(归并排序)

1、算法讲解

  • 利用分治策略求逆序对主要是利用归并排序,即合并两个有序数组。
  • 假设有两个有序的数组,我们对其进行归并排序
  • 从两数组开头开始比较。
  • 由于2>1,1先入队,并且知道1比前面数组的所有元素都小(数组有序),所以记录逆序个数=4。
  • 继续比较2和4,由于2<4,2先入队,并且2比后面数组的所有元素都小,所以没有逆序对。
  • 继续比较3和4,由于3<4,3先入队,并且3比后面数组的所有元素都小,所以没有逆序对。
  • 继续比较5和4,由于5>4,4先入队,并且知道4比前面数组的所有元素都小(数组有序),所以记录逆序个数=4+2。
  • 继续比较5和6,由于5<6,5先入队,并且5比后面数组的所有元素都小,所以没有逆序对。
  • 继续比较7和6,由于7>6,6先入队,并且知道6比前面数组的所有元素都小(数组有序),所以记录逆序个数=4+2+1。
  • 继续比较7和8,由于7<8,7先入队,并且7比后面数组的所有元素都小,所以没有逆序对。
  • 此时前面数组的所有元素都已合并,最后把8合并即可。最后计算出逆序对个数为7。
  • 上述算法的前提是两个子数组是有序的,那如何将上述算法的思想运用到普通的数组中呢?
  • 可以先将数组进行拆分,单个元素的数组一定是有序的。
  • 然后再依次合并两个有序数组,并且在合并的过程中计算逆序对的个数。
    • 最后的逆序对个数=1+1+1+1+4+4+16=28。
  • 这就是分治的思想。

2、实例

  • 采用分治策略完成下述数组的逆序对计数:[12, 14, 53, 8, 74, 23, 17, 66, 70, 9, 34, 75, 90, 34, 98, 50, 86, 94, 3, 67, 73, 79, 43, 66, 19, 20, 57,43, 28, 83]
  • python代码: 
    • # 计算逆序对
      def reverser_pairs(nums):
          length = len(nums)
          if length < 2:  # 数组只有一个元素,没有逆序对
              return 0
          copy = nums.copy()  # 需要对数组进行排序,故拷贝原数组
          temp = []  # 辅助数组
          return count_pairs(copy, 0, length - 1, temp)  # 递归计算逆序对
      
      
      #  划分数组,归并排序,计算逆序对个数
      def count_pairs(nums, left, right, temp):
          if left == right:  # 划分到只剩一个元素
              return 0
          mid = left + (right - left) // 2  # 与(left+right)/2效果一致,防止溢出,向下取整
          # 划分数组,记录每个子数组的逆序对
          left_pairs = count_pairs(nums, left, mid, temp)
          right_pairs = count_pairs(nums, mid + 1, right, temp)
          # 前面数组已经小于后边数组
          if nums[mid] <= nums[mid + 1]:
              return left_pairs + right_pairs
          # 对子区间内的元素进行归并排序并计算逆序对个数
          cross_pairs = merge_and_count(nums, left, mid, right, temp)
          return left_pairs + right_pairs + cross_pairs  # 累加
      
      
      # 归并排序,计算逆序对个数
      def merge_and_count(nums, left, mid, right, temp):
          temp = nums[left:right + 1].copy()  # 将区间元素复制出来进行归并排序
          i = left
          j = mid + 1
          count = 0
          for k in range(left, right + 1):
              if i == mid + 1:  # 前面数组都已入队,将后面数组全部入队
                  nums[k] = temp[j - left]
                  j += 1
              elif j == right + 1:  # 后面数组都已入队,将前面数组全部入队
                  nums[k] = temp[i - left]
                  i += 1
              elif temp[i - left] <= temp[j - left]:
                  nums[k] = temp[i - left]
                  i += 1
              else:
                  nums[k] = temp[j - left]
                  j += 1
                  count += mid - i + 1  # 小于前面所有的元素
          return count
      
      
      if __name__ == '__main__':
          nums = input("请输入整数数组,用空格分隔: ")
          nums = [int(i) for i in nums.split(' ')]  # 将每个数转换为整型后输出
          print("该数组的逆序对个数为:", reverser_pairs(nums))
  • 运行结果:
    • 输入:12 14 53 8 74 23 17 66 70 9 34 75 90 34 98 50 86 94 3 67 73 79 43 66 19 20 57 43 28 83

猜你喜欢

转载自blog.csdn.net/weixin_45100742/article/details/134545772