算法汇总
哈希表
49. 字母异位词分组
非常简单
vector<vector<string>> groupAnagrams(vector<string> &strs) {
unordered_map<string, vector<string >> map;
for (auto str: strs) {
string s = str;
sort(s.begin(), s.end());
map[s].push_back(str);
}
vector<vector<string>> ans;
for(auto [_,v]:map){
ans.push_back(v);
}
return ans;
}
128. 最长连续序列
int longestConsecutive(vector<int> &nums) {
unordered_set<int> set(nums.begin(), nums.end());
int ans = 0;
for (int x: set) {
if (!set.contains(x - 1)) {
int y = x + 1;
while (set.contains(y)) {
y++;
}
ans = max(ans, y - x);
}
}
return ans;
}
双指针
15. 三数之和 🌟🌟🌟
双指针题型的无冕之王
两数之和升级版,方法完全不一样,还用hash的话,会有很多特殊情况需要考虑,推荐使用 排序+双指针
法。字节常考题,必会。
vector<vector<int>> threeSum(vector<int> &nums) {
sort(nums.begin(), nums.end());
vector<vector<int>> ans;
int n = nums.size();
for (int i = 0; i < n - 2 && nums[i] <= 0; i++) {
if (i && nums[i] == nums[i - 1]) continue;
int j = i + 1, k = n - 1;
while (j < k) {
int x = nums[i] + nums[j] + nums[k];
if (x < 0) {
j++;
} else if (x > 0) {
k--;
} else {
ans.push_back({nums[i], nums[j++], nums[k--]});
while (j < k && nums[j] == nums[j - 1]) {
j++;
}
while (j < k && nums[k] == nums[k + 1]) {
k--;
}
}
}
}
return ans;
}
42. 接雨水
最简单的困难题,字节常考题
int trap(vector<int> &height) {
int n = height.size();
int left[n], right[n];
left[0] = height[0];
right[n - 1] = height[n - 1];
for (int i = 1; i < n; i++) {
left[i] = max(height[i], left[i - 1]);
}
for (int j = n - 2; j >= 0; j--) {
right[j] = max(height[j], right[j + 1]);
}
int ans = 0;
for (int i = 1; i < n - 1; i++) {
ans += min(left[i], right[i]) - height[i];
}
return ans;
}
滑动窗口
/* 滑动窗口算法框架 */
void slidingWindow(string s) {
// 用合适的数据结构记录窗口中的数据
unordered_map<char, int> window;
int left = 0, right = 0;
while (right < s.size()) {
// c 是将移入窗口的字符
char c = s[right];
window.add(c)
// 增大窗口
right++;
// 进行窗口内数据的一系列更新
...
/*** debug 输出的位置 ***/
// 注意在最终的解法代码中不要 print
// 因为 IO 操作很耗时,可能导致超时
printf("window: [%d, %d)\n", left, right);
/********************/
// 判断左侧窗口是否要收缩
while (left < right && window needs shrink) {
// d 是将移出窗口的字符
char d = s[left];
window.remove(d)
// 缩小窗口
left++;
// 进行窗口内数据的一系列更新
...
}
}
}
3. 无重复字符的最长子串
双指针+哈希表
int lengthOfLongestSubstring(string s) {
int n = s.size();
unordered_set<char> set;
int res=0;
for (int i = 0, j = 0; i < n; i++) {
while (set.contains(s[i])) {
set.erase(s[j++]);
}
set.insert(s[i]);
res = max(res,i-j+1);
}
return res;
}
438. 找到字符串中所有字母异位词
vector<int> findAnagrams(string s, string p) {
int m = s.size(), n = p.size();
vector<int> ans;
if (m < n) return ans;
vector<int> need(26), window(26);
for (int i = 0; i < n; i++) {
need[p[i]-'a']++ ;
window[s[i]-'a']++;
}
if(need==window){
ans.push_back(0);
}
for(int i=n;i<m;i++){
window[s[i]-'a']++;
window[s[i-n]-'a']--;
if(need==window){
ans.push_back(i-n+1);
}
}
return ans;
}
76. 最小覆盖子串
最经典的滑动窗口
string minWindow(string s, string t) {
int m = s.size(), n = t.size();
vector<int> need(128), window(128);
for (auto c: t) {
need[c]++;
}
int cnt = 0, start = -1, len = INT_MAX;
for (int i = 0, j = 0; i < m; i++) {
char c = s[i];
window[c]++;
if (need[c] >= window[c]) {
cnt++;
}
while (cnt == n) {
if (i - j + 1 < len) {
len = i - j + 1;
start = j;
}
char d = s[j];
if (need[d] >= window[d]) {
cnt--;
}
window[d]--;
j++;
}
}
return start < 0 ? "" : s.substr(start, len);
}
239. 滑动窗口最大值
优先级队列
vector<int> maxSlidingWindow(vector<int> &nums, int k) {
int n = nums.size();
priority_queue<pair<int, int>> q;
for (int i = 0; i < k - 1; i++) {
q.push(make_pair(nums[i], i));
}
vector<int> res;
for (int i = k - 1; i < n; i++) {
q.push({nums[i],i});
while(q.top().second<=i-k){
q.pop();
}
res.push_back(q.top().first);
}
return res;
}
53. 最大子数组和 🌟
滑动窗口 或 动态规划
int maxSubArray(vector<int>& nums) {
int left = 0, right = 0;
int windowSum = 0, maxSum = INT_MIN;
while(right < nums.size()){
// 扩大窗口并更新窗口内的元素和
windowSum += nums[right];
right++;
// 更新答案
maxSum = windowSum > maxSum ? windowSum : maxSum;
// 判断窗口是否要收缩
while(windowSum < 0) {
// 缩小窗口并更新窗口内的元素和
windowSum -= nums[left];
left++;
}
}
return maxSum;
}
int maxSubArray(vector<int>& nums) {
int ans = nums[0], f = nums[0];
for (int i = 1; i < nums.size(); ++i) {
f = max(f, 0) + nums[i];
ans = max(ans, f);
}
return ans;
}
209. 长度最小的子数组
int minSubArrayLen(int target, vector<int>& nums) {
int n=nums.size();
int l=0,r=0;
int sum=0,len=INT_MAX;
while(r<n){
sum+=nums[r];
r++;
while(sum>=target){
len = min(len,r-l);
sum-=nums[l++];
}
}
return len==INT_MAX?0:len;
}
数组
56. 合并区间
阿里面试题
vector<vector<int>> merge(vector<vector<int>> &intervals) {
sort(intervals.begin(), intervals.end(), [](const auto &a, const auto &b) {
return a[0] < b[0];
});
vector<vector<int>> ans;
for (int i = 0; i < intervals.size(); i++) {
int L = intervals[i][0], R = intervals[i][1];
if(ans.empty()||ans.back()[1]<L){
ans.push_back({L,R});
}
else{
ans.back()[1]=max(ans.back()[1],R);
}
}
return ans;
}
4. 寻找两个正序数组的中位数 🌟🌟🌟
困难,hot100,必会题,重点是求两个数组中第k大的数 , 备忘解析 (如果数组只会一题那么必然是它)
double findMedianSortedArrays(vector<int> &nums1, vector<int> &nums2) {
int m = nums1.size(), n = nums2.size();
int a = getKth(0, 0, (m + n + 1) / 2, nums1, nums2);
int b = getKth(0, 0, (m + n + 2) / 2, nums1, nums2);
return (a + b) / 2.0;
}
int getKth(int i, int j, int k, vector<int> &nums1, vector<int> &nums2) {
int m = nums1.size(), n = nums2.size();
if (i >= m) {
return nums2[j + k - 1];
}
if (j >= n) {
return nums1[i + k - 1];
}
if (k == 1) {
return min(nums1[i], nums2[j]);
}
int p = k / 2;
int x = i + p - 1 < m ? nums1[i + p - 1] : INT_MAX;
int y = j + p - 1 < n ? nums2[j + p - 1] : INT_MAX;
return x < y ? getKth(i + p, j, k - p, nums1, nums2) : getKth(i, j + p, k - p, nums1, nums2);
}
88. 合并两个有序数组
经典 逆向双指针
void merge(vector<int> &nums1, int m, vector<int> &nums2, int n) {
for (int i = m - 1, j = n - 1, k = m + n - 1; ~j; k--) {
nums1[k] = i >= 0 && nums1[i] > nums2[j] ? nums1[i--] : nums2[j--];
}
}
27. 移除元素
双指针 so easy
class Solution {
public:
int removeElement(vector<int> &nums, int val) {
int n = nums.size();
int i=0;
for (int j = 0; j < n; j++) {
if(nums[j]!=val){
nums[i++]=nums[j];
}
}
return i;
}
};
11. 盛最多水的容器
双指针
int maxArea(vector<int> &height) {
int i = 0, j = height.size() - 1;
int ans = 0;
while (i < j) {
int t = (j - i) * min(height[i], height[j]);
ans = max(ans, t);
if(height[i]<height[j]){
i++;
}else{
j--;
}
}
return ans;
}
560. 和为 K 的子数组
前缀和的经典应用
int subarraySum(vector<int> &nums, int k) {
unordered_map<int, int> map;
int ans = 0, pre = 0;
map[0] = 1;
for (auto x: nums) {
pre+=x;
if(map.count(pre-k)){
ans+=map[pre-k];
}
map[pre]++;
}
return ans;
}
41. 缺失的第一个正数 🌟
int firstMissingPositive(vector<int> &nums) {
int n = nums.size();
for (int i = 0; i < n; i++) {
while (nums[i] >= 1 && nums[i] <= n && nums[i] != nums[nums[i] - 1]) {
swap(nums[i], nums[nums[i - 1]]);
}
}
for (int i = 0; i < n; ++i) {
if (i + 1 != nums[i]) {
return i + 1;
}
}
return n + 1;
}
矩阵
54. 螺旋矩阵
vector<int> spiralOrder(vector<vector<int>> &matrix) {
vector<int> res;
int dirs[5] = {0, 1, 0, -1, 0};
int m = matrix.size(), n = matrix[0].size();
int i = 0, j = 0, k = 0;
for (int h = m * n; h > 0; h--) {
res.push_back(matrix[i][j]);
matrix[i][j] = 200;
int x = i + dirs[k], y = j + dirs[k + 1];
if (x < 0 || x >= m || y < 0 || y >= n || matrix[x][y] == 200) {
k = (k + 1) % 4;
}
i += dirs[k];
j += dirs[k + 1];
}
return res;
}
48. 旋转图像
vector<vector<int> > rotateMatrix(vector<vector<int> >& mat, int n) {
for(int i=0;i<n>>1;i++){
for(int j=0;j<n;j++){
swap(mat[i][j], mat[n-1-i][j]);
}
}
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
swap(mat[i][j], mat[j][i]);
}
}
return mat;
}
}
void rotate(vector<vector<int>> &matrix) {
int n = matrix.size();
for (int i = 0; i < n / 2; i++) {
for (int j = 0; j < (n + 1) / 2; j++) {
tie(matrix[i][j],matrix[j][n-i-1],matrix[n-i-1][n-j-1],matrix[n-j-1][i]) = \
make_tuple(matrix[n-j-1][i],matrix[i][j],matrix[j][n-i-1],matrix[n-i-1][n-j-1]);
}
}
}
240. 搜索二维矩阵 II
或则直接从左下角开始搜索
bool searchMatrix(vector<vector<int>> &matrix, int target) {
int m = matrix.size(), n = matrix[0].size();
for (int i = 0; i < m; i++) {
auto it = lower_bound(matrix[i].begin(), matrix[i].end(), target);
if (it < matrix[i].end() && *it == target) {
return true;
}
}
return false;
}
字符串
415. 字符串相加 🌟🌟🌟
字符串加减最最常考的字符串题
class Solution {
public:
string addStrings(string num1, string num2) {
int i = num1.size() - 1, j = num2.size() - 1;
string ans;
for (int c = 0; i >= 0 || j >= 0 || c; --i, --j) {
int a = i < 0 ? 0 : num1[i] - '0';
int b = j < 0 ? 0 : num2[j] - '0';
c += a + b;
ans += to_string(c % 10);
c /= 10;
}
reverse(ans.begin(), ans.end());
return ans;
}
string subStrings(string num1, string num2) {
int m = num1.size(), n = num2.size();
bool neg = m < n || (m == n && num1 < num2);
if (neg) {
swap(num1, num2);
}
int i = num1.size() - 1, j = num2.size() - 1;
string ans;
for (int c = 0; i >= 0; --i, --j) {
c = (num1[i] - '0') - c - (j < 0 ? 0 : num2[j] - '0');
ans += to_string((c + 10) % 10);
c = c < 0 ? 1 : 0;
}
while (ans.size() > 1 && ans.back() == '0') {
ans.pop_back();
}
if (neg) {
ans.push_back('-');
}
reverse(ans.begin(), ans.end());
return ans;
}
};
5. 最长回文子串 🌟🌟
必须掌握,方法1:动态规划,方法2:中心扩展法
/* f[i][j] 即 s[i...j]是否为回文串
* if s[i]=s[j],f[i][j]=f[i-1][j+1] */
string longestPalindrome(string s) {
int n = s.size();
vector<vector<bool>> f(n, vector<bool>(n, true));
int k = 0, mx = 1;
for (int i = n - 2; i >= 0; i++) {
for (int j = i + 1; j < n; j++) {
f[i][j] = false;
if (s[i] == s[j]) {
f[i][j] = f[i + 1][j - 1];
if (f[i][j] && mx < j - i + 1) {
mx = j - i + 1;
k = i;
}
}
}
}
return s.substr(k, mx);
}
// 中心扩展发
class Solution1 {
public:
string palindrome(string s, int l, int r) {
int n = s.size();
while (l >= 0 && r < n && s[l] == s[r]) {
l--;
r++;
}
return s.substr(l + 1, r - l - 1);
}
string longestPalindrome(string s) {
string res = "";
for (int i = 0; i < s.length(); i++) {
string s1 = palindrome(s, i, i);
string s2 = palindrome(s, i, i + 1);
res = res.size() > s1.size() ? res : s1;
res = res.size() > s2.size() ? res : s2;
}
return res;
}
};
2的1000次方
字符串乘法,腾讯面试题
#include <iostream>
#include <string>
using namespace std;
string multiplyByTow(const string &num) {
string res;
int carry = 0;
for (int i = num.size() - 1; i >= 0; i--) {
int digit = num[i] - '0';
int product = digit * 2 + carry;
carry = product / 10;
res.insert(res.begin(), '0' + (product % 10));
}
while (carry > 0) {
res.insert(res.begin(), '0' + (carry % 10));
carry /= 10;
}
return res;
}
int main() {
string res = "1";
for (int i = 0; i < 1000; i++) {
res = multiplyByTow(res);
}
cout << "2^1000 = " << res << endl;
}
14. 最长公共前缀
面试不会直接回家种田
string longestCommonPrefix(vector<string> &strs) {
for (int i = 0; i < strs[0].size(); i++) {
for (int j = 1; j < strs.size(); j++) {
if (i >= strs[j].size() || strs[j][i] != strs[0][i]) {
return strs[0].substr(0,i);
}
}
}
return strs[0];
}
1768. 交替合并字符串
超级简单
string mergeAlternately(string word1, string word2) {
int m = word1.size(), n = word2.size();
string ans;
for (int i = 0; i < m || i < n; ++i) {
if (i < m) ans += word1[i];
if (i < n) ans += word2[i];
}
return ans;
}
28. 找出字符串中第一个匹配项的下标 🌟🌟
KMP
void getNext(int *next, const string &s) {
int j = 0;
next[0] = 0;
for (int i = 1; i < s.size(); i++) {
while (j > 0 && s[i] != s[j]) {
j = next[j - 1];
}
if (s[i] == s[j]) {
j++;
}
next[i] = j;
}
}
int strStr(string haystack, string needle) {
if (needle.size() == 0) return 0;
int next[needle.size()];
getNext(next, needle);
int j = 0;
for (int i = 0; i < haystack.size(); i++) {
while (j > 0 && haystack[i] != needle[j]) {
j = next[j - 1];
}
if (haystack[i] == needle[j]) {
j++;
}
if (j == needle.size()) {
return (i - needle.size() + 1);
}
}
return -1;
}
// 面试实在想不出来就直接暴力
int strStr(string s, string p) {
int n = s.size(), m = p.size();
for (int i = 0; i <= n - m; i++) {
int j = i, k = 0;
while (k < m && s[j] == p[k]) {
j++;
k++;
}
if (k == m) return i;
}
return -1;
}
面试题 01.06. 字符串压缩
string compressString(string s) {
if (s.size() == 0) return s;
string res = "";
int cnt = 1;
res += s[0];
for (int i = 1; i < s.size(); i++) {
if (s[i] == s[i - 1]) {
cnt++;
} else {
res += to_string(cnt);
res += s[i];
cnt = 1;
}
}
res+= to_string(cnt);
return res.size() < s.size() ? res : s;
}
链表
206. 反转链表🌟
迭代和递归双方法,这个都不会回家种田吧
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *pre = nullptr;
ListNode *cur = head;
for(;cur!= nullptr;){
ListNode* next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
return pre;
}
};
#include<iostream>
using namespace std;
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(nullptr) {}
};
ListNode *constructList(vector<int> vec) {
ListNode *dummy = new ListNode(0);
ListNode *cur = dummy;
int n = vec.size();
for (int i = 0; i < n; i++) {
ListNode *newNode = new ListNode(vec[i]);
cur->next = newNode;
cur = cur->next;
}
return dummy->next;
}
void printListNode(ListNode *head) {
if (head == nullptr) {
cout << "list is empty" << endl;
return;
}
ListNode *cur = head;
while (cur != nullptr) {
cout << cur->val << " ";
cur = cur->next;
}
cout << endl;
}
ListNode *reverseList(ListNode *head) {
if (head == nullptr || head->next == nullptr) return head;
ListNode *last = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return last;
}
int main() {
vector<int> vec;
int n, val;
cin >> n;
while (n--) {
cin >> val;
vec.push_back(val);
}
ListNode *head = constructList();
printListNode(head);
printListNode(reverseList(head));
}
142. 环形链表 II
经典
ListNode *detectCycle(ListNode *head) {
ListNode *slow=head,*fast = head;
while(fast&& fast->next){
slow=slow->next;
fast=fast->next->next;
if(slow==fast){
slow=head;
while (slow!=fast){
slow=slow->next;
fast=fast->next;
}
return slow;
}
}
return nullptr;
}
21. 合并两个有序链表 🌟🌟🌟
太过经典,和反转链表是链表面试题的半壁江山
ListNode *mergeTwoLists(ListNode *list1, ListNode *list2) {
ListNode *dummy = new ListNode(-1);
ListNode *cur = dummy;
for (; list1 && list2;cur=cur->next) {
if (list1->val <= list2->val) {
cur->next = list1;
list1 = list1->next;
}else{
cur->next = list2;
list2 = list2->next;
}
}
if(list1){
cur->next=list1;
}
if(list2){
cur->next= list2;
}
return dummy->next;
}
2. 两数相加
ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
ListNode *dummy = new ListNode();
int carry = 0;
ListNode *cur = dummy;
while (l1 || l2 || carry) {
int s = (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + carry;
carry = s / 10;
cur->next = new ListNode(s % 10);
cur = cur->next;
l1 = l1?l1->next: nullptr;
l2 = l2?l2->next: nullptr;
}
return dummy->next;
}
19. 删除链表的倒数第 N 个结点 🥰🌟🌟
双指针,必背,必考
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(0,head);
ListNode* node = findKthFromEnd(dummy,n+1);
node->next = node->next->next;
return dummy->next;
}
ListNode* findKthFromEnd(ListNode* head, int k) {
ListNode *fast = head, *slow = head;
while (k--) fast = fast->next;
while (fast) {
fast = fast->next;
slow = slow->next;
}
return slow;
}
24. 两两交换链表中的节点
ListNode *swapPairs(ListNode *head) {
if (head == nullptr || head->next == nullptr) return head;
ListNode* nextNode = head->next;
head->next = swapPairs(nextNode->next);
nextNode->next = head;
return nextNode;
}
25. K 个一组翻转链表
困难
ListNode *reverseKGroup(ListNode *head, int k) {
if (k <= 1) return head;
ListNode *dummy = new ListNode(0, head);
ListNode *pre = dummy, *cur = dummy;
while (cur->next) {
for (int i = 0; i < k && cur != nullptr; i++) {
cur = cur->next;
}
if(cur== nullptr){
return dummy->next;
}
ListNode* nextGroupHead = cur->next;
cur->next= nullptr;
ListNode* start = pre->next;
pre->next = reverseList(start);
start->next = nextGroupHead;
pre = start;
cur = pre;
}
return dummy->next;
}
ListNode *reverseList(ListNode *head) {
if (head == nullptr || head->next == nullptr) return head;
ListNode *last = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return last;
}
138. 随机链表的复制
Node *copyRandomList(Node *head) {
Node *dummy = new Node(-1);
Node *pre = dummy;
unordered_map<Node *, Node *> map;
for (auto cur = head; cur; cur = cur->next) {
pre->next = new Node(cur->val);
pre = pre->next;
map[cur] = pre;
}
pre = dummy->next;
for(auto cur = head;cur;cur=cur->next){
pre->random = map[cur->random];
pre = pre->next;
}
return dummy->next;
}
148. 排序链表
归并排序
ListNode *sortList(ListNode *head) {
if (head == nullptr || head->next == nullptr) {
return head;
}
ListNode *midNode = middleNode(head);
ListNode *rightHead = midNode->next;
midNode->next = nullptr;
ListNode* left = sortList(head);
ListNode* right = sortList(rightHead);
return mergeTwoLists(left,right);
}
// 0876 变
ListNode *middleNode(ListNode *head) {
if (head == nullptr || head->next == nullptr) {
return head;
}
ListNode *slow = head;
ListNode *fast = head->next->next;
while (fast != nullptr && fast->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
// 0021
ListNode *mergeTwoLists(ListNode *list1, ListNode *list2) {
ListNode *dummy = new ListNode(-1);
ListNode *curr = dummy;
while (list1 != nullptr && list2 != nullptr) {
if (list1->val < list2->val) {
curr->next = list1;
list1 = list1->next;
} else {
curr->next = list2;
list2 = list2->next;
}
curr = curr->next;
}
curr->next = list1 != nullptr ? list1 : list2;
return dummy->next;
}
23. 合并 K 个升序链表 🌟🌟🌟
ListNode* mergeKLists(vector<ListNode*>& lists) {
auto fn = [](auto a,auto b){
return a->val>b->val;
};
priority_queue<ListNode*,vector<ListNode*>,decltype(fn)> pq(fn);
for(auto x:lists){
if(x) pq.push(x);
}
ListNode* dummy = new ListNode(-1),*cur = dummy;
while(!pq.empty()){
ListNode* node = pq.top();pq.pop();
cur->next = node;
node = node->next;
if(node) pq.push(node);
cur=cur->next;
}
return dummy->next;
}
归并排序
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode temp_head(0);
ListNode *pre = &temp_head;
while (l1 && l2){
if (l1->val < l2->val){
pre->next = l1;
l1 = l1->next;
}
else{
pre->next = l2;
l2 = l2->next;
}
pre = pre->next;
}
if (l1){
pre->next = l1;
}
if (l2){
pre->next = l2;
}
return temp_head.next;
}
ListNode* merge(vector <ListNode*>& lists,int l,int r){
if(l==r) return lists[l];
if(l>r) return nullptr;
int mid=(l+r)>>1;
return mergeTwoLists(merge(lists,l,mid),merge(lists,mid+1,r)) ;
}
ListNode* mergeKLists(std::vector<ListNode*>& lists) {
return merge(lists,0,lists.size()-1);
}
82. 删除排序链表中的重复元素 II
ListNode *deleteDuplicates(ListNode *head) {
ListNode *dummy = new ListNode(-1);
dummy->next = head;
ListNode *cur = head, *pre = dummy;
while (cur) {
while (cur->next && cur->val == cur->next->val) {
cur = cur->next;
}
if (pre->next == cur) {
pre = cur;
} else {
pre->next = cur->next;
}
}
return dummy->next;
}
83. 删除排序链表中的重复元素
ListNode *deleteDuplicates(ListNode *head) {
ListNode* cur = head;
while (cur != nullptr && cur->next != nullptr) {
if (cur->val == cur->next->val) {
cur->next = cur->next->next;
} else {
cur = cur->next;
}
}
return head;
}
设计
208. 实现 Trie (前缀树)
非常重要,还有他的扩展 基数树
class Trie {
bool end;
Trie *next[26];
public:
Trie() : end(false) {
memset(next, 0, sizeof next);
}
void insert(string word) {
Trie *cur = this;
for (char c: word) {
if (cur->next[c - 'a'] == nullptr) {
cur->next[c - 'a'] = new Trie();
}
cur = cur->next[c - 'a'];
}
cur->end = true;
}
Trie *searchNode(string word) {
Trie *cur = this;
for (char c: word) {
if (cur->next[c - 'a'] == nullptr) {
return nullptr;
}
cur = cur->next[c - 'a'];
}
return cur;
}
bool search(string word) {
Trie *node = searchNode(word);
return node != nullptr && node->end;
}
bool startsWith(string prefix) {
return searchNode(prefix)!= nullptr;
}
};
146. LRU 缓存
list+unordered_map,使用库函数更简单,但是常规写法还是手写双向链表,速度更快。
#include <iostream>
#include <unordered_map>
#include <list>
using namespace std;
class LRUCache {
public:
struct Node {
int key;
int val;
Node *pre;
Node *next;
Node(int x, int y) : key(x), val(y) {
}
};
Node *head, *last;
unordered_map<int, Node *> map;
int capacity;
int len;
LRUCache(int capacity) : capacity(capacity), len(0) {
head = new Node(-1, -1);
last = new Node(-1, -1);
head->next = last;
last->pre = head;
}
int get(int key) {
if (map.count(key)) {
Node *node = map[key];
moveToHead(node);
return node->val;
}
return -1;
}
void put(int key, int value) {
if (map.count(key)) {
Node *node = map[key];
node->val = value;
moveToHead(node);
} else {
len++;
if (len > capacity) {
len--;
Node *delNode = last->pre;
remove(delNode);
map.erase(delNode->key);
}
Node *newNode = new Node(key, value);
map[key] = newNode;
newNode->next = head->next;
head->next->pre = newNode;
newNode->pre = head;
head->next = newNode;
}
}
void moveToHead(Node *node) {
node->pre->next = node->next;
node->next->pre = node->pre;
node->next = head->next;
head->next->pre = node;
node->pre = head;
head->next = node;
}
void remove(Node *node) {
node->pre->next = node->next;
node->next->pre = node->pre;
}
};
int main() {
LRUCache lru(2);
lru.put(2,1);
lru.put(2,2);
cout<<lru.get(2)<<endl;
lru.put(1,1);
lru.put(4,1);
cout<<lru.get(2)<<endl;
}
341. 扁平化嵌套列表迭代器
二叉树
104. 二叉树的最大深度
int maxDepth(TreeNode* root) {
if (root== nullptr) return 0;
int left = maxDepth(root->left);
int right = maxDepth(root->right);
int res = max(left,right)+1;
return res;
}
226. 翻转二叉树
TreeNode *invertTree(TreeNode *root) {
if (root == nullptr) return nullptr;
TreeNode *left = invertTree(root->left);
TreeNode *right = invertTree(root->right);
root->left = right;
root->right = left;
return root;
}
101. 对称二叉树
bool isSymmetric(TreeNode *root) {
return dfs(root,root);
}
bool dfs(TreeNode *left, TreeNode *right) {
if (!left && !right) return true;
if (!left || !right || left->val != right->val) return false;
return dfs(left->left, right->right) && dfs(left->right, right->left);
}
543. 二叉树的直径
class Solution {
public:
int res = 0;
int diameterOfBinaryTree(TreeNode* root) {
maxDepth(root);
return res;
}
int maxDepth(TreeNode* root){
if(root== nullptr){
return 0;
}
int left = maxDepth(root->left);
int right = maxDepth(root->right);
int sum = left+right;
res = max(res,sum);
return max(left,right)+1;
}
};
114. 二叉树展开为链表
void flatten(TreeNode *root) {
if (root == nullptr) return;
flatten(root->left);
flatten(root->right);
TreeNode *left = root->left;
TreeNode *right = root->right;
root->left = nullptr;
root->right = left;
TreeNode *p = root;
while(p->right){
p=p->right;
}
p->right = right;
}
437. 路径总和 III
int rootSum(TreeNode *root, long targetSum) {
if (!root) {
return 0;
}
int ret = 0;
if (root->val == targetSum) {
ret++;
}
ret += rootSum(root->left, targetSum - root->val);
ret += rootSum(root->right, targetSum - root->val);
return ret;
}
int pathSum(TreeNode *root, int targetSum) {
if (!root) {
return 0;
}
int ret = rootSum(root, targetSum);
ret += pathSum(root->left, targetSum);
ret += pathSum(root->right, targetSum);
return ret;
}
236. 二叉树的最近公共祖先
func lowestCommonAncestor236(root, p, q *TreeNode) *TreeNode {
if root == nil || root == q || root == p {
return root
}
left := lowestCommonAncestor236(root.Left, p, q)
right := lowestCommonAncestor236(root.Right, p, q)
if left != nil {
if right != nil {
return root
}
return left
}
return right
}
124. 二叉树中的最大路径和 🌟🌟
最简单的困难题
class Solution {
public:
int res=INT_MIN;
int maxPathSum(TreeNode* root) {
maxSide(root);
return res;
}
int maxSide(TreeNode* root){
if(!root) return 0;
int left = maxSide(root->left);
left = max(0,left);
int right = maxSide(root->right);
right = max(0,right);
res=max(res,left+right+root->val);
return root->val+max(left,right);
}
};
层序遍历
102. 二叉树的层序遍历 必须掌握
vector<vector<int>> levelOrder(TreeNode *root) {
vector<vector<int>> res;
if (root == nullptr) return res;
queue<TreeNode *> q;
q.push(root);
while (!q.empty()) {
int n = q.size();
vector<int> temp;
for(int i=0;i<n;i++){
TreeNode* cur = q.front();
q.pop();
temp.push_back(cur->val);
if(cur->left) q.push(cur->left);
if(cur->right) q.push(cur->right);
}
res.push_back(temp);
}
return res;
}
扩展dfs方法 107. 二叉树的层序遍历 II
vector<vector<int>> levelOrderBottom(TreeNode *root) {
vector<vector<int>> ans;
dfs(ans, root, 0);
reverse(ans.begin(), ans.end());// 反转
return ans;
}
void dfs(vector<vector<int>> &ans, TreeNode *root, int level) {
if (root == nullptr)
return;
// 初始化下一层的集合
if (level == ans.size()) {
vector<int> tmp;
ans.emplace_back(tmp);
}
// 把节点的值添加到对应的集合中
ans[level].push_back(root->val);
// 递归左右子树
dfs(ans, root->left, level + 1);
dfs(ans, root->right, level + 1);
}
构造
105. 从前序与中序遍历序列构造二叉树 🌟🌟
重建二叉树_牛客题霸_牛客网 前中序构造二叉树, 太经典了
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
unordered_map<int,int>map;
for(int i=0;i<inorder.size();i++){
map[inorder[i]] = i;
}
function<TreeNode*(int,int,int)> build = [&](int i,int j,int n)->TreeNode*{
if(n<=0) return nullptr;
TreeNode* root = new TreeNode(preorder[i]);
int mid = map[preorder[i]];
int l = mid - j;
root->left = build(i+1,j,l);
root->right = build(i+l+1,mid+1,n-l-1);
return root;
};
return build(0,0,preorder.size());
}
二叉搜索树
108. 将有序数组转换为二叉搜索树
TreeNode *sortedArrayToBST(vector<int> &nums) {
return dfs(0,nums.size()-1,nums);
}
TreeNode *dfs(int l, int r, vector<int> &nums) {
if (l > r) return nullptr;
int mid = (l + r) >> 1;
TreeNode *root = new TreeNode(nums[mid]);
root->left = dfs(l, mid - 1,nums);
root->right = dfs(mid + 1, r,nums);
return root;
}
98. 验证二叉搜索树
class Solution {
public:
long pre = LONG_MIN;
bool isValidBST(TreeNode *root) {
if(root== nullptr) return true;
if(!isValidBST(root->left)) return false;
if(root->val<=pre) return false;
pre = root->val;
if(!isValidBST(root->right)) return false;
return true;
}
};
230. 二叉搜索树中第K小的元素 🌟
迭代遍历更加简单
int kthSmallest(TreeNode *root, int k) {
stack<TreeNode *> st;
while (root || !st.empty()) {
if (root) {
st.push(root);
root = root->left;
} else {
root = st.top();
st.pop();
if (--k == 0) return root->val;
root = root->right;
}
}
return 0;
}
// 递归法
class Solution {
public:
int k;
int kthSmallest(TreeNode* root, int k) {
this->k= k;
inorder(root);
return res;
}
int kth = 0;
int res;
void inorder(TreeNode* root){
if(!root) return;
inorder(root->left);
kth++;
if(kth==k){
res = root->val;
return;
}
inorder(root->right);
}
};
图
岛屿问题 dfs 版
如果面试出现图的问题,90%的可能性是岛屿问题
网格dfs遍历框架代码
// - 0 —— 海洋格子 - 1 —— 陆地格子(未遍历过) - 2 —— 陆地格子(已遍历过)
int dirs[5] = {-1, 0, 1, 0, -1};
function<void(int, int)> dfs = [&](int i, int j) {
grid[i][j] = '2';
for (int k = 0; k < 4; ++k) {
int x = i + dirs[k], y = j + dirs[k + 1];
if (x >= 0 && x < grid.size() && y >= 0 && y < grid[0].size() && grid[x][y] == '1') {
dfs(x, y);
}
}
};
200. 岛屿数量
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int m = grid.size();
int n = grid[0].size();
int ans = 0;
int dirs[5] = {-1, 0, 1, 0, -1};
function<void(int, int)> dfs = [&](int i, int j) {
grid[i][j] = '2';
for (int k = 0; k < 4; ++k) {
int x = i + dirs[k], y = j + dirs[k + 1];
if (x >= 0 && x < grid.size() && y >= 0 && y < grid[0].size() && grid[x][y] == '1') {
dfs(x, y);
}
}
};
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] == '1') {
dfs(i, j);
++ans;
}
}
}
return ans;
}
};
994. 腐烂的橘子
int orangesRotting(vector<vector<int>> &grid) {
int rows = grid.size(), cols = grid[0].size(), count = 0;
int dis[10][10];
memset(dis, -1, sizeof(dis));
vector<pair<int, int>> directions = {{1, 0},
{-1, 0},
{0, 1},
{0, -1}};
queue<pair<int, int>> que;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (grid[i][j] == 2) {
que.push(make_pair(i, j));
dis[i][j] = 0;
}
if (grid[i][j] == 1) {
count++;
}
}
}
int time=0;
while (!que.empty()) {
auto [i, j] = que.front();
time = dis[i][j];
que.pop();
for (const auto &dir: directions) {
int ni = i + dir.first;
int nj = j + dir.second;
if (ni >= 0 && ni < rows && nj >= 0 && nj < cols && grid[ni][nj] == 1) {
grid[ni][nj] = 2;
count--;
que.push(std::make_pair(ni, nj));
dis[ni][nj] = time+1;
}
}
}
return count == 0 ? time : -1;
}
207. 课程表
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<int> indegrees(numCourses,0);
vector<vector<int>> edges(numCourses);
for(const auto& info:prerequisites){
edges[info[1]].push_back(info[0]);
++indegrees[info[0]];
}
queue<int> que;
for(int i=0;i<numCourses;i++){
if(indegrees[i]==0)
que.push(i);
}
while(!que.empty()){
int pre = que.front();que.pop();
numCourses--;
for(auto &cur:edges[pre]){
if(--indegrees[cur]==0)
que.push(cur);
}
}
return numCourses==0;
}
210. 课程表 II
class Solution {
private:
// 存储有向图
vector<vector<int>> edges;
// 标记每个节点的状态:0=未搜索,1=搜索中,2=已完成
vector<int> visited;
// 用数组来模拟栈,下标 0 为栈底,n-1 为栈顶
vector<int> result;
// 判断有向图中是否有环
bool valid = true;
public:
void dfs(int u) {
// 将节点标记为「搜索中」
visited[u] = 1;
// 搜索其相邻节点
// 只要发现有环,立刻停止搜索
for (int v: edges[u]) {
// 如果「未搜索」那么搜索相邻节点
if (visited[v] == 0) {
dfs(v);
if (!valid) {
return;
}
}
// 如果「搜索中」说明找到了环
else if (visited[v] == 1) {
valid = false;
return;
}
}
// 将节点标记为「已完成」
visited[u] = 2;
// 将节点入栈
result.push_back(u);
}
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
edges.resize(numCourses);
visited.resize(numCourses);
for (const auto& info: prerequisites) {
edges[info[1]].push_back(info[0]);
}
// 每次挑选一个「未搜索」的节点,开始进行深度优先搜索
for (int i = 0; i < numCourses && valid; ++i) {
if (!visited[i]) {
dfs(i);
}
}
if (!valid) {
return {};
}
// 如果没有环,那么就有拓扑排序
// 注意下标 0 为栈底,因此需要将数组反序输出
reverse(result.begin(), result.end());
return result;
}
};
回溯
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
排列、组合|子集 🌟🌟
// 78 子集
vector<vector<int>> subsets(vector<int> &nums) {
backtrace(nums, 0);
return res;
}
vector<vector<int>> res;
vector<int> track;
void backtrace(vector<int> &nums, int start) {
res.push_back(track);
for (int i = start; i < nums.size(); i++) {
track.push_back(nums[i]);
backtrace(nums, i + 1);
track.pop_back();
}
}
// 77 组合
vector<vector<int>> combine(int n, int k) {
backtrace(n, k, 0);
return res;
}
vector<vector<int>> res;
vector<int> track;
void backtrace(int n, int k, int start) {
if (track.size() == k) {
res.push_back(track);
return;
}
for (int i = start; i < n; i++) {
track.push_back(i + 1);
backtrace(n, k, i + 1);
track.pop_back();
}
}
// 46 全排列
vector<vector<int>> permute(vector<int> &nums) {
visited = vector<bool>(nums.size());
backtrace(nums);
return res;
}
vector<vector<int>> res;
vector<int> track;
vector<bool> visited;
void backtrace(vector<int> nums) {
if (nums.size() == track.size()) {
res.push_back(track);
return;
}
for (int i = 0; i < nums.size(); i++) {
if (visited[i]) continue;
visited[i] = true;
track.push_back(nums[i]);
backtrace(nums);
track.pop_back();
visited[i] = false;
}
}
39. 组合总和
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
vector<int> candidates;
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
this->candidates = candidates;
backtrace(0, target);
return res;
}
void backtrace(int start, int target) {
if (target == 0) {
res.push_back(path);
return;
}
if (target < 0) {
return;
}
for (int i = start; i < candidates.size(); i++) {
path.push_back(candidates[i]);
backtrace(i, target - candidates[i]);
path.pop_back();
}
}
};
小于n的最大数
数组A中给定可以使用的1~9的数,返回由A数组中的元素组成的小于n的最大数。例如A={1, 2, 4, 9},x=2533,返回2499
#include <iostream>
#include <vector>
using namespace std;
vector<int> choice={1, 2, 4, 9};
int num = 0,res=0;
int target=2533;
void backtrace(int num) {
for(int i=0;i<choice.size();i++){
if(num*10+choice[i]>target){
return;
}else{
res = max(res,num*10+choice[i]);
backtrace(num*10+choice[i]);
}
}
}
int main() {
backtrace(0);
cout<<res;
}
17. 电话号码的字母组合
class Solution {
public:
unordered_map<char, string> phoneMap{
{'2', "abc"},
{'3', "def"},
{'4', "ghi"},
{'5', "jkl"},
{'6', "mno"},
{'7', "pqrs"},
{'8', "tuv"},
{'9', "wxyz"}
};
void backtrace(vector<string> &res, const string &digits, string &combination) {
int index = combination.size();
if (index == digits.size()) {
res.push_back(combination);
return;
}
char digit = digits[index++];
const string &letters = phoneMap[digit];
for (const char &letter: letters) {
combination.push_back(letter);
backtrace(res,digits,combination);
combination.pop_back();
}
}
vector<string> letterCombinations(string digits) {
vector<string> res;
if (digits == "")return res;
string combination = "";
backtrace(res, digits, combination);
return res;
}
};
22. 括号生成
void backtrace(string &cur, vector<string> &res, int n, int open, int close) {
if (cur.size() == 2 * n) {
res.push_back(cur);
return;
}
if (open < n) {
cur.push_back('(');
backtrace(cur, res, n, open + 1, close);
cur.pop_back();
}
if (close < open) {
cur.push_back(')');
backtrace(cur, res, n, open, close + 1);
cur.pop_back();
}
}
vector<string> generateParenthesis(int n) {
vector<string> res;
string cur;
backtrace(cur, res, n, 0, 0);
return res;
}
79. 单词搜索
class Solution {
public:
string word;
vector<vector<char>> board;
bool valid(int x,int y,int k){
if(x>=0&&x<board.size()&&y>=0&&y<board[0].size()&&board[x][y]==word[k]) return true;
return false;
}
bool dfs(int x,int y,int k){
if(k>=word.size()) return true;
if(!valid(x,y,k)) return false;
// cout<<x<<" "<<y<<" "<<board[x][y]<<endl;
board[x][y]='.';
int dirs[5]={-1,0,1,0,-1};
for(int i=0;i<4;i++){
int ax = x+dirs[i],ay = y+dirs[i+1];
if(dfs(ax,ay,k+1)) return true;
}
board[x][y]=word[k];
return false;
}
bool exist(vector<vector<char>>& board, string word) {
this->board = board;
this->word = word;
int m = board.size(),n=board[0].size();
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(dfs(i,j,0)) return true;
}
}
return false;
}
};
131. 分割回文串
class Solution {
public:
vector<vector<string>> partition(string s) {
int n = s.size();
bool f[n][n];
memset(f, true, sizeof(f));
for (int i = n - 1; i >= 0; --i) {
for (int j = i + 1; j < n; ++j) {
f[i][j] = s[i] == s[j] && f[i + 1][j - 1];
}
}
vector<vector<string>> ans;
vector<string> t;
function<void(int)> dfs = [&](int i) {
if (i == n) {
ans.push_back(t);
return;
}
for (int j = i; j < n; ++j) {
if (f[i][j]) {
t.push_back(s.substr(i, j - i + 1));
dfs(j + 1);
t.pop_back();
}
}
};
dfs(0);
return ans;
}
};
//或
bool isPalindrome(const string &s, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
if (s[i] != s[j])
return false;
}
return true;
}
51. N 皇后
class Solution {
public:
vector<vector<string >> res;
bool isValid(vector<string> &board, int row, int col) {
int n = board.size();
for (int i = 0; i < row; i++) {
if (board[i][col] == 'Q') return false;
}
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if(board[i][j]=='Q' ) return false;
}
for(int i=row-1,j=col+1;i>=0&&j<n;i--,j++){
if(board[i][j]=='Q' ) return false;
}
return true;
}
void backtrack(vector<string> &board, int row) {
if (row == board.size()) {
res.push_back(board);
return;
}
int n = board[row].size();
for (int col = 0; col < n; col++) {
if (isValid(board, row, col)) {
board[row][col] = 'Q';
backtrack(board, row + 1);
board[row][col] = '.';
}
}
}
vector<vector<string >> solveNQueens(int n) {
vector<string> board(n, string(n, '.'));
backtrack(board, 0);
return res;
}
};
排序与搜索
35. 搜索插入位置
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0, right = n - 1, ans = n;
while (left <= right) {
int mid = ((right - left) >> 1) + left;
if (target <= nums[mid]) {
ans = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return ans;
}
};
215. 数组中的第K个最大元素 🌟🌟🌟
经典快速选择排序 或 使用 堆(手写堆)
int findKthLargest(vector<int> &nums, int k) {
int n = nums.size();
return quicksort(nums, 0, n - 1, n - k);
}
int quicksort(vector<int> &nums, int l, int r, int k) {
if (l == r) return nums[l];
int i = l - 1, j = r + 1;
int x = nums[l + 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]);
}
return k<=j?quicksort(nums, l, j, k):quicksort(nums, j + 1, r, k);
}
快速排序
void quick_sort(int q[], int l, int r)
{
if (l >= r) return;
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j)
{
do i ++ ; while (q[i] < x);
do j -- ; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j), quick_sort(q, j + 1, r);
}
归并排序 🌟🌟
void merge_sort(int q[], int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(q, l, mid);
merge_sort(q, mid + 1, r);
int k = 0, i = l, j = mid + 1, tmp[r-l+1];
while (i <= mid && j <= r)
if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
else tmp[k ++ ] = q[j ++ ];
while (i <= mid) tmp[k ++ ] = q[i ++ ];
while (j <= r) tmp[k ++ ] = q[j ++ ];
for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}
二分搜索🌟
lower_bound(row.begin(), row.end(), target) // cpp 二分查找找到第一个大于等于 target 的元素
sort.SearchInts(row, target) // go
// 红蓝分区 神中神
int l = -1,r = n;
while(l+1 != r)
{
int mid = l + (r-l >>1);
if(check()) l=mid;
else r=mid;
//最后根据你所分左右两边区间的结果
//选取L或者R作为结果
}
// 经典方法 双闭区间
int binary_search(vector<int>& nums, int target) {
int left = 0, right = nums.size()-1;
while(left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if(nums[mid] == target) {
return mid;
}
}
return -1;
}
int left_bound(vector<int>& nums, int target) {
int left = 0, right = nums.size()-1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
// 别返回,锁定左侧边界
right = mid - 1;
}
}
// 判断 target 是否存在于 nums 中
if (left < 0 || left >= nums.size()) {
return -1;
}
// 判断一下 nums[left] 是不是 target
return nums[left] == target ? left : -1;
}
int right_bound(vector<int>& nums, int target) {
int left = 0, right = nums.size()-1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
// 别返回,锁定右侧边界
left = mid + 1;
}
}
if (right < 0 || right >= nums.size()) {
return -1;
}
return nums[right] == target ? right : -1;
}
154. 寻找旋转排序数组中的最小值 II
class Solution {
public:
int findMin(vector<int>& nums) {
int i = 0, j = nums.size() - 1;
while (i < j) {
int m = (i + j) / 2;
if (nums[m] > nums[j]) i = m + 1;
else if (nums[m] < nums[j]) j = m;
else j--;
}
return nums[i];
}
};
34. 在排序数组中查找元素的第一个和最后一个位置
class Solution {
public:
int left_bound(vector<int>& nums, int target) {
int left = 0, right = nums.size()-1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
// 别返回,锁定左侧边界
right = mid - 1;
}
}
// 判断 target 是否存在于 nums 中
if (left < 0 || left >= nums.size()) {
return -1;
}
// 判断一下 nums[left] 是不是 target
return nums[left] == target ? left : -1;
}
int right_bound(vector<int>& nums, int target) {
int left = 0, right = nums.size()-1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
// 别返回,锁定右侧边界
left = mid + 1;
}
}
if (right < 0 || right >= nums.size()) {
return -1;
}
return nums[right] == target ? right : -1;
}
vector<int> searchRange(vector<int>& nums, int target) {
int left = left_bound(nums,target);
int right = right_bound(nums,target);
return {left,right};
}
};
33. 搜索旋转排序数组
class Solution {
public:
int search(vector<int>& nums, int target) {
int n = (int)nums.size();
if (!n) {
return -1;
}
if (n == 1) {
return nums[0] == target ? 0 : -1;
}
int l = 0, r = n - 1;
while (l <= r) {
int mid = (l + r) / 2;
if (nums[mid] == target) return mid;
if (nums[0] <= nums[mid]) {
if (nums[0] <= target && target < nums[mid]) {
r = mid - 1;
} else {
l = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[n - 1]) {
l = mid + 1;
} else {
r = mid - 1;
}
}
}
return -1;
}
};
栈
155. 最小栈
class MinStack {
stack<int> st;
stack<int> minSt;
public:
MinStack() {
}
void push(int val) {
st.push(val);
if(minSt.empty()||minSt.top()>val){
minSt.push(val);
}else{
minSt.push(minSt.top());
}
}
void pop() {
st.pop();
minSt.pop();
}
int top() {
return st.top();
}
int getMin() {
return minSt.top();
}
};
394. 字符串解码
string decodeString(string s) {
stack<int> nums;
stack<string> strs;
string res = "";
int num = 0, n = s.size();
for (auto c : s) {
if (isdigit(c)) {
num = num * 10 + c - '0';
} else if (c >= 'a' && c <= 'z') {
res = res + c;
} else if (c == '[') // 将‘[’前的数字压入nums栈内,
// 字母字符串压入strs栈内
{
nums.push(num);
num = 0;
strs.push(res);
res = "";
} else // 遇到‘]’时,操作与之相配的‘[’之间的字符,使用分配律
{
int k = nums.top();
nums.pop();
for (int j = 0; j < k; ++j)
strs.top() += res;
res =
strs.top(); // 之后若还是字母,就会直接加到res之后,因为它们是同一级的运算
// 若是左括号,res会被压入strs栈,作为上一层的运算
strs.pop();
}
}
return res;
}
20. 有效的括号🌟🌟🌟
栈的最典型应用题
class Solution {
public:
bool isValid(string s) {
stack<char> st;
for (const char c: s) {
if(c=='('){
st.push(')');
}else if(c=='['){
st.push(']');
}else if(c=='{'){
st.push('}');
}else{
if(st.empty()||st.top()!=c){
return false;
}else{
st.pop();
}
}
}
return st.empty();
}
};
出栈合法性
典中典
#include <iostream>
#include <stack>
using namespace std;
int main() {
int n;
int nums[105];
while (cin >> n) {
if (n == 0) break;
for (int i = 0; i < n; i++) {
cin >> nums[i];
}
stack<int> st;
int index = 0;
for (int i = 1; i <= n; i++) {
st.push(i);
while (!st.empty() && st.top() == nums[index]) {
st.pop();
index++;
}
}
if (st.empty() && index == n) {
cout << "Yes" << endl;
} else {
cout << "No" << endl;
}
}
}
739. 每日温度
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
vector<int> res(n,0);
stack<int> stk;
for(int i=0;i<n;i++){
while(!stk.empty()&&temperatures[stk.top()]<temperatures[i]){
res[stk.top()] = i-stk.top();
stk.pop();
}
stk.push(i);
}
return res;
}
84. 柱状图中最大的矩形
int largestRectangleArea(vector<int> &heights)
{
int n = heights.size();
stack<int> st;
int res = 0;
heights.insert(heights.begin(), 0);
heights.push_back(0);
for (int i = 0; i < heights.size(); i++)
{
while(!st.empty()&&heights[st.top()]>heights[i]){
int cur = st.top();
st.pop();
int w = i-1-st.top();
res = max(res,w*heights[cur]);
}
st.push(i);
}
return res;
}
堆
priority_queue<int> max_heap; // 默认为最大堆
priority_queue<int, vector<int>, greater<int>> min_heap; // 最小堆
// 比较函数是子与父比较
703. 数据流中的第 K 大元素
优先级队列
class KthLargest {
public:
priority_queue<int, vector<int>, greater<int>> q;
int kth;
KthLargest(int k, vector<int> &nums) {
kth = k;
for (auto &x: nums) {
add(x);
}
}
int add(int val) {
q.push(val);
if(q.size()>kth){
q.pop();
}
return q.top();
}
};
347. 前 K 个高频元素
vector<int> topKFrequent(vector<int> &nums, int k) {
unordered_map<int, int> occurrences;
for (auto &v: nums) {
occurrences[v]++;
}
auto myComparison = [](const pair<int, int> &a, const pair<int, int> &b) {
return a.second > b.second; // 升序排序
};
// pair 的第一个元素代表数组的值,第二个元素代表了该值出现的次数
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(myComparison)> q(myComparison);
for (auto &[num, count]: occurrences) {
if (q.size() == k) {
if (q.top().second < count) {
q.pop();
q.emplace(num, count);
}
} else {
q.emplace(num, count);
}
}
vector<int> ret;
while (!q.empty()) {
ret.emplace_back(q.top().first);
q.pop();
}
return ret;
}
295. 数据流的中位数
class MedianFinder {
public:
/** initialize your data structure here. */
MedianFinder() {
}
void addNum(int num) {
q1.push(num);
q2.push(q1.top());
q1.pop();
if (q2.size() - q1.size() > 1) {
q1.push(q2.top());
q2.pop();
}
}
double findMedian() {
if (q2.size() > q1.size()) {
return q2.top();
}
return (double) (q1.top() + q2.top()) / 2;
}
private:
priority_queue<int, vector<int>, greater<int>> q1;
priority_queue<int> q2;
};
贪心
122. 买卖股票的最佳时机 II
可能是最简单的贪心题了,也可以用动态规划,稍难
int maxProfit(vector<int> &prices) {
int n = prices.size();
int ans=0;
for (int i = 0; i < n - 1; i++) {
ans+=max(0,(prices[i+1]-prices[i]));
}
return ans;
}
// 动规
int maxProfit(vector<int>& prices) {
int n = prices.size();
int f[n][2];
f[0][0] = -prices[0];
f[0][1] = 0;
for (int i = 1; i < n; ++i) {
f[i][0] = max(f[i - 1][0], f[i - 1][1] - prices[i]);
f[i][1] = max(f[i - 1][1], f[i - 1][0] + prices[i]);
}
return f[n - 1][1];
}
55. 跳跃游戏
class Solution {
public:
bool canJump(vector<int>& nums) {
int n=nums.size();
int maxjump=0;
for(int i=0;i<n;i++){
if(maxjump>=i){
maxjump=max(maxjump,nums[i]+i);
if(maxjump>=n-1){
return true;
}
}
}
return false;
}
};
45. 跳跃游戏 II
class Solution {
public:
int jump(vector<int>& nums) {
int n=nums.size();
if(n<2){
return 0;
}
int curr_max=nums[0];
int pre_max=nums[0];
int jumpMin=1;
for(int i=0;i<n;i++){
if(i>curr_max){
curr_max=pre_max;
jumpMin++;
if(curr_max>=n-1){
return jumpMin;
}
}
if(pre_max<i+nums[i]){
pre_max=i+nums[i];
}
}
return jumpMin;
}
};
763. 划分字母区间
class Solution {
public:
vector<int> partitionLabels(string s) {
int last[26];
int length = s.size();
for (int i = 0; i < length; i++) {
last[s[i] - 'a'] = i;
}
vector<int> partition;
int start = 0, end = 0;
for (int i = 0; i < length; i++) {
end = max(end, last[s[i] - 'a']);
if (i == end) {
partition.push_back(end - start + 1);
start = end+1;
}
}
return partition;
}
};
动态规划
70. 爬楼梯
唯一会的动态规划😅
class Solution {
public:
int climbStairs(int n) {
if (n <= 2) return n;
int dp[2] = {1, 2};
for (int i = 3; i <= n; i++) {
int next = dp[0] + dp[1];
dp[0] = dp[1];
dp[1] = next;
}
return dp[1];
}
};
118. 杨辉三角
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> ret(numRows);
for (int i = 0; i < numRows; ++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;
}
};
198. 打家劫舍
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size()==0) return 0;
if(nums.size()==1) return nums[0];
vector<int> dp(nums.size());
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
for(int i=2;i<nums.size();i++){
dp[i] = max(dp[i-2]+nums[i],dp[i-1]);
}
return dp[nums.size()-1];
}
};
279. 完全平方数
class Solution {
public:
int numSquares(int n) {
vector<int> f(n + 1);
for (int i = 1; i <= n; i++) {
int minn = INT_MAX;
for (int j = 1; j * j <= i; j++) {
minn = min(minn, f[i - j * j]);
}
f[i] = minn + 1;
}
return f[n];
}
};
322. 零钱兑换
class Solution {
public:
int coinChange(vector<int> &coins, int amount) {
vector<int> dp(amount + 1, amount+1);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (const auto &coin: coins) {
if(i-coin<0){
continue;
}
dp[i]=min(dp[i],dp[i-coin]+1);
}
}
return dp[amount]==amount+1?-1:dp[amount];
}
};
139. 单词拆分
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> words(wordDict.begin(), wordDict.end());
int n = s.size();
bool f[n + 1];
memset(f, false, sizeof(f));
f[0] = true;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < i; ++j) {
if (f[j] && words.count(s.substr(j, i - j))) {
f[i] = true;
break;
}
}
}
return f[n];
}
140. 单词拆分 II
class Solution {
private:
unordered_map<int, vector<string>> ans;
unordered_set<string> wordSet;
public:
vector<string> wordBreak(string s, vector<string>& wordDict) {
wordSet = unordered_set(wordDict.begin(), wordDict.end());
backtrack(s, 0);
return ans[0];
}
void backtrack(const string& s, int index) {
if (!ans.count(index)) {
if (index == s.size()) {
ans[index] = {""};
return;
}
ans[index] = {};
for (int i = index + 1; i <= s.size(); ++i) {
string word = s.substr(index, i - index);
if (wordSet.count(word)) {
backtrack(s, i);
for (const string& succ: ans[i]) {
ans[index].push_back(succ.empty() ? word : word + " " + succ);
}
}
}
}
}
};
1143. 最长公共子序列
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int m = text1.length(), n = text2.length();
vector<vector<int>> dp(m + 1, vector<int>(n + 1));
for (int i = 1; i <= m; i++) {
char c1 = text1.at(i - 1);
for (int j = 1; j <= n; j++) {
char c2 = text2.at(j - 1);
if (c1 == c2) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
};
32. 最长有效括号
class Solution {
public:
int longestValidParentheses(string s) {
int size = s.length();
vector<int> dp(size, 0);
int maxVal = 0;
for(int i = 1; i < size; i++) {
if (s[i] == ')') {
if (s[i - 1] == '(') {
dp[i] = 2;
if (i - 2 >= 0) {
dp[i] = dp[i] + dp[i - 2];
}
} else if (dp[i - 1] > 0) {
if ((i - dp[i - 1] - 1) >= 0 && s[i - dp[i - 1] - 1] == '(') {
dp[i] = dp[i - 1] + 2;
if ((i - dp[i - 1] - 2) >= 0) {
dp[i] = dp[i] + dp[i - dp[i - 1] - 2];
}
}
}
}
maxVal = max(maxVal, dp[i]);
}
return maxVal;
}
};
300. 最长递增子序列
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
vector<int> f(n, 1);
for (int i = 1; i < n; ++i) {
for (int j = 0; j < i; ++j) {
if (nums[j] < nums[i]) {
f[i] = max(f[i], f[j] + 1);
}
}
}
return *max_element(f.begin(), f.end());
}
};
91. 解码方法
int numDecodings(string s) {
int n = s.size();
int f[n + 1];
memset(f, 0, sizeof f);
f[0] = 1;
for (int i = 1; i <= n; i++) {
if (s[i - 1] != '0') {
f[i] = f[i - 1];
}
if (i > 1 && (s[i - 2] == '1' || s[i - 2] == '2' && s[i - 1] <= '6')) {
f[i] += f[i - 2];
}
}
return f[n];
}
121. 买卖股票的最佳时机
枚举+维护前缀最小值 (也可以使用单调栈),子问题见 贪心:122
class Solution {
public:
int maxProfit(vector<int> &prices) {
int ans = 0, mi = prices[0];
for (auto v: prices) {
ans = max(ans,v-mi);
mi = min(mi,v);
}
return ans;
}
};
10. 正则表达式匹配 🌟
虽然是困难题,但是面试特别容易考,不会写就背
定义 f[i][j]
表示字符串s的前 i 个字符和字符串p的前 j 个字符是否匹配。那么答案就是fmn。初始化f[0][0]= true
,表示
空字符串和空正则表达式是匹配的。
- 如果 $p[j - 1]$ 是
'*'
,那么我们可以选择匹配 $0$ 个 $s[i - 1]$ 字符,那么就是 $f[i][j] = f[i][j - 2]$。如果此时 $s[i - 1]$ 和 $p[j - 2]$ 匹配,那么我们可以选择匹配 $1$ 个 $s[i - 1]$ 字符,那么就是 $f[i][j] = f[i][j] \lor f[i - 1][j]$。 - 如果 $p[j - 1]$ 不是
'*'
,那么如果 $s[i - 1]$ 和 $p[j - 1]$ 匹配,那么就是 $f[i][j] = f[i - 1][j - 1]$。否则匹配失败。
bool isMatch(string s, string p) {
int m = s.size(), n = p.size();
bool f[m + 1][n + 1];
memset(f, false, sizeof f);
f[0][0] = true;
for (int i = 0; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (p[j - 1] == '*') {
f[i][j] = f[i][j - 2];
if (i && (p[j - 2] == '.' || p[j - 2] == s[i - 1])) {
f[i][j] |= f[i - 1][j];
}
} else if (i && (p[j - 1] == '.' || p[j - 1] == s[i - 1])) {
f[i][j] = f[i - 1][j - 1];
}
}
}
return f[m][n];
}
72. 编辑距离
int minDistance(string word1, string word2) {
int m = word1.size(), n = word2.size();
int f[m + 1][n + 1];
for (int i = 0; i <= n; i++) {
f[0][i] = i;
}
for (int j = 0; j <= m; j++) {
f[j][0] = j;
}
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if(word1[i-1]==word2[j-1]){
f[i][j] = f[i - 1][j - 1];
} else {
f[i][j] = min({f[i - 1][j], f[i][j - 1], f[i - 1][j - 1]}) + 1;
}
}
}
return f[m][n];
}
152. 乘积最大子数组
class Solution {
public:
int maxProduct(vector<int>& nums) {
int f = nums[0], g = nums[0], ans = nums[0];
for (int i = 1; i < nums.size(); ++i) {
int ff = f, gg = g;
f = max({nums[i], ff * nums[i], gg * nums[i]});
g = min({nums[i], ff * nums[i], gg * nums[i]});
ans = max(ans, f);
}
return ans;
}
};
416. 分割等和子集
class Solution {
public:
bool canPartition(vector<int>& nums) {
int s = accumulate(nums.begin(), nums.end(), 0);
if (s % 2 == 1) {
return false;
}
int n = nums.size();
int m = s >> 1;
bool f[n + 1][m + 1];
memset(f, false, sizeof(f));
f[0][0] = true;
for (int i = 1; i <= n; ++i) {
int x = nums[i - 1];
for (int j = 0; j <= m; ++j) {
f[i][j] = f[i - 1][j] || (j >= x && f[i - 1][j - x]);
}
}
return f[n][m];
}
};
62. 不同路径
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> dp(m, vector<int>(n, 0));
for (int i = 0; i < m; i++) dp[i][0] = 1;
for (int j = 0; j < n; j++) dp[0][j] = 1;
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
};
64. 最小路径和
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
int f[m][n];
f[0][0] = grid[0][0];
for (int i = 1; i < m; ++i) {
f[i][0] = f[i - 1][0] + grid[i][0];
}
for (int j = 1; j < n; ++j) {
f[0][j] = f[0][j - 1] + grid[0][j];
}
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
f[i][j] = min(f[i - 1][j], f[i][j - 1]) + grid[i][j];
}
}
return f[m - 1][n - 1];
}
};
背包问题🌟
0-1 背包
func knapsack(volumes, values []int, n, cap int) int {
dp := make([]int, cap+1)
for i := 0; i < n; i++ {
for j := cap; j >= volumes[i]; j-- {
dp[j] = max(dp[j],dp[j-volumes[i]]+values[i])
}
}
return dp[cap]
}
数学与数字
7. 整数反转
在 C++ 中,负数的取余运算的结果会保持与被除数的符号相同。
int reverse(int x) {
int ans = 0;
for (; x; x /= 10) {
if (ans < INT_MIN / 10 || ans > INT_MAX / 10) {
return 0;
}
ans = ans * 10 + x % 10;
}
return ans;
}
13. 罗马数字转整数
int romanToInt(string s) {
unordered_map<char, int> nums{
{'I', 1},
{'V', 5},
{'X', 10},
{'L', 50},
{'C', 100},
{'D', 500},
{'M', 1000},
};
int ans = nums[s.back()];
for (int i = 0; i < s.size() - 1; ++i) {
int sign = nums[s[i]] < nums[s[i + 1]] ? -1 : 1;
ans += sign * nums[s[i]];
}
return ans;
}
172. 阶乘后的零
int trailingZeroes(int n) {
int ans=0;
while(n){
n/=5;
ans+=n;
}
return ans;
}
模拟
2. 两数相加
常考题
ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
ListNode *dummy = new ListNode();
int carry = 0;
ListNode *cur = dummy;
while (l1 || l2 || carry) {
int s = (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + carry;
carry = s / 10;
cur->next = new ListNode(s % 10);
cur = cur->next;
l1 = l1?l1->next: nullptr;
l2 = l2?l2->next: nullptr;
}
return dummy->next;
}
ACM
cpp
全局变量默认初值是0。如果 int sum[N];
在这不被定义成全局,而是主函数内定义且不赋初值,则是随机数。
string s;
getline(cin, s); // 接受一整行字符串
gets(s)
cin >> s;
cin >> n;
getchar(); // 吸收一个回车,因为输入n之后,要输入一个回车 (如果后面有getline的话,可能会影响)
构造链表
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
explicit ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
// 构造
ListNode *constructList(vector<int> vec) {
ListNode *dummy = new ListNode(0);
ListNode *cur = dummy;
int n = vec.size();
for (int i = 0; i < n; i++) {
ListNode *newNode = new ListNode(vec[i]);
cur->next = newNode;
cur = cur->next;
}
return dummy->next;
}
// 打印链表
void printListNode(ListNode *head) {
if (head == nullptr) {
cout << "list is empty" << endl;
return;
}
ListNode *cur = head;
while (cur != nullptr) {
cout << cur->val << " ";
cur = cur->next;
}
cout << endl;
}
构造二叉树
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
方法一
// 通用的构造二叉树的方法
TreeNode *constructBinaryTree(const vector<int> &nodes, int index, int nullValue) {
if (index >= nodes.size() || nodes[index] == nullValue) {
// 如果当前节点为空,或者已经超出节点数组的范围,返回空指针
return nullptr;
}
// 创建当前节点
TreeNode *current = new TreeNode(nodes[index]);
// 递归构造左子树和右子树
current->left = constructBinaryTree(nodes, 2 * index + 1, nullValue);
current->right = constructBinaryTree(nodes, 2 * index + 2, nullValue);
return current;
}
方法二
TreeNode *buildTree(const vector<int> &nums, int nullValue) {
if (nums.empty()) {
return nullptr;
}
TreeNode *root = new TreeNode(nums[0]);
queue<TreeNode *> q;
q.push(root);
for (int i = 1; i < nums.size(); i += 2) {
TreeNode *current = q.front();
q.pop();
if (nums[i] != nullValue) {
current->left = new TreeNode(nums[i]);
q.push(current->left);
}
if (i + 1 < nums.size() && nums[i + 1] != nullValue) {
current->right = new TreeNode(nums[i + 1]);
q.push(current->right);
}
}
return root;
}
// 层序遍历验证
void levelOrderTraversal(TreeNode *root) {
if (!root) {
return;
}
queue<TreeNode *> q;
q.push(root);
while (!q.empty()) {
TreeNode *current = q.front();
q.pop();
cout << current->val << " ";
if (current->left) {
q.push(current->left);
}
if (current->right) {
q.push(current->right);
}
}
}
int main() {
vector<int> nums = {3, 9, 20, -1, -1, 15, 7};
TreeNode *root = buildTree(nums, -1);
cout << "层序遍历结果: ";
levelOrderTraversal(root);
return 0;
}
图
// 建图
vector<vector<int>> g(n + 1, vector<int>(n + 1, 0x3f3f3f3f));
for (int i = 0; i < m; ++i) {
int a, b, w;
cin >> a >> b >> w;
g[a][b] = min(g[a][b], w);
g[b][a] = min(g[b][a], w);
}
Go
Golang 主要掌握 fmt.Scan()
、 fmt.Scanln()
、 bufio.NewScanner(os.Stdin)
的使用
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
scanner.Split(scanner.Text(),",")
n, _ := strconv.Atoi(scanner.Text())
奇技淫巧
for(;~j;)
等价于 for(;j>=0;)
~j = -j - 1
__builtin_popcount(x ^ y)
计算二进制表达中 111 的数量的函数 (求汉明距离)
1--n的平方和 = n(n+1)(2n+1)/6
快速幂算法: 核心思想是将幂指数 n 拆分为若干个二进制位上的 1 的和,然后将 x 的 n 次幂转化为 x 的若干个幂的乘积。
long long fastExponentiationIterative(long long base, long long exponent) {
long long result = 1;
while (exponent > 0) {
if (exponent % 2 == 1) {
result *= base;
}
base *= base;
exponent /= 2;
}
return result;
}