一、分治法介绍
子问题与原问题性质相同,子问题相互独立。把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。分治法是很多高效算法的基础,如排序算法(快速排序,归并排序)。分治算法通常以递归的方式实现,当然也有非递归的实现方式。
分-治-合:1、分解子问题;2、解决子问题;3、合并子问题解为原问题的解
二、可使用分治法求解的一些经典问题
包括:选择排序、插入排序、冒泡排序、堆排序、二分搜索、合并排序、汉诺塔等。
1、选择排序
n个元素的排序问题转化为n-1次选择最小值问题
找到数组中最小的放到排头,剩下的元素组成一个数组,又从数组中找出最小的放到排头,随着一个个子问题的解决就可以得到大问题的解。
把6、3、1、7、4按从小到大顺序排
|6|3|1|7|4| ------------------|1|6|3|7|4|
|6|3|7|4|---------------------|3|6|7|4|
|6|7|4|-----------------------|4|6|7|
|6|7|-------------------------|6|7|
合并子问题:|1|3|4|6|7|
2、插入排序
插入排序法由n-1趟排序组成,先对前两个数进行排序,第三个数插入到前两个数合适的位置(进行排序),即插入排序保证从位置0到p位置上的元素为以排序状态。
3、递推归并排序
例题:leetcode 23
用分治方法进行合并
-将 k 个链表配对并将同一对中的链表合并;
-第一轮合并以后, k个链表被合并成了 k/2个链表,平均长度为 2n/k ,然后是k/4 个链表等等;
-重复这一过程,直到我们得到了最终的有序链表
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
return merge(lists, 0, lists.length - 1);
}
//递归合并,每两个链表合并成一个链表,合并好的链表继续合并
public ListNode merge(ListNode[] lists, int first, int last){
if(first==last) return lists[first];
if(first>last) return null;
int mid = (first+last)/2;
return mergeTwoList(merge(lists, first, mid), merge(lists, mid+1, last));
}
//合并两个链表
public ListNode mergeTwoList(ListNode a,ListNode b){
if(a == null || b == null)
return a != null ? a:b;
ListNode head = new ListNode(0);
ListNode pos = head, apra = a, aprb = b;
// ListNode apra = a;
// ListNode aprb = b;
while(apra != null && aprb != null){
if(apra.val<aprb.val){
pos.next = apra;
apra = apra.next;
}else{
pos.next = aprb;
aprb = aprb.next;
}
pos = pos.next;
}
pos.next = apra != null ? apra:aprb;
return head.next;
}
}
4、分治递归
如果子问题和原问题相同,只是规模不同,则可以对子问题重复上述过程,直到子问题可以直接求解。如:n!、汉诺塔、斐波拉契数列、整数乘法、幂乘、二分查找、归并排序。
1、分治递归求最大最小问题
1)分:将数组A从中间分为两个子数组AL和AR;
2)治:递归地求解AL的最大最小值、递归地求解AR的最大最小值;
3)合:AL的最大值和AR的最大值的大者作为A的最大值;同理得出A的最小值。
2、分治递归的算法描述:(1)解决小规模的问题;(2)分解问题;(3)递归解决子问题;(4)合并子问题的解为原问题的解。
3、归并排序:将序列从中间分成2个子序列