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

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
  • 17
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
归并排序是一种经典的排序算法,它通过将待排序的序列递归地划分成较小的子序列,然后将这些子序列进行合并,最终得到一个有序的序列。在归并排序的过程中,可以通过统计逆序对的数量来评估序列的有序程度。 使用归并排序逆序对的基本思想是:在合并两个有序子序列的过程中,如果左子序列中的元素大于右子序列中的元素,则构成了一个逆序对。在合并过程中,统计逆序对的数量,并将两个子序列合并成一个有序序列。 具体步骤如下: 1. 将待排序序列不断二分,直到每个子序列只有一个元素。 2. 逐层合并相邻的子序列,并在合并过程中统计逆序对的数量。 3. 重复步骤2,直到所有子序列合并成一个有序序列。 以下是使用归并排序逆序对的示例代码: ```python def merge_sort(arr): if len(arr) <= 1: return arr, 0 mid = len(arr) // 2 left, count_left = merge_sort(arr[:mid]) right, count_right = merge_sort(arr[mid:]) merged, count_merge = merge(left, right) return merged, count_left + count_right + count_merge def merge(left, right): merged = [] count = 0 i, j = 0, 0 while i < len(left) and j < len(right): if left[i] <= right[j]: merged.append(left[i]) i += 1 else: merged.append(right[j]) j += 1 count += len(left) - i merged.extend(left[i:]) merged.extend(right[j:]) return merged, count ``` 使用上述代码,可以通过调用`merge_sort`函数来解给定序列的逆序对数量。函数返回排序后的序列以及逆序对的数量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恣睢s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值