79.实现strStr(KMP)
方法一 .常规方法
class Solution {
public:
int strStr(string haystack, string needle) {
if(haystack.empty() && needle.empty()) return 0;
int m = haystack.length();
int n = needle.length();
for(int i=0;i+n<=m;++i)
{
bool flag = true;
for(int j=0;j<n;++j)
{
if(needle[j] != haystack[i+j])
{
flag = false;
break;
}
}
if(flag == true)
{
return i;
}
}
return -1;
}
};
方法二.KMP算法
【宫水三叶】简单题学 KMP 算法 - 实现 strStr() - 力扣(LeetCode) (leetcode-cn.com)
class Solution {
public:
int strStr(string haystack, string needle) {
int n = haystack.size();
int m = needle.size();
if(m == 0) return 0;
vector<int> next(m);
for(int i=1,j=0;i<m;++i)
{
while(j>0 && needle[i] != needle[j])
{
j = next[j-1];
}
if(needle[i] == needle[j])
{
++j;
}
next[i] = j;
}
for(int i =0,j=0;i<n;++i)
{
while(j>0 && haystack[i] != needle[j])
{
j = next[j-1];
}
if(haystack[i] == needle[j])
{
++j;
}
if(j == m)
{
return i-m+1;
}
}
return -1;
}
}
80.字符串转换整数(atoi)
class Solution {
public:
int myAtoi(string s) {
int n = s.length();
if(n == 0) return 0;
int index = 0;
while(index<n && s[index] == ' ') index++;//去除前导空格
if(index == n) return 0;
//判断正负号
bool flag = true;
if(s[index] == '-')
{
flag = false;
index++;
}
else if(s[index] == '+')
{
index++;
}
else if(!isdigit(s[index])) return 0;//其他字符
int ans = 0;
while(index < n && isdigit(s[index]))
{
int num = s[index]-'0';
/*本来应该是 ans * 10 + digit > Integer.MAX_VALUE
但是 *10 和 + digit 都有可能越界,所有都移动到右边去就可以了。*/
if(ans > (INT_MAX-num)/10)
{
return flag?INT_MAX:INT_MIN;
}
ans = ans*10+num;
index++;
}
return flag?ans:-ans;
}
};
81.三数之和(双指针)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums)
{
int size = nums.size();
if (size < 3) return {}; // 特判
vector<vector<int> >res; // 保存结果(所有不重复的三元组)
std::sort(nums.begin(), nums.end());// 排序(默认递增)
for (int i = 0; i < size; i++) // 固定第一个数,转化为求两数之和
{
if (nums[i] > 0) return res; // 第一个数大于 0,后面都是递增正数,不可能相加为零了
// 去重:如果此数已经选取过,跳过
if (i > 0 && nums[i] == nums[i-1]) continue;
// 双指针在nums[i]后面的区间中寻找和为0-nums[i]的另外两个数
int left = i + 1;
int right = size - 1;
while (left < right)
{
if (nums[left] + nums[right] + nums[i] > 0)
right--; // 两数之和太大,右指针左移
else if (nums[left] + nums[right] + nums[i] < 0)
left++; // 两数之和太小,左指针右移
else
{
// 找到一个和为零的三元组,添加到结果中,左右指针内缩,继续寻找
res.push_back({nums[i], nums[left], nums[right]});
left++;
right--;
// 去重:第二个数和第三个数也不重复选取
// 例如:[-4,1,1,1,2,3,3,3], i=0, left=1, right=5
while (left < right && nums[left] == nums[left-1]) left++;
while (left < right && nums[right] == nums[right+1]) right--;
}
}
}
return res;
}
};
82.最接近的三数之和(双指针)
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
int n = nums.size();
sort(nums.begin(),nums.end());
int pre = nums[0]+nums[1]+nums[2];
for(int i = 0;i<n-2;++i)
{
int left = i+1;
int right = n-1;
while(left < right)
{
int cur = nums[i] + nums[left] + nums[right];
if(abs(cur-target) < abs(pre - target)) pre = cur;
if(cur > target) --right;
else if(cur < target) ++left;
else return target;
}
}
return pre;
}
};
83.反转链表II
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
ListNode* newhead = new ListNode();
newhead->next = head;
ListNode* pre = newhead;
for(int i=0;i<left-1;++i)
{
pre = pre->next;
}
ListNode* cur = pre->next;
ListNode* next;
for(int i=0;i<right-left;++i)
{
next = cur->next;
cur->next = next->next;
next->next = pre->next;
pre->next = next;
}
return newhead->next;
}
};
84.和为K的子数组
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int,int> m;
m[0] = 1;
int ans = 0;
int sum = 0;
for(auto &x:nums)
{
sum+=x;
if(m.find(sum-k) != m.end())
{
ans+=m[sum-k];
}
m[sum]++;
}
return ans;
}
};
85.把二叉树转换为累加树
class Solution {
public:
int sum = 0;
TreeNode* convertBST(TreeNode* root) {
if (root != nullptr) {
convertBST(root->right);
sum += root->val;
root->val = sum;
convertBST(root->left);
}
return root;
}
};
86.两数之和
给你两个整数 a
和 b
,不使用 运算符 +
和 -
,计算并返回两整数之和。
class Solution {
public:
int getSum(int a, int b) {
while(b!=0)
{
unsigned int carry = (unsigned int)(a & b)<<1;//进位
a^=b;
b = carry;
}
return a;
}
};
87.实现前缀树
class Trie {
private:
vector<Trie*> children;
bool isEnd;
Trie* searchPrefix(string prefix) {
Trie* node = this;
for (char ch : prefix) {
ch -= 'a';
if (node->children[ch] == nullptr) {
return nullptr;
}
node = node->children[ch];
}
return node;
}
public:
Trie() : children(26), isEnd(false) {}
void insert(string word) {
Trie* node = this;
for (char ch : word) {
ch -= 'a';
if (node->children[ch] == nullptr) {
node->children[ch] = new Trie();
}
node = node->children[ch];
}
node->isEnd = true;
}
bool search(string word) {
Trie* node = this->searchPrefix(word);
return node != nullptr && node->isEnd;
}
bool startsWith(string prefix) {
return this->searchPrefix(prefix) != nullptr;
}
};
88.打家劫舍
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if(n == 0) return 0;
if(n == 1) return nums[0];
vector<int> dp(n);
dp[0] = nums[0];
dp[1] = max(nums[0],nums[1]);
for(int i = 2;i<n;++i)
{
dp[i] = max(dp[i-2]+nums[i],dp[i-1]);
}
return dp[n-1];
}
};
89.计数质数
class Solution {
public:
int countPrimes(int n) {
vector<bool> isPrime(n,true);
int ans = 0;
for (int i = 2; i < n; ++i) {
if(isPrime[i])
{
ans++;
for(int j=2;i*j<n;++j)
{
isPrime[i*j] = false;
}
}
}
return ans;
}
};
90.最大正方形
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
if(m == 0 || n == 0) return 0;
vector<vector<int>> dp(m,vector<int>(n));
int ans = 0;
for(int i=0;i<m;++i)
{
for(int j=0;j<n;++j)
{
if(matrix[i][j] == '1')
{
if(i == 0 || j == 0)
{
dp[i][j] = 1;
}
else
{
dp[i][j] = min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;
}
ans = max(ans,dp[i][j]);
}
}
}
return ans*ans;
}
};
91.统计全为1的正方形子矩阵
class Solution {
public:
int countSquares(vector<vector<int>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
vector<vector<int>> dp(m,vector<int>(n));
int ans = 0;
for(int i=0;i<m;++i)
{
for(int j=0;j<n;++j)
{
if(i == 0 || j == 0)
{
dp[i][j] = matrix[i][j];
}
else if(matrix[i][j] == 0)
{
dp[i][j] = 0;
}
else{
dp[i][j] = min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;
}
ans+=dp[i][j];
}
}
return ans;
}
};
92.打家劫舍2
class Solution {
public:
int rob(vector<int>& nums) {
if (nums.size() == 0) return 0;
if (nums.size() == 1) return nums[0];
int result1 = robRange(nums, 0, nums.size() - 2); // 情况二
int result2 = robRange(nums, 1, nums.size() - 1); // 情况三
return max(result1, result2);
}
// 198.打家劫舍的逻辑
int robRange(vector<int>& nums, int start, int end) {
if (end == start) return nums[start];
vector<int> dp(nums.size());
dp[start] = nums[start];
dp[start + 1] = max(nums[start], nums[start + 1]);
for (int i = start + 2; i <= end; i++) {
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
}
return dp[end];
}
};
93.打家劫舍3
class Solution {
public:
int rob(TreeNode* root) {
vector<int> ans = dfs(root);
return max(ans[0],ans[1]);
}
vector<int> dfs(TreeNode* root)
{
if(root == nullptr) return vector<int>{0,0};
vector<int> left = dfs(root->left);
vector<int> right = dfs(root->right);
int val1 = root->val + left[0] + right[0];
int val2 = max(left[0],left[1]) + max(right[0],right[1]);
return {val2,val1};
}
};
94.删除无效的括号
class Solution {
public:
vector<string> removeInvalidParentheses(string s) {
vector<string> ans;
unordered_set<string> curset;
curset.insert(s);
while(true)
{
for(auto & str : curset)
{
if((isValid(str)))
{
ans.push_back(str);
}
}
if(!ans.empty()) return ans;
unordered_set<string> nextstr;
for(auto &str:curset)
{
for(int i=0;i<str.size();++i)
{
if(i>0 && str[i] == str[i-1]) continue;
if(str[i] == '(' || str[i] == ')')
{
nextstr.insert(str.substr(0,i)+str.substr(i+1,str.size()));
}
}
}
curset = nextstr;
}
}
bool isValid(string str)
{
int count =0 ;
for(auto &x:str)
{
if(x == '(') count++;
else if(x == ')')
{
count--;
if(count < 0) return false;
}
}
return count == 0;
}
};
95.柱状图中最大的矩形
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> st;
int result = 0;
heights.insert(heights.begin(), 0);
heights.push_back(0);
// 第一个元素已经入栈,从下表1开始
for (int i = 0; i < heights.size(); i++) {
while (!st.empty()&&heights[i] < heights[st.top()]) { // 注意是while
int curheight = heights[st.top()];
st.pop();
int cur = curheight*(i-st.top()-1);
result = max(result, cur);
}
st.push(i);
}
return result;
}
};
96.最大矩形
class Solution {
public:
int maximalRectangle(vector<vector<char>>& matrix) {
if(matrix.empty()) return 0;
int m = matrix.size();
int n = matrix[0].size();
int ans = 0;
vector<int> heights(n+2);
for(int i=0;i<m;++i)
{
for(int j=0;j<n;++j)
{
if(matrix[i][j] == '1')
{
heights[j+1]+=1;
}
else
{
heights[j+1] = 0;
}
}
ans = max(ans,largestRectangleArea(heights));
}
return ans;
}
int largestRectangleArea(vector<int>& heights) {
stack<int> st;
int result = 0;
// 第一个元素已经入栈,从下表1开始
for (int i = 0; i < heights.size(); i++) {
while (!st.empty()&&heights[i] < heights[st.top()]) { // 注意是while
int curheight = heights[st.top()];
st.pop();
int cur = curheight*(i-st.top()-1);
result = max(result, cur);
}
st.push(i);
}
return result;
}
};
97.重新排序得到2的幂
class Solution {
public:
bool reorderedPowerOf2(int n) {
vector<int> nums(10,0);
while(n)
{
nums[n%10]++;
n/=10;
}
for(int i=1;i<=1e9;i<<=1)//i*=2
{
vector<int> temp(10,0);
int k = i;
while(k)
{
temp[k%10]++;
k/=10;
}
if(nums == temp) return true;
}
return false;
}
};
98.课程表
课程表 - 课程表 - 力扣(LeetCode) (leetcode-cn.com)
99.课程表2
class Solution {
private:
vector<vector<int>> edges;
vector<int> indeg;
vector<int> ans;
public:
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
edges.resize(numCourses);
indeg.resize(numCourses);
for(auto &x:prerequisites)
{
edges[x[1]].push_back(x[0]);
indeg[x[0]]++;
}
queue<int> q;
for(int i=0;i<numCourses;++i)
{
if(indeg[i] == 0)
{
q.push(i);
}
}
while(!q.empty())
{
int u = q.front();
q.pop();
ans.push_back(u);
for(int i:edges[u])
{
--indeg[i];
if(indeg[i] == 0)
{
q.push(i);
}
}
}
if(ans.size() != numCourses) return {};
return ans;
}
};
100.单词拆分
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> st(wordDict.begin(),wordDict.end());
int n = s.size();
vector<bool> dp(n+1);
dp[0] = true;
for(int i=1;i<=n;++i)
{
for(int j=0;j<i;++j)
{
if(dp[j] && st.find(s.substr(j,i-j))!=st.end())
{
dp[i] = true;
break;//可加可不加
}
}
}
return dp[n];
}
};
101.电话号码的字母组合
class Solution {
public:
vector<string> ans;
string temp;
vector<string> Map = {
"",
"",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz"
};
vector<string> letterCombinations(string digits) {
if(digits.empty()) return {};
func(digits,0);
return ans;
}
void func(string digits,int index)
{
if(index == digits.size())
{
ans.push_back(temp);
return ;
}
int ch = digits[index]-'0';
string s = Map[ch];
for(auto& x:s)
{
temp.push_back(x);
func(digits,index+1);
temp.pop_back();
}
}
};
102.2的幂
class Solution {
public:
bool isPowerOfTwo(int n) {
return n>0 && (1<<30)%n == 0;//2的30次方对n取余
}
};
103.用rand7()实现rand10()
class Solution {
public:
int rand10() {
int num = (rand7() -1)*7 + rand7();
/*
(1-1)*7 + 1 = 1
(7-1)*7 + 7 = 42+7 = 49
1~49
*/
while(num > 40)//去除大于40的值,使1~40等概率出现
{
num = (rand7()-1)*7 +rand7();
}
return 1+num%10;//1+ (0~9)
}
};
104.寻找两个正序数组的中位数
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int length = nums1.size()+nums2.size();
if(length % 2 == 1)
{
return func(nums1,nums2,(length+1)/2);//此时要找的第K个数为(length+1)/2
}
else
{
return (func(nums1,nums2,length/2) + func(nums1,nums2,(length/2+1)))/2.0;
//此事要找的第K个数为 length/2 + length/2+1
}
}
int func(vector<int> nums1,vector<int> nums2,int k)
{
int len1 = nums1.size();
int len2 = nums2.size();
int i = 0;
int j = 0;
while(true)
{
if(i == len1)//如果第一个数组为空
{
return nums2[j+k-1];
}
if(j == len2)//如果第二个数组为空
{
return nums1[i+k-1];
}
if(k == 1)//如果要找的中位数即K为1,则返回当前遍历的nums1和nums2起始位置的较小值即可
{
return min(nums1[i],nums2[j]);
}
int half = k/2;
int I = min(i+half,len1)-1;
int J = min(j+half,len2)-1;
if(nums1[I] <= nums2[J])
{
k-=(I-i+1);
i = I+1;
}
else{
k-=(J-j+1);
j = J+1;
}
}
}
};
105.天际线问题
class Solution {
public:
vector<vector<int>> getSkyline(vector<vector<int>>& buildings) {
vector<pair<int,int>> sortedBuildings;
for(auto& x:buildings)
{
sortedBuildings.push_back(make_pair(x[0],-x[2]));
sortedBuildings.push_back(make_pair(x[1],x[2]));
}
sort(sortedBuildings.begin(),sortedBuildings.end(),[](auto& left,auto& right)
{
if(left.first == right.first)
{
return left.second < right.second;
}
return left.first < right.first;
});//按横坐标进行排序
multiset<int,greater<int>> pq;
pq.insert(0);
vector<vector<int>> ans;
for(auto& build:sortedBuildings)
{
int x = build.first;
int y = build.second;
int top = *(pq.begin());
if(y<0)//高度小于0,说明此为左节点
{
y=-y;
if(y>top)
{
ans.push_back({x,y});
}
pq.insert(y);
}
else//右节点
{
pq.erase(pq.find(y));
int temp = *(pq.begin());
if(top > temp)
{
ans.push_back({x,temp});
}
}
}
return ans;
}
};
106.汉诺塔问题
class Solution {
public:
void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {
move(A.size(),A,B,C);//把A上的N个盘子通过B移到C A->B->C
}
void move(int n,vector<int>& A,vector<int>& B,vector<int>& C)
{
if(n == 1)
{
C.push_back(A.back());
A.pop_back();
return ;
}
move(n-1,A,C,B);//把A上面的n-1个盘子通过C移动到B A->C->B
C.push_back(A.back());
A.pop_back();
move(n-1,B,A,C);//把B上面的n-1个盘子通过A移动到C B->A->C
}
};
107.有序矩阵中的第K小的元素
class Solution {
public:
bool check(vector<vector<int>>& matrix, int mid, int k, int n) {
int i = n - 1;
int j = 0;
int num = 0;
while (i >= 0 && j < n) {
if (matrix[i][j] <= mid) {
num += i + 1;
j++;
} else {
i--;
}
}
return num >= k;
}
int kthSmallest(vector<vector<int>>& matrix, int k) {
int n = matrix.size();
int left = matrix[0][0];
int right = matrix[n - 1][n - 1];
while (left < right) {
int mid = left + ((right - left) >> 1);
if (check(matrix, mid, k, n)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
};
108.峰与谷
class Solution {
public:
void wiggleSort(vector<int>& nums) {
if(nums.empty()) return ;
sort(nums.begin(),nums.end());
for(int i = 0;i<nums.size()-1;i+=2)
{
swap(nums[i],nums[i+1]);
}
}
};
109.三角形最小路径和
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
int n = triangle.size();
vector<vector<int>> ans(n,vector<int>(n));
ans[0][0] = triangle[0][0];
for(int i = 1;i<n;++i)
{
ans[i][0] = ans[i-1][0] + triangle[i][0];
for(int j = 1;j<i;++j)
{
ans[i][j] = min(ans[i-1][j],ans[i-1][j-1])+triangle[i][j];
}
ans[i][i] = ans[i-1][i-1]+triangle[i][i];
}
return *min_element(ans[n-1].begin(),ans[n-1].end());
}
};
110.最大整数子集
class Solution {
public:
vector<int> largestDivisibleSubset(vector<int>& nums) {
int n = nums.size();
sort(nums.begin(),nums.end());
vector<int> dp(n,1);
int maxsize = 1;
int maxvalue = nums[0];
for(int i=1;i<n;++i)
{
for(int j = 0;j<i;++j)
{
if(nums[i] % nums[j] == 0)
{
dp[i] = max(dp[i],dp[j]+1);
}
}
if(dp[i] > maxsize)
{
maxsize = dp[i];
maxvalue = nums[i];
}
}
vector<int> ans;
if(maxsize == 1)
{
ans.push_back(nums[0]);
return ans;
}
for(int i=n-1;i>=0 && maxsize > 0;--i)
{
if(dp[i] == maxsize && maxvalue % nums[i] == 0)
{
ans.push_back(nums[i]);
maxvalue = nums[i];
maxsize--;
}
}
return ans;
}
};
111.接雨水2
class Solution {
public:
int trapRainWater(vector<vector<int>>& heightMap) {
int row = heightMap.size();
int col = heightMap[0].size();
int maxheight = 0;
//寻找三维矩阵中最高的高度
for(int i=0;i<row;++i)
{
maxheight = max(maxheight,*max_element(heightMap[i].begin(),heightMap[i].end()));
}
//将接到雨水以后的高度全部初始化为最高的高度
vector<vector<int>> water(row,vector<int>(col,maxheight));
queue<pair<int,int>> q;
for(int i=0;i<row;++i)
{
for(int j = 0;j<col;++j)
{
//如果是最外围的格子,则无法接雨水,则将他们接了雨水以后的高度赋值为没接雨水时本身的高度
if(i == 0 || i == row-1 || j == 0 || j == col-1)
{
if(water[i][j]>heightMap[i][j])
{
water[i][j] = heightMap[i][j];
q.push({i,j});
}
}
}
}
//从四周向内部收缩
while(!q.empty())
{
int x = q.front().first;
int y = q.front().second;
q.pop();
if(x-1 >= 0)//上
{
//如果上边格子接了雨水以后的高度比当前格子接了雨水的高度还要高,那么高出来的这部分雨水必然会流走,
if(water[x][y] < water[x-1][y] && water[x-1][y] > heightMap[x-1][y])
{
water[x-1][y] = max(water[x][y],heightMap[x-1][y]);
q.push({x-1,y});
}
}
if(x+1 < row)//下
{
if(water[x][y] < water[x+1][y] && water[x+1][y] > heightMap[x+1][y])
{
water[x+1][y] = max(water[x][y],heightMap[x+1][y]);
q.push({x+1,y});
}
}
if(y-1 >= 0)//左
{
if(water[x][y] < water[x][y-1] && water[x][y-1] > heightMap[x][y-1])
{
water[x][y-1] = max(water[x][y],heightMap[x][y-1]);
q.push({x,y-1});
}
}
if(y+1 < col)//右
{
if(water[x][y] < water[x][y+1] && water[x][y+1] > heightMap[x][y+1])
{
water[x][y+1] = max(water[x][y],heightMap[x][y+1]);
q.push({x,y+1});
}
}
}
int ans = 0;
for(int i=1;i<row;++i)
{
for(int j = 0;j<col;++j)
{
ans+=water[i][j] - heightMap[i][j];
}
}
return ans;
}
};
112.被围绕的区域
class Solution {
public:
void dfs(vector<vector<char>>& board,int x,int y,int row,int col)
{
if(x<0 || x>=row || y<0 || y>=col || board[x][y] != 'O') return;
board[x][y] = 'P';//标记
dfs(board,x+1,y,row,col);
dfs(board,x-1,y,row,col);
dfs(board,x,y+1,row,col);
dfs(board,x,y-1,row,col);
}
void solve(vector<vector<char>>& board) {
int row = board.size();
if(row == 0) return ;
int col = board[0].size();
for(int i=0;i<row;++i)
{
dfs(board,i,0,row,col);
dfs(board,i,col-1,row,col);
}
for(int i=1;i<col-1;++i)
{
dfs(board,0,i,row,col);
dfs(board,row-1,i,row,col);
}
for(int i=0;i<row;++i)
{
for(int j=0;j<col;++j)
{
if(board[i][j] == 'P')
{
board[i][j] = 'O';
}
else if(board[i][j] == 'O')
{
board[i][j] = 'X';
}
}
}
}
};
113.和为K的的子数组(前缀和)
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int,int> m;
int sum = 0;
int ans = 0;
m[0] = 1;
for(int i=0;i<nums.size();++i)
{
sum+=nums[i];
if(m.count(sum-k))
{
ans+=m[sum-k];
}
m[sum]++;
}
return ans;
}
};
114.构成交替字符串需要的最小交换次数
class Solution {
public:
int minSwaps(string s) {
//iOdd 1在奇数位上的总个数,iEven 1在偶数位上的总个数
int iOdd = 0, iEven = 0, n = s.length();
for(int i = 0; i < n; ++i){
if(s[i] == '1'){
if(i % 2 == 0) iEven++;
else iOdd++;
}
}
int n1 = iEven+iOdd;//1总共出现的次数
int n0 = n-n1;//0总共出现的次数
int ans = -1;
if(n1 >= n0-1 && n1<= n0+1){
if(n0==n1)ans = min(iEven, iOdd);
else if(n1 == n0+1) ans = iOdd;
else ans = iEven;
}
return ans;
}
};
115.乘积为正数的最长子数组的长度
class Solution {
public:
int getMaxLen(vector<int>& nums) {
int m = nums.size();
vector<int> positive(m);
vector<int> negative(m);
if(nums[0]>0) positive[0] = 1;
else if(nums[0]<0) negative[0] = 1;
int maxlen = positive[0];
for(int i=1;i<m;++i)
{
if(nums[i]>0)
{
positive[i] = positive[i-1]+1;
negative[i] = (negative[i-1]>0?negative[i-1]+1:0);
}
else if(nums[i]<0)
{
positive[i] = (negative[i-1]>0?negative[i-1]+1:0);
negative[i] = positive[i-1]+1;
}
else{
positive[i] = 0;
negative[i] = 0;
}
maxlen = max(maxlen,positive[i]);
}
return maxlen;
}
};
116.最长定差子序列
class Solution {
public:
int longestSubsequence(vector<int>& arr, int difference) {
if(arr.empty()) return 0;
unordered_map<int,int> m;
int ans = INT_MIN;
for(auto& x:arr)
{
m[x] = m[x-difference]+1;
ans = max(ans,m[x]);
}
return ans;
}
};
117.按字典序排在最后的子串
class Solution {
public:
string lastSubstring(string s) {
if(s.empty()) return "";
int left = 0;
int right = 1;
int k = 0;//偏移,对于相同的字符,则更长的字典序更大,而我们通过移动偏移k来判断当前的两个字符是否相等。
int len = s.size();
while(right+k<len)
{
if(s[left+k] == s[right+k]) k++;
else if(s[left]<s[right+k])
{
left = right+k;
right = left+1;
k = 0;
}
//len1 = right+k-left
//len2 = right+k-(left+k) = right-left
//len1 > len2
//所以先判断s[left] < s[right+k]
//再判断s[left+k] < s[right+k]
else if(s[left+k] < s[right+k])
{
left = right;
right++;
k=0;
}
else{
right++;
k=0;
}
}
return s.substr(left);
}
};
118.向下的路径节点之和(前缀和)
class Solution {
public:
unordered_map<int,int> m;
int dfs(TreeNode* root,int cur,int targetSum)
{
if(root == nullptr) return 0;
int ret = 0;
cur+=root->val;
if(m.count(cur-targetSum))
{
ret+=m[cur-targetSum];
}
m[cur]++;
ret+=dfs(root->left,cur,targetSum);
ret+=dfs(root->right,cur,targetSum);
m[cur]--;
return ret;
}
int pathSum(TreeNode* root, int targetSum) {
m[0] = 1;//前缀和为0的路径有1条
return dfs(root,0,targetSum);
}
};
119.求x的幂(位运算)
这类题通常是指,判断一个数是否为x的幂(x=1,2,3....),对于这类型题,有一个通用的模板。
例如,下面的代码是判断一个数是否为4的幂(其他的也一样)
class Solution {
public:
bool isPowerOfFour(int n) {
if(n == 0) return false;
while(n%4==0)
{
n/=4;
}
return n == 1;
}
};
120.汉明距离总和(位运算)
class Solution {
public:
int totalHammingDistance(vector<int>& nums) {
int ans = 0;
int n = nums.size();
for(int i=0;i<32;++i)
{
int cur = 0;
for(auto& x:nums)
{
cur+=(x>>i)&1;//(x>>i)&1计算的是当前的第i位是否为1
}
ans+=cur*(n-cur);//某位上有c个1,每个1对应n - c个0,总贡献就是c * (n - c)
}
return ans;
}
};
121.丢失的数字
/*class Solution {
public:
int missingNumber(vector<int>& nums) {
int n = nums.size();
int sum = (n*(n+1))/2;//前n项求和公式,可能存在数据溢出问题
int res = 0;
for(auto x:nums)
{
res+=x;
}
return sum-res;
}
};*/
class Solution {
public:
int missingNumber(vector<int>& nums) {
int ans = nums.size();
for(int i=0;i<nums.size();++i)
{
ans^=i^nums[i];
//如果i == nums[i]的话,则i^nums[i]==0,则ans^0==ans,不变
}
return ans;
}
};
/*我们可以先求得 [1, n]的异或和 ans,然后用 ans 对各个 nums[i] 进行异或。
这样最终得到的异或和表达式中,只有缺失元素出现次数为 1 次,其余元素均出现两次(x ⊕x = 0),即最终答案 ans 为缺失元素。
*/
122.乘积小于K的子数组(双指针)
class Solution {
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
if(nums.empty() || k == 0 || k == 1) return 0;
int res = 1;
int ans = 0;
int left = 0;
for(int right = 0;right<nums.size();++right)
{
res *= nums[right];
while(res >= k)
{
res/=nums[left++];
}
ans+=right-left+1;
/*为什么这里是right-left+1
比如现在有一个满足题意的子数组为{1,2,3,4},
那么{4} {3,4} {2,3,4} {1,2,3,4}也一定是满足题意的
所以其实就是这个子数组的元素个数,即长度
*/
}
return ans;
}
};
123.展开二叉搜索树(dfs)
class Solution {
public:
TreeNode* head = new TreeNode();
TreeNode* pre = head;
TreeNode* increasingBST(TreeNode* root) {
func(root);
return head->right;
}
void func(TreeNode*root)
{
if(root != nullptr)
{
func(root->left);
pre->right = root;
pre = root;
root->left = nullptr;
func(root->right);
}
}
};
124.所有路径(深度优先遍历)
class Solution {
public:
vector<vector<int>> ans;
vector<int> temp;
vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
if(graph.empty()) return {};
temp.push_back(0);//因为每一条路径一定是从0开始的,所以我们先将0放入temp中
dfs(graph,0);//开始深度优先遍历
return ans;
}
void dfs(vector<vector<int>> graph,int cur)
{
if(cur == graph.size()-1)//说明此时已经遍历到一条路径的终点了
{
ans.push_back(temp);
return;
}
for(auto& y:graph[cur])
{
temp.push_back(y);//接着从以y为出发点的路径开始遍历
dfs(graph,y);
temp.pop_back();//以y为出发点的路径完成遍历
}
}
};//深度优先遍历
125.爱吃香蕉的猩猩(二分法)
class Solution {
public:
int minEatingSpeed(vector<int>& piles, int h) {
int left = 1;
int right = 1000000000;
while(left<=right)
{
int mid = (right-left)/2+left;
if(Can(piles,mid,h))//如果以mid的速度可以吃完,则试着让狒狒吃的更慢一点
{
right = mid-1;
}
else
{
left = mid+1;
}
}
return left;
}
bool Can(vector<int>& piles,int K,int h)
{
int sum = 0;//以K速度吃完这些香蕉,总共需要的时间
for(auto& x:piles)
{
sum+=x/K;
if(x%K)
{
sum+=1;
}
}
return sum<=h;
}
};
126.数组中第K大的数字(快排)
class Solution {
public:
int partition(vector<int>& nums,int left,int right)
{
int temp = nums[left];//基准数->固定
int low = left;
while(left<right)
{
while(left<right && nums[right]>=temp)
{
--right;
}
while(left<right && nums[left]<=temp)
{
++left;
}
if(left<right)
{
swap(nums[left],nums[right]);
}
}
nums[low] = nums[left];
nums[left] = temp;
return left;
}
int findKthLargest(vector<int>& nums, int k) {
int target = nums.size()-k;
int left = 0;
int right = nums.size()-1;
while(left<right)
{
int pos = partition(nums,left,right);
if(pos == target) return nums[pos];
else if(pos < target) left=pos+1;
else right = pos-1;
}
return nums[left];
}
};
/*
class Solution {
public:
int partition(vector<int>& nums,int left,int right)
{
srand(time(0));
int index = rand()%(right-left+1)+left;
int temp = nums[index];//基准数->随机
swap(nums[left],nums[index]);//先把基准数换到最左边
while(left<right)//开始排序
{
while(left<right && nums[right]>=temp)
{
--right;
}
nums[left] = nums[right];
while(left<right && nums[left]<=temp)
{
++left;
}
nums[right] = nums[left];
}
nums[left] = temp;//再把基准数换回来
return left;
}
int findKthLargest(vector<int>& nums, int k) {
int target = nums.size()-k;
int left = 0;
int right = nums.size()-1;
while(left<right)
{
int pos = partition(nums,left,right);
if(pos == target) return nums[pos];
else if(pos < target) left=pos+1;
else right = pos-1;
}
return nums[left];
}
};
*/
127.省份数量(并查集)
class Solution {
public:
int Find(vector<int>& parent,int index)//返回index的根结点
{
while(parent[index] != index)
{
index = parent[index];
}
return index;
}
void Union(vector<int>& parent,int x,int y)
{
int X = Find(parent,x);
int Y = Find(parent,y);
if(X != Y)//如果两个节点的根结点不同,说明他们两个不在同一个集合里,则可以进行合并
{
parent[X] = Y;
}
}
int findCircleNum(vector<vector<int>>& isConnected) {
int n = isConnected.size();
int m = isConnected[0].size();
vector<int> parent(n+1);
for(int i=1;i<=n;++i)
{
parent[i] = i;
}
for(int i=0;i<n;++i)
{
for(int j=0;j<m;++j)
{
if(isConnected[i][j] == 1)
{
Union(parent,i,j);//将i与j连通
}
}
}
int ans = 0;
for(int i=0;i<n;++i)
{
if(parent[i] == i) ans++;//当前值与下标相等则说明为根结点,有几个根结点则有几个集合
}
return ans;
}
};
128.冗余连接(并查集)
class Solution {
public:
int Find(vector<int>& parent,int index)
{
while(parent[index] != index)
{
index = parent[index];
}
return index;
}
void Union(vector<int>& parent,int x,int y)
{
int X = Find(parent,x);
int Y = Find(parent,y);
if(X != Y)
{
parent[X] = Y;
}
}
bool connected(vector<int>& parent,int x,int y)//判断x和y是否已在同一集合中
{
return Find(parent,x) == Find(parent,y);
}
vector<int> findRedundantConnection(vector<vector<int>>& edges) {
int n = edges.size();
int m = edges[0].size();
vector<int> parent(n+1);
for(int i=1;i<=n;++i)
{
parent[i] = i;
}
for(auto& edge:edges)
{
int x = edge[0];
int y = edge[1];
if(!connected(parent,x,y))
{
Union(parent,x,y);
}
else
{
return {x,y};
}
}
return {};
}
};
129.矩阵中的距离 (BFS)
class Solution {
public:
vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
queue<pair<int,int>> q;
for(int i=0;i<mat.size();++i)
{
for(int j = 0;j<mat[0].size();++j)
{
if(mat[i][j] == 0)//先找到0的位置,然后把所有0的位置都加入到队列中
{
q.push({i,j});
}
else//不是0的话将他们标记为-1
{
mat[i][j]=-1;
}
}
}
int index_x[] = {-1,1,0,0};
int index_y[] = {0,0,-1,1};
//分别用来表示当前这个0位置的上下左右
while(!q.empty())
{
int x = q.front().first;
int y = q.front().second;
q.pop();
for(int i=0;i<4;++i)
{
int nx = x+index_x[i];
int ny = y+index_y[i];
if(nx>=0 && ny>=0 && nx<mat.size() && ny<mat[0].size() && mat[nx][ny] == -1)
{
mat[nx][ny] = mat[x][y]+1;//如果当前位置为-1,则它的值为据他最近的不为-1的值再加上距离1
q.push({nx,ny});
}
}
}
return mat;
}
};
130.计算后缀表达式
class Solution {
public:
int evalRPN(vector<string>& tokens) {
if(tokens.empty()) return 0;
stack<int> s;
for(auto& x:tokens)
{
if(isdigit(x[0]) || x[0] == '-' && x.size()>1)//当前为数字,数字分为正负,如果是-的话,要判断当前字符长度,不然可能和符号-混淆
{
s.push(stoi(x));//如果是数字,则push到栈中
}
else//如果是符号,则将栈中的前两个元素拿出来,计算完后,将结果再插入栈中
{
int a = s.top();
s.pop();
int b = s.top();
s.pop();
if(x=="+") s.push(a+b);
else if(x == "-") s.push(b-a);
else if(x == "*") s.push(a*b);
else s.push(b/a);
}
}
return s.top();
}
};
131.向下路径节点之和(深度优先遍历+前缀和)
class Solution {
public:
unordered_map<int,int> m;
int dfs(TreeNode* root,int cur,int targetSum)
{
if(root == nullptr) return 0;
int ret = 0;
cur+=root->val;
if(m.count(cur-targetSum))
{
ret+=m[cur-targetSum];
}
m[cur]++;
ret+=dfs(root->left,cur,targetSum);
ret+=dfs(root->right,cur,targetSum);
m[cur]--;
return ret;
}
int pathSum(TreeNode* root, int targetSum) {
m[0] = 1;//前缀和为0的路径有1条
return dfs(root,0,targetSum);
}
};
132.按字典序排在最后的子串(双指针)
class Solution {
public:
string lastSubstring(string s) {
if(s.empty()) return "";
int left = 0;
int right = 1;
int k = 0;//偏移,对于相同的字符,则更长的字典序更大,而我们通过移动偏移k来判断当前的两个字符是否相等。
int len = s.size();
while(right+k<len)
{
if(s[left+k] == s[right+k]) k++;
else if(s[left]<s[right+k])
{
left = right+k;
right = left+1;
k = 0;
}
//len1 = right+k-left
//len2 = right+k-(left+k) = right-left
//len1 > len2
//所以先判断s[left] < s[right+k]
//再判断s[left+k] < s[right+k]
else if(s[left+k] < s[right+k])
{
left = right;
right++;
k=0;
}
else{
right++;
k=0;
}
}
return s.substr(left);
}
};
133.删除有序数组中的重复项(双指针)
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int n = nums.size();
if(n<=2) return n;
int slow = 2;
int fast = 2;
while(fast<n)
{
if(nums[slow-2] != nums[fast])
{
nums[slow++] = nums[fast];
}
fast++;
}
return slow;
}
};
134.所有子字符串美丽值之和
class Solution {
public:
int beautySum(string s) {
if(s.empty()) return 0;
int sum = 0;
int len = s.size();
for(int i=0;i<len;++i)
{
int nums[26] = {0};
for(int j=i;j<len;++j)
{
int Max = INT_MIN;
int Min = INT_MAX;
nums[s[j]-'a']++;
for(auto& x:nums)
{
if(x!=0)
{
Max = max(Max,x);
Min = min(Min,x);
}
}
sum+=(Max-Min);
}
}
return sum;
}
};
135.乘积为正数的最长子数组长度(动态规划)
class Solution {
public:
int getMaxLen(vector<int>& nums) {
int m = nums.size();
vector<int> positive(m);
vector<int> negative(m);
if(nums[0]>0) positive[0] = 1;
else if(nums[0]<0) negative[0] = 1;
int maxlen = positive[0];
for(int i=1;i<m;++i)
{
if(nums[i]>0)
{
positive[i] = positive[i-1]+1;
negative[i] = (negative[i-1]>0?negative[i-1]+1:0);
}
else if(nums[i]<0)
{
positive[i] = (negative[i-1]>0?negative[i-1]+1:0);
negative[i] = positive[i-1]+1;
}
else{
positive[i] = 0;
negative[i] = 0;
}
maxlen = max(maxlen,positive[i]);
}
return maxlen;
}
};
136.有效的完全平方数
class Solution {
public:
bool isPerfectSquare(int num) {
if(num == 0) return false;
int left = 1;
int right = num;
while(left<=right)
{
int mid = ((right-left)>>1)+left;
if((long long)mid*mid>num) right = mid-1;
else left = mid+1;
}
return right*right == num;
}
};
137.多次搜索
class Solution {
public:
struct TrieNode
{
int index;
vector<TrieNode*> child;
TrieNode():child(26),index(-1){}
};
TrieNode* root = new TrieNode();
void Delete(TrieNode* node)
{
if(node == nullptr) return ;
for(int i=0;i<26;++i)
{
Delete(node->child[i]);
node->child[i] = nullptr;
}
delete node;
}
void insert(string word,int s)
{
TrieNode* cur = root;
for(auto& ch:word)
{
ch-='a';
if(cur->child[ch] == nullptr)
{
cur->child[ch] = new TrieNode();
}
cur = cur->child[ch];
}
cur->index = s;
}
void search(string word,vector<vector<int>>& ans,int s)
{
TrieNode* cur = root;
for(auto& ch:word)
{
ch-='a';
if(cur->index != -1)
{
ans[cur->index].push_back(s);
}
if(cur->child[ch] == nullptr) return;
cur = cur->child[ch];
}
if(cur->index != -1) ans[cur->index].push_back(s);
}
vector<vector<int>> multiSearch(string big, vector<string>& smalls) {
int m = big.size();
int n = smalls.size();
vector<vector<int>> ans(n,vector<int>{});
for(int i=0;i<n;++i)
{
if(smalls[i].size() == 0) continue;
insert(smalls[i],i);
}
for(int i=0;i<m;++i)
{
string word = big.substr(i,m-i);
search(word,ans,i);
}
Delete(root);
return ans;
}
};
138.复原IP地址
class Solution {
private:
vector<string> ans;
vector<int> segments;
public:
void dfs(const string& s, int segId, int segStart) {
// 如果找到了 4 段 IP 地址并且遍历完了字符串,那么就是一种答案
if (segId == 4) {
if (segStart == s.size()) {
string ipAddr;
for (int i = 0; i < 4; ++i) {
ipAddr += to_string(segments[i]);
if (i != 3) {
ipAddr += ".";
}
}
ans.push_back(move(ipAddr));
}
return;
}
// 如果还没有找到 4 段 IP 地址就已经遍历完了字符串,那么提前回溯
if (segStart == s.size()) {
return;
}
// 由于不能有前导零,如果当前数字为 0,那么这一段 IP 地址只能为 0
if (s[segStart] == '0') {
segments[segId] = 0;
dfs(s, segId + 1, segStart + 1);
}
// 一般情况,枚举每一种可能性并递归
int addr = 0;
for (int segEnd = segStart; segEnd < s.size(); ++segEnd) {
addr = addr * 10 + (s[segEnd] - '0');
if (addr > 0 && addr <= 0xFF) {
segments[segId] = addr;
dfs(s, segId + 1, segEnd + 1);
} else {
break;
}
}
}
vector<string> restoreIpAddresses(string s) {
segments.resize(4);
dfs(s, 0, 0);
return ans;
}
};
139.范围求和2
class Solution {
public:
int maxCount(int m, int n, vector<vector<int>>& ops) {
int mina = m;
int minb = n;
for(auto& x:ops)
{
mina = min(mina,x[0]);
minb = min(minb,x[1]);
}
return mina*minb;
}
};
140.把数字翻译成字符串(动态规划)
class Solution {
public:
int translateNum(int num) {
string src = to_string(num);
int n = src.size();
vector<int> dp(n+1);
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i <= n; ++i) {
auto pre = src.substr(i - 2, 2);
if (pre <= "25" && pre >= "10") {
dp[i] = dp[i-1]+dp[i-2];
}
else
{
dp[i] = dp[i-1];
}
}
return dp[n];
}
};
141.
int func(int n, int m)
{
vector<vector<int>> dp(122,vector<int>(122,0));
//dp[i][j]代表i个数(从1-i)和是j的组合数
dp[0][0] = 1;
for(int k=1;k<=m;++k)
dp[0][k] = 0;
for(int k=1;k<=n;++k)
dp[k][0] = 1;
for(int j=1;j<=m;++j)
for(int i=1;i<=n;++i)
if(j>=i)
dp[i][j] = dp[i-1][j] + dp[i-1][j-i];
else
dp[i][j] = dp[i-1][j];
return dp[n][m];
}
142.栈的压入、弹出序列
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
if(pushed.empty() || popped.empty()) return true;
stack<int> s;
int index = 0;
for(auto& x:pushed)
{
s.push(x);
while(!s.empty() && s.top() == popped[index])
{
s.pop();
index++;
}
}
return s.empty();
}
};
143.字符串的排列
class Solution {
public:
vector<string> permutation(string s) {
vector<string> res;
dfs(res,s,0);
return res;
}
void dfs(vector<string> &res,string &s,int pos){
if(pos == s.size())
res.push_back(s);
for(int i=pos;i<s.size();i++){
bool flag = true;
for(int j = pos;j<i;j++)//字母相同时,等效,剪枝
if(s[j] == s[i])
flag = false;
if(flag == true){
swap(s[pos],s[i]);
dfs(res,s,pos+1);
swap(s[pos],s[i]);
}
}
}
};
144.扑克牌中的顺子
class Solution {
public:
bool isStraight(vector<int>& nums) {
if(nums.empty()) return false;
sort(nums.begin(),nums.end());
int temp = 0;
for(int i=1;i<5;++i)
{
if(nums[i-1] == 0) continue;
if(nums[i] == nums[i-1]) return false;
temp+=nums[i]-nums[i-1];
}
return temp<5;
}
};
145.和为s的连续正数序列
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
vector<vector<int>> ans;
vector<int> temp;
for(int left = 1,right = 2;left < right;)
{
int sum = (right-left+1)*(left+right)/2;
/*
等差数列求和公式:
Sn = n*a1+n(n-1)d/2
Sn = n(a1+an)/2;
此题中,n = (right-left+1) a1 = left an = right
*/
if(sum == target)
{
temp.clear();
for(int i=left;i<=right;++i)
{
temp.push_back(i);
}
ans.push_back(temp);
++left;
}
else if(sum < target) right++;
else left++;
}
return ans;
}
};
146.剪绳子2
class Solution {
public:
int cuttingRope(int n) {
if(n == 2) return 1;
if(n == 3) return 2;
if(n == 4) return 4;
long ans = 1;
while(n>4)
{
ans*=3;
n-=3;
ans%=1000000007;
}
//循环结束后,绳子的长度还剩下n,此时n=1,2,3,4
return (ans*n)%1000000007;
}
};
147.猜数字大小2(动态规划)
class Solution {
public:
int getMoneyAmount(int n) {
vector<vector<int>> dp(n+1,vector<int>(n+1));
for(int i=n-1;i>=1;--i)
{
for(int j=i+1;j<=n;++j)
{
int cur = INT_MAX;
for(int k=i;k<j;++k)
{
cur = min(cur,k+max(dp[i][k-1],dp[k+1][j]));
/*
k为i~j中我们每次猜测的数字
即 i<=k<j (如果是i<=k<=j 那么可能会出现i=j的情况,说明此时只有一个数字,
所选数字一定是范围内的唯一的数字,不存在猜错的情况)
*/
}
dp[i][j] = cur;
}
}
return dp[1][n];
}
};
148.数组中的逆序对(归并排序解法)
class Solution {
public:
int ans;
int reversePairs(vector<int>& nums) {
ans = 0;
if(nums.empty()) return 0;
vector<int> temp(nums.size());
MergeSort(nums,temp,0,nums.size()-1);
return ans;
}
void MergeSort(vector<int>& nums,vector<int>& temp,int left,int right)
{
if(left < right)
{
int mid = (right-left)/2+left;
MergeSort(nums,temp,left,mid);
MergeSort(nums,temp,mid+1,right);
Merge(nums,temp,left,right);
}
}
void Merge(vector<int>& nums,vector<int>& temp,int left,int right)
{
int mid = (right-left)/2+left;
int i = left;
int j = mid+1;
int index = 0;
while(i<=mid && j<=right)
{
if(nums[i] <= nums[j])
{
temp[index++] = nums[i++];
}
else
{
ans+=(mid-i+1);
temp[index++] = nums[j++];
}
}
while(i<=mid)
{
temp[index++] = nums[i++];
}
while(j<=right)
{
temp[index++] = nums[j++];
}
index = 0;
while(left<=right)
{
nums[left++] = temp[index++];
}
}
};
149.构建乘积数组
class Solution {
public:
vector<int> constructArr(vector<int>& a) {
int n = a.size();
vector<int> ans(n);
int sum = 1;
for(int i=0;i<n;++i)
{
ans[i] = sum;// 先乘左边的数(不包括自己)
sum*=a[i];
}
sum=1;
for(int i=n-1;i>=0;--i)
{
ans[i]*=sum;// 再乘右边的数(不包括自己)
sum*=a[i];
}
return ans;
}
};
150.和为s的连续正数序列
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
vector<vector<int>> ans;
vector<int> temp;
for(int left = 1,right = 2;left < right;)
{
int sum = (right-left+1)*(left+right)/2;
/*
等差数列求和公式:
Sn = n*a1+n(n-1)d/2
Sn = n(a1+an)/2;
此题中,n = (right-left+1) a1 = left an = right
*/
if(sum == target)
{
temp.clear();
for(int i=left;i<=right;++i)
{
temp.push_back(i);
}
ans.push_back(temp);
++left;
}
else if(sum < target) right++;
else left++;
}
return ans;
}
};
151.扑克牌中的顺子
class Solution {
public:
bool isStraight(vector<int>& nums) {
if(nums.empty()) return false;
sort(nums.begin(),nums.end());
int temp = 0;
for(int i=1;i<5;++i)
{
if(nums[i-1] == 0) continue;
if(nums[i] == nums[i-1]) return false;
temp+=nums[i]-nums[i-1];
}
return temp<5;
}
};
152.字符串的排列
class Solution {
public:
vector<string> permutation(string s) {
vector<string> res;
dfs(res,s,0);
return res;
}
void dfs(vector<string> &res,string &s,int pos){
if(pos == s.size())
res.push_back(s);
for(int i=pos;i<s.size();i++){
bool flag = true;
for(int j = pos;j<i;j++)//字母相同时,等效,剪枝
if(s[j] == s[i])
flag = false;
if(flag == true){
swap(s[pos],s[i]);
dfs(res,s,pos+1);
swap(s[pos],s[i]);
}
}
}
};
153.栈的压入、弹出序列
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
if(pushed.empty() || popped.empty()) return true;
stack<int> s;
int index = 0;
for(auto& x:pushed)
{
s.push(x);
while(!s.empty() && s.top() == popped[index])
{
s.pop();
index++;
}
}
return s.empty();
}
};
154.范围求和2
class Solution {
public:
int maxCount(int m, int n, vector<vector<int>>& ops) {
int mina = m;
int minb = n;
for(auto& x:ops)
{
mina = min(mina,x[0]);
minb = min(minb,x[1]);
}
return mina*minb;
}
};
155.键值映射
struct TrieNode {
int val;
TrieNode * next[26];
TrieNode() {
this->val = 0;
for (int i = 0; i < 26; ++i) {
this->next[i] = nullptr;
}
}
};
class MapSum {
public:
MapSum() {
this->root = new TrieNode();
}
void insert(string key, int val) {
int delta = val;
if (cnt.count(key)) {
delta -= cnt[key];
}
cnt[key] = val;
TrieNode * node = root;
for (auto c : key) {
if (node->next[c - 'a'] == nullptr) {
node->next[c - 'a'] = new TrieNode();
}
node = node->next[c - 'a'];
node->val += delta;
}
}
int sum(string prefix) {
TrieNode * node = root;
for (auto c : prefix) {
if (node->next[c - 'a'] == nullptr) {
return 0;
} else {
node = node->next[c - 'a'];
}
}
return node->val;
}
private:
TrieNode * root;
unordered_map<string, int> cnt;
};
class MapSum {
public:
unordered_map<string,int> m;
MapSum() {
}
void insert(string key, int val) {
m[key] = val;
}
int sum(string prefix) {
int ans = 0;
for(auto& it:m)
{
if(it.first.find(prefix) == 0)
{
ans+=it.second;
}
}
return ans;
}
};
/**
* Your MapSum object will be instantiated and called as such:
* MapSum* obj = new MapSum();
* obj->insert(key,val);
* int param_2 = obj->sum(prefix);
*/
156.二叉搜索树的后序遍历序列
class Solution {
public:
bool verifyPostorder(vector<int>& postorder) {
return func(postorder,0,postorder.size()-1);
}
bool func(vector<int>& postorder,int left,int right)
{
if(left >= right) return true;
int index = left;
while(postorder[index] < postorder[right] && index < right) ++index;
for(int i=index;i<right;++i)
{
if(postorder[i] < postorder[right]) return false; 进行判断后续的区域是否所有的值都是大于当前的根节点,如果出现小于的值就直接返回false
}
return func(postorder,left,index-1) && func(postorder,index,right-1);
}
};
157.山峰数组的顶部
class Solution {
public:
int peakIndexInMountainArray(vector<int>& arr) {
int n = arr.size();
int left = 1;
int right = n-2;
while(left <= right)
{
int mid = (left+right)/2;
if(arr[mid] > arr[mid-1])
{
left = mid+1;
}
else
{
right = mid-1;
}
}
return right;
}
};
普通方法找最大值
class Solution {
public:
int peakIndexInMountainArray(vector<int>& arr) {
int ans = 0;
int max = INT_MIN;
for(int i=0;i<arr.size();++i)
{
if(arr[i] > max)
{
max = arr[i];
ans = i;
}
}
return ans;
}
};
158.二叉树的坡度
class Solution {
public:
int ans;
int findTilt(TreeNode* root) {
ans = 0;
dfs(root);
return ans;
}
int dfs(TreeNode* root)
{
if(root == nullptr) return 0;
int leftsum = dfs(root->left);
int rightsum = dfs(root->right);
ans+=abs(leftsum-rightsum);
return leftsum+rightsum+root->val;
}
};
159.解码方式(动态规划)
class Solution {
public:
int numDecodings(string s) {
int n = s.size();
if(n == 0) return 0;
vector<int> dp(n+1);
dp[0] = 1;
for(int i=1;i<=n;++i)
{
if(s[i-1] != '0')
{
dp[i]+=dp[i-1];
}
if(i>1 && s[i-2] != '0' && ((s[i-2]-'0')*10+(s[i-1]-'0') <= 26))
{
dp[i] += dp[i-2];
}
}
return dp[n];
}
};
160.使用最小花费爬楼梯
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n = cost.size();
vector<int> dp(n+1);
dp[0] = 0;
dp[1] = 0;
for(int i=2;i<=n;++i)
{
dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
}
return dp[n];
}
};
161.整数拆分
class Solution {
public:
int integerBreak(int n) {
vector<int> dp(n+1);
for(int i=2;i<=n;++i)
{
int cur = 0;
for(int j=1;j<i;++j)
{
cur = max(cur,max(j*(i-j),j*dp[i-j]));
}
dp[i] = cur;
}
return dp[n];
}
};
162.无重叠区间
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if (intervals.empty()) {
return 0;
}
sort(intervals.begin(), intervals.end(), [](const auto& u, const auto& v) {
return u[0] < v[0];
});
int n = intervals.size();
vector<int> f(n, 1);
for (int i = 1; i < n; ++i) {
for (int j = 0; j < i; ++j) {
if (intervals[j][1] <= intervals[i][0]) {
f[i] = max(f[i], f[j] + 1);
}
}
}
return n - *max_element(f.begin(), f.end());
}
};
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
int n = intervals.size();
if(n == 0) return 0;
sort(intervals.begin(),intervals.end(),[](const auto& a,const auto& b)
{
return a[1] < b[1];
});
int ans = 1;
int right = intervals[0][1];
for(int i=1;i<n;++i)
{
if(intervals[i][0] >= right)
{
ans++;
right = intervals[i][1];
}
}
return n-ans;
}
};
163.目标和
class Solution {
public:
int ans;
int findTargetSumWays(vector<int>& nums, int target) {
ans = 0;
dfs(nums,target,0,0);
return ans;
}
void dfs(vector<int>& nums,int target,int index,int sum)
{
if(index == nums.size())
{
if(sum == target)
{
ans++;
}
}
else{
dfs(nums,target,index+1,sum+nums[index]);
dfs(nums,target,index+1,sum-nums[index]);
}
}
};
164.数组中的最长山脉
class Solution {
public:
int longestMountain(vector<int>& arr) {
int n = arr.size();
int ans = 0;
int left = 0;
while(left+2<n)
{
int right = left+1;
if(arr[left]<arr[left+1])
{
while(right+1<n && arr[right]<arr[right+1])
{
++right;
}//此时right代表了当前这座山的山峰
if(right<n-1 && arr[right]>arr[right+1])
{
while(right+1 < n && arr[right] > arr[right+1])
{
++right;
}//此时right代表了当前这座山的右山脚
ans = max(ans,right-left+1);
}
else//arr[right] == arr[right+1];
{
++right;
}
}
left = right;
}
return ans;
}
};
165.摆动序列
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int up = 1;
int down = 1;
int n = nums.size();
for(int i=1;i<n;++i)
{
if(nums[i]>nums[i-1])
{
up = down+1;
}
else if(nums[i] < nums[i-1])
{
down = up+1;
}
}
return n < 2?n:max(up,down);
}
};
166.二叉树的坡度
class Solution {
public:
int ans;
int findTilt(TreeNode* root) {
ans = 0;
dfs(root);
return ans;
}
int dfs(TreeNode* root)
{
if(root == nullptr) return 0;
int leftsum = dfs(root->left);
int rightsum = dfs(root->right);
ans+=abs(leftsum-rightsum);
return leftsum+rightsum+root->val;
}
};
167.后继者
class Solution {
public:
TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
if(root == nullptr || p == nullptr) return nullptr;
if(root->val <= p->val)// 当前节点值小于等于目标值,那么当前目标值的后继者必然在右子树
{
return inorderSuccessor(root->right,p);
}
// 否则结果有可能是当前节点,或者在当前节点的左子树中
// 那么先去左子树找一下试试,找不到的话返回当前节点即是结果
TreeNode* ans = inorderSuccessor(root->left,p);
return ans == nullptr?root:ans;
}
};
168.判定字符是否唯一
class Solution {
public:
bool isUnique(string astr) {
if(astr.empty()) return true;
int mask = 0;
for(auto& ch:astr)
{
int temp = ch-'a';
if(mask & (1<<temp))
{
return false;
}
mask |= (1<<temp);
}
return true;
}
};
169.最大单词长度乘积
class Solution {
public:
int maxProduct(vector<string>& words) {
int len = words.size();
vector<int> mask(len);
for(int i=0;i<len;++i)
{
string word = words[i];
int wordlen = word.size();
for(int j=0;j<wordlen;++j)
{
mask[i] |= 1<<(word[j]-'a');
}
}
int ans = 0;
for(int i=0;i<len;++i)
{
for(int j=i+1;j<len;++j)
{
if((mask[i] & mask[j]) == 0)
{
ans = max(ans,int(words[i].size()*words[j].size()));
}
}
}
return ans;
}
};
170.最长和谐子序列(双指针)
class Solution {
public:
int findLHS(vector<int>& nums) {
sort(nums.begin(),nums.end());
int begin = 0;
int ans = 0;
for(int end = 0;end<nums.size();++end)
{
while(nums[end]-nums[begin]>1)
{
++begin;
}
if(nums[end]-nums[begin] == 1)
{
ans = max(ans,end-begin+1);
}
}
return ans;
}
};
171.硬币
class Solution {
public:
int waysToChange(int n) {
int coins[] = {1, 5, 10, 25};
vector<int> dp(n+1);
dp[0] = 1;
for(int &coin : coins){
for(int i = coin; i <= n; i++){
dp[i] = (dp[i] + dp[i - coin]) % 1000000007;
}
}
return dp[n];
}
};
172.最大子矩阵
这要从最大子序和说起,由简到繁 - 最大子矩阵 - 力扣(LeetCode) (leetcode-cn.com)
class Solution {
public:
vector<int> getMaxMatrix(vector<vector<int>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
int max = matrix[0][0];
vector<int> ans(4);
vector<vector<int>> presum(m+1,vector<int>(n));
for(int i=1;i<=m;++i)
{
for(int j = 0;j<n;++j)
{
presum[i][j] = presum[i-1][j]+matrix[i-1][j];
}
}
for(int top = 0;top<m;++top)
{
for(int end = top;end<m;++end)
{
vector<int> arr(n);
for(int i=0;i<n;++i)
{
arr[i] = presum[end+1][i]-presum[top][i];
}
int start = 0;
int sum = arr[0];
for(int i=1;i<n;++i)
{
if(sum>0)
{
sum+=arr[i];
}
else
{
sum = arr[i];
start =i;
}
if(sum>max)
{
max = sum;
ans[0] = top;
ans[1] = start;
ans[2] = end;
ans[3] = i;
}
}
}
}
return ans;
}
};
173.迷路的机器人(回溯)
class Solution {
public:
vector<vector<int>> ans;
vector<vector<int>> pathWithObstacles(vector<vector<int>>& obstacleGrid)
{
if(obstacleGrid.empty() || obstacleGrid[0][0] == 1 ||obstacleGrid.back().back() == 1)
{
return {};
}
vector<vector<bool>> visited(obstacleGrid.size(),vector<bool>(obstacleGrid[0].size(),false));
ans.push_back({0,0});
if(dfs(obstacleGrid,visited,0,0))
{
return ans;
}
return {};
}
bool dfs(vector<vector<int>>& obstacleGrid,vector<vector<bool>>& visited,int i,int j)
{
if(i == obstacleGrid.size()-1 && j == obstacleGrid[0].size()-1)
{
return true;
}
if(j+1<obstacleGrid[0].size() && obstacleGrid[i][j+1] != 1 && !visited[i][j+1])
{
ans.push_back({i,j+1});
visited[i][j+1] = true;
if(dfs(obstacleGrid,visited,i,j+1))
{
return true;
}
ans.pop_back();
}
if(i+1<obstacleGrid.size() && obstacleGrid[i+1][j] != 1 && !visited[i+1][j])
{
ans.push_back({i+1,j});
visited[i+1][j] = true;
if(dfs(obstacleGrid,visited,i+1,j))
{
return true;
}
ans.pop_back();
}
return false;
}
};
174.最长单词
class Solution {
public:
unordered_set<string> hset;
// 单词组里面的某个单词可以由另外的两个单词组合而成
string longestWord(vector<string>& words) {
sort(words.begin(), words.end(), [](string &a, string &b){
if(a.size() == b.size()) return a <= b;//如果长度相同则按字典序排
else return a.size() > b.size();//否则按长度降序排列
});
vector<string> ans;
for(auto w:words) hset.insert(w);//先把所有单词放到hash里
for(auto w:words) {
hset.erase(w);
if(Check(w)) return w;
hset.insert(w);
}
return "";
}
bool Check(string word) {
if(hset.count(word)) return true;//说明单词word在words中不止一个
// 长度
for(int i = 1; i < word.size(); i++) {
string left = word.substr(0, i);
string right = word.substr(i, word.size() - left.size());
if(hset.count(left) && Check(right)) return true;
}
return false;
}
};
175.交换和
class Solution {
public:
vector<int> findSwapValues(vector<int>& array1, vector<int>& array2) {
int sum1 = 0;
int sum2 = 0;
for(int x:array1)
{
sum1+=x;
}
for(int x:array2)
{
sum2+=x;
}
int dif = sum1-sum2;
if((dif & 1) == 1)//如果是奇数
{
return {};
}
dif/=2;
unordered_set<int> s(array2.begin(),array2.end());
for(int x:array1)
{
if(s.count(x-dif))
{
return {x,x-dif};
}
}
return {};
}
};
176.求和路径
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int ans = 0;
int pathSum(TreeNode* root, int sum) {
if(root == nullptr)
{
return 0;
}
dfs(root,sum);
pathSum(root->left,sum);
pathSum(root->right,sum);
return ans;
}
void dfs(TreeNode* root,int sum)
{
if(root == nullptr)
{
return;
}
sum-=root->val;
if(sum == 0)
{
ans++;
}
dfs(root->left,sum);
dfs(root->right,sum);
}
};
177.最小差
class Solution {
public:
int smallestDifference(vector<int>& a, vector<int>& b) {
int m=a.size(),n=b.size();
sort(a.begin(),a.end());
sort(b.begin(),b.end());
long res=INT_MAX;
int i=0,j=0;
while(i<m&&j<n){
res=min(res,abs(long(a[i])-long(b[j])));
if(a[i]<b[j]){
i++;
}
else{
j++;
}
}
return res;
}
};
178.回复空格
class Solution {
public:
int respace(vector<string>& dictionary, string sentence) {
int sz = sentence.size();
vector<int> dp(sz + 1, 0); dp[0] = 0;
for (int i = 1; i <= sz; i++) {
dp[i] = dp[i - 1] + 1;
for (auto& p : dictionary) {
int pz = p.size();
if (i >= p.size()) {
string substr = sentence.substr(i - pz, pz);
if (substr == p) dp[i] = min(dp[i], dp[i - pz]);
else dp[i] = min(dp[i], dp[i - pz] + pz);
}
}
}
return dp[sz];
}
};
179.统计封闭岛屿的数目
class Solution {
public:
int closedIsland(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
for(int i = 0;i<m;++i)
{
dfs(grid,i,0);// 把靠左边的岛屿淹掉
dfs(grid,i,n-1);// 把靠右边的岛屿淹掉
}
for(int j=0;j<n;++j)
{
dfs(grid,0,j);// 把靠上边的岛屿淹掉
dfs(grid,m-1,j);// 把靠下边的岛屿淹掉
}
int ans = 0;
for(int i=0;i<m;++i)
{
for(int j=0;j<n;++j)
{
if(grid[i][j] == 0)
{
ans++;
dfs(grid,i,j);
}
}
}
return ans;
}
void dfs(vector<vector<int>>& grid,int i,int j)
{
if(i<0 || j<0 || i>=grid.size() || j>=grid[0].size() || grid[i][j] == 1)
{
return;
}
grid[i][j] = 1;
dfs(grid,i+1,j);
dfs(grid,i-1,j);
dfs(grid,i,j+1);
dfs(grid,i,j-1);
}
};
180.统计子岛屿
什么情况下grid2
中的一个岛屿B
是grid1
中的一个岛屿A
的子岛?
当岛屿B
中所有陆地在岛屿A
中也是陆地的时候,岛屿B
是岛屿A
的子岛。
反过来说,如果岛屿B
中存在一片陆地,在岛屿A
的对应位置是海水,那么岛屿B
就不是岛屿A
的子岛。
那么,我们只要遍历grid2
中的所有岛屿,把那些不可能是子岛的岛屿排除掉,剩下的就是子岛。
class Solution {
public:
int countSubIslands(vector<vector<int>>& grid1, vector<vector<int>>& grid2) {
int m = grid1.size();
int n = grid1[0].size();
for(int i=0;i<m;++i)
{
for(int j=0;j<n;++j)
{
if(grid1[i][j] == 0 && grid2[i][j] == 1)
{
dfs(grid2,i,j);
}
}
}
int ans = 0;
for(int i=0;i<m;++i)
{
for(int j = 0;j<n;++j)
{
if(grid2[i][j] == 1)
{
ans++;
dfs(grid2,i,j);
}
}
}
return ans;
}
void dfs(vector<vector<int>>& grid,int i,int j)
{
if(i<0 || j<0 || i>=grid.size() || j>=grid[0].size() || grid[i][j] == 0)
{
return ;
}
grid[i][j] = 0;
dfs(grid,i+1,j);
dfs(grid,i-1,j);
dfs(grid,i,j+1);
dfs(grid,i,j-1);
}
};
181.岛屿的周长
计算出总的岛屿数量,因为有一对相邻两个陆地,边的总数就减2,那么在计算出相邻岛屿的数量就可以了。
class Solution {
public:
int islandPerimeter(vector<vector<int>>& grid) {
int sum = 0; // 陆地数量
int cover = 0; // 相邻数量
for (int i = 0; i < grid.size(); i++) {
for (int j = 0; j < grid[0].size(); j++) {
if (grid[i][j] == 1) {
sum++;
// 统计上边相邻陆地
if(i - 1 >= 0 && grid[i - 1][j] == 1) cover++;
// 统计左边相邻陆地
if(j - 1 >= 0 && grid[i][j - 1] == 1) cover++;
// 为什么没统计下边和右边? 因为避免重复计算
//因为我们遍历的方式是从上到下,从左到右进行的
}
}
}
return sum * 4 - cover * 2;
}
};
182.不同的岛屿数量
比如题目输入下面这个二维矩阵:
其中有四个岛屿,但是左下角和右上角的岛屿形状相同,所以不同的岛屿共有三个,算法返回 3。
很显然我们得想办法把二维矩阵中的「岛屿」进行转化,变成比如字符串这样的类型,然后利用 HashSet 这样的数据结构去重,最终得到不同的岛屿的个数。
如果想把岛屿转化成字符串,说白了就是序列化,序列化说白了遍历嘛。
首先,对于形状相同的岛屿,如果从同一起点出发,dfs
函数遍历的顺序肯定是一样的。
所以,遍历顺序从某种意义上说就可以用来描述岛屿的形状,比如下图这两个岛屿:
假设它们的遍历顺序是:
下,右,上,撤销上,撤销右,撤销下
如果我用分别用1, 2, 3, 4
代表上下左右,用-1, -2, -3, -4
代表上下左右的撤销,那么可以这样表示它们的遍历顺序:
2, 4, 1, -1, -4, -2
你看,这就相当于是岛屿序列化的结果,只要每次使用dfs
遍历岛屿的时候生成这串数字进行比较,就可以计算到底有多少个不同的岛屿了。
要想生成这段数字,需要稍微改造dfs
函数,添加一些函数参数以便记录遍历顺序:
void dfs(int[][] grid, int i, int j, StringBuilder sb, int dir) {
int m = grid.length, n = grid[0].length;
if (i < 0 || j < 0 || i >= m || j >= n
|| grid[i][j] == 0) {
return;
}
// 前序遍历位置:进入 (i, j)
grid[i][j] = 0;
sb.append(dir).append(',');
dfs(grid, i - 1, j, sb, 1); // 上
dfs(grid, i + 1, j, sb, 2); // 下
dfs(grid, i, j - 1, sb, 3); // 左
dfs(grid, i, j + 1, sb, 4); // 右
// 后序遍历位置:离开 (i, j)
sb.append(-dir).append(',');
}
dir
记录方向,dfs
函数递归结束后,sb
记录着整个遍历顺序,其实这就是回溯算法框架,你看到头来这些算法都是相通的。
有了这个dfs
函数就好办了,我们可以直接写出最后的解法代码:
int numDistinctIslands(int[][] grid) {
int m = grid.length, n = grid[0].length;
// 记录所有岛屿的序列化结果
HashSet<String> islands = new HashSet<>();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1) {
// 淹掉这个岛屿,同时存储岛屿的序列化结果
StringBuilder sb = new StringBuilder();
// 初始的方向可以随便写,不影响正确性
dfs(grid, i, j, sb, 666);
islands.add(sb.toString());
}
}
}
// 不相同的岛屿数量
return islands.size();
}
183.分发糖果
这道题目一定是要确定一边之后,再确定另一边,例如比较每一个孩子的左边,然后再比较右边,如果两边一起考虑一定会顾此失彼。
先确定右边评分大于左边的情况(也就是从前向后遍历)
此时局部最优:只要右边评分比左边大,右边的孩子就多一个糖果,全局最优:相邻的孩子中,评分高的右孩子获得比左边孩子更多的糖果
局部最优可以推出全局最优。
如果ratings[i] > ratings[i - 1] 那么[i]的糖 一定要比[i - 1]的糖多一个,所以贪心:candyVec[i] = candyVec[i - 1] + 1
再确定左孩子大于右孩子的情况(从后向前遍历)
遍历顺序这里有同学可能会有疑问,为什么不能从前向后遍历呢?
因为如果从前向后遍历,根据 ratings[i + 1] 来确定 ratings[i] 对应的糖果,那么每次都不能利用上前一次的比较结果了。
所以确定左孩子大于右孩子的情况一定要从后向前遍历!
如果 ratings[i] > ratings[i + 1],此时candyVec[i](第i个小孩的糖果数量)就有两个选择了,一个是candyVec[i + 1] + 1(从右边这个加1得到的糖果数量),一个是candyVec[i](之前比较右孩子大于左孩子得到的糖果数量)。
那么又要贪心了,局部最优:取candyVec[i + 1] + 1 和 candyVec[i] 最大的糖果数量,保证第i个小孩的糖果数量即大于左边的也大于右边的。全局最优:相邻的孩子中,评分高的孩子获得更多的糖果。
局部最优可以推出全局最优。
所以就取candyVec[i + 1] + 1 和 candyVec[i] 最大的糖果数量,candyVec[i]只有取最大的才能既保持对左边candyVec[i - 1]的糖果多,也比右边candyVec[i + 1]的糖果多。
class Solution {
public:
int candy(vector<int>& ratings) {
vector<int> candyVec(ratings.size(), 1);
// 从前向后
for (int i = 1; i < ratings.size(); i++) {
if (ratings[i] > ratings[i - 1]) candyVec[i] = candyVec[i - 1] + 1;
}
// 从后向前
for (int i = ratings.size() - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1] ) {
candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1);
}
}
// 统计结果
int result = 0;
for (int x:candyVec) result += x;
return result;
}
};
184.解数独
class Solution {
public:
void solveSudoku(vector<vector<char>>& board) {
if(!board.empty())
{
backtrack(board,0,0);
}
}
bool backtrack(vector<vector<char>>& board, int i, int j)
{
int m = 9, n = 9;
if (j == n) {
// 穷举到最后一列的话就换到下一行重新开始。
return backtrack(board, i + 1, 0);
}
if (i == m) {
// 找到一个可行解,触发 base case
return true;
}
if (board[i][j] != '.') {
// 如果有预设数字,不用我们穷举
return backtrack(board, i, j + 1);
}
for (char ch = '1'; ch <= '9'; ch++) {
// 如果遇到不合法的数字,就跳过
if (!isValid(board, i, j, ch))
continue;
board[i][j] = ch;
// 如果找到一个可行解,立即结束
if (backtrack(board, i, j + 1)) {
return true;
}
board[i][j] = '.';
}
// 穷举完 1~9,依然没有找到可行解,此路不通
return false;
}
bool isValid(vector<vector<char>>&board, int r, int c, char n)
{
for (int i = 0; i < 9; i++) {
// 判断行是否存在重复
if (board[r][i] == n) return false;
// 判断列是否存在重复
if (board[i][c] == n) return false;
// 判断 3 x 3 方框是否存在重复
if (board[(r/3)*3 + i/3][(c/3)*3 + i%3] == n)
return false;
}
return true;
}
};
185.可怜的小猪
本质上在考察数学中的 进制 问题
换底公式:
class Solution {
public:
int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
int base = minutesToTest / minutesToDie + 1;
int ans = ceil(log(buckets) / log(base));//log2 buckets / log2 base = log base buckets
return ans;
}
};
186.找到字符串中所有字母的异位词
class Solution {
public:
//滑动窗口
vector<int> findAnagrams(string s, string p) {
if(s.size() < p.size()) return {};
vector<int> cnt_s(26);
vector<int> cnt_p(26);
vector<int> ans;
int left = 0;//滑动窗口左边界
int right = -1;//滑动窗口右边界
for(char& ch:p)
{
cnt_p[ch-'a']++;
cnt_s[s[++right]-'a']++;
}
if(cnt_p == cnt_s)
{
ans.emplace_back(left);
}
while(right < s.size()-1)
{
cnt_s[s[++right]-'a']++;//滑动窗口右边界右移,对应的新的右边界的字符个数加一
cnt_s[s[left++]-'a']--;//滑动窗口左边界右移,对应的之前的左边界的字符个数减一
if(cnt_s == cnt_p)//如果现在两个串的字符个数相等,则当前子串符合条件
{
ans.emplace_back(left);
}
}
return ans;
}
/*
在方法一的基础上,我们不再分别统计滑动窗口和字符串 p 中每种字母的数量,而是统计滑动窗口和字符串p 中 每种字母数量的差;并引入变量 differ 来记录当前窗口与字符串 pp 中数量不同的字母的个数,并在滑动窗口的过程中维护它。
在判断滑动窗口中每种字母的数量与字符串 p 中每种字母的数量是否相同时,只需要判断 differ 是否为p的长度即可。
*/
vector<int> findAnagrams_(string s, string p) {
int m = s.size();
int n = p.size();
if(m<n) return {};
vector<int> cnt(26);
vector<int> ans;
for(char& ch:p)
{
cnt[ch-'a']++;
}
int left = 0;
int right = 0;
while(right<m)
{
if(cnt[s[right]-'a']>0)
{
cnt[s[right++]-'a']--;
if(right - left == n) ans.emplace_back(left);//判断differ
}
else{
cnt[s[left++]-'a']++;
}
}
return ans;
}
};