系列文章目录
目录
系列文章目录
前言
一、分解问题
二、解决子问题
三、合并结果
总结
前言
刷题按照:
[力扣刷题攻略] Re:从零开始的力扣刷题生活 - 力扣(LeetCode)
参考:
「五大常用算法」一文搞懂分治算法 - 知乎 (zhihu.com)
分治算法(divide and conquer)是五大常用算法(分治算法、动态规划算法、贪心算法、回溯法、分治界限法)之一,分治,字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。在计算机科学中,分治法就是运用分治思想的一种很重要的算法。分治法是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)等等。
一、分解问题
169. 多数元素 - 力扣(LeetCode)
//方法一:排序取中值
// class Solution {
// public int majorityElement(int[] nums) {
// Arrays.sort(nums);
// return nums[nums.length/2];
// }
// }//方法二:投票法,众数的个数大于n/2,顾投票总数大于0
// class Solution {
// public int majorityElement(int[] nums) {
// int count = 0;
// Integer mode = null;// for(int num : nums)
// {
// if(count == 0)
// {
// mode = num;
// }
// count += (num == mode) ? 1 : -1;
// }
// return mode;
// }
// }//方法三:HashMap,数组元素作为键值对的Key,出现个数作为键值对的Value,存放时出现相同的Key,将对应Value进行加1后放到
//HashMap中,遍历数据完成后返回Value值大于n/2的即可。
// class Solution {
// public int majorityElement(int[] nums) {
// Map<Integer, Integer> countMap = new HashMap<> ();
// for(int num : nums)
// {
// if(!countMap.containsKey(num))
// {
// countMap.put(num, 1);
// }
// else
// {
// countMap.put(num, countMap.get(num) + 1);
// }// if(countMap.get(num) > nums.length / 2)
// {
// return num;
// }
// }
// return -1;
// }
// }// //方法四:回调投票
class Solution {public int majorityElement(int[] nums) {return moore(0, nums, nums[0]);}public int moore(int i, int[] nums, int mode){int count = 0;for(int j = i; j < nums.length; j++){if(nums[j] == mode){count++;}else{count--;}if(count < 0){return moore(j, nums, nums[j]);}}return mode;}
}
4. 寻找两个正序数组的中位数 - 力扣(LeetCode)
class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {int totalLength = nums1.length + nums2.length;int halfLength = totalLength / 2;if (totalLength % 2 == 0) {// 如果总长度是偶数,中位数是左侧部分的最大值和右侧部分的最小值之和的一半double leftMedian = findKthElement(nums1, 0, nums2, 0, halfLength);double rightMedian = findKthElement(nums1, 0, nums2, 0, halfLength + 1);return (leftMedian + rightMedian) / 2.0;} else {// 如果总长度是奇数,中位数就是右侧部分的最小值return findKthElement(nums1, 0, nums2, 0, halfLength + 1);}}private double findKthElement(int[] nums1, int start1, int[] nums2, int start2, int k) {if (start1 == nums1.length) {// 如果 nums1 部分已经为空,则直接返回 nums2 部分的第 k 个元素return nums2[start2 + k - 1];}if (start2 == nums2.length) {// 如果 nums2 部分已经为空,则直接返回 nums1 部分的第 k 个元素return nums1[start1 + k - 1];}if (k == 1) {// 如果 k 等于 1,则直接返回两个数组当前部分的最小值return Math.min(nums1[start1], nums2[start2]);}// 在两个数组中寻找第 k/2 个元素,并更新两个数组的起始位置int mid1 = start1 + k / 2 - 1 < nums1.length ? nums1[start1 + k / 2 - 1] : Integer.MAX_VALUE;int mid2 = start2 + k / 2 - 1 < nums2.length ? nums2[start2 + k / 2 - 1] : Integer.MAX_VALUE;if (mid1 < mid2) {// 如果 nums1 的中间元素小于 nums2 的中间元素,则舍弃 nums1 的左侧部分return findKthElement(nums1, start1 + k / 2, nums2, start2, k - k / 2);} else {// 如果 nums1 的中间元素大于等于 nums2 的中间元素,则舍弃 nums2 的左侧部分return findKthElement(nums1, start1, nums2, start2 + k / 2, k - k / 2);}}
}
543. 二叉树的直径 - 力扣(LeetCode)
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {int max = 0;public int diameterOfBinaryTree(TreeNode root) {if (root == null) {return 0;}dfs(root);return max;}private int dfs(TreeNode root) {if (root.left == null && root.right == null) {return 0;}int leftSize = root.left == null? 0: dfs(root.left) + 1;int rightSize = root.right == null? 0: dfs(root.right) + 1;max = Math.max(max, leftSize + rightSize);return Math.max(leftSize, rightSize);}
}
二、解决子问题
69. x 的平方根 - 力扣(LeetCode)
牛顿迭代法:Integer square root - Wikipedia
class Solution {public int mySqrt(int x) {if (x == 0) {return 0;}double x0 = x; // 初始猜测值double x1 = 0.5 * (x0 + x / x0); // 使用牛顿迭代法更新猜测值// 迭代直到猜测值不再变化while (Math.abs(x1 - x0) >= 1) {x0 = x1;x1 = 0.5 * (x0 + x / x0);}return (int) x1; // 将最终结果转换为整数并返回}
}
一个特殊的方法来计算平方根,即通过取指数和对数的方式。实际上,这是一种常见的近似计算方法。它的基本思想是,计算 x 的平方根,可以转换为计算 e^(0.5 * ln(x))。在这里,Math.exp(0.5 * Math.log(x))
计算的是 e^(0.5 * ln(x))。
class Solution {public int mySqrt(int x) {if (x == 0) {return 0;}int ans = (int) Math.exp(0.5 * Math.log(x));return (long) (ans + 1) * (ans + 1) <= x ? ans + 1 : ans;}
}
53. 最大子数组和 - 力扣(LeetCode)
class Solution {public int maxSubArray(int[] nums) {int ans = Integer.MIN_VALUE;int count = 0;for (int i=0; i<nums.length; i++) {count = Math.max(count + nums[i], nums[i]);ans = Math.max(ans, count);}return ans;}
}
34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)
class Solution {public int[] searchRange(int[] nums, int target) {int[] res = {-1, -1};// 寻找第一个出现的位置int left = 0, right = nums.length - 1;while (left <= right) {int mid = left + (right - left) / 2;if (nums[mid] == target) {res[0] = mid;right = mid - 1; // 继续在左半边搜索} else if (nums[mid] < target) {left = mid + 1;} else {right = mid - 1;}}// 如果未找到目标值,直接返回if (res[0] == -1) {return res;}// 寻找最后一个出现的位置left = res[0]; // 左边界从第一次找到的位置开始right = nums.length - 1;while (left <= right) {int mid = left + (right - left) / 2;if (nums[mid] == target) {res[1] = mid;left = mid + 1; // 继续在右半边搜索} else if (nums[mid] < target) {left = mid + 1;} else {right = mid - 1;}}return res;}
}
三、合并结果
23. 合并 K 个升序链表 - 力扣(LeetCode)
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val = val; }* ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {// 合并两个有序链表public ListNode mergeKLists(ListNode[] lists) {if (lists.length == 0) {return null;}return split(lists, 0, lists.length - 1);}public ListNode split(ListNode[] lists, int i, int j) {
// System.out.println(i + " " + j);if (j == i) {return lists[i];}int m = (i + j) >>> 1;return mergeTwoLists(split(lists, i, m),split(lists, m + 1, j));}public ListNode mergeTwoLists(ListNode p1, ListNode p2) {if (p2 == null || p1 == null) {return p2 == null ? p1 : p2;}if (p1.val < p2.val) {p1.next = mergeTwoLists(p1.next, p2);return p1;} else {p2.next = mergeTwoLists(p1, p2.next);return p2;}}
}
1277. 统计全为 1 的正方形子矩阵 - 力扣(LeetCode)
动态规划
class Solution {public int countSquares(int[][] matrix) {// 动态规划, 二维数组dp[i][j]表示以i,j为右下角的最大的全1正方形的边长// if matrix[i][j] == '1' -> dp[i][j] = Min{dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]} + 1int m = matrix.length;int n = matrix[0].length;int[][] dp = new int[m][n];// 初始化边界int result = 0;for (int i = 0; i < m; i++) {if (matrix[i][0] == 1) {dp[i][0] = 1;}}for (int i = 0; i < n; i++) {if (matrix[0][i] == 1) {dp[0][i] = 1;}}// 自底向上for (int i = 1; i < m; i++) {for (int j = 1; j < n; j++) {if (matrix[i][j] == 0) {dp[i][j] = 0;} else {dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1;}}}// 遍历dpfor (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {result += dp[i][j];}}return result;}}
总结
分治法很重要,然后我发现做题勾勾画画还挺有用的,希望多敲多做,慢慢来吧。