剑指力扣
链表
206 反转链表 & 剑指 Offer 24 反转链表(简单)
执行用时:8 ms, 在所有 C++ 提交中击败了90.86%的用户
内存消耗:8.5 MB, 在所有 C++ 提交中击败了58.16%的用户
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if (head == nullptr || head->next == nullptr) return head;
if (head->next->next == nullptr) {
ListNode* phead = head->next;
head->next->next = head;
head->next = nullptr;
return phead;
}
ListNode* first = head, * second = first->next, * third = second->next;
first->next = nullptr;
while (third != nullptr) {
second->next = first;
first = second;
second = third;
third = third->next;
}
second->next = first;
return second;
}
};
注释:经常看看,记住关键步骤即可
876 链表的中间结点(简单)
法一:两次遍历
执行用时:4 ms, 在所有 C++ 提交中击败了35.09%的用户
内存消耗:7 MB, 在所有 C++ 提交中击败了10.13%的用户
class Solution {
public:
ListNode* middleNode(ListNode* head) {
if (head == nullptr) return nullptr;
ListNode* p = head;
int count = 1;
while (p->next != 0) {
p = p->next;
count++;
}
count /= 2;
p = head;
for (int i = 0; i < count; i++) {
p = p->next;
}
return p;
}
};
法二:快慢指针
执行用时:4 ms, 在所有 C++ 提交中击败了35.09%的用户
内存消耗:6.9 MB, 在所有 C++ 提交中击败了32.93%的用户
class Solution {
public:
ListNode* middleNode(ListNode* head) {
if (head == nullptr) return nullptr;
ListNode* p = head;
int count = 1;
while (p->next != 0) {
p = p->next;
count++;
}
count /= 2;
p = head;
for (int i = 0; i < count; i++) {
p = p->next;
}
return p;
}
};
剑指 Offer 06 从尾到头打印链表(简单)
执行用时:12 ms, 在所有 C++ 提交中击败了28.33%的用户
内存消耗:9 MB, 在所有 C++ 提交中击败了39.63%的用户
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
if (head == nullptr) return {};
ListNode* phead = head;
stack<int> s;
vector<int> res;
while (phead != nullptr) {
s.push(phead->val);
phead = phead->next;
}
while (!s.empty()) {
res.push_back(s.top());
s.pop();
}
return res;
}
};
但凡提到反转,都可以考虑一下辅助栈。
1290 二进制链表转整数(简单)
执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:8.5 MB, 在所有 C++ 提交中击败了24.03%的用户
class Solution {
public:
int getDecimalValue(ListNode* head) {
int num = 0;
while (head != nullptr) {
num = num * 2 + head->val;
head = head->next;
}
return num;
}
};
141 环形链表(简单)
执行用时:8 ms, 在所有 C++ 提交中击败了93.73%的用户
内存消耗:7.9 MB, 在所有 C++ 提交中击败了29.02%的用户
快慢指针法:
class Solution {
public:
bool hasCycle(ListNode* head) {
if (head == nullptr || head->next == nullptr) return false;
ListNode* fast= head, * slow = head;
while (fast != nullptr) {
fast = fast->next;
if (fast == nullptr) break;
fast = fast->next;
slow = slow->next;
if (fast == slow) return true;
}
return false;
}
};
24. 两两交换链表中的节点(简单)
执行用时:4 ms, 在所有 C++ 提交中击败了72.30%的用户
内存消耗:7.9 MB, 在所有 C++ 提交中击败了7.02%的用户
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if (head == nullptr || head->next == nullptr) return head;
ListNode* phead = new ListNode, * first = phead, * second = head, * third = head->next;
phead->next = head;
while (third != nullptr) {
first->next = third;
second->next = third->next;
third->next = second;
if (second->next == nullptr || second->next->next == nullptr) break;
first = second;
second = second->next;
third = second->next;
}
return phead->next;
}
};
找准“中间态”即可。
2. 两数相加
执行用时:56 ms, 在所有 C++ 提交中击败了43.45%的用户
内存消耗:69.4 MB, 在所有 C++ 提交中击败了88.33%的用户
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* p1 = l1, * p2 = l2;
int len1 = 0, len2 = 0;
while (p1 != nullptr) { p1 = p1->next; len1++; }
while (p2 != nullptr) { p2 = p2->next; len2++; }
if (len1 < len2) return addTwoNumbers(l2, l1);
p1 = l1, p2 = l2;
while (p2 != nullptr) {
p1->val += p2->val;
p1 = p1->next, p2 = p2->next;
}
p1 = l1;
while (p1->next != nullptr) {
if (p1->val > 9) {p1->val -= 10; p1->next->val++; }
p1 = p1->next;
}
if (p1->val > 9) {
ListNode* new_node = new ListNode;
new_node->next = nullptr;
new_node->val = 1;
p1->next = new_node;
p1->val -= 10;
}
return l1;
}
};
数组
剑指 Offer 03. 数组中重复的数字(简单)
法一:哈希表
执行用时:132 ms, 在所有 C++ 提交中击败了30.13%的用户
内存消耗:27.3 MB, 在所有 C++ 提交中击败了20.05%的用户
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
unordered_set<int> table;
for (int i = 0; i < nums.size(); i++) {
if (table.count(nums[i]) > 0) return nums[i];
table.insert(nums[i]);
//思考:为什么下面不行?
// table.insert(nums[i]);
// if (table.count(nums[i]) > 1) return nums[i];
}
return 0;
}
};
法二:原地置换
无~
剑指 Offer 45. 把数组排成最小的数(中等)
执行用时:12 ms, 在所有 C++ 提交中击败了72.75%的用户
内存消耗:11.7 MB, 在所有 C++ 提交中击败了14.89%的用户
class Solution {
public:
static bool str_cmp(const string& str1, const string& str2) {
return str1 + str2 < str2 + str1 ? true : false;
}
string minNumber(vector<int>& nums) {
int n = nums.size();
vector<string> str;
string res = "";
if (n == 0) return res;
for (int i = 0; i < n; i++) str.push_back(to_string(nums[i]));
sort(str.begin(), str.end(), str_cmp);
for (int j = 0; j < str.size(); j++) res += str[j];
return res;
}
};
难点:
1.定义排序规则:若字符串 x + y > y + x。则定义 x > y。
2.定义排序函数
剑指 Offer 56 - II. 数组中数字出现的次数 II(中等)
剑指 Offer 56 - II. 数组中数字出现的次数 II
法一:哈希表
执行用时:76 ms, 在所有 C++ 提交中击败了74.05%的用户
内存消耗:18.4 MB, 在所有 C++ 提交中击败了24.49%的用户
class Solution {
public:
int singleNumber(vector<int>& nums) {
unordered_map<int, int> table;
for (int i = 0; i < nums.size(); i++) table[nums[i]]++;
for (unordered_map<int, int>::iterator it = table.begin(); it != table.end(); it++)
if (it->second == 1)
return it->first;
/*用下标访问也可:
for (int i = 0; i < nums.size(); i++)
if (table[nums[i]] == 1)
return nums[i];
*/
return -1;
}
};
法二:二进制各位数字出现的次数
执行用时:80 ms, 在所有 C++ 提交中击败了68.87%的用户
内存消耗:16.4 MB, 在所有 C++ 提交中击败了33.36%的用户
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = 0, arr[32] = { 0 }; //记录各个位上1的个数
for (int i = 0; i < nums.size(); i++) {
for (int j = 0; j < 32; j++) {
if (nums[i] % 2 != 0) arr[j]++;
nums[i] /= 2;
}
}
for (int k = 0; k < 32; k++)
if (arr[k] % 3 != 0)
res += pow(2, k);
return res;
}
};
46. 全排列
执行用时:4 ms, 在所有 C++ 提交中击败了92.27%的用户
内存消耗:7.8 MB, 在所有 C++ 提交中击败了86.53%的用户
class Solution {
public:
int Num(int num) {
int res = 1;
for (int i = 1; i <= num; i++) res *= i;
return res;
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int> > res;
for (int i = 0; i < Num(nums.size()); i++) {
res.push_back(nums);
next_permutation(nums.begin(), nums.end());
}
return res;
}
};
使用algorithm中的next_permutation函数会使这道题变得很无脑。以后有机会尝试用其他方法解决。
不要过度依赖algorithm库!
239. 滑动窗口最大值(困难)
滑动窗口法:(超时)
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
for (int i = 0; i < nums.size() - k + 1; i++) {
int max = INT_MIN;
for (int j = 0; j < k; i++) {
if (nums[j] > max) max = nums[j];
}
res.push_back(max);
}
return res;
}
};
字符串
387. 字符串中的第一个唯一字符(简单)
执行用时:28 ms, 在所有 C++ 提交中击败了80.91%的用户
内存消耗:10.6 MB, 在所有 C++ 提交中击败了93.55%的用户
class Solution {
public:
int firstUniqChar(string s) {
int table[26] = { 0 };
for (int i = 0; i < s.size(); i++)
table[s[i] - 'a']++;
for (int j = 0; j < s.size(); j++)
if (table[s[j] - 'a'] == 1)
return j;
return -1;
}
};
567. 字符串的排列(中等)
执行用时:368 ms, 在所有 C++ 提交中击败了5.02%的用户
内存消耗:7.4 MB, 在所有 C++ 提交中击败了79.27%的用户
滑动窗口+哈希表
class Solution {
public:
bool checkInclusion(string s1, string s2) {
int n1 = s1.size(), n2 = s2.size(), table[26] = { 0 };
for (int i = 0; i < n1; i++) table[s1[i] - 'a']++;
for (int j = 0; j < n2; j++) {
int tmp[26]; //创建table的复制版
for (int k = 0; k < 26; k++) tmp[k] = table[k];
int count = 0, index_s2 = j;
while (index_s2 < n2 && tmp[s2[index_s2++] - 'a']-- > 0) count++;
if (count == n1) return true;
}
return false;
}
};
14.最长公共前缀(简单)
执行用时:4 ms, 在所有 C++ 提交中击败了92.77%的用户
内存消耗:9.5 MB, 在所有 C++ 提交中击败了33.07%的用户
纵向扫描法
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if (strs.size() == 0) return "";
string res = "";
for (int j = 0; ; j++) { //i代表行数,j代表列数
for (int i = 0; i < strs.size(); i++) {
char c = strs[0][j];
if (c != strs[i][j] || j == strs[i].size()) return res;
}
res += strs[0][j];
}
return res;
}
};
可以尝试横向扫描法。
贪心
455. 分发饼干(简单)
执行用时:32 ms, 在所有 C++ 提交中击败了99.05%的用户
内存消耗:17.1 MB, 在所有 C++ 提交中击败了98.40%的用户
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(), g.end());
sort(s.begin(), s.end());
int index_s = 0, index_g = 0;
while (index_s < s.size() && index_g < g.size())
if (g[index_g] <= s[index_s]) { index_s++; index_g++; }
else index_s++;
return index_g;
}
};
贪心的标志算法
55.跳跃游戏(中等)
执行用时:8 ms, 在所有 C++ 提交中击败了99.24%的用户
内存消耗:12.5 MB, 在所有 C++ 提交中击败了94.64%的用户
class Solution {
public:
bool canJump(vector<int>& nums) {
int dist = 0;
for (int i = 0; i <= dist && i < nums.size(); i++)
dist = max(dist, nums[i] + i);
return dist >= nums.size() - 1;
}
};
45. 跳跃游戏II
执行用时:8 ms, 在所有 C++ 提交中击败了99.81%的用户
内存消耗:15.1 MB, 在所有 C++ 提交中击败了93.23%的用户
class Solution {
public:
int jump(vector<int>& nums) {
if (nums.size() == 1) return 0;
int left = 0, right = 0, steps = 0; //用left和right维护一个区间
while (left <= right) {
int max_r = 0;
for (int i = left; i <= right; i++)
max_r = max(max_r, nums[i] + i);
left = right, right = max_r;
steps++;
if (right >= (int)nums.size() - 1) break;
}
return steps;
}
};
位运算
231. 2的幂(简单)
方法一:
执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:5.7 MB, 在所有 C++ 提交中击败了98.60%的用户
class Solution {
public:
bool isPowerOfTwo(int n) {
return n > 0 && (1 << 30) % n == 0;
}
};
方法二:
执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:5.8 MB, 在所有 C++ 提交中击败了97.10%的用户
class Solution {
public:
bool isPowerOfTwo(int n) {
return n > 0 && (n & -n) == n;
}
};
x & -x表示x的二进制中最靠近右边的1及以后的数
762. 二进制表示中质数个计算置位(简单)
执行用时:40 ms, 在所有 C++ 提交中击败了39.26%的用户
内存消耗:6.2 MB, 在所有 C++ 提交中击败了58.97%的用户
class Solution {
public:
int countPrimeSetBits(int L, int R) {
unordered_set<int> table = { 2,3,5,7,11,13,17,19,23,29,31 };
int num = 0;
for (int i = L; i <= R; i++) {
int n = 0;
for (int j = i; j; j >>= 1) n += (j & 1); //计算二进制1的个数。常见技巧!
if (table.count(n)) num++;
}
return num;
}
};
136. 只出现一次的数字
执行用时:12 ms, 在所有 C++ 提交中击败了99.83%的用户
内存消耗:16.5 MB, 在所有 C++ 提交中击败了96.73%的用户
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ret = 0;
for (auto x : nums) ret ^= x;
return ret;
}
};
t countPrimeSetBits(int L, int R) {
unordered_set table = { 2,3,5,7,11,13,17,19,23,29,31 };
int num = 0;
for (int i = L; i <= R; i++) {
int n = 0;
for (int j = i; j; j >>= 1) n += (j & 1); //计算二进制1的个数。常见技巧!
if (table.count(n)) num++;
}
return num;
}
};
### 136. 只出现一次的数字
[136. 只出现一次的数字](https://leetcode-cn.com/problems/single-number/)
>执行用时:12 ms, 在所有 C++ 提交中击败了99.83%的用户
>
>内存消耗:16.5 MB, 在所有 C++ 提交中击败了96.73%的用户
~~~cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ret = 0;
for (auto x : nums) ret ^= x;
return ret;
}
};
技巧: a ^ a = 0 a ^ 0 = a