7专题总结-高频题high frequency
Outline
. Single Number I, II, III
. Majority Number I, II, III
. Best Time to Buy and Sale Stock I, II, II
. Subarray I, II, III, IV
. -Sum, -Sum, -Sum, k-Sum, -Sum Closest
. Partition Array
. Quick Questions
方法:除2取余法,即每次将整数部分除以2,余数为该位权上的数,而商继续除以2,余数又为上一个位权上的数,这个步骤一直持续下去,直到商为0为止,最后读数时候,从最后一个余数读起,一直到最前面的一个余数。
例:将十进制的(43)D转换为二进制的步骤如下:
1. 将商43除以2,商21余数为1;
2. 将商21除以2,商10余数为1;
3. 将商10除以2,商5余数为0;
4. 将商5除以2,商2余数为1;
5. 将商2除以2,商1余数为0;
6. 将商1除以2,商0余数为1;
7. 读数,因为最后一位是经过多次除以2才得到的,因此它是最高位,读数字从最后的余数向前读,101011,即(43)D=(101011)B。
(Figure:图解十进制 → 二进制)
思路:将所有数进行异或运算,异或运算是对应位相同则为0,不同则为1,相当于不进位加法,将所有数进行异或运算最后得到的就是不同的那个数。
class Solution {
public:
int singleNumber(vector<int>& nums) {
if(nums.size() == ){
return INT_MIN;
}
int result = ;
for(int i = ;i < nums.size();++i){
result = result ^ nums[i];
}
return result;
}
};
single number
思路:也是利用第一题的思路,定义一个32位的数组,将所有数的特定一位进行统计,并对3取模,存入对应位里面,只有不同的那个数对应位才位1,内部循环结束后就得到最后结果对应位的1,然后二进制转化为十进制,将第几位进行转化就向左移动i位,记住这里的i是下标。不断加起来就可以得到结果。
bits[i] += (nums[j] >> i) & 1;//对应第i位进行统计,将十进制转化为二进制,想统计哪一位就除以i
bits[i] %= 3;
result += bits[i] << i;//二进制转化为十进制,将第几位进行转化就向左移动i位,记住这里的i是下标。
class Solution {
public:
int singleNumber(vector<int>& nums) {
if(nums.size() == ){
return -;
}
int result = ;
vector<int> bits(,);
for(int i = ;i < ;++i){
for(int j = ;j < nums.size();++j){
bits[i] += (nums[j] >> i) & ;//对应第i位进行统计,将十进制转化为二进制,想统计哪一位就除以i
bits[i] %= ;
}
result += bits[i] << i;//二进制转化为十进制,将第几位进行转化就向左移动i位,记住这里的i是下标。
}
return result;
}
};
single number II
思路:使用异或运算找出那两个数。1)使用异或运算得到XOR;2)找到XOR中最后一位是1,得到的是肯定不同的那一位:int lastBit = XOR - (XOR & (XOR - 1));解释XOR - 1:
1xxxxxx1000 1xxxxxx1000
& 1xxxxxx0111 ==> - 1xxxxxx0000
--------------------------------- --------------------------------
1xxxxxx0000 10000
3)因为第一步中经过异或运算最后是两个不同的数left和right进行异或的,他们和lastbit进行与运算,就可以找出不同的那一位,将这两个数分开。然后两边分别异或运算就可以了。
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
if(nums.size() == ){
return {};
}
vector<int> result;
int left = ,right = ;
int XOR = ;
for(int i = ;i < nums.size();++i){
XOR ^= nums[i];
}
//find last bit equal to 1
int lastBit = XOR - (XOR & (XOR - )); for(int j = ;j < nums.size();++j){
if(nums[j] & lastBit){
left ^= nums[j];
}
else{
right ^= nums[j];
}
}
result.push_back(left);
result.push_back(right); return result;
} };
single number
2.1 Majority Number
http://www.lintcode.com/en/problem/majority-number/
思路:定义一个count,一个candidate,没次选择一个进行抵消,因为最终的数肯定大于50%,所以最后的candidate一定就是结果。
class Solution {
public:
/**
* @param nums: A list of integers
* @return: The majority number
*/
int majorityNumber(vector<int> nums) {
// write your code here
if(nums.size() == ){
return -;
}
int count = ,flag = -;
for(int i = ;i < nums.size();++i){
if(count == ){
flag = nums[i];
count = ;
}
else{
if(flag == nums[i]){
++count;
}
else{
--count;
}
}
}
return flag;
}
};
majority number
2.2 Majority Number II
http://www.lintcode.com/en/problem/majority-number-ii/
给定一个整型数组,找到主元素,它在数组中的出现次数严格大于数组元素个数的三分之一。
思路:注意if else的顺序很重要,只要判断了count1以及flag1是否和nums[i]相等,再按照同样的思路判断count2.不然就会出错,使得flag1和flag2相等。
与Majority Number 1相似。但我们要保存2个number.
1. 遇到第一个不同的值,先记录number 2.
2. 新值与n1,n2都不同,则cnt1,cnt2都减少
3. 当n1,n2任意一个为0时,从新值中挑出一个记录下来。
4. 最后再对2个候选值进行查验,得出最终的解。
主页君其实也想不太明白这个题目为什么这样解。
还是举个例子吧
7 1 7 7 61 61 61 10 10 10 61
n1 7 7 7 7 7 7 7 7 7 10 10
cnt1 1 1 2 3 2 2 2 1 0 1 1
n2 0 1 1 1 1 61 61 61 61 61 61
cnt2 0 1 1 1 0 1 2 1 0 0 1
证明:
1. 我们对cnt1,cnt2减数时,相当于丢弃了3个数字(当前数字,n1, n2)。也就是说,每一次丢弃数字,我们是丢弃3个不同的数字。
而Majority number超过了1/3所以它最后一定会留下来。
设定总数为N, majority number次数为m。丢弃的次数是x。则majority 被扔的次数是x
而m > N/3, N - 3x > 0.
3m > N, N > 3x 所以 3m > 3x, m > x 也就是说 m一定没有被扔完
最坏的情况,Majority number每次都被扔掉了,但它一定会在n1,n2中。
2. 为什么最后要再检查2个数字呢?因为数字的编排可以让majority 数被过度消耗,使其计数反而小于n2,或者等于n2.前面举的例子即是。
另一个例子:
1 1 1 1 2 3 2 3 4 4 4 这个 1就会被消耗过多,最后余下的反而比4少。
class Solution {
public:
/**
* @param nums: A list of integers
* @return: The majority number occurs more than 1/3.
*/
int majorityNumber(vector<int> nums) {
// write your code here
if(nums.size() == ){
return -;
}
int count1 = ,count2 = ;
int flag1 = ,flag2 = ;
for(int i = ;i < nums.size();++i){
if(count1 == ){
flag1 = nums[i];
count1 = ;
}
else if(flag1 == nums[i]){
++count1;
}
else if(count2 == ){
flag2 = nums[i];
count2 = ;
}
else if(flag2 == nums[i]){
++count2;
}
else{
--count1;
--count2;
}
}
count1 = count2 = ;
for(int j = ;j < nums.size();++j){
if(flag1 == nums[j]){
++count1;
}
if(flag2 == nums[j]){
++count2;
}
}
return count1 > count2 ? flag1 : flag2;
}
};
majority number II
2.3 Majority Number III
http://www.lintcode.com/en/problem/majority-number-iii/
给定一个整型数组,找到主元素,它在数组中的出现次数严格大于数组元素个数的1/k。
思路:思路和Majority NumberII 一样,维护k-1个candidate 在map里面,key为数字值,value为出现次数。先找到这k-1个candidate后,扫描所有元素,如果该元素存在在map里面,update map;如果不存在,1: 如果map里面有值为count= 0,那么删除掉这个元素,加入新元素;2:map里面没有0出现,那么就每个元素的count--
剩下的map里面的值都有可能是majority,所以重新扫描数组,记录下每一个元素出现次数,次数最大的就是majority
solution:http://www.jiuzhang.com/solution/majority-number-iii
3.1 121. Best Time to Buy and Sell Stock
https://leetcode.com/problems/best-time-to-buy-and-sell-stock/description/
思路:不断的用当前值减去前面的最小值。
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size() == ){
return ;
}
int minNum = INT_MAX,maxProfic = INT_MIN;
for(int i = ;i < prices.size();++i){
minNum = min(minNum,prices[i]);
maxProfic = max(maxProfic,prices[i] - minNum);
}
if(maxProfic < ){
return ;
}
return maxProfic;
}
};
Best Time to Buy and Sell Stock
3.2 122. Best Time to Buy and Sell Stock II
思路:实质是 贪心法,只要后一天比前一天的价钱高,就卖出,不断的进行prices[i] - prices[i -1]的判断。
https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/description/
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size() == ){
return ;
}
int profit = ; for(int i = ;i < prices.size();++i){
if(prices[i] > prices[i - ]){
profit += (prices[i] - prices[i - ]);
} }
return profit;
}
};
est Time to Buy and Sell Stock II
3.3 123. Best Time to Buy and Sell Stock III
https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/description/
思路:
才意识到可以在整个区间的每一点切开,然后分别计算左子区间和右子区间的最大值,然后再用O(n)时间找到整个区间的最大值。
看来以后碰到与2相关的问题,一定要想想能不能用二分法来做!
下面复制参考的讲解,我觉得我不能比他讲的更好了
O(n^2)的算法很容易想到:
找寻一个点j,将原来的price[0..n-1]分割为price[0..j]和price[j..n-1],分别求两段的最大profit。
进行优化:
对于点j+1,求price[0..j+1]的最大profit时,很多工作是重复的,在求price[0..j]的最大profit中已经做过了。
类似于Best Time to Buy and Sell Stock,可以在O(1)的时间从price[0..j]推出price[0..j+1]的最大profit。
但是如何从price[j..n-1]推出price[j+1..n-1]?反过来思考,我们可以用O(1)的时间由price[j+1..n-1]推出price[j..n-1]。
最终算法:
数组l[i]记录了price[0..i]的最大profit,
数组r[i]记录了price[i..n]的最大profit。
已知l[i],求l[i+1]是简单的,同样已知r[i],求r[i-1]也很容易。
最后,我们再用O(n)的时间找出最大的l[i]+r[i],即为题目所求。
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size() == ){
return ;
}
int len = prices.size();
vector<int> left(len,);
vector<int> right(len,); //left -> right
int minNum = prices[];
for(int i = ;i < len;++i){
minNum = min(minNum,prices[i]);
left[i] = max(left[i - ],prices[i] - minNum); } //right -> left
int maxNum = prices[len - ];
for(int j = len - ;j >= ;--j){
maxNum = max(maxNum,prices[j]);
right[j] = max(right[j + ],maxNum - prices[j]); } //find maxprofit
int maxProfit = ;
for(int k = ;k < len;++k){
maxProfit = max(maxProfit,left[k] + right[k]);
} return maxProfit;
}
};
Best Time to Buy and Sell Stock III
3.4 188. Best Time to Buy and Sell Stock IV
https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/description/
思路:Code Ganker ,Grandyang。
使用动态规划求解,难点在于递推式。
一个是当前到达第i天可以最多进行j次交易,最好的利润是多少(global[i][j]),
另一个是当前到达第i天,最多可进行j次交易,并且最后一次交易在当天卖出的最好的利润是多少(local[i][j])。
global[i][j]=max(local[i][j],global[i-1][j]),依据是否包含最后一天的交易来分析,包含最后一天就是local[i][j]
local[i][j]=max(global[i-1][j-1]+max(diff,0),local[i-1][j]+diff),第一个是全局到i-1天进行j-1次交易,然后加上今天的交易,如果今天是赚钱的话(也就是前面只要j-1次交易,最后一次交易取当天),第二个量则是取local第i-1天j次交易,然后加上今天的差值(这里因为local[i-1][j]比如包含第i-1天卖出的交易,所以现在变成第i天卖出,并不会增加交易次数,而且这里无论diff是不是大于0都一定要加上,因为否则就不满足local[i][j]必须在最后一天卖出的条件了,也相当于将第i - 1天和第i天的交易合并在一起了)。
用II的解法优化k > prices.size / 2的情况,因为一次交易需要两个数据,最多只能prices.size() / 2交易
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
if(prices.size() == ) {
return ;
} //用II的解法优化k > prices.size / 2的情况,因为一次交易需要两个数据,最多只能prices.size() / 2交易
if(k > prices.size() / ){
int sum = ;
for(int i = ; i < prices.size(); i++){
if(prices[i] > prices[i - ]){
sum += prices[i] - prices[i - ];
}
}
return sum;
}
//初始化全局变量和局部变量
vector<vector<int>> global(prices.size(),vector<int> (k + ,));
vector<vector<int>> local(prices.size(),vector<int> (k + ,)); for(int i = ; i < prices.size(); i++){
int diff = prices[i] - prices[i - ];
for(int j = ; j < k + ; j++){
//更新局部变量
local[i][j] = max(global[i - ][j - ] + max(, diff), local[i - ][j] + diff);
//更新全局变量
global[i][j] = max(global[i - ][j], local[i][j]);
}
}
return global[prices.size() - ][k];
}
};
Best Time to Buy and Sell Stock IV
4.1 53. Maximum Subarray
https://leetcode.com/problems/maximum-subarray/description/
注意INT_MIN是计算机能表示的最小的数-2147483648,在减去一个数就变为正数2147483647。
思路:参考这是一道非常经典的动态规划的题目,用到的思路我们在别的动态规划题目中也很常用,以后我们称为”局部最优和全局最优解法“。
基本思路是这样的,在每一步,我们维护两个变量,一个是全局最优,就是到当前元素为止最优的解是,一个是局部最优,就是必须包含当前元素的最优的解。接下来说说动态规划的递推式(这是动态规划最重要的步骤,递归式出来了,基本上代码框架也就出来了)。假设我们已知第i步的global[i](全局最优)和local[i](局部最优),那么第i+1步的表达式是:
local[i+1]=Math.max(A[i], local[i]+A[i]),就是局部最优是一定要包含当前元素,所以不然就是上一步的局部最优local[i]+当前元素A[i](因为local[i]一定包含第i个元素,所以不违反条件),但是如果local[i]是负的,那么加上他就不如不需要的,所以不然就是直接用A[i];
global[i+1]=Math(local[i+1],global[i]),有了当前一步的局部最优,那么全局最优就是当前的局部最优或者还是原来的全局最优(所有情况都会被涵盖进来,因为最优的解如果不包含当前元素,那么前面会被维护在全局最优里面,如果包含当前元素,那么就是这个局部最优)。
接下来我们分析一下复杂度,时间上只需要扫描一次数组,所以时间复杂度是O(n)。空间上我们可以看出表达式中只需要用到上一步local[i]和global[i]就可以得到下一步的结果,所以我们在实现中可以用一个变量来迭代这个结果,不需要是一个数组,也就是如程序中实现的那样,所以空间复杂度是两个变量(local和global),即O(2)=O(1)。
技巧:local,global初始化的时候不要直接初始化为INT_MIN,这样计算的时候遇到负数就会出错,直接初始化为nums[0],然后循环从1开始。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if(nums.size() == ){
return ;
}
int local = ,global = ;
for(int i = ;i < nums.size();++i){
local = max(nums[i],local + nums[i]);//必须包含当前元素
global = max(local,global);//包含当前元素和不包含当前元素的值选最大的
}
return global;
}
};
maximum subarray
4.2 minimum-subarray
http://www.lintcode.com/problem/minimum-subarray/
思路:上面那题将max 换成min就可以了。
4.3 maximum-subarray-difference
http://www.lintcode.com/problem/maximum-subarray-difference/
思路:使用四个数组,分别求出从左往右的最大值和最小值数组;在求出从右往左的最大值和最小值数组。
最后用一个循环将leftMax -rightMin,leftMin - rightMax;
class Solution {
public:
/**
* @param nums: A list of integers
* @return: An integer indicate the value of maximum difference between two
* Subarrays
*/
int maxDiffSubArrays(vector<int> nums) {
// write your code here
if(nums.size() == ){
return ;
}
int len = nums.size();
vector<int> leftMax(len,),leftMin(len,);
vector<int> rightMax(len,),rightMin(len,); //left => right find max
int leftSum = nums[];
leftMax[] = nums[];
for(int i = ;i < len;++i){
leftSum = max(leftSum + nums[i],nums[i]);
leftMax[i] = max(leftMax[i - ],leftSum);
} //right => left find max
int rightSum = nums[len - ];
rightMax[len - ] = nums[len - ];
for(int i = len - ;i >= ;--i){
rightSum = max(rightSum + nums[i],nums[i]);
rightMax[i] = max(rightSum,rightMax[i + ]);
} //left => right find min
int leftSum1 = nums[];
leftMin[] = nums[];
for(int i = ;i < len;++i){
leftSum1 = min(leftSum1 + nums[i],nums[i]);
leftMin[i] = min(leftMin[i - ],leftSum1);
} //right => left find min
int rightSum1 = nums[len - ];
rightMin[len - ] = nums[len - ];
for(int i = len - ;i >= ;--i){
rightSum1 = min(rightSum1 + nums[i],nums[i]);
rightMin[i] = min(rightSum1,rightMin[i + ]);
} //find maximum
int result = INT_MIN;
for(int i = ;i < len - ;++i){
int lMinRMax = abs(leftMin[i] - rightMax[i + ]);
int lMaxRMin = abs(leftMax[i] - rightMin[i + ]); result = max(result,max(lMinRMax,lMaxRMin));
} return result;
}
};
maximum subarray difference
4.4 subarray-sum-closest
http://www.lintcode.com/problem/subarray-sum-closest/
思路:参考
题目的意思是在一个数组中找一段连续的区间,使得这段区间的和的绝对值最小。做法就是利用前缀和,先用一个数组acc[i]来保存从nums[0]到nums[i]的和,同时还要记录下标,所以这里我用pair<int, int>来保存。那么,我们想要得到nums[i]到nums[j]的和,只要用acc[j] - acc[i-1]就可以了。但是这里有一点要注意要加一个辅助的节点,那就是[0, -1],这样就可以确保可以找到以nums[0]开始的区间了。剩下的工作就是对acc数组排序,找到排序后相邻的差的绝对值最小的那一对节点。
7专题总结-高频题high frequency的更多相关文章
- C++面试高频题
作者:守望者1028链接:https://www.nowcoder.com/discuss/55353来源:牛客网 面试高频题: 校招过程中参考过牛客诸位大佬的面经,但是具体哪一块是参考谁的我也忘记了 ...
- 面试高频题:说一说对Spring和SpringMvc父子容器的理解?
引言 以前写了几篇关于SpringBoot的文章<面试高频题:springBoot自动装配的原理你能说出来吗>.<保姆级教程,手把手教你实现一个SpringBoot的starter& ...
- X-NUCA 2017 web专题赛训练题 阳光总在风雨后和default wp
0X0.前言 X-NUCA 2017来了,想起2016 web专题赛,题目都打不开,希望这次主办方能够搞好点吧!还没开赛,依照惯例会有赛前指导,放一些训练题让CTFer们好感受一下题目. 题目有一大 ...
- 2019春招面试高频题(Java版),持续更新(答案来自互联网)
第一模块--并发与多线程 Java多线程方法: 实现Runnable接口, 继承thread类, 使用线程池 操作系统层面的进程与线程(对JAVA多线程和高并发有了解吗?) 计算机资源=存储资源+计算 ...
- 专题:DP杂题1
A POJ 1018 Communication System B POJ 1050 To the Max C POJ 1083 Moving Tables D POJ 1125 Stockbroke ...
- 恶补一下DP+背包专题(刷刷水题)L2
开心的金明 题目大意 就是求一定背包容量的最大值 思路 想必大家都知道,一看到这种题目,就会想起01背包 虽然特别简单但是还是讲一下吧 状态设置 由于这题差不多是一个01背包的版子题,那么我们就只需要 ...
- 2022GDUT寒训专题一I题
题目 题面 给一个长度为 N的数组,一个长为 K的滑动窗体从最左端移至最右端,你只能看到窗口中的 K 个数,每次窗体向右移动一位,如下图: 窗口位置 最小值 最大值 [1 3 -1] -3 5 3 6 ...
- 2022GDUT寒训专题一J题
题目 题面 给你一个长度为 n的整数序列{A1,A2,⋯,A**n},要求从中找出一段连续的长度不超过 m的非空子序列,使得这个序列的和最大. 输入格式 第一行为两个整数 n,m: 第二行为 n个用空 ...
- 2022GDUT寒训专题一C题
题目 题面 马在中国象棋以日字形规则移动. 请编写一段程序,给定n×m大小的棋盘,以及马的初始位置 (x, y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点. 输入格式 ...
随机推荐
- ROS学习资源
1.ROS机器人操作系统自主学习实践环境 https://www.shiyanlou.com/courses/854 2.ROS官方网站 http://wiki.ros.org/ ROS中文官方网站 ...
- Python:json 模块
字符串转dict.list data = "[{....},{...},...]" list_data = json.loads(data) dict.list转字符串 list ...
- 吴裕雄--天生自然TensorFlow2教程:链式法则
import tensorflow as tf x = tf.constant(1.) w1 = tf.constant(2.) b1 = tf.constant(1.) w2 = tf.consta ...
- Follow somebody
networkersdiary A personnel blog with Network Engineering articles https://networkersdiary.com/cisco ...
- 【SSM 项目】实战总结
项目源代码
- vue项目注意事项
vue项目注意事项 1. 文件和路由命名规范 views里面代表的是你下面导航中的每一块,每个文件名 需要大写,路由命名全部小写,第一层路由就是最下面的那几个导航的名字,二级路由是在一 级路由的基础上 ...
- 「Luogu P5080 Tweetuzki 爱序列」
题目大意 给出一些数,需要求出 \(\frac{a_{i+1}}{3}=a_i\) 或 \(a_{i+1}=2 \times a_i\) 时最长的序列 \(a\). 分析 可以发现符合条件的序列 \( ...
- if条件语句!
1.if 单分支语句 if [ 条件语句 ] then 条件操作fi 例子: [root@localhost ~]# if [ 1 -eq 0 ] //如果1等 ...
- 大数据篇:YARN
YARN YARN是什么? YARN是一种新的 Hadoop 资源管理器,它是一个通用资源管理系统,可为上层应用提供统一的资源管理和调度,它的引入为集群在利用率.资源统一管理和数据共享等方面带来了巨大 ...
- 2019年5月17日A股暴跌行情思考
2019年5月17日A股暴跌行情思考 原因:特朗普针对华为的禁商令,人民币对美元汇率大跌 盘面:平开,单边下跌,收盘80多股跌停 操作:下午2点加仓,满仓 总结: 本次操作太过激进. 局势不明朗时抄底 ...