LeetCode刷题 -- 分治算法

分治算法

思路:

就是把一个复杂的问题分成两个或者更多相同或者相似的子问题(分),在把子问题分成更小的子问题...一直到最后的子问题可以简单的直接求解出来,那么原问题的解就是分出来的这些子问题的合并(治)。

步骤:

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

240. 搜索二维矩阵 II

题目

        搜索二维矩阵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

 

23. 合并K个升序链表

题目:

        给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。

解题思路:

       首先要实现两个链表的合并,实现这一步骤后,问题就迎刃而解了

    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));
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值