分治算法
思路:
就是把一个复杂的问题分成两个或者更多相同或者相似的子问题(分),在把子问题分成更小的子问题...一直到最后的子问题可以简单的直接求解出来,那么原问题的解就是分出来的这些子问题的合并(治)。
步骤:
1、分解:将原问题分解为若干规模较小,相互独立,并且与原问题相同的子问题
2、解决:分解出来的子问题较小且容易解决,如果不容易解决就在继续分解
3、合并:将已求解的各个子问题的解,逐步合并为原问题的解
有的问题分解后不需要合并,可以省略第三步,而多数情况下,原题的解需要按照恰当的方法将子问题的解合并成最终解。
算法框架:
Divide-and-Conquer(int n){ if(n<=n0){//n为问题规模 ,n0为可解子问题的规模 解子问题; return(子问题的解); } for(i=1;i<=k;i++){//分解成较小的子问题p1,p2,...,pk yi=Divide-and-Conquer(|Pi|);//递归解决 } T=MERGE(y1,y2,...yk);//合并子问题 return(T);//返回问题的解 }
例题
50. Pow(x, n) 求x的n次幂
题目:
求x的n次幂
分析:
1、分解:
n为奇数,n为偶数 大类
n>=2时,或者n<=-2时,怎么计算? ==》递归
2、解决
简单子问题:
n = 0 时,返回1
n = 1 时,返回x
n = -1 时,返回1/x
复杂子问题:递归调用,一直到
n为偶数时:x^n = x^(n/2) * x^(n/2) ===》这个场景不需要考虑n的正负
n为奇数时:
x^n = x^(n/2) * x^(n/2) * x ===》n为正数
x^n = x^(n/2) * x^(n/2) / x ===》n为负数
3、合并:
上述递归调用的过程就是在合并。
public double myPow(double x, int n) {
// 简单场景,可以直接计算得到值(也就是递归的边界)
if (n == 0) {
return 1;
}
if (n == 1) {
return x;
}
if (n == -1) {
return 1/x;
}
// 对场景进行划分:偶数次幂,奇数次幂(正奇数次幂,负奇数次幂)
if (n % 2 == 0) {
// 偶数次幂时,可以划分为这个数的两个二分之n次幂 相乘
double v = myPow(x, n / 2);
return v * v;
} else if (n > 0) {
// 奇数次幂时,可以划分为这个数的两个二分之n次幂 相乘,然后在乘以这个数
double v = myPow(x, n / 2);
return v * v * x;
} else {
// 奇数次幂时,可以划分为这个数的两个二分之n次幂 相乘,然后在除以这个数
double v = myPow(x, n / 2);
return v * v / x;
}
}
class Test50:
def myPow(self, x: float, n: int) -> float:
if n == 0:
return 1
if n == 1:
return x
if n == -1:
return 1 / x
y = self.myPow(x, n // 2)
if n % 2 == 0:
return y * y
elif n % 2 == 1:
return y * y * x
else:
return y * y / x
题目
搜索二维矩阵m*n中是否有目标值target,其中,该矩阵从左到右升序,从上到下升序
分析
1、分解:在二维矩阵中寻找值,可以分解成在每个一维矩阵里面寻找值。
2、解决:单个一维矩阵怎么找值?可以采用二分法
3、合并:此问题不需要合并,在1、2步骤中如果找到了目标值,就可以直接得到结果。
public class Test240 {
public static boolean searchMatrix(int[][] matrix, int target) {
// 遍历二维数组的每一行
for (int[] row : matrix) {
//二分查找一维数组时,只要返回值>=0,就说明存在目标值,因此直接返回true。
int i = divideSearch(row, target);
if (i >= 0) {
return true;
}
}
return false;
}
// 二分法查找一维数组中是否存在目标值,存在则返回坐标,不存在则返回一个-1;
public static int divideSearch(int[] row, int target) {
int low = 0;
int high = row.length - 1;
while (low <= high) {
int mid = (high - low) / 2 + low;
if (row[mid] == target) {
return mid;
} else if (row[mid] > target) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
}
另外一种解法
因为二维数据从左到右,从上到下都是递增的
那么可以从二维数据的右上角进行寻找:
1、如果当前位置的值大于目标值,那么可以往左移动
2、如果当前位置的值小于目标值,那么可以向下移动
3、如果等于目标值,可以直接返回true
(从左下角开始寻找也是一样的。)
public class Test240 {
public static boolean searchMatrix(int[][] matrix, int target) {
int row = matrix.length;
int col = matrix[0].length;
int srow=0;
int scol=col-1;
while (srow < row && scol >= 0) {
if (matrix[srow][scol] == target) {
return true;
} else if (matrix[srow][scol]> target) {
scol--;
} else {
srow++;
}
}
return false;
}
}
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
m = 0
n = len(matrix[0]) - 1
while n >= 0 and m < len(matrix):
x = matrix[m][n]
if x > target:
n -= 1
elif x < target:
m += 1
else:
return True
return False
题目:
给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。
解题思路:
首先要实现两个链表的合并,实现这一步骤后,问题就迎刃而解了
private ListNode mergeTwoList(ListNode a, ListNode b) { 定义一个头节点和尾节点,同时指向一个新建的节点 ListNode head = new ListNode(0); ListNode tail = head; 分别获取入参两个节点链表的头节点 ListNode ap = a; ListNode bp = b; 当两个链表当前都不为空的时候,将较小的值插入到tail节点后面, 然后tail节点向后移动一位 while (ap !=null && bp!=null) { if (ap.val > bp.val) { 把小的节点插入到tail节点后面 tail.next = bp; 然后节点向后移动 bp = bp.next; } else { tail.next = ap; ap = ap.next; } 插入后,tail节点向后移一位,方便后续继续插入 tail = tail.next; } 上面循环跳出,意味着有一个链表已经空了,那么将非空的链表在插入到tail后面 tail.next = ap == null ? bp:ap; 返回head节点后面一个节点(因为head节点时自己定义出来的) return head.next; }
那么如何将一个数组里面所有的链表都合并呢?------循环即可
public ListNode mergeKLists(ListNode[] lists) { // 定义一个空的链表 ListNode res = null; for (int i = 0; i < lists.length - 1; i++) { for循环,逐一合并链表,返回值赋给res,然后进行下一轮合并 res = mergeTwoList(res, lists[i]); } return res; }
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
if len(lists) == 0:
return None
if len(lists) == 1:
return lists[0]
for i in range(len(lists) - 1):
lists[i + 1] = self.mergeTwoLists(lists[i], lists[i + 1])
i += 1
return lists[len(lists) - 1]
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
if not list1:
return list2
if not list2:
return list1
head = res = ListNode(-1)
while list1 and list2:
if list1.val > list2.val:
res.next = list2
list2 = list2.next
else:
res.next = list1
list1 = list1.next
res = res.next
res.next = list1 if list1 else list2
return head.next
优化:分治思想
1、分解:k个链表合并 --》相邻链表两两合并,转为k/2个链表合并,然后转为k/4个链表。。。一直到两个链表合并
2、解决:合并两个链表
3、合并:见示意图
public ListNode mergeKLists(ListNode[] lists) { return merge(lists, 0, lists.length-1); } private ListNode merge(ListNode[] lists, int l, int r) { if ( l == r) { return lists[l]; } if ( l > r) { return null; } int mid = (l + r) / 2; 返回的就是l-r区间,左半边和右半边合并 return mergeTwoList(merge(lists, l, mid), merge(lists, mid+1, r)); }