力扣OJ(401-1000)
目錄
416. 分割等和子集
題目:
給定一個只包含正整數的非空陣列。是否可以將這個陣列分割成兩個子集,使得兩個子集的元素和相等。
注意:
每個陣列中的元素不會超過 100
示例 1:
輸入: [1, 5, 11, 5]
輸出: true
解釋: 陣列可以分割成 [1, 5, 5] 和 [11].
示例2:
輸入: [1, 2, 3, 5]
輸出: false
解釋: 陣列不能分割成兩個元素和相等的子集.
程式碼:
int s[2]; class Solution { public: bool canPartitionKSubsets(vector<int> &nums, int deep) { if (deep >= nums.size())return true; for (int j = 0; j <= deep && j < 2; j++) { if (s[j] < nums[deep])continue; s[j] -= nums[deep]; if (canPartitionKSubsets(nums, deep + 1))return true; s[j] += nums[deep]; } return false; } bool canPartition(vector<int> &nums) { if (nums.size() < 2)return false; sort(nums.begin(), nums.end(), greater<int>()); int anss = 0; for (auto it = nums.begin(); it != nums.end(); it++) anss += *it; if (anss % 2) return false; anss /= 2; s[0] = s[1] = anss; return canPartitionKSubsets(nums, 0); } };
421. 陣列中兩個數的最大異或值
題目:
給定一個非空陣列,陣列中元素為 a0, a1, a2, … , an-1,其中 0 ≤ ai < 231。
找到 ai 和aj最大的異或 (XOR) 運算結果,其中0 ≤ i,j < n。
你能在O(n)的時間解決這個問題嗎?
示例:
輸入: [3, 10, 5, 25, 2, 8]
輸出: 28
解釋: 最大的結果是 5 ^ 25 = 28.
思路:
字典樹
程式碼:
const int m = 3000000;
int ans[m][2], key;
int f(int x)
{
for (int i = 30, k = 0; i >= 0; i--)
{
if (ans[k][1 - (x >> i) % 2] == 0)
{
k = ans[k][(x >> i) % 2];
if ((x >> i) % 2)x ^= (1 << i);
}
else
{
k = ans[k][1 - (x >> i) % 2];
if ((x >> i) % 2 == 0)x ^= (1 << i);
}
}
return x;
}
class Solution {
public:
int findMaximumXOR(vector<int>& nums) {
int n=nums.size(), m, r = INT_MIN;
memset(ans[0], 0, sizeof(ans[0]));
key = 0;
for (int i = 0; i < n; i++)
{
m = nums[i];
if (f(m) > r)r = f(m);
for (int k = 30, j = 0; k >= 0; k--)
{
if (ans[j][(m >> k) % 2] == 0)
{
ans[j][(m >> k) % 2] = ++key;
memset(ans[key], 0, sizeof(ans[key]));
}
j = ans[j][(m >> k) % 2];
}
}
return r;
}
};
429. N叉樹的層序遍歷
題目:
給定一個 N 叉樹,返回其節點值的層序遍歷。 (即從左到右,逐層遍歷)。
例如,給定一個3叉樹:
返回其層序遍歷:
[
[1],
[3,2,4],
[5,6]
]
說明:
樹的深度不會超過1000。
樹的節點總數不會超過5000。
程式碼:
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
vector<int>tmp1;
vector<Node*>tmp2, tmp3;
vector<vector<int>>ans;
if (!root)return ans;
tmp2.insert(tmp2.end(), root);
while (!tmp2.empty())
{
tmp1.clear();
int k = tmp2.size();
while (k--)
{
tmp1.insert(tmp1.end(), tmp2[0]->val);
tmp3 = tmp2[0]->children;
for (int j = 0; j < tmp3.size(); j++)tmp2.insert(tmp2.end(), tmp3[j]);
tmp2.erase(tmp2.begin());
}
ans.insert(ans.end(),tmp1);
}
return ans;
}
};
451. 根據字元出現頻率排序
題目:
給定一個字串,請將字串裡的字元按照出現的頻率降序排列。
示例 1:
輸入:
"tree"
輸出:
"eert"
解釋:
'e'出現兩次,'r'和't'都只出現一次。
因此'e'必須出現在'r'和't'之前。此外,"eetr"也是一個有效的答案。
示例 2:
輸入:
"cccaaa"
輸出:
"cccaaa"
解釋:
'c'和'a'都出現三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正確的,因為相同的字母必須放在一起。
示例 3:
輸入:
"Aabb"
輸出:
"bbAa"
解釋:
此外,"bbaA"也是一個有效的答案,但"Aabb"是不正確的。
注意'A'和'a'被認為是兩種不同的字元。
程式碼:
struct dist
{
char c;
int fre;
};
struct cmp
{
bool operator()(dist a, dist b)
{
return a.fre < b.fre;
}
};
class Solution {
public:
string frequencySort(string s) {
map<char, int> m;
priority_queue<dist, vector<dist>, cmp>que;
dist d;
for (int i = 0; i < s.length();i++)
{
m[s[i]]++;
}
for (auto it = m.begin(); it != m.end(); it++)
{
d.c = (*it).first, d.fre = (*it).second, que.push(d);
}
string ans = "";
while (!que.empty())
{
d = que.top();
que.pop();
while(d.fre--)ans += d.c;
}
return ans;
}
};
454. 四數相加 II
給定四個包含整數的陣列列表A , B , C , D ,計算有多少個元組 (i, j, k, l),使得A[i] + B[j] + C[k] + D[l] = 0。
為了使問題簡單化,所有的 A, B, C, D 具有相同的長度N,且 0 ≤ N ≤ 500 。所有整數的範圍在 -228 到 228 - 1 之間,最終結果不會超過231 - 1 。
例如:
輸入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]
輸出:
2
解釋:
兩個元組如下:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
//給vector拓展,加上id並排序
template<typename T>
bool cmp(T x,T y)
{
return x<y;
}
template<typename T>
vector<pair<T,int>> sortWithId(vector<T>v)
{
vector<pair<T,int>>ans;
ans.resize(v.size());
for(int i=0;i<v.size();i++)ans[i].first=v[i],ans[i].second=i;
sort(ans.begin(),ans.end(),[](pair<T,int>p1,pair<T,int>p2){return cmp(p1.first,p2.first);});
return ans;
}
//2個vector中尋找和為s的對,返回結果的每一行都是[id1,id2]
template<typename T>
vector<vector<int>> findSum(vector<T>v1,vector<T>v2,T s)
{
vector<vector<int>>ans;
int m=min((long long)(v1.size()*v2.size()),(long long)12345678);
ans.reserve(m);
vector<int>tmp(2);
vector<pair<T,int>>v3=sortWithId(v2);
sort(v2.begin(), v2.end(),cmp<T>);
for(int i=0;i<v1.size();i++)
{
auto it1=lower_bound(v2.begin(), v2.end(), s-v1[i]);
auto it2=upper_bound(v2.begin(), v2.end(), s-v1[i]);
tmp[0]=i;
for(auto j=it1;j<it2;j++)
{
tmp[1]=v3[j-v2.begin()].second;
ans.push_back(tmp);
}
}
return ans;
}
//列舉2個vector的兩數之和
template<typename T>
vector<T> everySum(vector<T>&v1,vector<T>&v2)
{
vector<T>ans;
ans.resize(v1.size()*v2.size());
int k=0;
for(int i=0;i<v1.size();i++)for(int j=0;j<v2.size();j++)ans[k++]=v1[i]+v2[j];
return ans;
}
//判斷有序陣列中有多少個不同的數
template<typename T>
int getDifNum(vector<T>v)
{
int ans=1;
for(int i=1;i<v.size();i++)ans+=(v[i]!=v[i-1]);
return ans;
}
//收縮計數,把[1 1 4 4 4]變成[(1,2)(4,3)]
template<typename T>
vector<pair<T,int>> fshr(vector<T>v)
{
vector<pair<T,int>>ans;
if(v.size()==0)return ans;
sort(v.begin(),v.end());
ans.resize(getDifNum(v));
ans[0]=make_pair(v[0],1);
for(int i=1,j=0;i<v.size();i++)
{
if(v[i]==v[i-1])ans[j].second++;
else ans[++j]=make_pair(v[i],1);
}
return ans;
}
//提取pair陣列的first
template<typename T1,typename T2>
vector<T1> fdraw(vector<pair<T1,T2>>v)
{
vector<T1>ans(v.size());
for(int i=0;i<v.size();i++)ans[i]=v[i].first;
return ans;
}
//提取pair陣列的second
template<typename T1,typename T2>
vector<T2> fdraw2(vector<pair<T1,T2>>v)
{
vector<T2>ans(v.size());
for(int i=0;i<v.size();i++)ans[i]=v[i].second;
return ans;
}
class Solution {
public:
int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
vector<int> v1 = everySum(A,B),v2 = everySum(C,D);
vector<pair<int,int>>v3=fshr(v1),v4=fshr(v2);
vector<int> v5=fdraw(v3),v6=fdraw(v4);
vector<int> v7=fdraw2(v3),v8=fdraw2(v4);
vector<vector<int>> ans = findSum(v5,v6,0);
int res=0;
for(int i=0;i<ans.size();i++)res+=v7[ans[i][0]]*v8[ans[i][1]];
return res;
}
};
461. 漢明距離
題目:
兩個整數之間的漢明距離指的是這兩個數字對應二進位制位不同的位置的數目。
給出兩個整數 x 和 y,計算它們之間的漢明距離。
注意:
0 ≤ x, y < 231.
示例:
輸入: x = 1, y = 4
輸出: 2
解釋:
1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑
上面的箭頭指出了對應二進位制位不同的位置。
程式碼:
class Solution {
public:
int hammingWeight(uint32_t n) {
int ans = 0;
while (n)
{
n ^= (n&(-n));
ans++;
}
return ans;
}
int hammingDistance(int x, int y) {
return hammingWeight(x^y);
}
};
470. 用 Rand7() 實現 Rand10()
題目:
已有方法rand7可生成 1 到 7 範圍內的均勻隨機整數,試寫一個方法rand10生成 1 到 10 範圍內的均勻隨機整數。
不要使用系統的Math.random()方法。
示例 1:
輸入: 1
輸出: [7]
示例 2:
輸入: 2
輸出: [8,4]
示例 3:
輸入: 3
輸出: [8,1,10]
提示:
rand7已定義。
傳入引數:n表示rand10的呼叫次數。
進階:
rand7()呼叫次數的期望值是多少?
你能否儘量少呼叫 rand7() ?
程式碼:
class Solution {
public:
int rand10() {
int a = rand7()-1;//0-6
int b = rand7() - 1;//0-6
int c = a * 7 + b; //0-48
if (c < 40)return c % 10 + 1;
return rand10();
}
};
486. 預測贏家
給定一個表示分數的非負整數陣列。 玩家1從陣列任意一端拿取一個分數,隨後玩家2繼續從剩餘陣列任意一端拿取分數,然後玩家1拿,……。每次一個玩家只能拿取一個分數,分數被拿取之後不再可取。直到沒有剩餘分數可取時遊戲結束。最終獲得分數總和最多的玩家獲勝。
給定一個表示分數的陣列,預測玩家1是否會成為贏家。你可以假設每個玩家的玩法都會使他的分數最大化。
示例 1:
輸入: [1, 5, 2]
輸出: False
解釋: 一開始,玩家1可以從1和2中進行選擇。
如果他選擇2(或者1),那麼玩家2可以從1(或者2)和5中進行選擇。如果玩家2選擇了5,那麼玩家1則只剩下1(或者2)可選。
所以,玩家1的最終分數為 1 + 2 = 3,而玩家2為 5。
因此,玩家1永遠不會成為贏家,返回 False。
示例 2:
輸入: [1, 5, 233, 7]
輸出: True
解釋: 玩家1一開始選擇1。然後玩家2必須從5和7中進行選擇。無論玩家2選擇了哪個,玩家1都可以選擇233。
最終,玩家1(234分)比玩家2(12分)獲得更多的分數,所以返回 True,表示玩家1可以成為贏家。
注意:
1 <= 給定的陣列長度<= 20.
數組裡所有分數都為非負數且不會大於10000000。
如果最終兩個玩家的分數相等,那麼玩家1仍為贏家。
區間DP:
int res[20][20],flag[20][20];
int PredictTheWinner2(int* nums, int numsSize,int low,int high){
if(low>high)return 0;
if(flag[low][high])return res[low][high];
flag[low][high]=1;
return res[low][high]=max(nums[low]-PredictTheWinner2(nums,numsSize,low+1,high),nums[high]-PredictTheWinner2(nums,numsSize,low,high-1));
}
bool PredictTheWinner(int* nums, int numsSize){
memset(flag,0,sizeof(res));
return PredictTheWinner2(nums,numsSize,0,numsSize-1)>=0;
}
496. 下一個更大元素 I
給定兩個 沒有重複元素 的陣列nums1 和nums2,其中nums1是nums2的子集。找到nums1中每個元素在nums2中的下一個比其大的值。
nums1中數字x的下一個更大元素是指x在nums2中對應位置的右邊的第一個比x大的元素。如果不存在,對應位置輸出 -1 。
示例 1:
輸入: nums1 = [4,1,2], nums2 = [1,3,4,2].
輸出: [-1,3,-1]
解釋:
對於num1中的數字4,你無法在第二個陣列中找到下一個更大的數字,因此輸出 -1。
對於num1中的數字1,第二個陣列中數字1右邊的下一個較大數字是 3。
對於num1中的數字2,第二個陣列中沒有下一個更大的數字,因此輸出 -1。
示例 2:
輸入: nums1 = [2,4], nums2 = [1,2,3,4].
輸出: [3,-1]
解釋:
對於 num1 中的數字 2 ,第二個陣列中的下一個較大數字是 3 。
對於 num1 中的數字 4 ,第二個陣列中沒有下一個更大的數字,因此輸出 -1 。
提示:
nums1和nums2中所有元素是唯一的。
nums1和nums2的陣列大小都不超過1000。
這個題目很簡單,我主要用來鍛造我的模板:
//翻轉vector
template<typename T>
vector<T> frev(vector<T> v)
{
vector<T> ans;
for(int i=v.size()-1;i>=0;i--)ans.push_back(v[i]);
return ans;
}
//vector乘一個數
template<typename T1,typename T2>
void fcheng(vector<T1> &v,T2 n)
{
for(int i=v.size()-1;i>=0;i--)v[i]*=n;
}
//vector加一個數
template<typename T1,typename T2>
void fjia(vector<T1> &v,T2 n)
{
for(int i=v.size()-1;i>=0;i--)v[i]+=n;
}
//id處,覆蓋或者新增x
template<typename T>
void finsert(vector<T>&v,int id,T x)
{
if(id<0||id>v.size())return;
if(id==v.size())v.push_back(x);
v[id]=x;
}
//返回vector每個數前面最近的滿足pr關係的數的ID,-1 或者 0到size-1
template<typename T,class P>inline
vector<int>firstInLeft(vector<T>v,P pr)
{
vector<int> ans;
if(v.size()==0)return ans;
stack<T>st;
st.push(0);
ans.push_back(-1);
for(int i=1;i<v.size();i++)
{
while(!st.empty() && !pr(v[st.top()],v[i]))st.pop();
if(st.empty())ans.push_back(-1);
else ans.push_back(st.top());
st.push(i);
}
return ans;
}
//返回vector每個數前面最近的比它小的數的ID,-1 或者 0到size-1
template<typename T>
vector<int> fminlef(vector<T> v)
{
return firstInLeft(v,[](T a,T b){return a<b;});
}
//返回vector每個數前面最近的比它小或等於的數的ID,-1 或者 0到size-1
template<typename T>
vector<int> fminlef2(vector<T> v)
{
return firstInLeft(v,[](T a,T b){return a<=b;});
}
//返回vector每個數前面最近的比它大的數的ID,-1 或者 0到size-1
template<typename T>
vector<int> fmaxlef(vector<T> v)
{
fcheng(v,-1);
vector<int>ans=fminlef(v);
return ans;
}
//返回vector每個數前面最近的比它大或等於的數的ID,-1 或者 0到size-1
template<typename T>
vector<int> fmaxlef2(vector<T> v)
{
fcheng(v,-1);
vector<int>ans=fminlef2(v);
return ans;
}
//返回vector每個數後面最近的比它小的數的ID,size 或者 0到size-1
template<typename T>
vector<int> fminrig(vector<T> v)
{
vector<int>v1=frev(v),v2=fminlef(v1);
fcheng(v2,-1);
fjia(v2,v.size()-1);
return frev(v2);
}
//返回vector每個數後面最近的比它小或等於的數的ID,size 或者 0到size-1
template<typename T>
vector<int> fminrig2(vector<T> v)
{
vector<int>v1=frev(v),v2=fminlef2(v1);
fcheng(v2,-1);
fjia(v2,v.size()-1);
return frev(v2);
}
//返回vector每個數後面最近的比它大的數的ID,size 或者 0到size-1
template<typename T>
vector<int> fmaxrig(vector<T> v)
{
vector<int>v1=frev(v),v2=fmaxlef(v1);
fcheng(v2,-1);
fjia(v2,v.size()-1);
return frev(v2);
}
//返回vector每個數後面最近的比它大或等於的數的ID,size 或者 0到size-1
template<typename T>
vector<int> fmaxrig2(vector<T> v)
{
vector<int>v1=frev(v),v2=fmaxlef2(v1);
fcheng(v2,-1);
fjia(v2,v.size()-1);
return frev(v2);
}
//把id轉化為對應的數
template<typename T>
vector<int> fgetNumFromId(vector<T> &v,vector<int>id)
{
vector<T>ans;
ans.resize(id.size());
for(int i=0;i<id.size();i++)ans[i]= (id[i]>=0 && id[i]<v.size()) ? v[id[i]] : -1;
return ans;
}
//把v1和v2 轉化成1個map<v1,v2>
template<typename T1,typename T2>
map<T1,T2> vectorToMap(vector<T1> &v1,vector<T2> &v2)
{
map<T1,T2>m;
for(int i=0;i<v1.size();i++)m[v1[i]]=v2[i];
return m;
}
//1個vector加1個map轉化成map[vector]
template<typename T1,typename T2>
vector<T2> vmToVector(vector<T1> &v,map<T1,T2>&m)
{
vector<T2>ans;
ans.resize(v.size());
for(int i=0;i<v.size();i++)ans[i]=m[v[i]];
return ans;
}
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
vector<int>ans=fgetNumFromId(nums2,fmaxrig(nums2));
map<int,int>m=vectorToMap(nums2,ans);
ans=vmToVector(nums1,m);
return ans;
}
};
503. 下一個更大元素 II
給定一個迴圈陣列(最後一個元素的下一個元素是陣列的第一個元素),輸出每個元素的下一個更大元素。數字 x 的下一個更大的元素是按陣列遍歷順序,這個數字之後的第一個比它更大的數,這意味著你應該迴圈地搜尋它的下一個更大的數。如果不存在,則輸出 -1。
示例 1:
輸入: [1,2,1]
輸出: [2,-1,2]
解釋: 第一個 1 的下一個更大的數是 2;
數字 2 找不到下一個更大的數;
第二個 1 的下一個最大的數需要迴圈搜尋,結果也是 2。
//翻轉vector
template<typename T>
vector<T> frev(vector<T> v)
{
vector<T> ans;
for(int i=v.size()-1;i>=0;i--)ans.push_back(v[i]);
return ans;
}
//vector乘一個數
template<typename T1,typename T2>
void fcheng(vector<T1> &v,T2 n)
{
for(int i=v.size()-1;i>=0;i--)v[i]*=n;
}
//vector加一個數
template<typename T1,typename T2>
void fjia(vector<T1> &v,T2 n)
{
for(int i=v.size()-1;i>=0;i--)v[i]+=n;
}
//id處,覆蓋或者新增x
template<typename T>
void finsert(vector<T>&v,int id,T x)
{
if(id<0||id>v.size())return;
if(id==v.size())v.push_back(x);
v[id]=x;
}
//返回vector每個數前面最近的滿足pr關係的數的ID,-1 或者 0到size-1
template<typename T,class P>inline
vector<int>firstInLeft(vector<T>v,P pr)
{
vector<int> ans;
if(v.size()==0)return ans;
stack<T>st;
st.push(0);
ans.push_back(-1);
for(int i=1;i<v.size();i++)
{
while(!st.empty() && !pr(v[st.top()],v[i]))st.pop();
if(st.empty())ans.push_back(-1);
else ans.push_back(st.top());
st.push(i);
}
return ans;
}
//返回vector每個數前面最近的比它小的數的ID,-1 或者 0到size-1
template<typename T>
vector<int> fminlef(vector<T> v)
{
return firstInLeft(v,[](T a,T b){return a<b;});
}
//返回vector每個數前面最近的比它小或等於的數的ID,-1 或者 0到size-1
template<typename T>
vector<int> fminlef2(vector<T> v)
{
return firstInLeft(v,[](T a,T b){return a<=b;});
}
//返回vector每個數前面最近的比它大的數的ID,-1 或者 0到size-1
template<typename T>
vector<int> fmaxlef(vector<T> v)
{
fcheng(v,-1);
vector<int>ans=fminlef(v);
return ans;
}
//返回vector每個數前面最近的比它大或等於的數的ID,-1 或者 0到size-1
template<typename T>
vector<int> fmaxlef2(vector<T> v)
{
fcheng(v,-1);
vector<int>ans=fminlef2(v);
return ans;
}
//返回vector每個數後面最近的比它小的數的ID,size 或者 0到size-1
template<typename T>
vector<int> fminrig(vector<T> v)
{
vector<int>v1=frev(v),v2=fminlef(v1);
fcheng(v2,-1);
fjia(v2,v.size()-1);
return frev(v2);
}
//返回vector每個數後面最近的比它小或等於的數的ID,size 或者 0到size-1
template<typename T>
vector<int> fminrig2(vector<T> v)
{
vector<int>v1=frev(v),v2=fminlef2(v1);
fcheng(v2,-1);
fjia(v2,v.size()-1);
return frev(v2);
}
//返回vector每個數後面最近的比它大的數的ID,size 或者 0到size-1
template<typename T>
vector<int> fmaxrig(vector<T> v)
{
vector<int>v1=frev(v),v2=fmaxlef(v1);
fcheng(v2,-1);
fjia(v2,v.size()-1);
return frev(v2);
}
//返回vector每個數後面最近的比它大或等於的數的ID,size 或者 0到size-1
template<typename T>
vector<int> fmaxrig2(vector<T> v)
{
vector<int>v1=frev(v),v2=fmaxlef2(v1);
fcheng(v2,-1);
fjia(v2,v.size()-1);
return frev(v2);
}
//把v和id轉化為v[id]
template<typename T>
vector<int> fgetNumFromId(vector<T> &v,vector<int>id)
{
vector<T>ans;
ans.resize(id.size());
for(int i=0;i<id.size();i++)ans[i]= (id[i]>=0 && id[i]<v.size()) ? v[id[i]] : -1;
return ans;
}
//把v1和v2 轉化成1個map<v1,v2>
template<typename T1,typename T2>
map<T1,T2> vectorToMap(vector<T1> &v1,vector<T2> &v2)
{
map<T1,T2>m;
for(int i=0;i<v1.size();i++)m[v1[i]]=v2[i];
return m;
}
//1個vector加1個map轉化成map[vector]
template<typename T1,typename T2>
vector<T2> vmToVector(vector<T1> &v,map<T1,T2>&m)
{
vector<T2>ans;
ans.resize(v.size());
for(int i=0;i<v.size();i++)ans[i]=m[v[i]];
return ans;
}
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
vector<int>t=nums;
t.resize(nums.size()*2);
copy(nums.begin(),nums.end(),t.begin()+nums.size());
vector<int>tmp=fgetNumFromId(t,fmaxrig(t));
vector<int>ans=nums;
copy(tmp.begin(),tmp.begin()+tmp.size()/2,ans.begin());
return ans;
}
};
523. 連續的子陣列和
給定一個包含非負數的陣列和一個目標整數k,編寫一個函式來判斷該陣列是否含有連續的子陣列,其大小至少為 2,總和為 k 的倍數,即總和為 n*k,其中 n 也是一個整數。
示例 1:
輸入: [23,2,4,6,7], k = 6
輸出: True
解釋: [2,4] 是一個大小為 2 的子陣列,並且和為 6。
示例 2:
輸入: [23,2,6,4,7], k = 6
輸出: True
解釋: [23,2,6,4,7]是大小為 5 的子陣列,並且和為 42。
說明:
陣列的長度不會超過10,000。
你可以認為所有數字總和在 32 位有符號整數範圍內。
class Solution {
public:
bool checkSubarraySum(vector<int>& nums, int k) {
if(k==0)
{
for(int i=1;i<nums.size();i++)
{
if(nums[i]==0&&nums[i-1]==0)return true;
}
return false;
}
map<int,int>m;
int s=0;
if(k<0)k=-k;
for(int i=0;i<nums.size();i++)
{
s+=nums[i];
s%=k;
if(m[s]>0||s==0)
{
if(m[s]<i)return true;
}
else m[s]=i+1;
}
return false;
}
};
556. 下一個更大元素 III (整數和字串互相轉化)
給定一個32位正整數n,你需要找到最小的32位整數,其與n中存在的位數完全相同,並且其值大於n。如果不存在這樣的32位整數,則返回-1。
示例 1:
輸入: 12
輸出: 21
示例 2:
輸入: 21
輸出: -1
char* itoa(int num,char* str,int radix) //copy from 百度百科
{/*索引表*/
char index[]="0123456789ABCDEF";
unsigned unum;/*中間變數*/
int i=0,j,k;
/*確定unum的值*/
if(radix==10&&num<0)/*十進位制負數*/
{
unum=(unsigned)-num;
str[i++]='-';
}
else unum=(unsigned)num;/*其他情況*/
/*轉換*/
do{
str[i++]=index[unum%(unsigned)radix];
unum/=radix;
}while(unum);
str[i]='\0';
/*逆序*/
if(str[0]=='-')
k=1;/*十進位制負數*/
else
k=0;
for(j=k;j<=(i-1)/2;j++)
{ char temp;
temp=str[j];
str[j]=str[i-1+k-j];
str[i-1+k-j]=temp;
}
return str;
}
class Solution {
public:
int nextGreaterElement(int n) {
char sn[40];
itoa(n,sn,10);
int len=strlen(sn);
for(int i=len-2;i>=0;i--)
{
if(sn[i]>=sn[i+1])continue;
for(int j=len-1;j>i;j--)
{
if(sn[i]>=sn[j])continue;
sn[j]^=sn[i]^=sn[j]^=sn[i];
sort(sn+i+1,sn+len);
long long res = atol(sn);
if(res==int(res))return res;
return -1;
}
}
return -1;
}
};
後來發現可以用c++的庫函式獲取上一個下一個全排列,就更簡單了。
class Solution {
public:
int nextGreaterElement(int n) {
char sn[40];
itoa(n,sn,10);
int len=strlen(sn);
if(!next_permutation(sn,sn+len))return -1;
long long res = atol(sn);
if(res==int(res))return res;
return -1;
}
};
621. 任務排程器
題目:
給定一個用字元陣列表示的 CPU 需要執行的任務列表。其中包含使用大寫的 A - Z 字母表示的26 種不同種類的任務。任務可以以任意順序執行,並且每個任務都可以在 1 個單位時間內執行完。CPU 在任何一個單位時間內都可以執行一個任務,或者在待命狀態。
然而,兩個相同種類的任務之間必須有長度為n 的冷卻時間,因此至少有連續 n 個單位時間內 CPU 在執行不同的任務,或者在待命狀態。
你需要計算完成所有任務所需要的最短時間。
示例 1:
輸入: tasks = ["A","A","A","B","B","B"], n = 2
輸出: 8
執行順序: A -> B -> (待命) -> A -> B -> (待命) -> A -> B.
注:
任務的總個數為[1, 10000]。
n 的取值範圍為 [0, 100]。
程式碼:
class Solution {
public:
int leastInterval(vector<char>& tasks, int n) {
int num[26] = { 0 };
for (auto it = tasks.begin(); it != tasks.end(); it++)
{
num[*it - 'a']++;
}
int maxNum = 0, k = 0, sumNum = 0;
for (int i = 0; i < 26; i++)maxNum = max(maxNum, num[i]), sumNum += num[i];
for (int i = 0; i < 26; i++)k += (maxNum == num[i]);
return max((maxNum - 1)*(n + 1) + k, sumNum);
}
};
630. 課程表 III(c語言線段樹實現優先佇列)
這裡有 n 門不同的線上課程,他們按從 1 到 n編號。每一門課程有一定的持續上課時間(課程時間)t 以及關閉時間第 d天。一門課要持續學習 t 天直到第 d 天時要完成,你將會從第 1 天開始。
給出 n 個線上課程用 (t, d) 對錶示。你的任務是找出最多可以修幾門課。
示例:
輸入: [[100, 200], [200, 1300], [1000, 1250], [2000, 3200]]
輸出: 3
解釋:
這裡一共有 4 門課程, 但是你最多可以修 3 門:
首先, 修第一門課時, 它要耗費 100 天,你會在第 100 天完成, 在第 101 天準備下門課。
第二, 修第三門課時, 它會耗費 1000 天,所以你將在第 1100 天的時候完成它, 以及在第 1101 天開始準備下門課程。
第三, 修第二門課時, 它會耗時 200 天,所以你將會在第 1300 天時完成它。
第四門課現在不能修,因為你將會在第 3300 天完成它,這已經超出了關閉日期。
提示:
整數 1 <= d, t, n <= 10,000 。
你不能同時修兩門課程。
解題思路:
貪心,把課程按照d增序排序,逐步維護當前已選的課程列表,並用整數tsum記錄他們的總時間。
依次讀取每一個新課程,如果再加上這個課程,時間d沒有超,即tsum+t<=d, 那麼就加上,ans加1,
如果加上之後時間d超了,那麼就用這個課程替換掉所有已選課程裡面,t最小的那個課程,ans不變。
所以需要優先佇列。
C語言實現方法:
用max型線段樹,實現大頂堆的優先佇列。
注意:這裡的maxx陣列記錄的是最大元素的下標,query查詢的返回結果也是下標。
struct anode
{
int t;
int d;
};
int n;
int num[10001],maxx[40001];
struct anode node[10001];
int cmpnode(const void* n1,const void* n2)
{
return ((struct anode*)n1)->d > ((struct anode*)n2)->d;
}
void build(int key, int low, int high)//id
{
if (low == high)
{
maxx[key] = low;
return;
}
int mid = (low + high) / 2;
build(key * 2, low, mid);
build(key * 2 + 1, mid + 1, high);
maxx[key] = (num[maxx[key * 2]] > num[maxx[key * 2 + 1]]) ? maxx[key * 2] : maxx[key * 2 + 1];
}
void update(int key, int low, int high, int uplace)//id
{
if (low == high)
{
maxx[key] = low;
return;
}
int mid = (low + high) / 2;
if (uplace <= mid)update(key * 2, low, mid, uplace);
else update(key * 2 + 1, mid + 1, high, uplace);
maxx[key] = (num[maxx[key * 2]] > num[maxx[key * 2 + 1]]) ? maxx[key * 2] : maxx[key * 2 + 1];
}
int query(int key, int low, int high, int x, int y)//id
{
if (low == x && high == y)return maxx[key];
int mid = (low + high) / 2;
if (mid < x)return query(key * 2 + 1, mid + 1, high, x, y);
if (mid >= y)return query(key * 2, low, mid, x, y);
int a = query(key * 2, low, mid, x, mid);
int b = query(key * 2 + 1, mid + 1, high, mid + 1, y);
return (num[a]>num[b]) ? a : b;
}
void push(int loc)
{
num[loc]=node[loc].t; ///根據實際情況修改
update(1,1,n,loc);
}
void pop(int loc)
{
num[loc]=0; ///根據實際情況修改
update(1,1,n,loc);
}
int top()//id
{
return query(1,1,n,1,n);
}
int scheduleCourse(int** courses, int coursesSize, int* coursesColSize){
n=coursesSize;
memset(num,0,sizeof(num));
build(1,1,n);
for(int i=1;i<=n;i++)node[i].t=courses[i-1][0],node[i].d=courses[i-1][1];
qsort(node+1,n,sizeof(struct anode),cmpnode);
int tsum=0,ans=0;
for(int i=1;i<=n;i++)
{
if(tsum+node[i].t<=node[i].d)
{
tsum+=node[i].t,ans++;
push(i);
}
else if(node[i].t<node[top()].t)
{
tsum+=node[i].t - node[top()].t;
pop(top());
push(i);
}
}
return ans;
}
646. 最長數對鏈
題目:
給出n個數對。在每一個數對中,第一個數字總是比第二個數字小。
現在,我們定義一種跟隨關係,當且僅當b < c時,數對(c, d)才可以跟在(a, b)後面。我們用這種形式來構造一個數對鏈。
給定一個對數集合,找出能夠形成的最長數對鏈的長度。你不需要用到所有的數對,你可以以任何順序選擇其中的一些數對來構造。
示例 :
輸入: [[1,2], [2,3], [3,4]]
輸出: 2
解釋: 最長的數對鏈是 [1,2] -> [3,4]
注意:
給出數對的個數在[1, 1000] 範圍內。
分析:
動態規劃。
因為資料量只有1000,所以直接寫個簡單的O(n*n)的時間的演算法即可
程式碼:
bool cmp(vector<int> it1,vector<int> it2)
{
return it1[1]<it2[1];
}
class Solution {
public:
int findLongestChain(vector<vector<int>>& pairs) {
int res=0;
vector<vector<int>>ans;
vector<int>temp;
ans.clear();
sort(pairs.begin(),pairs.end(),cmp);
for(auto it=pairs.begin();it!=pairs.end();it++){
temp.clear();
temp.insert(temp.end(),(*it)[1]);
temp.insert(temp.end(),1);
for(auto it2=ans.begin();it2!=ans.end();it2++){
if((*it)[0]>(*it2)[0] && temp[1]<(*it2)[1]+1){
temp[1]=(*it2)[1]+1;
}
}
ans.insert(ans.end(),temp);
if(res<temp[1]){
res=temp[1];
}
}
return res;
}
};
還有一種貪心的演算法:
以第二個數為第一關鍵字,第一個數為第二個關鍵字,進行排序。
673. 最長遞增子序列的個數
給定一個未排序的整數陣列,找到最長遞增子序列的個數。
示例 1:
輸入: [1,3,5,4,7]
輸出: 2
解釋: 有兩個最長遞增子序列,分別是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
示例 2:
輸入: [2,2,2,2,2]
輸出: 5
解釋: 最長遞增子序列的長度是1,並且存在5個子序列的長度為1,因此輸出5。
注意:給定的陣列長度不超過 2000 並且結果一定是32位有符號整數。
class Solution {
public:
int findNumberOfLIS(vector<int>& nums) {
if(nums.size()==0)return 0;
vector<int>len=nums,res=nums;
len[0] = 1, res[0] = 1;
for (int i = 1; i < nums.size(); i++)
{
len[i] = 1, res[i] = 1;
for (int j = 0; j < i; j++)if (nums[j] < nums[i])
{
if (len[i] < len[j] + 1)len[i] = len[j] + 1, res[i] = res[j];
else if (len[i] == len[j] + 1)res[i] += res[j];
}
}
int m = 0, ans = 0;
for (int i = 0; i < nums.size(); i++)if (m < len[i])m = len[i], ans = res[i];
else if (m == len[i])ans += res[i];
return ans;
}
};
693. 交替位二進位制數
題目:
給定一個正整數,檢查他是否為交替位二進位制數:換句話說,就是他的二進位制數相鄰的兩個位數永不相等。
示例 1:
輸入: 5
輸出: True
解釋:
5的二進位制數是: 101
示例 2:
輸入: 7
輸出: False
解釋:
7的二進位制數是: 111
示例3:
輸入: 11
輸出: False
解釋:
11的二進位制數是: 1011
示例 4:
輸入: 10
輸出: True
解釋:
10的二進位制數是: 1010
程式碼:
class Solution {
public:
bool hasAlternatingBits2(int n) {
if (n == 0)return true;
if (n % 4 == 1)return hasAlternatingBits2(n / 4);
return false;
}
bool hasAlternatingBits(int n) {
if (n % 2 == 0)n /= 2;
return hasAlternatingBits2(n);
}
};
698. 劃分為k個相等的子集
題目:
給定一個整數陣列nums 和一個正整數 k,找出是否有可能把這個陣列分成 k 個非空子集,其總和都相等。
示例 1:
輸入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
輸出: True
說明: 有可能將其分成 4 個子集(5),(1,4),(2,3),(2,3)等於總和。
注意:
1 <= k <= len(nums) <= 16
0 < nums[i] < 10000
思路:
DFS,注意剪枝,我這裡採用的是簡單的降序排序,排序可以讓執行時間從2000ms降為4ms
程式碼:
int s[16];
class Solution {
public:
bool canPartitionKSubsets(vector<int> &nums, int k, int deep)
{
if (deep >= nums.size())return true;
for (int j = 0; j <= deep && j < k; j++) {
if (s[j] < nums[deep])continue;
s[j] -= nums[deep];
if (canPartitionKSubsets(nums, k, deep + 1))return true;
s[j] += nums[deep];
}
return false;
}
bool canPartitionKSubsets(vector<int> &nums, int k)
{
if (nums.size() < k || k <= 0 || k > 16)return false;
sort(nums.begin(),nums.end(),greater<int>());
int anss = 0;
for (auto it = nums.begin(); it != nums.end(); it++) anss += *it;
if (anss % k) return false;
anss /= k;
for (int i = 0; i < k; i++) s[i] = anss;
return canPartitionKSubsets(nums, k, 0);
}
};
738. 單調遞增的數字
題目:
給定一個非負整數N,找出小於或等於N的最大的整數,同時這個整數需要滿足其各個位數上的數字是單調遞增。
(當且僅當每個相鄰位數上的數字x和y滿足x <= y時,我們稱這個整數是單調遞增的。)
示例 1:
輸入: N = 10
輸出: 9
示例 2:
輸入: N = 1234
輸出: 1234
示例 3:
輸入: N = 332
輸出: 299
說明: N是在[0, 10^9]範圍內的一個整數。
程式碼:
class Solution {
public:
int monotoneIncreasingDigits(int N) {
int key = 1111111111, ans = 0, num = 9;
while (key)
{
while (N >= key && num > 0)
{
N -= key, ans += key, num--;
}
key /= 10;
}
return ans;
}
};
740. 刪除與獲得點數
給定一個整數陣列nums,你可以對它進行一些操作。
每次操作中,選擇任意一個nums[i],刪除它並獲得nums[i]的點數。之後,你必須刪除每個等於nums[i] - 1或nums[i] + 1的元素。
開始你擁有 0 個點數。返回你能通過這些操作獲得的最大點數。
示例 1:
輸入: nums = [3, 4, 2]
輸出: 6
解釋:
刪除 4 來獲得 4 個點數,因此 3 也被刪除。
之後,刪除 2 來獲得 2 個點數。總共獲得 6 個點數。
示例2:
輸入: nums = [2, 2, 3, 3, 3, 4]
輸出: 9
解釋:
刪除 3 來獲得 3 個點數,接著要刪除兩個 2 和 4 。
之後,再次刪除 3 獲得 3 個點數,再次刪除 3 獲得 3 個點數。
總共獲得 9 個點數。
注意:
nums的長度最大為20000。
每個整數nums[i]的大小都在[1, 10000]範圍內。
class Solution {
public:
int s[10001];
int ans[10001];
int dp(int k)
{
if(k<0)return 0;
if(ans[k])return ans[k];
return ans[k]=max(s[k]+dp(k-2),dp(k-1));
}
int deleteAndEarn(vector<int>& nums) {
if(nums.size()==0)return 0;
memset(ans,0,sizeof(int)*10001);
memset(s,0,sizeof(int)*10001);
for(int i=0;i<nums.size();i++)s[nums[i]]+=nums[i];
return dp(10000);
}
};
762. 二進位制表示中質數個計算置位
題目:
給定兩個整數L和R,找到閉區間[L, R]範圍內,計算置位位數為質數的整數個數。
(注意,計算置位代表二進位制表示中1的個數。例如21的二進位制表示10101有 3 個計算置位。還有,1 不是質數。)
示例 1:
輸入: L = 6, R = 10
輸出: 4
解釋:
6 -> 110 (2 個計算置位,2 是質數)
7 -> 111 (3 個計算置位,3 是質數)
9 -> 1001 (2 個計算置位,2 是質數)
10-> 1010 (2 個計算置位,2 是質數)
示例 2:
輸入: L = 10, R = 15
輸出: 5
解釋:
10 -> 1010 (2 個計算置位, 2 是質數)
11 -> 1011 (3 個計算置位, 3 是質數)
12 -> 1100 (2 個計算置位, 2 是質數)
13 -> 1101 (3 個計算置位, 3 是質數)
14 -> 1110 (3 個計算置位, 3 是質數)
15 -> 1111 (4 個計算置位, 4 不是質數)
注意:
L, R是L <= R且在[1, 10^6]中的整數。
R - L的最大值為 10000。
程式碼:
class Solution {
public:
int hammingWeight(int n) {
int ans = 0;
while (n)
{
n ^= (n&(-n));
ans++;
}
return ans;
}
int countPrimeSetBits(int L, int R) {
set<int>se = { 2, 3, 5, 7 ,11,13,17,19};
int ans = 0;
for (int i = L; i <= R; i++)
{
if (se.find(hammingWeight(i)) != se.end())ans++;
}
return ans;
}
};
767. 重構字串
題目:
給定一個字串S,檢查是否能重新排布其中的字母,使得兩相鄰的字元不同。
若可行,輸出任意可行的結果。若不可行,返回空字串。
示例1:
輸入: S = "aab"
輸出: "aba"
示例 2:
輸入: S = "aaab"
輸出: ""
注意:
S 只包含小寫字母並且長度在[1, 500]區間內。
思路:
分2步,
先判斷最多的字元是否超過其他所有字元的數量+1,如果超過就無法重構,不超過就可以重構。
其次,按照貪心原則,每次取數量最多的字元即可。
程式碼:
class Solution {
public:
string reorganizeString(string S) {
int num[26] = { 0 };
int len = S.length();
for (int i = 0; i < len; i++)num[S[i] - 'a']++;
for (int i = 0; i < 26; i++)if (num[i]>(len + 1) / 2)return "";
string ans = S;
char tmp = '0';
for (int i = 0; i < len; i++)
{
int maxNum = 0, maxId = 0;
for (int j = 0; j < 26; j++)
{
if (tmp - 'a' == j)continue;
if (maxNum < num[j])maxNum = num[j], maxId = j;
}
ans[i] = tmp = 'a' + maxId;
num[maxId]--;
}
return ans;
}
};
817. 連結串列元件
給定一個連結串列(連結串列結點包含一個整型值)的頭結點head。
同時給定列表G,該列表是上述連結串列中整型值的一個子集。
返回列表G中元件的個數,這裡對元件的定義為:連結串列中一段最長連續結點的值(該值必須在列表G中)構成的集合。
示例1:
輸入:
head: 0->1->2->3
G = [0, 1, 3]
輸出: 2
解釋:
連結串列中,0 和 1 是相連線的,且 G 中不包含 2,所以 [0, 1] 是 G 的一個元件,同理 [3] 也是一個元件,故返回 2。
示例 2:
輸入:
head: 0->1->2->3->4
G = [0, 3, 1, 4]
輸出: 2
解釋:
連結串列中,0 和 1 是相連線的,3 和 4 是相連線的,所以 [0, 1] 和 [3, 4] 是兩個元件,故返回 2。
注意:
如果N是給定連結串列head的長度,1 <= N <= 10000。
連結串列中每個結點的值所在範圍為[0, N - 1]。
1 <= G.length <= 10000
G 是連結串列中所有結點的值的一個子集.
程式碼:
class Solution {
public:
bool has(vector<int>& G, int k)
{
return (lower_bound(G.begin(), G.end(), k) != upper_bound(G.begin(), G.end(), k));
}
int numComponents(ListNode* head, vector<int>& G) {
int ans = 0, num = 0;
ListNode* p = head;
sort(G.begin(), G.end());
while (p)
{
if(has(G, p->val))num++;
else if (num > 0)ans++, num = 0;
p = p->next;
}
if (num > 0)ans++;
return ans;
}
};
853. 車隊
題目:
N 輛車沿著一條車道駛向位於target英里之外的共同目的地。
每輛車i以恆定的速度speed[i](英里/小時),從初始位置position[i](英里) 沿車道駛向目的地。
一輛車永遠不會超過前面的另一輛車,但它可以追上去,並與前車以相同的速度緊接著行駛。
此時,我們會忽略這兩輛車之間的距離,也就是說,它們被假定處於相同的位置。
車隊是一些由行駛在相同位置、具有相同速度的車組成的非空集合。注意,一輛車也可以是一個車隊。
即便一輛車在目的地才趕上了一個車隊,它們仍然會被視作是同一個車隊。
會有多少車隊到達目的地?
示例:
輸入:target = 12, position = [10,8,0,5,3], speed = [2,4,1,1,3]
輸出:3
解釋:
從 10 和 8 開始的車會組成一個車隊,它們在 12 處相遇。
從 0 處開始的車無法追上其它車,所以它自己就是一個車隊。
從 5 和 3 開始的車會組成一個車隊,它們在 6 處相遇。
請注意,在到達目的地之前沒有其它車會遇到這些車隊,所以答案是 3。
提示:
0 <= N <= 10 ^ 4
0 < target<= 10 ^ 6
0 <speed[i] <= 10 ^ 6
0 <= position[i] < target
所有車的初始位置各不相同。
程式碼:
bool cmp(pair<int, int>a, pair<int, int>b)
{
return a.first > b.first;
}
class Solution {
public:
int carFleet(int target, vector<int>& position, vector<int>& speed) {
if (position.empty())return 0;
vector<pair<int, int>>pos;
for (int i = 0; i < position.size(); i++)
{
pos.insert(pos.end(), make_pair(position[i], speed[i]));
}
sort(pos.begin(), pos.end(), cmp);
int ans = pos.size(), k = 0;
for (int i = 1; i < pos.size(); i++)
{
long long a = target - pos[k].first, b = target - pos[i].first;
if (a*pos[i].second >= b*pos[k].second)ans--;
else k = i;
}
return ans;
}
};
869. 重新排序得到 2 的冪
題目:
給定正整數 N,我們按任何順序(包括原始順序)將數字重新排序,注意其前導數字不能為零。
如果我們可以通過上述方式得到2 的冪,返回 true;否則,返回 false。
示例 1:
輸入:1
輸出:true
示例 2:
輸入:10
輸出:false
示例 3:
輸入:16
輸出:true
示例 4:
輸入:24
輸出:false
示例 5:
輸入:46
輸出:true
提示:
1 <= N <= 10^9
思路:
2的冪非常少,一個個匹配就行了。
列出所有的2的冪,依次判斷是不是能對應上即可。
程式碼:
class Solution {
public:
bool isSame(int a,int b) {
int num[10]={0};
while(a>0){
num[a%10]++;
a/=10;
}
while(b>0){
num[b%10]--;
b/=10;
}
for(int i=0;i<10;i++){
if(num[i]!=0){
return false;
}
}
return true;
}
bool reorderedPowerOf2(int N) {
for(int i=0;i<30;i++){
if(isSame(N,(1<<i))){
return true;
}
}
return false;
}
};
870. 優勢洗牌
題目:
給定兩個大小相等的陣列A和B,A 相對於 B 的優勢可以用滿足A[i] > B[i]的索引 i的數目來描述。
返回A的任意排列,使其相對於 B的優勢最大化。
示例 1:
輸入:A = [2,7,11,15], B = [1,10,4,11]
輸出:[2,11,7,15]
示例 2:
輸入:A = [12,24,8,32], B = [13,25,32,11]
輸出:[24,32,8,12]
提示:
1 <= A.length = B.length <= 10000
0 <= A[i] <= 10^9
0 <= B[i] <= 10^9
思路:
田忌賽馬
對於B的任意序遍歷,每次找A中比B[i]大的最小的A[j],如果沒有就選最小的A[k]
程式碼:
class Solution {
public:
vector<int> advantageCount(vector<int>& A, vector<int>& B) {
vector<int>ans;
multiset<int>mset;
multiset<int>::iterator it;
ans.clear();
mset.clear();
for(int i=0;i<A.size();i++){
mset.insert(A[i]);
}
for(int i=0;i<B.size();i++){
it=mset.upper_bound(B[i]); //找比B[i]小的最小A[?]
if(it==mset.end()){
it=mset.begin(); //如果沒有就取A[]中最小數
}
ans.insert(ans.end(),*it);
mset.erase(it);
}
return ans;
}
};
873. 最長的斐波那契子序列的長度
題目:
如果序列X_1, X_2, ..., X_n滿足下列條件,就說它是斐波那契式的:
n >= 3
對於所有i + 2 <= n,都有X_i + X_{i+1} = X_{i+2}
給定一個嚴格遞增的正整數陣列形成序列,找到 A 中最長的斐波那契式的子序列的長度。如果一個不存在,返回0 。
(回想一下,子序列是從原序列 A中派生出來的,它從 A中刪掉任意數量的元素(也可以不刪),而不改變其餘元素的順序。例如,[3, 5, 8]是[3, 4, 5, 6, 7, 8]的一個子序列)
示例 1:
輸入: [1,2,3,4,5,6,7,8]
輸出: 5
解釋:
最長的斐波那契式子序列為:[1,2,3,5,8] 。
示例2:
輸入: [1,3,7,11,12,14,18]
輸出: 3
解釋:
最長的斐波那契式子序列有:
[1,11,12],[3,11,14] 以及 [7,11,18] 。
提示:
3 <= A.length <= 1000
1 <= A[0] < A[1] < ... < A[A.length - 1] <= 10^9
(對於以 Java,C,C++,以及C# 的提交,時間限制被減少了 50%)
思路:
動態規劃。DP[x][y]表示以A[x]和A[y]開頭的最長斐波那契子數列的長度。
程式碼一:
備忘錄寫法
#define MAX 1000
int ans[MAX][MAX];
int dp(int x,int y,vector<int>& A)
{
if(ans[x][y]){
return ans[x][y];
}
auto it= lower_bound(A.begin(),A.end(),A[x]+A[y]);
ans[x][y]=((it!=A.end() && *it==A[x]+A[y])?(dp(y,it-A.begin(),A)+1):2);
return ans[x][y];
}
class Solution {
public:
int lenLongestFibSubseq(vector<int>& A) {
int res=0;
for(int i=0;i<A.size();i++){
memset(ans[i],0,sizeof(ans[0][0])*A.size());
}
for(int i=0;i<A.size();i++){
for(int j=i+1;j<A.size();j++){
if(res<dp(i,j,A)){
res=dp(i,j,A);
}
}
}
if(res<3)res=0;
return res;
}
};
結果:AC 820ms
程式碼二:
非備忘錄方法,控制計算順序,省略初始化步驟
#define MAX 1000
int ans[MAX][MAX];
class Solution {
public:
int lenLongestFibSubseq(vector<int>& A) {
int res=0;
for(int j=A.size()-1;j>=0;j--){
for(int i=j-1;i>=0;i--){
auto it= lower_bound(A.begin(),A.end(),A[i]+A[j]);
ans[i][j]=((it!=A.end() && *it==A[i]+A[j])?(ans[j][it-A.begin()]+1):2);
res=max(res,ans[i][j]);
}
}
if(res<3)res=0;
return res;
}
};
結果:AC 436ms
改進思路:
上述演算法時間複雜度O(n* n * lg n)
利用雙指標代替lower_bound完成查詢工作,演算法時間複雜度可以降為O(n* n)
875. 愛吃香蕉的珂珂
珂珂喜歡吃香蕉。這裡有N堆香蕉,第 i 堆中有piles[i]根香蕉。警衛已經離開了,將在H小時後回來。
珂珂可以決定她吃香蕉的速度K(單位:根/小時)。每個小時,她將會選擇一堆香蕉,從中吃掉 K 根。如果這堆香蕉少於 K 根,她將吃掉這堆的所有香蕉,然後這一小時內不會再吃更多的香蕉。
珂珂喜歡慢慢吃,但仍然想在警衛回來前吃掉所有的香蕉。
返回她可以在 H 小時內吃掉所有香蕉的最小速度 K(K 為整數)。
示例 1:
輸入: piles = [3,6,7,11], H = 8
輸出: 4
示例2:
輸入: piles = [30,11,23,4,20], H = 5
輸出: 30
示例3:
輸入: piles = [30,11,23,4,20], H = 6
輸出: 23
提示:
1 <= piles.length <= 10^4
piles.length <= H <= 10^9
1 <= piles[i] <= 10^9
bool ok(vector<int>&b, int m,int ans)
{
int s=0;
for(int i=0;i<b.size();i++)
{
m-=(b[i]%ans>0)+b[i]/ans;
}
return m>=0;
}
class Solution {
public:
int minEatingSpeed(vector<int>& piles, int H) {
int low=0,high=1000000000;
while (low < high-1)
{
int mid = (low + high) / 2;
if(ok(piles,H,mid))high = mid;
else low = mid;
}
return high;
}
};
877. 石子游戲
題目:
亞歷克斯和李用幾堆石子在做遊戲。偶數堆石子排成一行,每堆都有正整數顆石子piles[i]。
遊戲以誰手中的石子最多來決出勝負。石子的總數是奇數,所以沒有平局。
亞歷克斯和李輪流進行,亞歷克斯先開始。 每回合,玩家從行的開始或結束處取走整堆石頭。 這種情況一直持續到沒有更多的石子堆為止,此時手中石子最多的玩家獲勝。
假設亞歷克斯和李都發揮出最佳水平,當亞歷克斯贏得比賽時返回true,當李贏得比賽時返回false。
示例:
輸入:[5,3,4,5]
輸出:true
解釋:
亞歷克斯先開始,只能拿前 5 顆或後 5 顆石子 。
假設他取了前 5 顆,這一行就變成了 [3,4,5] 。
如果李拿走前 3 顆,那麼剩下的是 [4,5],亞歷克斯拿走後 5 顆贏得 10 分。
如果李拿走後 5 顆,那麼剩下的是 [3,4],亞歷克斯拿走後 4 顆贏得 9 分。
這表明,取前 5 顆石子對亞歷克斯來說是一個勝利的舉動,所以我們返回 true 。
提示:
2 <= piles.length <= 500
piles.length 是偶數。
1 <= piles[i] <= 500
sum(piles)是奇數。
程式碼:
#define MAX_LENGTH 555
class Solution {
public:
bool maxScore(vector<int>& piles, int i, int j){
if (j <= i)return piles[i];
static map<int, int>ans;
if (ans[i*MAX_LENGTH + j])return ans[i*MAX_LENGTH + j];
int res1 = piles[i]-maxScore(piles,i+1,j);
int res2 = piles[j]-maxScore(piles,i,j-1);
return ans[i*MAX_LENGTH + j] = max(res1, res2);
}
bool stoneGame(vector<int>& piles) {
return maxScore(piles, 0, piles.size() - 1) > 0;
}
};
907. 子陣列的最小值之和(單調棧)
給定一個整數陣列 A,找到 min(B)的總和,其中 B 的範圍為A 的每個(連續)子陣列。
由於答案可能很大,因此返回答案模 10^9 + 7。
示例:
輸入:[3,1,2,4]
輸出:17
解釋:
子陣列為 [3],[1],[2],[4],[3,1],[1,2],[2,4],[3,1,2],[1,2,4],[3,1,2,4]。
最小值為 3,1,2,4,1,1,2,1,1,1,和為 17。
提示:
1 <= A <= 30000
1 <= A[i] <= 30000
//翻轉vector
template<typename T>
vector<T> frev(vector<T> v)
{
vector<T> ans;
for(int i=v.size()-1;i>=0;i--)ans.push_back(v[i]);
return ans;
}
//vector乘一個數
template<typename T>
void fcheng(vector<T> &v,int n)
{
for(int i=v.size()-1;i>=0;i--)v[i]*=n;
}
//vector加一個數
template<typename T>
void fjia(vector<T> &v,int n)
{
for(int i=v.size()-1;i>=0;i--)v[i]+=n;
}
//返回vector每個數前面最近的比它小的數的ID,-1 或者 0到size-1
template<typename T>
vector<int> fminlef(vector<T> v)
{
vector<int> ans;
if(v.size()==0)return ans;
stack<T>st;
st.push(0);
ans.push_back(-1);
for(int i=1;i<v.size();i++)
{
while(!st.empty() && v[st.top()]>=v[i])st.pop();
if(st.empty())ans.push_back(-1);
else ans.push_back(st.top());
st.push(i);
}
return ans;
}
//返回vector每個數後面最近的比它小的數的ID,size 或者 0到size-1
template<typename T>
vector<int> fminrig(vector<T> v)
{
vector<int>v1=frev(v),v2=fminlef(v1);
fcheng(v2,-1);
fjia(v2,v.size()-1);
return frev(v2);
}
//返回vector每個數前面最近的比它小或等於的數的ID,-1 或者 0到size-1
template<typename T>
vector<int> fminlef2(vector<T> v)
{
vector<int> ans;
if(v.size()==0)return ans;
stack<T>st;
st.push(0);
ans.push_back(-1);
for(int i=1;i<v.size();i++)
{
while(!st.empty() && v[st.top()]>v[i])st.pop();
if(st.empty())ans.push_back(-1);
else ans.push_back(st.top());
st.push(i);
}
return ans;
}
//返回vector每個數後面最近的比它小或等於的數的ID,size 或者 0到size-1
template<typename T>
vector<int> fminrig2(vector<T> v)
{
vector<int>v1=frev(v),v2=fminlef2(v1);
fcheng(v2,-1);
fjia(v2,v.size()-1);
return frev(v2);
}
class Solution {
public:
int sumSubarrayMins(vector<int>& A) {
vector<int>v1=fminlef(A),v2=fminrig2(A);
int ans=0;
for(int i=0;i<A.size();i++)
{
int m=min(i-v1[i],v2[i]-i);
ans+=(v2[i]-v1[i]-m)*m*A[i],ans%=1000000007;
}
return ans;
}
};
934. 最短的橋
在給定的二維二進位制陣列A中,存在兩座島。(島是由四面相連的 1 形成的一個最大組。)
現在,我們可以將0變為1,以使兩座島連線起來,變成一座島。
返回必須翻轉的0 的最小數目。(可以保證答案至少是 1。)
示例 1:
輸入:[[0,1],[1,0]]
輸出:1
示例 2:
輸入:[[0,1,0],[0,0,0],[0,0,1]]
輸出:2
示例 3:
輸入:[[1,1,1,1,1],[1,0,0,0,1],[1,0,1,0,1],[1,0,0,0,1],[1,1,1,1,1]]
輸出:1
提示:
1 <= A.length =A[0].length <= 100
A[i][j] == 0 或A[i][j] == 1
思路:一次暴力列舉+兩次BFS
首先暴力列舉找到隨意1個起點
然後BFS找到和他相鄰的所有1
最後BFS找到另外一堆1
#define NUM(x,y) ((x<0||x>=A.size()||y<0||y>=A.size())? -1:A[x][y])
int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
class Solution {
public:
int shortestBridge(vector<vector<int>>& A) {
int si,sj,len[10000],flag=0;
queue<int>q1,q2;
memset(len,-1,sizeof(len));
for(si=0;si<A.size();si++)for(sj=0;sj<A.size();sj++)if(A[si][sj])goto g1;
g1:
q1.push(si*100+sj);
len[si*100+sj]=0;
while(!q1.empty())
{
int k=q1.front();
q1.pop();
q2.push(k);
int kx=k/100,ky=k%100;
for(int dire=0;dire<4;dire++)
{
int kxx=kx+dx[dire],kyy=ky+dy[dire];
if(NUM(kxx,kyy)==-1||NUM(kxx,kyy)==flag)continue;
if(len[kxx*100+kyy]!=-1)continue;
q1.push(kxx*100+kyy);
len[kxx*100+kyy]=flag?len[k]+1:0;
if(flag && NUM(kxx,kyy)==1)return len[k];
}
}
q1=q2,flag=2;
goto g1;
}
};
948. 令牌放置
題目:
你的初始能量為P,初始分數為0,只有一包令牌。
令牌的值為token[i],每個令牌最多隻能使用一次,可能的兩種使用方法如下:
如果你至少有token[i]點能量,可以將令牌置為正面朝上,失去token[i]點能量,並得到1分。
如果我們至少有1分,可以將令牌置為反面朝上,獲得token[i]點能量,並失去1分。
在使用任意數量的令牌後,返回我們可以得到的最大分數。
示例 1:
輸入:tokens = [100], P = 50
輸出:0
示例 2:
輸入:tokens = [100,200], P = 150
輸出:1
示例 3:
輸入:tokens = [100,200,300,400], P = 200
輸出:2
提示:
tokens.length <= 1000
0 <= tokens[i] < 10000
0 <= P < 10000
思路:
排序,貪心。注意tokens為空的情況
程式碼:
class Solution {
public:
int bagOfTokensScore(vector<int>& tokens, int P)
{
if(tokens.empty())return 0;
int ans=0;
sort(tokens.begin(),tokens.end());
auto it1=tokens.begin();
auto it2=tokens.end()-1;
while(it1<=it2){
if(*it1<=P){
P-=*it1;
ans++,it1++;
}
else if(ans){
P-=*it1-*it2;
it1++,it2--;
}
else{
break;
}
}
return ans;
}
};
971. 翻轉二叉樹以匹配先序遍歷
給定一個有 N 個節點的二叉樹,每個節點都有一個不同於其他節點且處於 {1, ..., N} 中的值。
通過交換節點的左子節點和右子節點,可以翻轉該二叉樹中的節點。
考慮從根節點開始的先序遍歷報告的 N 值序列。將這一 N 值序列稱為樹的行程。
(回想一下,節點的先序遍歷意味著我們報告當前節點的值,然後先序遍歷左子節點,再先序遍歷右子節點。)
我們的目標是翻轉最少的樹中節點,以便樹的行程與給定的行程voyage相匹配。
如果可以,則返回翻轉的所有節點的值的列表。你可以按任何順序返回答案。
如果不能,則返回列表 [-1]。
示例 1:
輸入:root = [1,2], voyage = [2,1]
輸出:[-1]
示例 2:
輸入:root = [1,2,3], voyage = [1,3,2]
輸出:[1]
示例 3:
輸入:root = [1,2,3], voyage = [1,2,3]
輸出:[]
提示:
1 <= N <= 100
第一次AC的程式碼:
class Solution {
public:
map<int,int>m;
vector<int>ans;
int fin(vector<int>& voyage,int low,int high,int k)
{
if(m[k]>=low && m[k]<high)return m[k];
return high;
}
bool f(TreeNode* root, vector<int>& voyage,int low,int high){
if(!root)return low==high;
if(high<=low || voyage[low]!=root->val)return false;
if(!root->left && !root->right)return high==low+1;
if(high<=low+1)return false;
if(root->left && root->left->val==voyage[low+1])
{
if(root->right)return f(root->left,voyage,low+1,fin(voyage,low,high,root->right->val)) && f(root->right,voyage,fin(voyage,low,high,root->right->val),high);
return f(root->left,voyage,low+1,high);
}
if(root->right && root->right->val==voyage[low+1])
{
if(root->left)
{
ans.push_back(root->val);
return f(root->left,voyage,fin(voyage,low,high,root->left->val),high) && f(root->right,voyage,low+1,fin(voyage,low,high,root->left->val));
}
return f(root->right,voyage,low+1,high);
}
return false;
}
vector<int> flipMatchVoyage(TreeNode* root, vector<int>& voyage) {
m.clear();
ans.clear();
for(int i=0;i<voyage.size();i++)m[voyage[i]]=i;
if(f(root,voyage,0,voyage.size()))return ans;
ans.clear();
ans.push_back(-1);
return ans;
}
};
然後感覺寫的太繁瑣,fin函式是不需要的,優化程式碼:
class Solution {
public:
map<int,int>m;
vector<int>ans;
bool f(TreeNode* root, vector<int>& voyage,int low,int high){
if(!root)return low==high;
if(high<=low || voyage[low]!=root->val)return false;
if(high==low+1)return !root->left && !root->right;
if(root->left && root->left->val==voyage[low+1])
{
if(root->right)return f(root->left,voyage,low+1,m[root->right->val]) && f(root->right,voyage,m[root->right->val],high);
return f(root->left,voyage,low+1,high);
}
if(root->right && root->right->val==voyage[low+1])
{
if(root->left)
{
ans.push_back(root->val);
return f(root->left,voyage,m[root->left->val],high) && f(root->right,voyage,low+1,m[root->left->val]);
}
return f(root->right,voyage,low+1,high);
}
return false;
}
vector<int> flipMatchVoyage(TreeNode* root, vector<int>& voyage) {
m.clear();
ans.clear();
for(int i=0;i<voyage.size();i++)m[voyage[i]]=i;
if(f(root,voyage,0,voyage.size()))return ans;
ans.clear();
ans.push_back(-1);
return ans;
}
};
因為這題沒有要求說不能改變二叉樹的結構,所以我還試了一下改變二叉樹結構的寫法,使得程式碼更簡潔:
class Solution {
public:
map<int,int>m;
vector<int>ans;
bool f(TreeNode* root, vector<int>& voyage,int low,int high){
if(!root)return low==high;
if(high<=low || voyage[low]!=root->val)return false;
if(high==low+1)return !root->left && !root->right;
if(root->right && root->right->val==voyage[low+1])
{
if(root->left)ans.push_back(root->val);
TreeNode* p=root->left;
root->left=root->right,root->right=p;
}
if(!root->left || root->left->val!=voyage[low+1])return false;
if(!root->right)return f(root->left,voyage,low+1,high);
return f(root->left,voyage,low+1,m[root->right->val]) && f(root->right,voyage,m[root->right->val],high);
}
vector<int> flipMatchVoyage(TreeNode* root, vector<int>& voyage) {
ans.clear();
for(int i=0;i<voyage.size();i++)m[voyage[i]]=i;
if(f(root,voyage,0,voyage.size()))return ans;
ans.clear();
ans.push_back(-1);
return ans;
}
};
979. 在二叉樹中分配硬幣
給定一個有 N 個結點的二叉樹的根結點 root,樹中的每個結點上都對應有 node.val 枚硬幣,並且總共有 N 枚硬幣。
在一次移動中,我們可以選擇兩個相鄰的結點,然後將一枚硬幣從其中一個結點移動到另一個結點。(移動可以是從父結點到子結點,或者從子結點移動到父結點。)。
返回使每個結點上只有一枚硬幣所需的移動次數。
示例 1:
輸入:[3,0,0]
輸出:2
解釋:從樹的根結點開始,我們將一枚硬幣移到它的左子結點上,一枚硬幣移到它的右子結點上。
示例 2:
輸入:[0,3,0]
輸出:3
解釋:從根結點的左子結點開始,我們將兩枚硬幣移到根結點上 [移動兩次]。然後,我們把一枚硬幣從根結點移到右子結點上。
示例 3:
輸入:[1,0,2]
輸出:2
示例 4:
輸入:[1,0,0,null,3]
輸出:4
提示:
1<= N <= 100
0 <= node.val <= N
class Solution {
public:
int distributeCoins(TreeNode* root,long &dif) {
if(!root)
{
dif=0;
return 0;
}
long dif1,dif2;
int ans1=distributeCoins(root->left,dif1),ans2=distributeCoins(root->right,dif2);
dif=dif1+dif2+root->val-1;
return ans1+ans2+abs(dif);
}
int distributeCoins(TreeNode* root) {
long dif=0;
return distributeCoins(root,dif);
}
};
988. 從葉結點開始的最小字串
題目:
給定一顆根結點為root的二叉樹,書中的每個結點都有一個從0 到25的值,分別代表字母'a' 到'z':值0 代表'a',值1代表'b',依此類推。
找出按字典序最小的字串,該字串從這棵樹的一個葉結點開始,到根結點結束。
(小貼士:字串中任何較短的字首在字典序上都是較小的:例如,在字典序上"ab" 比"aba"要小。葉結點是指沒有子結點的結點。)
示例 1:
輸入:[0,1,2,3,4,3,4]
輸出:"dba"
示例 2:
輸入:[25,1,3,1,3,0,2]
輸出:"adz"
示例 3:
輸入:[2,2,1,null,1,0,null,0]
輸出:"abc"
提示:
給定樹的結點數介於1 和8500之間。
樹中的每個結點都有一個介於0和25之間的值。
程式碼:
class Solution {
public:
void smallestFromLeaf(TreeNode* root, string cur, string &ans)
{
if (!root)return;
cur.insert(0, 1, char(int('a') + root->val));
if (!root->left && !root->right)
{
ans = min(ans, cur);
return;
}
smallestFromLeaf(root->left, cur, ans);
smallestFromLeaf(root->right, cur, ans);
}
string smallestFromLeaf(TreeNode* root) {
string ans = " ";
ans[0] = char(int('z') + 1);
smallestFromLeaf(root, "", ans);
return ans;
}
};
PS:
這題和另外一題基本一樣:力扣 OJ 129. 求根到葉子節點數字之和
990. 等式方程的可滿足性
給定一個由表示變數之間關係的字串方程組成的陣列,每個字串方程 equations[i] 的長度為 4,並採用兩種不同的形式之一:"a==b" 或"a!=b"。在這裡,a 和 b 是小寫字母(不一定不同),表示單字母變數名。
只有當可以將整數分配給變數名,以便滿足所有給定的方程時才返回true,否則返回 false。
示例 1:
輸入:["a==b","b!=a"]
輸出:false
解釋:如果我們指定,a = 1 且 b = 1,那麼可以滿足第一個方程,但無法滿足第二個方程。沒有辦法分配變數同時滿足這兩個方程。
示例 2:
輸入:["b==a","a==b"]
輸出:true
解釋:我們可以指定 a = 1 且 b = 1 以滿足滿足這兩個方程。
示例 3:
輸入:["a==b","b==c","a==c"]
輸出:true
示例 4:
輸入:["a==b","b!=c","c==a"]
輸出:false
示例 5:
輸入:["c==c","b==d","x!=z"]
輸出:true
提示:
1 <= equations.length <= 500
equations[i].length == 4
equations[i][0] 和equations[i][3]是小寫字母
equations[i][1] 要麼是'=',要麼是'!'
equations[i][2]是'='
int fa[26];
int find(int x) //找祖先
{
if (fa[x] == x)return x;
return fa[x] = find(fa[x]);
}
class Solution {
public:
bool equationsPossible(vector<string>& equations) {
for (int i = 0; i <26; i++)fa[i] = i;
for (int i = 0; i <equations.size(); i++)if(equations[i][1]=='=')fa[find(equations[i][0]-'a')]=find(equations[i][3]-'a');
for (int i = 0; i <equations.size(); i++)if(equations[i][1]=='!' && find(equations[i][0]-'a')==find(equations[i][3]-'a'))return false;
return true;
}
};