LeetCode分治法 divide and conquer (共17题)
Posted zhangwanying
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode分治法 divide and conquer (共17题)相关的知识,希望对你有一定的参考价值。
链接:https://leetcode.com/tag/divide-and-conquer/
【4】Median of Two Sorted Arrays
【23】Merge k Sorted Lists
【53】Maximum Subarray (2019年1月23日, 谷歌tag复习)
最大子段和。
题解:
follow up 是divide and conquer
If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
【169】Majority Element
【215】Kth Largest Element in an Array (2018年12月11日,wiggle sort 专题,需要复习)
用 O(n) 的时间复杂度找到数组中第 K 大的元素。重复元素也计入 K。
题解:本题可以用 heap 解答,时间复杂度是 O(nlogK)。其实还可以用 quick select 解答(也就是快排的partition(2-way partition)),平均复杂度是 O(n),最坏是 O(n^2)。为了搞平均,一开始要把数组 random_shuffle 一下,尽可能避免 worst case。
1 class Solution { 2 public: 3 int findKthLargest(vector<int>& nums, int k) { 4 random_shuffle(nums.begin(), nums.end()); 5 const int n = nums.size(); 6 int start(0), end(n-1), index(n-k); 7 while (start < end) { 8 int idx = partition(nums, start, end); 9 if (idx < index) { 10 start = idx + 1; 11 } else if (idx > index) { 12 end = idx - 1; 13 } else { 14 return nums[idx]; 15 } 16 } 17 return nums[start]; 18 } 19 int partition(vector<int>& nums, int start, int end) { 20 int pivot = start; 21 while (start < end) { 22 while (nums[start] <= nums[pivot]) { 23 start++; 24 } 25 while (nums[end] > nums[pivot]) { 26 end--; 27 } 28 if (start > end) {break;} 29 swap(nums[start], nums[end]); 30 } 31 swap(nums[end], nums[pivot]); 32 return end; 33 } 34 35 };
【218】The Skyline Problem
【240】Search a 2D Matrix II
【241】Different Ways to Add Parentheses (2018年11月15日,算法群)
给了一个字符串算式,里面含有 “+”,“-”,“*” 这三种运算符,可以在算式的任何一个地方加括号,整个算式能加的括号数不限。问这个算式所有可能的答案。
Example 1: Input: "2-1-1" Output: [0, 2] Explanation: ((2-1)-1) = 0 (2-(1-1)) = 2 Example 2: Input: "2*3-4*5" Output: [-34, -14, -10, -10, 10] Explanation: (2*(3-(4*5))) = -34 ((2*3)-(4*5)) = -14 ((2*(3-4))*5) = -10 (2*((3-4)*5)) = -10 (((2*3)-4)*5) = 10
题解:我们可以在任意地方加括号,每个运算符的两边都可以加个括号看成是一个子问题,先把子问题的所有解求出来,然后把两个子问题的解集合做笛卡尔积,形成大问题的解集合。
审题很重要,我一度以为如果运算符是 ‘-’ 的话,那么后面的算式的 加号要变成减号,减号要变成加号,这个题意里面是没有的。
1 class Solution { 2 public: 3 vector<int> diffWaysToCompute(string input) { 4 return calPart(input); 5 } 6 vector<int> calPart(string s) { 7 const int n = s.size(); 8 if (record.find(s) != record.end()) {return record[s];} 9 //纯数字的情况 10 if (s.find("+") == string::npos && s.find("-") == string::npos && s.find("*") == string::npos) { 11 vector<int> ret{stoi(s)}; 12 return ret; 13 } 14 //含有运算符的情况 15 vector<int> res; 16 for (int i = 0; i < n; ++i) { 17 if (!isdigit(s[i])) { 18 string front = s.substr(0, i), back = s.substr(i+1); 19 vector<int> resFront = calPart(front), resBack = calPart(back); 20 for (auto f : resFront) { 21 for (auto b : resBack) { 22 int tempres = 0; 23 if (s[i] == ‘+‘) { 24 tempres = f + b; 25 } else if (s[i] == ‘-‘) { 26 tempres = f - b; 27 } else if (s[i] == ‘*‘) { 28 tempres = f * b; 29 } 30 res.push_back(tempres); 31 } 32 } 33 } 34 } 35 //sort(res.begin(), res.end()); 36 record[s] = res; 37 return res; 38 } 39 unordered_map<string, vector<int>> record; 40 };
【282】Expression Add Operators
【312】Burst Balloons
【315】Count of Smaller Numbers After Self (2019年2月12日,归并排序)
给了一个数组,要求返回一个数组,返回数组中的元素是原数组中每个元素右边比它小的个数。
Input: [5,2,6,1]
Output: [2,1,1,0]
Explanation:
To the right of 5 there are 2 smaller elements (2 and 1).
To the right of 2 there is only 1 smaller element (1).
To the right of 6 there is 1 smaller element (1).
To the right of 1 there is 0 smaller element.
题解:这个题可以用线段数,树状数组,分治法来解。下面说一下分治法怎么解。
在每一轮中,我们可以把整个数组分成左右两半,利用归并排序的思想,把左区间和右区间变的有序之后,这样我们就可以把左区间内的任意一个元素nums[i],在右区间内用 lower_bound 找到有多少个元素小于它,然后把这个值加到res[i]上。
一共需要三个数组:nums, sorted, count。原来的数据存在nums, 归并排序后的数组存在sortedNums, count[i]对应的是nums[i]的 number of smaller elements to the right.
1 class Solution { 2 public: 3 vector<int> countSmaller(vector<int>& nums) { 4 const int n = nums.size(); 5 vector<int> sorted(nums.begin(), nums.end()), res(n, 0); 6 if (n == 0) {return res;} 7 mergesort(nums, sorted, 0, n-1, res); 8 return res; 9 } 10 void mergesort(vector<int>& nums, vector<int>& sorted, int begin, int end, vector<int>& res) { 11 if (begin == end) { return; } 12 int mid = (begin + end) / 2; 13 mergesort(nums, sorted, begin, mid, res); 14 mergesort(nums, sorted, mid + 1, end, res); 15 for (int i = begin; i <= mid; ++i) { 16 int value = nums[i]; 17 auto iter = lower_bound(sorted.begin() + mid + 1, sorted.begin() + end + 1, value); 18 res[i] += distance(sorted.begin() + mid + 1, iter); 19 } 20 //merge 21 vector<int> arr1(sorted.begin() + begin, sorted.begin() + mid + 1), 22 arr2(sorted.begin() + mid + 1, sorted.begin() + end + 1); 23 int p1 = 0, p2 = 0; 24 for (int idx = begin; idx <= end; ++idx) { 25 if (p1 == arr1.size()) { 26 sorted[idx] = arr2[p2++]; 27 } else if (p2 == arr2.size()) { 28 sorted[idx] = arr1[p1++]; 29 } else { 30 if (arr1[p1] < arr2[p2]) { 31 sorted[idx] = arr1[p1++]; 32 } else { 33 sorted[idx] = arr2[p2++]; 34 } 35 } 36 } 37 } 38 };
【327】Count of Range Sum (2019年2月14日,谷歌tag,归并排序)
给了一个数组nums,和一个范围 [lower, upper],返回有多少个子数组的和在这个范围之内。
题解:
【426】Convert Binary Search Tree to Sorted Doubly Linked List
【493】Reverse Pairs
【514】Freedom Trail
【903】Valid Permutations for DI Sequence
【932】Beautiful Array
以上是关于LeetCode分治法 divide and conquer (共17题)的主要内容,如果未能解决你的问题,请参考以下文章
c_cpp 合并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。合并排序法是将两个(或两个以上)有序表合并成一个新的有序表,