数组
lc.136 只出现过一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
- 位运算
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ret = 0;
for (auto e: nums) ret ^= e;
return ret;
}
};
lc.169 多数元数
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
- 排序后返回最中间的数
class Solution {
public:
int majorityElement(vector<int>& nums) {
sort(nums.begin(), nums.end());
return nums[nums.size() / 2];
}
};
lc.75 颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
sort 肯定可以过。
- 双指针
class Solution {
public:
void sortColors(vector<int>& nums) {
int n = nums.size();
int p0 = 0, p1 = 0;
for (int i = 0; i < n; ++i) {
if (nums[i] == 1) {
swap(nums[i], nums[p1]);
++p1;
} else if (nums[i] == 0) {
swap(nums[i], nums[p0]);
if (p0 < p1) {
swap(nums[i], nums[p1]);
}
++p0;
++p1;
}
}
}
};
交换 0 ,1 。
- 也可以交换 0 ,2 。
class Solution {
public:
void sortColors(vector<int>& nums) {
int n = nums.size();
int p0 = 0, p2 = n - 1;
for (int i = 0; i <= p2; ++i) {
while (i <= p2 && nums[i] == 2) {
swap(nums[i], nums[p2]);
--p2;
}
if (nums[i] == 0) {
swap(nums[i], nums[p0]);
++p0;
}
}
}
};
lc.56 合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
- 一定要先排序···差点用set了
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
vector<vector<int>> ans;
vector<int> tmp;
sort(intervals.begin(),intervals.end());
int l = intervals[0][0] , r = intervals[0][1];
int i = 0;
while(i < intervals.size() - 1){
if(r >= intervals[i + 1][0]){
r = max(r , intervals[i + 1][1]);
}
else{
ans.push_back({l,r});
l = intervals[i + 1][0];
r = intervals[i + 1][1];
}
i ++;
}
ans.push_back({l,r});
return ans;
}
};
为了避免数组越界,最后一组在循环外返回。
ans.push_back({l,r});
emplace_back 只能返回一个单值,要是想返回一组只能用 push_back。
lc.706 设计哈希映射
- 我只能说设计一窍不通。
class MyHashMap {
private:
vector<list<pair<int, int>>> data;
static const int base = 769;
static int hash(int key) {
return key % base;
}
public:
/** Initialize your data structure here. */
MyHashMap(): data(base) {}
/** value will always be non-negative. */
void put(int key, int value) {
int h = hash(key);
for (auto it = data[h].begin(); it != data[h].end(); it++) {
if ((*it).first == key) {
(*it).second = value;
return;
}
}
data[h].push_back(make_pair(key, value));
}
/** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
int get(int key) {
int h = hash(key);
for (auto it = data[h].begin(); it != data[h].end(); it++) {
if ((*it).first == key) {
return (*it).second;
}
}
return -1;
}
/** Removes the mapping of the specified value key if this map contains a mapping for the key */
void remove(int key) {
int h = hash(key);
for (auto it = data[h].begin(); it != data[h].end(); it++) {
if ((*it).first == key) {
data[h].erase(it);
return;
}
}
}
};
lc.119 杨辉三角II
class Solution {
public:
vector<int> getRow(int rowIndex) {
vector<vector<int>> ret(rowIndex+1);
for (int i = 0; i < rowIndex+1; ++i) {
ret[i].resize(i + 1);
ret[i][0] = ret[i][i] = 1;
for (int j = 1; j < i; ++j) {
ret[i][j] = ret[i - 1][j] + ret[i - 1][j - 1];
}
}
return ret[rowIndex];
}
};
lc.48 旋转图像
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
class Solution {
public:
void rnmb(int i , int j , vector<vector<int>>& matrix){
int n = matrix.size();
matrix[i][j] = matrix[n-1-j][i];
return;
}
void change(int a , int b , vector<vector<int>>& matrix){
int tmp = matrix[a][b];
int n = matrix.size();
rnmb(a , b , matrix);
rnmb(n-1-b , a , matrix);
rnmb(n-1-a , n-1-b , matrix);
matrix[b][n-1-a] = tmp;
return;
}
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
for(int i = 0 ; i < n / 2 ; i ++){
for(int j = i ; j < n - i - 1 ; j ++){
change(i , j , matrix);
}
}
return;
}
};
一开始方向转反了,一气之下痛改函数名。
lc.54 螺旋矩阵
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
- 模拟
class Solution {
private:
static constexpr int direction[4][2] = {{0,1}, {1,0}, {0,-1}, {-1,0}};
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if(matrix.size() == 0 || matrix[0].size() == 0) return {};
int m = matrix.size() , n = matrix[0].size();
vector<vector<bool>> isVisited(m , vector<bool>(n));
int len = m * n ;
vector<int> ans(len);
int row = 0 , col = 0 , pre = 0;
for(int i = 0 ; i < len ; i ++){
ans[i] = matrix[row][col];
isVisited[row][col] = true;
int nextrow = row + direction[pre][0] , nextcol = col + direction[pre][1];
if(nextrow < 0 || nextrow >= m || nextcol < 0 || nextcol >= n || isVisited[nextrow][nextcol] == true){
pre = (pre + 1) % 4 ;
}
row += direction[pre][0] , col += direction[pre][1];
}
return ans;
}
};
真的开了一个 bool 数组以记录是否走过,我愿称之为,唯一路径 DFS 。
lc.59 螺旋矩阵 II
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
- 同 lc.54 模拟
class Solution {
private:
static constexpr int direction[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> ans(n , vector<int>(n));
vector<vector<bool>> isVisited(n , vector<bool>(n));
int row = 0 , col = 0 , i = 1 , pre = 0;
int max = n * n ;
while(i <= max){
ans[row][col] = i;
isVisited[row][col] = true;
int nextrow = row + direction[pre][0], nextcol = col + direction[pre][1];
if(nextrow < 0 || nextrow >= n || nextcol < 0 || nextcol >= n || isVisited[nextrow][nextcol]){
pre = (pre + 1) % 4;
}
row += direction[pre][0] , col += direction[pre][1];
i ++;
}
return ans;
}
};
这题倒是用不着 bool
class Solution {
//private:
// static constexpr int direction[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> ans(n , vector<int>(n));
vector<vector<int>> direction = {{0,1},{1,0},{0,-1},{-1,0}};
int row = 0 , col = 0 , i = 1 , pre = 0;
int Max = n * n ;
while(i <= Max){
ans[row][col] = i; i ++;
int nextrow = row + direction[pre][0], nextcol = col + direction[pre][1];
if(nextrow < 0 || nextrow >= n || nextcol < 0 || nextcol >= n || ans[nextrow][nextcol] != 0){
pre = (pre + 1) % 4;
}
row += direction[pre][0] , col += direction[pre][1];
}
return ans;
}
};
lc.334 递增三元子列
class Solution {
public:
bool increasingTriplet(vector<int>& nums) {
int n = nums.size();
if (n < 3) {
return false;
}
vector<int> leftMin(n);
leftMin[0] = nums[0];
for (int i = 1; i < n; i++) {
leftMin[i] = min(leftMin[i - 1], nums[i]);
}
vector<int> rightMax(n);
rightMax[n - 1] = nums[n - 1];
for (int i = n - 2; i >= 0; i--) {
rightMax[i] = max(rightMax[i + 1], nums[i]);
}
for (int i = 1; i < n - 1; i++) {
if (nums[i] > leftMin[i - 1] && nums[i] < rightMax[i + 1]) {
return true;
}
}
return false;
}
};
lc.74 搜索二维矩阵
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
- 两次二分
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size() , n = matrix[0].size() ;
int l = 0 , r = m - 1 ;
while(l < r){
int mid = l + r + 1 >> 1;
if(matrix[mid][0] <= target) l = mid ;
else r = mid - 1;
}
int row = l ;
l = 0 , r = n - 1 ;
while(l < r){
int mid = l + r >> 1;
if(matrix[row][mid] >= target) r = mid;
else l = mid + 1;
}
if(matrix[row][l] == target) return true;
else return false;
}
};
lc.240 搜索二维矩阵 II
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
如果使用二分,就得对每一行都二分一次。O(mlogn)
- 从右上角往左下角搜
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int x = 0 , y = matrix[0].size() - 1 ;
while(x < matrix.size() && y > -1){
if(matrix[x][y] > target) y --;
else if(matrix[x][y] < target) x ++;
else return true;
}
return false;
}
};
如果当前的数小了,那么所在行左侧一定都小,往下一行挪动。
如果当前的数大了,那么往左侧挪动。
O(m+n)
lc.435 无重叠区间
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
可以认为区间的终点总是大于它的起点。
区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
- 贪心
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[1] < v[1];
});
int n = intervals.size();
int right = intervals[0][1];
int ans = 1;
for (int i = 1; i < n; ++i) {
if (intervals[i][0] >= right) {
++ans;
right = intervals[i][1];
}
}
return n - ans;
}
};
对右侧值进行排序,注意到sort(intervals.begin(), intervals.end(), [](const auto& u, const auto& v) { return u[1] < v[1]; });
sort 函数:sort(begin, end, cmp)
,cmp 是一个 bool 类型的判断函数。
lambda 匿名函数:C++11, [ capture ] ( params ) opt -> ret { body; };
,其中carpture是捕获列表,params是参数,opt是选项,ret则是返回值的类型,body则是函数的具体实现。
lc.238 除自身以外数组的乘积
给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
- 前缀积 * 后缀积
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
vector<int> ans(nums.size()); ans[0] = 1;
for(int i = 1 ; i < nums.size() ; i ++){
ans[i] = ans[i - 1] * nums[i - 1] ;
}
int r = nums[nums.size() - 1] ;
for(int i = nums.size() - 2 ; i >= 0 ; i --){
ans[i] *= r ;
r *= nums[i] ;
}
return ans;
}
};
lc.560 和为 k 的子数组
给你一个整数数组 nums 和一个整数 k ,请你统计并返回该数组中和为 k 的连续子数组的个数。
由于nums里的值会有负数,所以没法用滑动窗口
- 哈希表 + 前缀和
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int,int> hm;
hm[0] = 1 ;
int cnt = 0 , sum = 0;
for(auto& i : nums){
sum += i ;
if(hm.find(sum - k) != hm.end()){
cnt += hm[sum - k];
}
hm[sum] ++ ;`在这里插入代码片`
}
return cnt;
}
};
当前 i 个数的和与前 j 个数的和 的差值为k , cnt ++ 。注意hm[0] = 1 ;
字符串
lc.415 字符串相加
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
class Solution {
public:
string addStrings(string num1, string num2) {
int i = num1.length() - 1, j = num2.length() - 1, add = 0;
string ans = "";
while (i >= 0 || j >= 0 || add != 0) {
int x = i >= 0 ? num1[i] - '0' : 0;
int y = j >= 0 ? num2[j] - '0' : 0;
int result = x + y + add;
ans.push_back('0' + result % 10);
add = result / 10;
i -= 1;
j -= 1;
}
reverse(ans.begin(), ans.end());
return ans;
}
};
lc.409 最长回文串
给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 “Aa” 不能当做一个回文字符串。
注意:假设字符串的长度不会超过 1010。
class Solution {
public:
int longestPalindrome(string s) {
int cnt = 0 ;
unordered_map<char,int> hm;
for(auto& i : s){
hm[i] ++;
}
for(auto& i : hm){
int x = i.second;
cnt += x % 2;
}
if(cnt == 0) return s.size() ;
else return s.size() + 1 - cnt ;
}
};
注意unordered_map
值的遍历与调用。
lc.290 单词规律
给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。
这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。
class Solution {
public:
bool wordPattern(string pattern, string s) {
int size_pattern = pattern.size();
vector<string> words;
string temp = "";
// Divide the string to a string vector by words.
for (int i = 0; i <= s.size(); i++) {
if (i < s.size() && s[i] != ' ') {
temp += s[i];
}
else {
words.push_back(temp);
temp = "";
}
}
// If the size of pattern is not equal to the number of words.
if (size_pattern != words.size()) return false;
// Traverse through pattern[0,size-2] with iterator "i".
for (int i = 0; i < size_pattern - 1; i++) {
// Search for the matched element in pattern[i+1,size-1] with iterator "j".
for (int j = i + 1; j < size_pattern; j++) {
// No matter matched or not, turn to words to check if it is the same.
if (pattern[j] == pattern[i]) {
if (words[i] != words[j]) {
return false;
}
break;
}
else {
if (words[i] == words[j]) {
return false;
}
}
}
}
return true;
}
};
取代双hashmap
lc.763 划分字母区间
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
- hashmap
class Solution {
public:
vector<int> partitionLabels(string s) {
vector<int> ans;
unordered_map<char,int> hm ;
for(int i = 0 ; i < s.size() ; i ++){
hm[s[i]] = i ;
}
int l = 0 , r = 0;
for(int i = 0 ; i < s.size() ; i ++){
r = max(r , hm[s[i]]);
if(i == r){
ans.emplace_back(r - l + 1);
l = i + 1 ;
}
}
return ans;
}
};
if(i == r)
在一组数的最后一个判断,就不用遇到新的字符就判断一次。
lc.49 字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。
- hashmap 排序后作为 first
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> mp;
for (string& str: strs) {
string key = str;
sort(key.begin(), key.end());
mp[key].emplace_back(str);
}
vector<vector<string>> ans;
for (auto it = mp.begin(); it != mp.end(); ++it) {
ans.emplace_back(it->second);
}
return ans;
}
};
- 把每个 str 各个字母出现的次数作为 first
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
// 自定义对 array<int, 26> 类型的哈希函数
auto arrayHash = [fn = hash<int>{}] (const array<int, 26>& arr) -> size_t {
return accumulate(arr.begin(), arr.end(), 0u, [&](size_t acc, int num) {
return (acc << 1) ^ fn(num);
});
};
unordered_map<array<int, 26>, vector<string>, decltype(arrayHash)> mp(0, arrayHash);
for (string& str: strs) {
array<int, 26> counts{};
int length = str.length();
for (int i = 0; i < length; ++i) {
counts[str[i] - 'a'] ++;
}
mp[counts].emplace_back(str);
}
vector<vector<string>> ans;
for (auto it = mp.begin(); it != mp.end(); ++it) {
ans.emplace_back(it->second);
}
return ans;
}
};
lc.43 字符串相乘
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。
- 位数相加
class Solution {
public:
string multiply(string num1, string num2) {
string ans = "";
string cnt0 = "";
if(num1 == "0" || num2 == "0") return "0";
for(int i = num2.size() - 1 ; i >= 0 ; i --){
string tmp = "";
for(int j = 0 ; j < num2[i] - '0' ; j ++){
tmp = plus(tmp , num1);
}
cnt0 += "0";
tmp += cnt0;
ans = plus(ans , tmp);
}
ans.pop_back();
return ans;
}
string plus(string num1, string num2){
int add = 0, i = num1.size() - 1, j = num2.size() - 1 ;
string ans = "";
while(i >= 0 || j >= 0 || add > 0){
int x = i >= 0 ? num1[i] - '0' : 0 ;
int y = j >= 0 ? num2[j] - '0' : 0 ;
int result = x + y + add;
ans.push_back('0' + result % 10);
add = result / 10;
i -- ; j -- ;
}
reverse(ans.begin() , ans.end());
return ans;
}
};
if(num1 == "0" || num2 == "0") return "0";
边界条件:0 * anything == 0 。
链表
lc.2 两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *head = nullptr, *tail = nullptr;
int carry = 0;
while (l1 || l2) {
int n1 = l1 ? l1->val: 0;
int n2 = l2 ? l2->val: 0;
int sum = n1 + n2 + carry;
if (!head) {
head = tail = new ListNode(sum % 10);
} else {
tail->next = new ListNode(sum % 10);
tail = tail->next;
}
carry = sum / 10;
if (l1) {
l1 = l1->next;
}
if (l2) {
l2 = l2->next;
}
}
if (carry > 0) {
tail->next = new ListNode(carry);
}
return head;
}
};