【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。
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
random_shuffle(nums.begin(), nums.end());
const int n = nums.size();
int start(), end(n-), index(n-k);
while (start < end) {
int idx = partition(nums, start, end);
if (idx < index) {
start = idx + ;
} else if (idx > index) {
end = idx - ;
} else {
return nums[idx];
}
}
return nums[start];
}
int partition(vector<int>& nums, int start, int end) {
int pivot = start;
while (start < end) {
while (nums[start] <= nums[pivot]) {
start++;
}
while (nums[end] > nums[pivot]) {
end--;
}
if (start > end) {break;}
swap(nums[start], nums[end]);
}
swap(nums[end], nums[pivot]);
return end;
}
};
【218】The Skyline Problem
【240】Search a 2D Matrix II
【241】Different Ways to Add Parentheses (2018年11月15日,算法群)
给了一个字符串算式,里面含有 “+”,“-”,“*” 这三种运算符,可以在算式的任何一个地方加括号,整个算式能加的括号数不限。问这个算式所有可能的答案。
Example :
Input: "2-1-1"
Output: [, ]
Explanation:
((-)-) =
(-(-)) = Example :
Input: "2*3-4*5"
Output: [-, -, -, -, ]
Explanation:
(*(-(*))) = -
((*)-(*)) = -
((*(-))*) = -
(*((-)*)) = -
(((*)-)*) =
题解:我们可以在任意地方加括号,每个运算符的两边都可以加个括号看成是一个子问题,先把子问题的所有解求出来,然后把两个子问题的解集合做笛卡尔积,形成大问题的解集合。
审题很重要,我一度以为如果运算符是 ‘-’ 的话,那么后面的算式的 加号要变成减号,减号要变成加号,这个题意里面是没有的。
class Solution {
public:
vector<int> diffWaysToCompute(string input) {
return calPart(input);
}
vector<int> calPart(string s) {
const int n = s.size();
if (record.find(s) != record.end()) {return record[s];}
//纯数字的情况
if (s.find("+") == string::npos && s.find("-") == string::npos && s.find("*") == string::npos) {
vector<int> ret{stoi(s)};
return ret;
}
//含有运算符的情况
vector<int> res;
for (int i = ; i < n; ++i) {
if (!isdigit(s[i])) {
string front = s.substr(, i), back = s.substr(i+);
vector<int> resFront = calPart(front), resBack = calPart(back);
for (auto f : resFront) {
for (auto b : resBack) {
int tempres = ;
if (s[i] == '+') {
tempres = f + b;
} else if (s[i] == '-') {
tempres = f - b;
} else if (s[i] == '*') {
tempres = f * b;
}
res.push_back(tempres);
}
}
}
}
//sort(res.begin(), res.end());
record[s] = res;
return res;
}
unordered_map<string, vector<int>> record;
};
【282】Expression Add Operators (2019年3月16日,dfs,打卡题)
Given a string that contains only digits 0-9 and a target value, return all possibilities to add binary operators (not unary) +, -, or *between the digits so they evaluate to the target value.
Example 1:
Input: num = "123", target = 6
Output: ["1+2+3", "1*2*3"] Example 2:
Input: num = "232", target = 8
Output: ["2*3+2", "2+3*2"] Example 3:
Input: num = "105", target = 5
Output: ["1*0+5","10-5"] Example 4:
Input: num = "00", target = 0
Output: ["0+0", "0-0", "0*0"] Example 5:
Input: num = "3456237490", target = 9191
Output: []
题解:如果本题只有加号和减号,那么就是一个直接的dfs。但是本题还有一个乘号,乘号的可以提前优先级运算。所以我们需要保存最后一个单项式的值。
比如说我们已经计算了 1 + 2 - 3 ___ 5 要填写 3 和 5 之间的符号的时候, 如果我们想填个乘号,那么,我们需要最后一项 -3,所以我们用一个变量保存当前表达式的最后一个参数,作为 dfs 的参数。
class Solution {
public:
vector<string> addOperators(string num, int target) {
vector<string> res;
string temp;
dfs(num, temp, res, (long)target, , 0LL, 0LL);
return res;
}
void dfs(const string num, string temp, vector<string>& res, long target, int start, long curRes, long lastVal) {
if (start == num.size()) {
if (target == curRes) {res.push_back(temp);}
return;
}
for (int i = start; i < num.size(); ++i) {
string strCur = num.substr(start, i - start + );
if (strCur.size() > && strCur[] == '') {break;} //leading zeros
long iCur = stol(strCur);
if (start == ) {
dfs(num, strCur, res, target, i+, iCur, iCur);
} else {
dfs(num, temp + "+" + strCur, res, target, i + , curRes + iCur, iCur);
dfs(num, temp + "-" + strCur, res, target, i + , curRes - iCur, -iCur);
dfs(num, temp + "*" + strCur, res, target, i + , curRes - lastVal + lastVal * iCur, lastVal * iCur);
}
}
}
};
【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.
class Solution {
public:
vector<int> countSmaller(vector<int>& nums) {
const int n = nums.size();
vector<int> sorted(nums.begin(), nums.end()), res(n, );
if (n == ) {return res;}
mergesort(nums, sorted, , n-, res);
return res;
}
void mergesort(vector<int>& nums, vector<int>& sorted, int begin, int end, vector<int>& res) {
if (begin == end) { return; }
int mid = (begin + end) / ;
mergesort(nums, sorted, begin, mid, res);
mergesort(nums, sorted, mid + , end, res);
for (int i = begin; i <= mid; ++i) {
int value = nums[i];
auto iter = lower_bound(sorted.begin() + mid + , sorted.begin() + end + , value);
res[i] += distance(sorted.begin() + mid + , iter);
}
//merge
vector<int> arr1(sorted.begin() + begin, sorted.begin() + mid + ),
arr2(sorted.begin() + mid + , sorted.begin() + end + );
int p1 = , p2 = ;
for (int idx = begin; idx <= end; ++idx) {
if (p1 == arr1.size()) {
sorted[idx] = arr2[p2++];
} else if (p2 == arr2.size()) {
sorted[idx] = arr1[p1++];
} else {
if (arr1[p1] < arr2[p2]) {
sorted[idx] = arr1[p1++];
} else {
sorted[idx] = arr2[p2++];
}
}
}
}
};
【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 (2019年2月19日,归并排序)
给定一个数组,求它的逆序对个数。本题的逆序对的定义和别的题不同:if i < j and nums[i] > 2*nums[j].
题解:我们这个题目同315,用分治法解题。我们把这整个数组分成两个区间,然后分别对这两个左右区间做归并排序。然后对右边区间的每一个元素 nums[j], target = nums[j] * 2。然后用 upper_bound() 求出左区间内第一个比 target 大的元素,从这个元素开始 到左区间结束,这些元素都能和 target 组成逆序对。所以把这些元素加到结果上。
class Solution {
public:
typedef long long LL;
int reversePairs(vector<int>& nums) {
if (nums.empty()) {return ;}
int n = nums.size();
vector<LL> arr(n);
for (int i = ; i < n; ++i) {
arr[i] = nums[i];
}
divideAndConquer(arr, , n-);
return res;
}
int res = ;
void divideAndConquer(vector<LL>& nums, int start, int end) {
if (start == end) {return;}
int mid = start + (end - start) / ;
divideAndConquer(nums, start, mid);
divideAndConquer(nums, mid + , end);
for (int j = mid + ; j <= end; ++j) {
LL target = nums[j] * ;
auto iter = upper_bound(nums.begin() + start, nums.begin() + mid + , target);
res += distance(iter, nums.begin() + mid + );
}
// print(nums, start, end);
inplace_merge(nums.begin() + start, nums.begin() + mid + , nums.begin() + end + );
// print(nums, start, end);
}
void print(vector<LL>& nums, int start, int end) {
for (int i = start; i <= end; ++i) {
printf("%lld ", nums[i]);
}
printf("\n");
}
};
【514】Freedom Trail
【903】Valid Permutations for DI Sequence
【932】Beautiful Array
【LeetCode】分治法 divide and conquer (共17题)的更多相关文章
- 算法与数据结构基础 - 分治法(Divide and Conquer)
分治法基础 分治法(Divide and Conquer)顾名思义,思想核心是将问题拆分为子问题,对子问题求解.最终合并结果,分治法用伪代码表示如下: function f(input x size ...
- 分治法 - Divide and Conquer
在计算机科学中,分治法是一种很重要的算法.分治法即『分而治之』,把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的 ...
- 【LeetCode】回溯法 backtracking(共39题)
[10]Regular Expression Matching [17]Letter Combinations of a Phone Number [22]Generate Parentheses ( ...
- 【LeetCode】线段树 segment-tree(共9题)+ 树状数组 binary-indexed-tree(共5题)
第一部分---线段树:https://leetcode.com/tag/segment-tree/ [218]The Skyline Problem [307]Range Sum Query - Mu ...
- 【LeetCode】前缀树 trie(共14题)
[208]Implement Trie (Prefix Tree) (2018年11月27日) 实现基本的 trie 树,包括 insert, search, startWith 操作等 api. 题 ...
- 【LeetCode】链表 linked list(共34题)
[2]Add Two Numbers (2018年11月30日,第一次review,ko) 两个链表,代表两个整数的逆序,返回一个链表,代表两个整数相加和的逆序. Example: Input: ( ...
- 【LeetCode】深搜DFS(共85题)
[98]Validate Binary Search Tree [99]Recover Binary Search Tree [100]Same Tree [101]Symmetric Tree [1 ...
- 【LeetCode】随机化算法 random(共6题)
[384]Shuffle an Array(2019年3月12日) Shuffle a set of numbers without duplicates. 实现一个类,里面有两个 api,struc ...
- 【LeetCode】拓扑排序 topological-sort(共5题)
[207]Course Schedule [210]Course Schedule II [269]Alien Dictionary [329]Longest Increasing Path in a ...
随机推荐
- 用redlock实现redis的分布式锁
本文是一个demo,利用多进程,来模拟使用redis分布式锁的使用场景.本机需要安装redis,python3.7下运行代码.分布式锁用redlock这个包实现,实现步骤分三步: 实例化锁:rlock ...
- mui初级入门教程(一)— 小白入手mui的学习路线
文章来源:小青年原创发布时间:2016-05-15关键词:mui,html5+转载需标注本文原始地址:http://zhaomenghuan.github.io/#!/blog/20160515 写在 ...
- ubuntu python3相关
安装pip3 sudo apt install python3-pipsudo apt-get install python3-virtualenv sudo pip3 install virtual ...
- potplayer录制视频包含字幕
用potplayer录制视频,只能保存视频,外挂字幕的视频字幕无法录制进去 在字幕设置里将几个选项更改,即 字幕输出方式改为直接, 渲染方式不要选矢量即可. 如图:
- java后端发送请求并获取响应
URL wsUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) wsUrl.openConnection(); conn. ...
- Jquery浅析
目录 jquery 通过jquery改变标签字体颜色 jquery和js对象之间值转化 Jquery基本选择器 Jquery层级选择器 基本筛选器 操作类属性 模太框 表单筛选器 筛选器方法 设置多个 ...
- Vagrant 手册之 box - box 的信息格式
原文地址 创建 Vagrant 的 box 时,可以提供在运行 vagrant box list -i 时展示的与用户相关的其他信息.例如,可以打包 box,以包含有关该 box 的作者和网站信息: ...
- js-判断当前页面是否在移动端打开显示的
if (/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)==false) { //该页面不是在移动端打开的, }
- v-text、v-html、v-bind、v-show
<!doctype html> <html> <head> <meta charset="UTF-8"> <title> ...
- Leapin' Lizards [HDU - 2732]【网络流最大流】
题目链接 网络流直接最大流就是了,只是要拆点小心一个点的流超出了原本的正常范围才是. #include <iostream> #include <cstdio> #includ ...