分治算法是经典算法之一,其“分而治之”的思想在大多数有名的算法中都有应用。大致说下分治算法的基本原理吧:
- 将大规模问题每次通过分解成小问题进行处理。
- 其次,将分解出的这些子问题逐个解决。
- 最后,将已解的子问题合并,最终得出原问题的解
这里说的很简单,具体可参考博客
本次根据书中的分治算法伪代码与一些大佬参考代码,利用python来实现。
Python 算法入门教程 - 分治算法介绍 - IT学院 - 中国软件协会智能应用服务分会 (itxueyuan.com) 1.参考原书代码
由于分治算法最终的步骤是实现排序后的数组,所以该部分是先实现两个有序数组的合并。
书中的伪代码貌似不太好理解,没关系,可以看下某位大佬画的图片更好理解。参考来源 Python 算法入门教程 - 分治算法介绍 - IT学院 - 中国软件协会智能应用服务分会 (itxueyuan.com)
由于实现过程比较繁琐,可以将整个思路拆分成两部分。
部分1:实现有两个有序结列表的合并
部分2:实现一个无需列表的递归划分,和排序
最后,在部分2中调用部分1函数,即可将众多子数组不断合并
下方代码参考:Python 算法入门教程 - 分治算法介绍 - IT学院 - 中国软件协会智能应用服务分会 (itxueyuan.com)
部分1
# -*- coding: utf-8 -*-
"""
Created on Fri Aug 5 15:30:34 2022
Content: 分治算法:实现有序两数组的合并排序
分治算法基本原理:
1> 对一个数组进行分解,分解成2个或多个
2> 计算分解后的子数组
3> 合并每个子数组的排序结果
4> 得到最终结果
@author: xiaofang
"""
def merge_sorted_list(nums1, nums2):
"""
合并有序数组
"""
#创建1*n的初始化数组
nums = [0] * (len(nums1) + len(nums2))
#初始化索引
id, id1, id2 = 0, 0, 0
#>1 确保两数组都没走完。
#当人一个数组和索引超出后数组自身长度,即数组已完成比较,终止
while id1 < len(nums1) and id2 <len(nums2):
#较两数组第一位数,并执行存储
while id1 < len(nums1) and id2 <len(nums2) and nums1[id1] <= nums2[id2]:
nums[id] = nums1[id1]
id += 1
id1 += 1
while id1 < len(nums1) and id2 <len(nums2) and nums1[id1] > nums2[id2]:
nums[id] = nums2[id2]
id += 1
id2 += 1
#>2 将没走完的数组剩余部分放输出数组后面
#任何情况都有先走完的数组,后走的数组至少剩余一个元素
if id1 !=len(nums1):
nums[id: ] = nums1[id1: ]
if id2 != len(nums2):
nums[id:] = nums2[id2: ]
return nums
nums1 = [1, 5, 21, 89, 90]
nums2 = [6, 12, 21, 65, 230,]
print(merge_sorted_list(nums1, nums2))
部分2
def merge_sort(nums):
"""
对一列无序的数组进行划,每次划分成2等份,并且进行排序
"""
if len(nums) == 0 or len(nums) == 1:
return nums
if len(nums) == 2:
if nums[0] > nums[1]:
nums[0], nums[1] = nums[1], nums[0]
return nums
#递归公式,将列表分成两份,递归排序下去
half = len(nums) // 2
nums1 = merge_sort(nums[:half])
nums1 = merge_sort(nums[half: ])
return merge_sorted_list(nums1, nums2)
输出
nums = [1, 5, 2, 4, 45, 2, 535, 73]
print(merge_sort(nums)) # 输出[1, 2, 2, 4, 5, 45, 73, 535]
总结
分治算法的理论核心,正如其名一样,分而治之。先将一列无序列表递归划分足够小的列表(本文是2元素列表),在对子列表进行排序,最后调用合并算法合并结果。