977. 有序数组的平方
https://leetcode.cn/problems/squares-of-a-sorted-array/comments/
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
暴力排序法
Copy class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for(int i = 0; i < nums.size(); i++){
nums[i] = nums[i] * nums[i];
}
sort(nums.begin(), nums.end());
return nums;
}
};
双指针法
Copy class Solution {
public:
vector<int> sortedSquares(vector<int> &nums) {
vector<int> result(nums.size(), 0);
int k = nums.size() - 1;
for (int i = 0, j = k; i < nums.size(), j >= i;) {
if (nums[j] * nums[j] > nums[i] * nums[i]) {
result[k--] = nums[j] * nums[j];
j--;
} else {
result[k--] = nums[i] * nums[i];
i++;
}
}
return result;
}
};
数组其实是有序的, 只不过负数平方之后可能成为最大数了。那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。此时可以考虑双指针法了,i 指向起始位置,j 指向终止位置。定义一个新数组 result,和 nums 数组一样的大小,让 k 指向 result 数组终止位置。
189. 轮转数组
https://leetcode.cn/problems/rotate-array/
给你一个数组,将数组中的元素向右轮转 k
个位置,其中 k
是非负数。
暴力轮转
Copy class Solution {
public:
void rotate(vector<int>& nums, int k) {
vector<int> result(nums.size(), 0);
k = k % nums.size();
for(int i = 0; i < nums.size(); i++){
if(i >= nums.size() - k){
result[i + k - nums.size()] = nums[i];
continue;
}
result[i + k] = nums[i];
}
nums = result;
}
};
双指针原地算法
Copy class Solution {
public:
void rotate(vector<int>& nums, int k) {
k = k % nums.size();
reverse(nums.begin(), nums.end());
reverse(nums.begin(), nums.begin() + k);
reverse(nums.begin() + k, nums.end());
}
};
先全部翻转一遍,再翻转前一段,最后翻转后一段。
88. 合并两个有序数组
https://leetcode.cn/problems/merge-sorted-array/
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
Copy class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int k = m + n - 1;
int i = m - 1;
int j = n - 1;
while(j >= 0 && i >= 0){
if(nums1[i] > nums2[j]) nums1[k--] = nums1[i--];
else nums1[k--] = nums2[j--];
}
while(j >= 0) nums1[k--] = nums2[j--];
}
};
因为我们要放在 nums1 里面,所以我们从后往前遍历,从最大的开始比较,一个一个放进去,最后 nums2 还有剩就全部扔进去,nums1 还有剩说明已经全部放在了正确的位置,不用管。
283. 移动零
https://leetcode.cn/problems/move-zeroes
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
Copy class Solution {
public:
void moveZeroes(vector<int>& nums) {
int j = 0;
for(int i = 0; i < nums.size(); i++){
if(nums[i] != 0){
nums[j++] = nums[i];
}
}
for(int i = j; i < nums.size(); i++){
nums[i] = 0;
}
}
};
i 来遍历整个数组,遇到不是 0 的就交给 j 进行填充,最后再补 0。
167. 两数之和 II - 输入有序数组
https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted
给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。
以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。
Copy class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int i = 0;
int j = nums.size() - 1;
while(i < j){
if(nums[i] + nums[j] < target){
i++;
}else if(nums[i] + nums[j] > target){
j--;
}else{
return {i + 1, j + 1};
}
}
return {};
}
};
i 在最小,j 在最大,如果大了 j 就减一,如果小了 i 就加一。这样做可以减少很多不必要情况的判断。
344. 反转字符串
https://leetcode.cn/problems/reverse-string
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s
的形式给出。
Copy class Solution {
public:
void reverseString(vector<char>& s) {
for(int i = 0, j = s.size() - 1; i < s.size() / 2; i++, j--){
swap(s[i], s[j]);
}
}
};
无脑题,甚至直接 reverse
也能过。
557. 反转字符串中的单词 III
https://leetcode.cn/problems/reverse-words-in-a-string-iii
给定一个字符串 s
,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
Copy class Solution {
public:
string reverseWords(string s) {
int left = 0;
for(int i = 0; i < s.length(); i++){
if(s[i] == 32){
reverse(s.begin() + left, s.begin() + i);
left = i + 1;
}
}
reverse(s.begin() + left, s.end());
return s;
}
};
遇到空格就反转之前的,最后再反转一下最后一个单词即可。reverse
可以用双指针实现。
876. 链表的中间结点
https://leetcode.cn/problems/middle-of-the-linked-list
给定一个头结点为 head
的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
普通双指针
Copy /**
* 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* middleNode(ListNode* head) {
int n = 0;
auto q = head, p = head;
while(p){
p = p -> next;
n++;
}
for(int i = 0; i < n / 2; i++){
q = q -> next;
}
return q;
}
};
遍历第一遍求长度,第二遍走到 n / 2 即可。
快慢指针
Copy /**
* 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* middleNode(ListNode* head) {
auto q = head, p = head;
while(q && q->next){
p = p->next;
q = q->next->next;
}
return p;
}
};
奇数情况是 q 到最后一个就不行了,偶数情况是到最后一个的下一个,也就是空。
3. 无重复字符的最长子串
https://leetcode.cn/problems/longest-substring-without-repeating-characters/
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
Copy class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<int, int> Hash;
int ans = 0;
for(int i = 0, j = 0; i < s.size(); i++){
Hash[s[i]] ++;
while(Hash[s[i]] > 1) Hash[s[j++]]--;
ans = max(ans, i - j + 1);
}
return ans;
}
};
用一个哈希表来维护,当有重复字符的时候,j 指针就往右移,直到没有重复字符,最后取 max 即可。
567. 字符串的排列
https://leetcode.cn/problems/permutation-in-string/
给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。
换句话说,s1 的排列之一是 s2 的 子串 。
Copy class Solution {
public:
unordered_map<int, int> h1;
unordered_map<int, int> h2;
bool check(int c){
if(h1.count(c) && h1[c] == h2[c]) return true;
return false;
}
bool checkInclusion(string s1, string s2) {
for(int i = 0; i < s1.size(); i++){
h1[s1[i]]++;
}
for(int i = 0, j = 0, cnt = 0; i < s2.size(); i++){
if(check(s2[i])) cnt--;
h2[s2[i]]++;
if(check(s2[i])) cnt++;
if(i - j >= s1.size()){
if(check(s2[j])) cnt--;
h2[s2[j]]--;
if(check(s2[j])) cnt++;
j++;
}
if(cnt == h1.size()) return true;
}
return false;
}
};
用两个哈希表进行维护,最后比较两个哈希表相同的“柱子数”是否为 h1 的总长度即可。
Copy class Solution {
public:
bool checkInclusion(string s1, string s2) {
unordered_map<char, int> need, win;
for(auto c : s1) need[c]++;
int left = 0, right = 0, vl = 0;
while(right < s2.size()) {
char c1 = s2[right];
right++;
if(need.count(c1)) {
win[c1]++;
if(win[c1] == need[c1]) vl++;
}
while(vl == need.size()) {
if(right - left == s1.size()) return true;
char c2 = s2[left];
left++;
if(need.count(c2)) {
if(win[c2] == need[c2]) vl--;
win[c2]--;
}
}
}
return false;
}
};
套模板即可,当窗口长度为 s1 的长度且有效位数符合时就可以返回 true。
27. 移除元素
https://leetcode.cn/problems/remove-element/
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
暴力
Copy class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
for(int i = 0; i < size; i++){
if(nums[i] == val) {
for(int j = i + 1; j < size; j++){
nums[j - 1] = nums[j];
}
size--;
i--;
}
}
return size;
}
};
遇到需要删除的就将后面的元素一起往前移一格即可。因为少了一个,所以 i 也需要往前一格。
快慢指针
Copy class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow = 0;
for(int fast = 0; fast < nums.size(); fast++){
if(nums[fast] != val) {
nums[slow++] = nums[fast];
}
}
return slow;
}
};
当快指针遇到需要删除的元素,慢指针不动,直到快指针指向不需要删除的数字,然后让快指针把不需删除的一个一个覆盖过去。
26. 删除有序数组中的重复项
https://leetcode.cn/problems/remove-duplicates-from-sorted-array/
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。
由于在某些语言中不能改变数组的长度,所以必须将结果放在数组 nums 的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。
将最终结果插入 nums 的前 k 个位置后返回 k 。
不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
Copy class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int j = 0;
for(int i = 0; i < nums.size(); i++){
if(!i || nums[i] != nums[i - 1]){
nums[j++] = nums[i];
}
}
return j;
}
};
一个指在前面一个指在后面,经典双指针问题。注意特判 i = 0 的情况。
Copy class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int j = 0;
for(int i = 0; i < nums.size(); i++){
if(nums[i] != nums[j]){
j++;
nums[j] = nums[i];
}
}
return j + 1;
}
};
更好的快慢指针做法,更加直观。
844. 比较含退格的字符串
https://leetcode.cn/problems/backspace-string-compare/
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空。
Copy class Solution {
public:
void changestring(string &s) {
int slow=0;
for(int fast = 0; fast < s.size(); fast++)
{
if(s[fast] != '#') s[slow++] = s[fast];
else if(slow) slow--;
}
s.resize(slow);
}
bool backspaceCompare(string s, string t) {
changestring(s);
changestring(t);
return s==t;
}
};
注意使用 resize()
将后面无用的字符给去掉。
209. 长度最小的子数组
https://leetcode.cn/problems/minimum-size-subarray-sum/
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
Copy class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int res = INT_MAX;
for(int i = 0, j = 0, sum = 0; i < nums.size(); i++){
sum += nums[i];
while(sum >= target){
res = min(res, i - j + 1);
sum -= nums[j++];
}
}
if(res == INT_MAX) res = 0;
return res;
}
};
经典滑动窗口问题,注意判断 res 没有被更新的情况。
904. 水果成篮
https://leetcode.cn/problems/fruit-into-baskets/
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。 给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。
Copy class Solution {
public:
int totalFruit(vector<int>& fruits) {
int res = 0;
unordered_map<int, int> h;
for(int i = 0, j = 0, s = 0; i < fruits.size(); i++){
if(++h[fruits[i]] == 1) s++;
while(s > 2){
if(--h[fruits[j++]] == 0) s--;
}
res = max(res, i - j + 1);
}
return res;
}
};
用哈希表,并且用一个变量来保存哈希表中有几个不同的水果种类,每次这个种类清空了或者存在了都会对 s 有所改变。
76. 最小覆盖子串
https://leetcode.cn/problems/minimum-window-substring/
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
Copy class Solution {
public:
string minWindow(string s, string t) {
unordered_map<char, int> hs;
unordered_map<char, int> ht;
for(auto c : t) ht[c]++;
string res;
for(int i = 0, j = 0, cnt = 0; i < s.length(); i++){
if(++ hs[s[i]] <= ht[s[i]]) cnt++;
while(hs[s[j]] > ht[s[j]]) hs[s[j++]]--;
if(cnt == t.length()){
if(res.empty() || i - j + 1 < res.length()){
res = s.substr(j, i - j + 1);
}
}
}
return res;
}
};
用两个哈希表和一个 cnt 进行维护,经典滑动窗口。
Copy class Solution {
public:
string minWindow(string s, string t) {
unordered_map<char, int> need, win;
for(auto c : t) need[c]++;
int left = 0, right = 0, vl = 0, st = 0, len = INT_MAX;
while(right < s.size()) {
char c1 = s[right];
right++;
if(need.count(c1)) {
win[c1]++;
if(win[c1] == need[c1]) vl++;
}
while(vl == need.size()) {
if(right - left < len) {
st = left;
len = right - left;
}
char c2 = s[left];
left++;
if(need.count(c2)) {
if(win[c2] == need[c2]) vl--;
win[c2]--;
}
}
}
return len == INT_MAX ? "" : s.substr(st, len);
}
};
更好的做法,可作为滑动窗口模板。
202. 快乐数
https://leetcode.cn/problems/happy-number/
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。 如果这个过程 结果为 1,那么这个数就是快乐数。 如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
Copy class Solution {
public:
int get(int x){
int res = 0;
while(x){
res += (x % 10) * (x % 10);
x /= 10;
}
return res;
}
bool isHappy(int n) {
int f = get(n), s = n;
while(f != s){
f = get(get(f));
s = get(s);
}
return f == 1;
}
};
用一个快慢指针,因为他会有循环,所以判断循环的数字是否为 1 就行。
15. 三数之和
https://leetcode.cn/problems/3sum/
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
Copy class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
sort(nums.begin(), nums.end());
unordered_map<int, int> h;
for(int i = 0; i < nums.size(); i++){
if(i && nums[i] == nums[i - 1]) continue;
for(int j = i + 1, k = nums.size() - 1; j < k; j++){
if(j > i + 1 && nums[j] == nums[j - 1]) continue;
while(j < k - 1 && nums[i] + nums[j] + nums[k - 1] >= 0) k--;
if(nums[i] + nums[j] + nums[k] == 0){
res.push_back({nums[i], nums[j], nums[k]});
}
}
}
return res;
}
};
首先需要把 i 给定下来,然后遍历 j 与 k 即可,需要注意的是我们需要进行去重,不然会有重复答案的出现。
18. 四数之和
https://leetcode.cn/problems/4sum/
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n a、b、c 和 d 互不相同 nums[a] + nums[b] + nums[c] + nums[d] == target 你可以按 任意顺序 返回答案 。
Copy class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> res;
sort(nums.begin(), nums.end());
for(int i = 0; i < nums.size(); i++){
if(i && nums[i] == nums[i - 1]) continue;
for(int j = i + 1; j < nums.size(); j++){
if(j > i + 1 && nums[j] == nums[j - 1]) continue;
for(int k = j + 1, u = nums.size() - 1; k < u; k++){
if(k > j + 1 && nums[k] == nums[k - 1]) continue;
while(k < u - 1 && ((long)nums[i] + nums[j] + nums[k] + nums[u - 1]) >= target) u--;
if(((long)nums[i] + nums[j] + nums[k] + nums[u]) == target) res.push_back({nums[i], nums[j], nums[k], nums[u]});
}
}
}
return res;
}
};
与三数之和区别不大,就是加一层循环就行了。
151. 反转字符串中的单词
https://leetcode.cn/problems/reverse-words-in-a-string/
给你一个字符串 s ,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s 中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
Copy class Solution {
public:
string reverseWords(string s) {
reverse(s.begin(), s.end());
int k = 0;
for(int i = 0; i < s.length(); i++){
if(s[i] == ' ') continue;
int j = i, t = k;
while(j < s.length() && s[j] != ' ') s[t++] = s[j++];
reverse(s.begin() + k, s.begin() + t);
s[t++] = ' ';
k = t, i = j;
}
if(k) k--;
s.erase(s.begin() + k, s.end());
return s;
}
};
将每一个单词都放到前面,一个指针指向原本的字符串,另外一个指向最后成为结果的字符串。注意找到一个单词就要将其翻转。
5. 最长回文子串
https://leetcode.cn/problems/longest-palindromic-substring/
给你一个字符串 s,找到 s 中最长的回文子串。
如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。
Copy class Solution {
public:
string longestPalindrome(string s) {
string res = "";
for(int i = 0; i < s.size(); i++){
string s1 = getPalindrome(s, i, i);
string s2 = getPalindrome(s, i, i + 1);
res = s1.size() > res.size() ? s1 : res;
res = s2.size() > res.size() ? s2 : res;
}
return res;
}
string getPalindrome(string s, int l, int r){
while(l >= 0 && r < s.size() && s[l] == s[r]){
l--, r++;
}
return s.substr(l + 1, r - l - 1);
}
};
这道题难点在于需要从中间向外来走,还要考虑是奇数还是偶数的情况,注意 substr
时的边界问题。
438. 找到字符串中所有字母异位词
https://leetcode.cn/problems/find-all-anagrams-in-a-string/
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
Copy class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> res;
unordered_map<char, int> need, win;
for(auto c : p) need[c]++;
int right = 0, left = 0, vl = 0;
while(right < s.size()) {
char c1 = s[right];
right++;
if(need.count(c1)) {
win[c1]++;
if(win[c1] == need[c1]) vl++;
}
while(right - left >= p.size()) {
if(vl == need.size()) res.push_back(left);
char c2 = s[left];
left++;
if(need.count(c2)) {
if(win[c2] == need[c2]) vl--;
win[c2]--;
}
}
}
return res;
}
};
按照模板写即可,值得一提的是这里处理缩小的时候和模板有所不同,根据题型自行适配。
912. 排序数组
https://leetcode.cn/problems/sort-an-array/
给你一个整数数组 nums,请你将该数组升序排列。
Copy class Solution {
public:
vector<int> tmp;
vector<int> sortArray(vector<int>& nums) {
tmp = vector<int>(nums.size());
Sort(nums, 0, nums.size() - 1);
return nums;
}
void Sort(vector<int>& nums, int l, int r) {
if(l == r) return;
int mid = (l + r) / 2;
Sort(nums, l, mid);
Sort(nums, mid + 1, r);
merge(nums, l, mid, r);
}
void merge(vector<int>& nums, int l, int mid, int r) {
for(int i = l; i <= r; i++) tmp[i] = nums[i];
int i = l, j = mid + 1;
for (int p = l; p <= r; p++) {
if (i == mid + 1) nums[p] = tmp[j++];
else if (j == r + 1) nums[p] = tmp[i++];
else if (tmp[i] > tmp[j]) nums[p] = tmp[j++];
else nums[p] = tmp[i++];
}
}
};
用归并排序解决。
Copy class Solution {
public:
vector<int> sortArray(vector<int>& nums) {
quick_sort(nums, 0, nums.size() - 1);
return nums;
}
void quick_sort(vector<int>& nums, int l, int r) {
if(l >= r) return;
int x = nums[l + r >> 1], i = l - 1, j = r + 1;
while(i < j) {
do i++; while(nums[i] < x);
do j--; while(nums[j] > x);
if(i < j) swap(nums[i], nums[j]);
}
quick_sort(nums, l, j);
quick_sort(nums, j + 1, r);
}
};
快排。
493. 翻转对
https://leetcode.cn/problems/reverse-pairs/
给定一个数组 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。
你需要返回给定数组中的重要翻转对的数量。
Copy class Solution {
public:
vector<int> tmp;
int count = 0;
int reversePairs(vector<int>& nums) {
tmp = vector<int>(nums.size());
sort(nums, 0, nums.size() - 1);
return count;
}
void sort(vector<int>& nums, int l, int r) {
if(l == r) return;
int mid = (l + r) / 2;
sort(nums, l, mid);
sort(nums, mid + 1, r);
merge(nums, l, mid, r);
}
void merge(vector<int>& nums, int l, int mid, int r) {
for(int i = l; i <= r; i++) tmp[i] = nums[i];
int end = mid + 1;
for(int i = l; i <= mid; i++) {
while(end <= r && (long)nums[i] > (long)nums[end] * 2) end++;
count += end - (mid + 1);
}
int i = l, j = mid + 1;
for(int p = l; p <= r; p++) {
if(i == mid + 1) nums[p] = tmp[j++];
else if(j == r + 1) nums[p] = tmp[i++];
else if(tmp[i] > tmp[j]) nums[p] = tmp[j++];
else nums[p] = tmp[i++];
}
}
};
归并排序加几行代码即可。
215. 数组中的第 K 个最大元素
https://leetcode.cn/problems/kth-largest-element-in-an-array/
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
Copy class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
quick_sort(nums, 0, nums.size() - 1);
return nums[nums.size() - k];
}
void quick_sort(vector<int>& nums, int l, int r) {
if(l >= r) return;
int x = nums[l + r >> 1], i = l - 1, j = r + 1;
while(i < j) {
do i++; while(nums[i] < x);
do j--; while(nums[j] > x);
if(i < j) swap(nums[i], nums[j]);
}
quick_sort(nums, l, j);
quick_sort(nums, j + 1, r);
}
};
42. 接雨水
https://leetcode.cn/problems/trapping-rain-water/
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
Copy class Solution {
public:
int trap(vector<int>& height) {
int l = 0, r = height.size() - 1;
int l_max = 0, r_max = 0, res = 0;
while(l < r) {
l_max = max(l_max, height[l]);
r_max = max(r_max, height[r]);
if(l_max < r_max) res += l_max - height[l++];
else res += r_max - height[r--];
}
return res;
}
};
31. 下一个排列
https://leetcode.cn/problems/longest-common-subsequence/
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。 整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。 而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。 给你一个整数数组 nums ,找出 nums 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。
Copy class Solution {
public:
void nextPermutation(vector<int>& nums) {
int k = nums.size() - 1;
while(k > 0 && nums[k - 1] >= nums[k]) k--;
if(k <= 0) reverse(nums.begin(), nums.end());
else {
int t = k;
while(t < nums.size() && nums[t] > nums[k - 1]) t++;
swap(nums[t - 1], nums[k - 1]);
reverse(nums.begin() + k, nums.end());
}
}
};
165. 比较版本号
https://leetcode.cn/problems/compare-version-numbers/
给你两个版本号 version1 和 version2 ,请你比较它们。
版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。
比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1 。
返回规则如下:
如果 version1 > version2 返回 1, 如果 version1 < version2 返回 -1, 除此之外返回 0。
Copy class Solution {
public:
int compareVersion(string v1, string v2) {
for(int i = 0, j = 0; i < v1.size() || j < v2.size();) {
int a = i, b = j;
while(a < v1.size() && v1[a] != '.') a++;
while(b < v2.size() && v2[b] != '.') b++;
int m = a == i ? 0 : stoi(v1.substr(i, a - i));
int n = b == j ? 0 : stoi(v2.substr(j, b - j));
if(m > n) return 1;
if(m < n) return -1;
i = a + 1, j = b + 1;
}
return 0;
}
};
43. 字符串相乘
https://leetcode.cn/problems/multiply-strings/
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
Copy class Solution {
public:
string multiply(string num1, string num2) {
int m = num1.size(), n = num2.size();
vector<int> res(m + n, 0);
for(int i = m - 1; i >= 0; i--) {
for(int j = n - 1; j >= 0; j--) {
int mul = (num1[i] - '0') * (num2[j] - '0');
int r1 = i + j, r2 = i + j + 1;
int sum = mul + res[r2];
res[r2] = sum % 10;
res[r1] += sum / 10;
}
}
int i = 0;
string str;
while(i < res.size() && res[i] == 0) i++;
for(; i < res.size(); i++) str.push_back('0' + res[i]);
return str.size() == 0 ? "0" : str;
}
};
240. 搜索二维矩阵 II
https://leetcode.cn/problems/search-a-2d-matrix-ii/
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。 每列的元素从上到下升序排列。
Copy class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size(), n = matrix[0].size();
int i = 0, j = n - 1;
while(i < m && j >= 0) {
if(matrix[i][j] == target) return true;
else if(matrix[i][j] > target) j--;
else i++;
}
return false;
}
};
209. 长度最小的子数组
https://leetcode.cn/problems/minimum-size-subarray-sum/
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
Copy class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int r = 0, l = 0, sum = 0, res = INT_MAX;
while(r < nums.size()) {
sum += nums[r++];
while(sum >= target) {
res = min(res, r - l);
sum -= nums[l++];
}
}
return res == INT_MAX ? 0 : res;
}
};
11. 盛最多水的容器
https://leetcode.cn/problems/container-with-most-water/
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
Copy class Solution {
public:
int maxArea(vector<int>& height) {
int l = 0, r = height.size() - 1;
int res = INT_MIN;
while(l < r) {
int s = min(height[l], height[r]) * (r - l);
res = max(res, s);
if(height[l] < height[r]) l++;
else r--;
}
return res;
}
};