目录
- 剑指Offer II 012. 左右两边子数组的和相等
- 剑指Offer II 015. 字符串中的所有变位词
- 剑指Offer II 018. 有效的回文
- 剑指Offer II 023. 两个链表的第一个重合节点
- 剑指Offer II 033. 变位词组
- 剑指Offer II 006. 排序数组中两个数字之和
- 剑指Offer II 014. 字符串中的变位词
- 剑指Offer II 074. 合并区间
- 剑指Offer II 095. 最长公共子序列
- 剑指Offer II 013. 二维子矩阵的和
- 剑指Offer II 026. 重排链表
- 剑指Offer II 007. 数组中和为 0 的三个数
- 剑指Offer II 027. 回文链表
- 剑指Offer II 035. 最小时间差
剑指Offer II 012. 左右两边子数组的和相等
分析:
前缀和:对于下标i,其左半部分之和为pre[i] - nums[i],右半部分之和为pre[n - 1] - pre[i]。
代码:
class Solution {
public:
int pivotIndex(vector<int>& nums) {
int n = nums.size();
vector<int> pre(n);
pre[0] = nums[0];
for(int i = 1; i < n; i++) {
pre[i] = nums[i] + pre[i - 1];
}
for(int i = 0; i < n; i++) {
if(pre[i] - nums[i] == pre[n - 1] - pre[i]) {
return i;
}
}
return -1;
}
};
剑指Offer II 015. 字符串中的所有变位词
剑指Offer II 018. 有效的回文
分析:
先按照要求处理字符串,再判断是否是回文串。
代码:
class Solution {
public:
bool isPalindrome(string s) {
string ans;
for(char x : s) {
if(x >= 'A' && x <= 'Z') {
ans.push_back(x + 32);
}else if(x >= 'a' && x <= 'z') {
ans.push_back(x);
}else if(x >= '0' && x <= '9') {
ans.push_back(x);
}else {
continue;
}
}
string res = ans;
reverse(ans.begin(), ans.end());
return res == ans;
}
};
剑指Offer II 023. 两个链表的第一个重合节点
方法一:
集合:先将headA中所有结点存入集合s,再遍历headB:如果headB中某个结点在s中出现,说明这个结点就是第一个重合的结点。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
set<ListNode*> st;
ListNode* ptr = headA;
while(ptr) {
st.insert(ptr);
ptr = ptr->next;
}
ptr = headB;
while(ptr) {
if(st.count(ptr)) {
return ptr;
}
ptr = ptr->next;
}
return NULL;
}
};
方法二:
一次遍历:如果当前p1 == p2说明两个链表中存在同一结点,返回,否则循环遍历(遍历到最后再从头开始遍历),这样即使两个链表长度不同,最终也肯定会遍历到同一个结点。
代码:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* p1 = headA;
ListNode* p2 = headB;
while (p1 != nullptr || p2 != nullptr) {
if (p1 == p2) {
return p1;
}
p1 = (p1 == nullptr) ? headB : p1->next;
p2 = (p2 == nullptr) ? headA : p2->next;
}
return nullptr;
}
};
剑指Offer II 033. 变位词组
分析:
排序+哈希。
代码:
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>> res;
unordered_map<string, vector<string>> mp;
for(string s : strs) {
string t = s;
sort(s.begin(), s.end());
mp[s].push_back(t);
}
for(auto& [x, y] : mp) {
res.push_back(y);
}
return res;
}
};
剑指Offer II 006. 排序数组中两个数字之和
分析:
二分。
代码:
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
vector<int> res;
int n = numbers.size();
int low = 0, high = n - 1;
while(low < high) {
int x = numbers[low] + numbers[high];
if(x == target) {
return {low, high};
}else if(x > target) {
high--;
}else {
low++;
}
}
return res;
}
};
剑指Offer II 014. 字符串中的变位词
分析:
滑动窗口:判断s2当前窗口中各个字母出现的次数是否等于s1中各个字母出现的次数。
代码:
class Solution {
public:
bool checkInclusion(string s1, string s2) {
int n = s1.size(), m = s2.size();
if(m < n) {
return false;
}
vector<int> ans(26);
vector<int> res(26);
for(char x : s1) {
ans[x - 'a']++;
}
for(int i = 0; i < n; i++) {
res[s2[i] - 'a']++;
}
int left = 0, right = n;
for(; right <= m; right++) {
if(ans == res) {
return true;
}
if(right == m) {
break;
}
res[s2[left] - 'a']--;
res[s2[right] - 'a']++;
left++;
}
return false;
}
};
剑指Offer II 074. 合并区间
剑指Offer II 095. 最长公共子序列
分析:
动态规划:设dp[i][j]表示s1前i个字符组成的字符串和s2前j个字符组成的字符串的最长公共子序列的长度,设s1和s2长度分别为n和m,最终返回的即dp[n][m]。
转移方程:
if(text1[i - 1] == text2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
代码:
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int n = text1.size(), m = text2.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1));
// if(text1[0] == text2[0]) {
// dp[1][1] = 1;
// }
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(text1[i - 1] == text2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[n][m];
}
};
剑指Offer II 013. 二维子矩阵的和
分析:
前缀和:求出每一行的前缀和数组,然后按行依次处理。
代码:
class NumMatrix {
public:
vector<vector<int>> prefix; //存储每一行的前缀和
vector<vector<int>> ans;
NumMatrix(vector<vector<int>>& matrix) {
ans = matrix;
int n = matrix.size(), m = matrix[0].size();
prefix = vector<vector<int>>(n, vector<int>(m));
for(int i = 0; i < n; i++) {
prefix[i][0] = matrix[i][0];
}
for(int i = 0; i < n; i++) {
for(int j = 1; j < m; j++) {
prefix[i][j] = prefix[i][j - 1] + matrix[i][j];
}
}
}
int sumRegion(int row1, int col1, int row2, int col2) {
int res = 0;
for(int i = row1; i <= row2; i++) {
res += (prefix[i][col2] - prefix[i][col1] + ans[i][col1]);
}
return res;
}
};
/**
* Your NumMatrix object will be instantiated and called as such:
* NumMatrix* obj = new NumMatrix(matrix);
* int param_1 = obj->sumRegion(row1,col1,row2,col2);
*/
剑指Offer II 026. 重排链表
剑指Offer II 007. 数组中和为 0 的三个数
剑指Offer II 027. 回文链表
剑指Offer II 035. 最小时间差
分析:
将所有时间全部转为分钟数,然后进行排序。由于是按照升序排列,所以要找出最小时间差只需要依次求出相邻时间的时间差再判断即可。
代码:
class Solution {
public:
int h2m(string t) {
string h = t.substr(0, 2), m = t.substr(3, 2);
stringstream ss, rr;
int x, y;
ss << h;
ss >> x;
rr << m;
rr >> y;
return x * 60 + y;
}
int findMinDifference(vector<string>& timePoints) {
vector<int> res;
for(string x : timePoints) {
res.push_back(h2m(x));
}
sort(res.begin(), res.end());
res.push_back(res[0] + 24 * 60);
int ans = INT_MAX;
for(int i = 1; i < res.size(); i++) {
ans = min(ans, res[i] - res[i - 1]);
}
return ans;
}
};