文章目录
第一章 栈与队列
简单
20. 用两个栈实现队列
class MyQueue {
public:
/** Initialize your data structure here. */
stack<int> stk, cache;
MyQueue() {
}
/** Push element x to the back of queue. */
void push(int x) {
stk.push(x);
}
void copy(stack<int> &a, stack<int> &b) {
while (a.size()) {
b.push(a.top());
a.pop();
}
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
copy(stk, cache);
int res = cache.top();
cache.pop();
copy(cache, stk);
return res;
}
/** Get the front element. */
int peek() {
copy(stk, cache);
int res = cache.top();
copy(cache, stk);
return res;
}
/** Returns whether the queue is empty. */
bool empty() {
return stk.empty();
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* bool param_4 = obj.empty();
*/
单调栈
单调栈:具有单调性的栈
830. 单调栈*
#include<iostream>
using namespace std;
#include<stack>
int main(){
int n;
stack<int> sta;
cin>>n;
int num;
while(n--){
cin>>num;
while(sta.size() != 0){
if(sta.top() < num){
cout<<sta.top()<<" ";
break;
}
else sta.pop();
}
if(sta.size()==0){
cout<<-1<<" ";
}
//这里的单调栈是单调上升的
//所以输入的每一个数都要在栈中找到栈顶元素比自己小的
//然后把自己放到栈中,这个栈依然单调上升
sta.push(num);
}
return 0;
}
41. 包含min函数的栈
class MinStack {
public:
/** initialize your data structure here. */
stack<int> stackValue;
stack<int> stackMin;
MinStack() {
}
void push(int x) {
stackValue.push(x);
// 这里有一个等于,最小数的可以相同不止一个
if (stackMin.empty() || stackMin.top() >= x)
stackMin.push(x);
}
void pop() {
if (stackMin.top() == stackValue.top()) stackMin.pop();
stackValue.pop();
}
int top() {
return stackValue.top();
}
int getMin() {
return stackMin.top();
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
第二章 双指针
简单
剑指 Offer 18. 删除链表的节点
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
ListNode * h = new ListNode(-1 , head);
ListNode * befPtr = h;
ListNode * ptr = h->next;
while(ptr){
if(ptr->val == val){
befPtr->next = ptr->next;
}
befPtr = ptr;
ptr = ptr->next;
}
return h->next;
}
};
解法2:
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
ListNode p(-1);
p.next = head;
ListNode* ptr = &p;
while(ptr && ptr->next){
if(ptr->next->val == val)
ptr->next = ptr->next->next;
ptr = ptr->next;
}
return p.next;
}
};
[19. 删除链表的倒数第 N 个结点]
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。(在没有提到循环链表的时候,默认都是单链表)
- 删除节点一般要找删除节点的前一个节点
- 删除节点的前一个节点和NULL之间有n个节点,我们就可以利用这个性质
- 定义两个指针,先让快指针走n步,使两个指针之间有n个节点,然后保持这个性质,让两个指针每次走一步,直到快指针指向NULL,此时慢指针指向删除节点的前一个节点
- 此时进行删除操作
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode ret(0,head);
//这里不是指向同一个起点,这里只是保证了两个指针之间的节点数为0
ListNode * fast = head , * slow = & ret;
//使快指针先走n步,
while(n--)fast = fast->next;
while(fast)
{
fast = fast->next;
slow = slow->next;
};
//此时slow指向删除节点的前一个节点
slow->next = slow->next->next;
return ret.next;
}
};
47. 二叉树中和为某一值的路径
class Solution {
public:
vector<vector<int>> ans;
vector<int> temp;
vector<vector<int>> findPath(TreeNode* root, int sum) {
int total = 0;
dfs(root,sum,total);
return ans;
}
void dfs(TreeNode* root, int sum , int total){
if(root == NULL) return;
total += root->val;
temp.push_back(root->val);
//必须是叶子节点,而且total == sum
if(!root->left && !root->right && total == sum) ans.push_back(temp);
if(root->left) dfs(root->left , sum , total);
if(root->right) dfs(root->right , sum , total);
temp.pop_back();
}
};
如何快速找到奇数节点链表的中间值
- 设置两个指针都指向单链表头节点
- 第一个指针的速度是第二个指针的两倍
- 当第一个指针指向末尾的时候,第二个就指向了链表的中间值
简单
66. 两个链表的第一个公共结点
- 使用的方法十分的巧妙,看不明白就看Y总的讲解
class Solution {
public:
ListNode *findFirstCommonNode(ListNode *headA, ListNode *headB) {
if(!headA || !headB) return NULL;
auto pa = headA;
auto pb = headB;
// 没有else的时候,有时候一个指针在一个循环中执行两步
while(pa != pb){
if(pa) pa = pa->next;
else pa = headB;
if(pb) pb = pb->next;
else pb = headA;
}
return pa; //这里返回 pa pb 都可以
}
};
简单
32. 调整数组顺序使奇数位于偶数前面
class Solution {
public:
vector<int> exchange(vector<int>& nums) {
if(nums.empty()) return nums;
int l = -1 , r = nums.size();
while(r > l){
do l++; while(r > l && nums[l] %2 == 1);
do r--; while(r > l && nums[r] %2 == 0);
if(r>l) swap(nums[l] , nums[r]);
}
return nums;
}
};
剑指 Offer 58 - I. 翻转单词顺序
class Solution {
public:
string reverseWords(string s) {
int n = s.size();
if(n == 0) return s;
string ans = "";
for(int i = n - 1 ; i >= 0 ; i--){
if(s[i] != ' '){
int end = i;
//首先不可以越界,再判断不能等于空格
while(i >= 0 && s[i] != ' ') i--;
ans += s.substr(i + 1 , end - i) + ' ';
}
}
//最后一位不能有空格
return ans.substr(0 , ans.size() - 1);
}
};
70. 二叉搜索树的第k个结点
中序遍历输出二叉搜索树:1 2 3 -------单调递增
先输出的元素就是先回溯的元素,即也是先执行语句的元素
class Solution {
public:
vector<TreeNode*> ans;
TreeNode* kthNode(TreeNode* root, int k) {
dfs(root,k);
return ans[k - 1];
}
void dfs(TreeNode* root, int k){
if(root == NULL) return;
if(root->left) dfs(root->left, k);
ans.push_back(root);
if(root->right) dfs(root->right, k);
}
};
第三章 链表
简单
17. 从尾到头打印链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
vector<int> printListReversingly(ListNode* head) {
vector<int> res;
while (head) {
res.push_back(head->val);
head = head->next;
}
return vector<int>(res.rbegin(), res.rend());
}
};
反转链表
[206. 反转链表]
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == NULL || head->next == NULL)return head;
//使用头插法实现链表翻转
//把旧链表的每一个头结点,从前往后头插到新链表中
auto h = new ListNode(-1 , NULL);
ListNode * curh = h , * cur = head , * curNext = NULL;
while(cur){
curNext = cur->next;
cur->next = curh->next;
curh->next = cur;
cur = curNext;
}
return h->next;
}
};
第二种做法:
思路:
- 先把每个节点的下一个节点保存一下,
- 递归到最深层,返回最后一个节点作为头结点,
- 然后局部翻转,之前 head -> tail 翻转后tail -> head->NULL
- 函数可以实现翻转链表,并且返回头结点.
代码实现:
/**
* 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* reverseList(ListNode* head) {
if(head == nullptr||head->next==nullptr)return head;
//记录当前头结点后面一个节点,方便稍后翻转两个节点
ListNode * tail = head->next;
//递归函数放在执行任务之前,意味着先操作后面的链表
//因为是翻转,所以最后一个节点翻转为头结点即p指向头结点(在最深一层完成头结点指向)
ListNode * p = reverseList(head->next);
//局部翻转两个节点 执行任务
head->next = tail->next;//这里也可以改为NULL,因为链表的开头最后变为结尾,结尾最后指向NULL
tail->next = head;
return p;
}
};
[92. 反转链表 II]
- 给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
实现代码:
/**
* 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:
//此函数实现,反转head前numCode个节点
ListNode* reverseN(ListNode* head ,int numCode){
if(numCode==1)return head;//当head是最后一个要反转的节点的时候,直接返回执行反转任务
ListNode * tail = head->next;
ListNode * p = reverseN(head->next , numCode - 1);
head->next = tail->next;
tail->next = head;
return p;
}
ListNode* reverseBetween(ListNode* head, int left, int right) {
ListNode ret(0,head);//创建虚拟头节点,指向传入链表的第一个节点
ListNode * p = &ret;
int numCode = right - left + 1;
while(--left)p = p->next;//使指针指向翻转链表的前一个节点
p->next = reverseN(p->next , numCode);
return ret.next;//此时不可以返回head,因为第一个节点也有可能被反转
}
};
48. 复杂链表的复刻
class Solution {
public:
ListNode *copyRandomList(ListNode *head) {
auto cur = head;
while(cur){
auto temp = new ListNode(cur->val);
temp->next = cur->next;
cur->next = temp;
cur = cur->next->next;
}
cur = head;
while(cur){
if(cur->random){
cur->next->random = cur->random->next;
}
cur = cur->next->next;
}
auto h = new ListNode(-1);
auto hcur = h;
cur = head;
while(cur){
hcur->next = cur->next;
hcur = hcur->next;
cur->next=cur->next->next;
cur=cur->next;//不可以修改初始链表
}
return h->next;
}
};
剑指 Offer 54. 二叉搜索树的第k大节点
class Solution {
public:
int kthLargest(TreeNode* root, int k)
{
int res = 0;
dfs(root, k, res);
return res;
}
void dfs(TreeNode* root, int& k , int& res)
{
//遍历顺序为:右中左
if (root == nullptr)return;
//这里写成dfs(root->right , k - 1 , res)不行
//这里我们暂且理解为,这是执行语句,执行语句根据进栈顺序依次执行
dfs(root->right, k, res);
k--;
if (k == 0) res = root->val;
dfs(root->left, k, res);
}
};
删除链表重复元素
[83. 删除排序链表中的重复元素]
- 给定一个已排序的链表的头
head
, 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 - 解题思路比较简单,直接上代码
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
// 在链表中删除重复元素
if(!head || !head->next) return head;
ListNode *ptr = head;
while(ptr && ptr->next){
while(ptr->next && ptr->val == ptr->next->val) ptr->next = ptr->next->next;
ptr = ptr->next;
}
return head;
}
};
[82. 删除排序链表中的重复元素 II]
-
给定一个已排序的链表的头
head
, 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。 -
这个题目有点绕,但是还不好画图,必须结合实际的节点来看
-
可以假设 1 2 2 2 3 3 3 4 4 6
-
仔细看看注释
/**
* 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* deleteDuplicates(ListNode* head) {
if(head==NULL)return NULL;
ListNode ret(0,head);
//str:始终指向值不重复的节点,用于找出值相同的节点
//temp:用于跳过值相同的节点,找到与之前值不相同的节点
ListNode * str = &ret , * temp = NULL;
//经过下面变化 str->next 有可能为NULL
//当为空的时候,str就指向最后一个节点,也不需要往后面遍历了
while(str->next)
{
//str指向的是前一个没有重复val的节点
//至少后面有两个节点才有可能发生重复,所以需要判断str->next->next
if(str->next->next && str->next->val == str->next->next->val)
{
//可以假设 1 2 2 2 3 3 3 4 4 6
temp = str->next->next;
while(temp!=NULL && str->next->val==temp->val)temp = temp->next;
//这里不直接用str接收是为了,防止temp中保存的值和后面的重复,例如上面第一个3节点
str->next = temp;
}
else
{
str = str->next;
}
};
return ret.next;
}
};
第四章 字符串
简单
16. 替换空格
class Solution {
public:
string replaceSpaces(string &str) {
string res;
for (auto x : str)
if (x == ' ')
res += "%20";
else
res += x;
return res;
}
};
78. 左旋转字符串
class Solution {
public:
string leftRotateString(string str, int n) {
if(str.size() == 0 || str.size() == 1) return str;
string ans = "";
int len = str.size();
for(int i = n; i < str.size(); i++) ans += str[i];
for(int i = 0; i < n; i++) ans += str[i];
return ans;
}
};
71. 二叉树的深度
class Solution {
public:
int treeDepth(TreeNode* root) {
if(!root) return 0;
//后序遍历
int left = treeDepth(root->left);
int right = treeDepth(root->right);
int height = max(left , right) + 1;
return height;
}
};
第五章 查找算法
查找算法(简单)
13. 找出数组中重复的数字
class Solution {
public:
int duplicateInArray(vector<int>& nums) {
unordered_map<int,int> mm;
int ans = -1;
for(auto num:nums){
if(num < 0 || num > nums.size()-1)return -1;
mm[num]++;
if(mm[num] == 2)ans = num;
}
return ans;
}
};
67. 数字在排序数组中出现的次数
class Solution {
public:
int getNumberOfK(vector<int>& nums , int k) {
if(nums.size() == 0) return 0;
int l = 0 , r = nums.size() - 1;
while(r > l){
int mid = (l + r)/2 ;
if(nums[mid] >= k)r=mid;
else l = mid + 1;
}
if(nums[l] != k)return 0;
int ll=0,rr=nums.size()-1;
while(rr > ll){
int mid = (ll + rr + 1)/2;
if(nums[mid] <= k)ll = mid;
else rr = mid - 1;
}
return ll - l + 1;
}
};
68. 0到n-1中缺失的数字
class Solution {
public:
// 0 1 2 3 4 5
// 0 1 3 4 5 6
int getMissingNumber(vector<int>& nums) {
if(nums.empty()) return 0;
// 如果缺少最后一个数字就直接返回
if(nums[nums.size() - 1] == nums.size() - 1) return nums.size();
int l = 0 , r = nums.size() - 1;
while(r > l){
int mid = (r + l) / 2;
if(nums[mid] != mid) r = mid;
else l = mid + 1;
}
return r;
}
};
查找算法(中等)
15. 二维数组中的查找
class Solution {
public:
bool searchArray(vector<vector<int>> array, int target) {
if (array.empty() || array[0].empty()) return false;
int i = 0, j = array[0].size() - 1;
while (i < array.size() && j >= 0) {
if (array[i][j] == target) return true;
if (array[i][j] > target) j -- ;
else i ++ ;
}
return false;
}
};
72. 平衡二叉树
class Solution {
public:
bool ans = true;
bool isBalanced(TreeNode* root) {
dfs(root);
return ans;
}
int dfs(TreeNode * root){
if(!root) return 0;
int left = dfs(root->left);
int right = dfs(root->right);
if(abs(left - right) > 1) ans = false;
int height = max(left , right) + 1;
return height;
}
};
22. 旋转数组的最小数字*
- 分析:去重后,前面的数字一定小于后面的数字,若遇到前面的数字大于后面的数字,则这个较小的数字就是答案
- 在没有重复数字的情况下
- 去重
因为mid与right指向的值相同,不能确定分界点
先把right的指向向前移动一位,然后更新mid的值
这时候发现right指向的值大于mid指向的值又可以移动了
class Solution {
public:
int findMin(vector<int>& nums) {
int l = 0 , r = nums.size() - 1 ;
if(r == -1) return -1;
while(r > l){
int mid = (l + r)/2;
if(nums[mid] < nums[r]) r = mid ;
else if(nums[mid] > nums[r]) l = mid + 1;
else if(nums[mid] == nums[r]) r--;
}
return nums[l];
}
};
63. 字符串中第一个只出现一次的字符
class Solution {
public:
char firstNotRepeatingChar(string s) {
char ch = '#';
unordered_map<char,int> mm;
for(char chh : s) mm[chh]++;
for(int i = 0 ; i < s.size() ; i++){
if(mm[s[i]] == 1) return s[i];
}
return ch;
}
};
第七章 分治算法
18. 重建二叉树*
class Solution {
public:
unordered_map<int,int> pos;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = preorder.size();
for (int i = 0; i < n; i ++ )
pos[inorder[i]] = i;//把元素全部加入哈希表中,方便查找根节点
return dfs(preorder, inorder, 0, n - 1, 0, n - 1);
}
TreeNode* dfs(vector<int>&pre, vector<int>&in, int pl, int pr, int il, int ir)
{
if (pl > pr) return NULL;
TreeNode* root = new TreeNode(pre[pl]);
int k = pos[pre[pl]] - il;//左子树元素个数 = 中序根节点下标 - 中序首节点下标
//这两个左右子树,要对照着写,先都写完前序下标范围,再.....
root->left = dfs(pre, in, pl + 1, pl + k, il, pos[pre[pl]] - 1);
root->right = dfs(pre, in, pl + k + 1, pr, pos[pre[pl]] + 1, ir);
return root;
}
};
剑指 Offer 17. 打印从1到最大的n位数
class Solution {
public:
vector<int> printNumbers(int n) {
int sum = 1;
while(n--) sum *= 10;
vector<int> ans;
for(int i = 1; i < sum; i++) ans.push_back(i);
return ans;
}
};
27. 数值的整数次方*
这个题主要采用了快速幂
- 例如:计算n^k
如果是按照朴素做法的话,则需要循环k次时间复杂度为 O ( k ) ,则这个复杂度就比较高了,而如果是 采用快速幂时间复杂度是 O(logn)。
快速幂的核心思路是:反复平方法(思想上有点类似逆向二分。二分是每次在当前基础上减一半,快速幂是每次在当前基础上扩大一倍)。
例如
快速幂讲解视频
class Solution {
public:
double Power(double base, int exponent) {
if(base == 0) return 0;
double ans = 1 , temp = base;
//-2的31次方 <= n <= 2的31次方-1
//所以int的最小数无法转化为正数
long y = exponent;
if(exponent < 0) y = - y , temp = 1 / temp;
while(y){
//若二进制最后一位为1
// 010111101 & 1 = 1;
if(y & 1 == 1) ans *= temp;
//为下一次所准备元素
temp = temp * temp;
y = y >> 1; // 位运算
}
return ans;
}
};
第八章 搜索与回溯算法
简单
84. 求1+2+…+n
class Solution {
public:
int getSum(int n) {
int ans = n;
//当n = -1的时候,就return回溯
(n > 0) && (ans += getSum(n-1));
return ans;
}
};
23. 矩阵中的路径
如果不在遍历节点前加上if (!ans) 那么就会出现运行超时
class Solution {
public:
bool ans = false;
bool hasPath(vector<vector<char>>& matrix, string &str) {
for(int i = 0 ; i < matrix.size() ; i++)
for(int j = 0 ; j < matrix[0].size() ; j++)
if(!ans) (dfs(matrix , str , 0 , i , j));
return ans;
}
int dir[5] = {1,0,-1,0,1};
void dfs(vector<vector<char>>& matrix, string &str , int u , int x , int y){
if(matrix[x][y] != str[u]) return;
if(u == str.size() - 1) {
ans = true;
return;
}
//层层深度的时候,标记这个点已经被访问
char ch = matrix[x][y];
matrix[x][y] = '*';
for(int i = 0 ; i < 4 ; i++){
int a = x + dir[i] , b = y + dir[i+1];
if(a >= 0 && a < matrix.size() && b >= 0 && b < matrix[0].size())
if(!ans) (dfs(matrix , str , u + 1 , a , b));
}
//当回溯的时候,把标记点去掉
matrix[x][y] = ch;
}
};
24. 机器人的运动范围
class Solution {
public:
int ans = 0 , dir[5] = {1,0,-1,0,1};
int movingCount(int threshold, int rows, int cols)
{
if(rows == 0 || cols == 0) return 0;
bfs(threshold , rows , cols);
return ans;
}
void bfs(int k, int rows, int cols){
vector<vector<bool>> v(rows , vector<bool>(cols , false));//用于标记已经访问
queue<pair<int , int>> qu; //用于广度优先遍历
qu.push({0,0});
while(qu.size()){
auto t = qu.front();
qu.pop();
if(v[t.first][t.second] == true || k < getNum(t)) continue;
ans++;
v[t.first][t.second] = true;
for(int i = 0 ; i < 4 ; i++){
int x = t.first + dir[i] , y = t.second + dir[i+1];
if(x >= 0 && x < rows && y >= 0 && y < cols) qu.push({x , y});
}
}
}
int getNum(pair<int , int> p){
int num = 0;
while(p.first){
num += p.first % 10;
p.first /= 10;
}
while(p.second){
num += p.second % 10;
p.second /= 10;
}
return num;
}
};
第九章 动态规划
简单
21. 斐波那契数列
class Solution {
public:
int Fibonacci(int n) {
if (n == 0) return 0;
//n 是所要求的相数
vector<int> v(n + 1 , 0);
v[1] = 1 , v[2] = 1;
for(int i = 3 ; i <= n ; i++){
v[i] = v[i - 1] + v[i - 2];
}
return v[n];
}
};
剑指 Offer 10- II. 青蛙跳台阶问题
class Solution {
public:
int numWays(int n) {
int MOD = 1000000007;
if (n <= 1) return 1;
vector<int>dp(n + 1);
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; ++i)
{
dp[i] = (dp[i - 1] + dp[i - 2]) % MOD;
}
return dp[n];
}
};
83. 股票的最大利润
class Solution {
public:
int maxDiff(vector<int>& nums) {
if(nums.size() == 0) return 0;
int minNum = nums[0] , ans = 0;
for(int i = 1 ; i < nums.size() ; i++){
//找出下标比i小的最小值
minNum = min(minNum , nums[i - 1]);
//用下标为i的值减去,他之前的最小值,并与ans比较取较大的值
ans = max(ans , nums[i] - minNum);
}
return ans;
}
};
88. 树中两个结点的最低公共祖先
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(!root) return NULL;
if(root == p || root == q) return root;
//若节点非空,且没有想要的元素那就接着递归
auto left = lowestCommonAncestor(root->left , p , q);
auto right = lowestCommonAncestor(root->right , p , q);
//代表root是p和q的最低公共祖先
if(left != NULL && right != NULL) return root;
//代表left接收到公共祖先或者接收到与p或q相同的节点
if(left != NULL && right == NULL) return left;
if(left == NULL && right != NULL) return right;
return NULL; //两个都为空的时候返回空
}
};
中等
55. 连续子数组的最大和
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int s = 0;
int res = -300;
for(auto x : nums)
{
if(s > 0) s = x + s; //如果前面的序列大于0则加上
else s = s = x; //如果前面的序列小于0就舍去
res = max(res, s); // 取出子数组的和的最大值
}
return res;
}
};
60. 礼物的最大价值
不会就根据代码画图
class Solution {
public:
int getMaxValue(vector<vector<int>>& grid) {
if(!grid.size()) return 0;
int x = grid.size() , y = grid[0].size();
vector<vector<int>> f(x + 1 , vector<int>(y + 1));
for(int i =1 ; i <= x ; i++){
for(int j = 1 ; j <= y ; j++){
//相对f[i - 1][j],f[i][j]是向下移动
//相对f[i][j - 1],f[i][j]是向右移动
f[i][j] = max(f[i - 1][j] , f[i][j - 1]) + grid[i - 1][j - 1];
}
}
return f[x][y];
}
};
中等
59. 把数字翻译成字符串
class Solution {
public:
int translateNum(int num) {
string s = to_string(num);
int n = s.size();
if(n == 0 ) return 0;
vector<int> f(n);
f[0] = 1;
//当至少有两个元素的时候,数组中第二个元素为1或者2
if(n >= 2 ) {
int t = s[1] - '0' + (s[0] - '0') * 10;
//06 26之类的数字都不行 0是a所以z是25
if(t >= 10 && t <= 25 ) f[1] = 2;
else f[1] = 1;
}
//vector数组从下标为1开始计数
for (int i = 2 ; i < n; i ++ ) {
f[i] = f[i - 1];
int t = s[i] - '0' + (s[i - 1] - '0') * 10;
if (t >= 10 && t <= 25) f[i] += f[i - 2];
}
return f[n - 1];
}
};
62. 丑数
class Solution {
public:
int getUglyNumber(int n) {
vector<int> ans(n , 0);
//注意这里不可以使用push_back,使用后会把1加入到0的后面
ans[0] = 1; //加入第一个丑数
int a = 0 , b = 0 , c = 0;
//丑数的因子只可以是 2,3,5所以丑数一定是这三个相乘得到的
//每次取最小值,是防止漏掉较小的丑数
for(int i = 1 ; i < n ; i++){
ans[i] = min(min(ans[a] * 2 , ans[b] * 3) , ans[c] * 5);
if(ans[i] == ans[a] * 2) a++;
if(ans[i] == ans[b] * 3) b++;
if(ans[i] == ans[c] * 5) c++;
}
return ans[n - 1];
}
};
第十章 排序
简单
58. 把数组排成最小的数
class Solution {
public:
string printMinNumber(vector<int>& nums) {
vector<string> v;
string ans = "";
for(auto & t : nums) v.push_back(to_string(t));
//return a + b < b + a;本题灵魂 若不符合就交换两个数
sort(v.begin() , v.end() , [](string & a , string & b){ return a + b < b + a;});
for(auto & t : v) ans += t;
return ans;
}
};
81. 扑克牌的顺子
class Solution {
public:
bool isContinuous( vector<int> numbers ) {
if(numbers.size() != 5) return false;
// 排序
sort(numbers.begin() , numbers.end());
// i 为0的个数
int i = 0;
while(numbers[i] == 0) i++;
// 最大数 - 最小数(除0)
int n = numbers.size() - 1;
if(numbers[n] - numbers[i] > 4) return false;
// 不能有相等的数
for(int j = i ; j < numbers.size() - 1 ; j++){
if (numbers[i] == numbers[i + 1]) return false;
}
return true;
}
};
53. 最小的k个数
class Solution {
public:
vector<int> getLeastNumbers_Solution(vector<int> input, int k) {
priority_queue<int> temp;
vector<int> ans;
//优先队列,存放k个最小数.
for(auto & t : input){
temp.push(t);
if(temp.size() > k) temp.pop();
}
while(temp.size()){
ans.push_back(temp.top());
temp.pop();
}
return vector<int>(ans.rbegin() , ans.rend());
}
};
46. 二叉搜索树的后序遍历序列
class Solution {
public:
bool verifySequenceOfBST(vector<int> sequence) {
return dfs(sequence,0,sequence.size() - 1);
}
//[4, 8, 6, 12, 16, 14, 10] 二叉搜索树后序遍历
//最后一个元素为根节点,特性根节点大于左子树所有元素,小于右子树所有元素
//所有 使用根节点可以分出左子树(4,8,6),右子树(12,15,14)
//当后序遍历不符合,用最后一个元素分出左右子树的时候,就不是二叉搜索树
//例如 [4, 18, 6, 12, 16, 14, 10]
bool dfs(vector<int> sequence , int l , int r){
if(l >= r) return true;
int head = sequence[r];// 取出根节点
// l为左子树第一个元素对应的索引
int k = l , kk = 0;
while(k < r && sequence[k] < head) k++;
//此时k指向右子树对应的第一个索引,判断右子树所有元素是否小于根节点,若是则符合特性
kk = k;
while(kk < r && sequence[kk] > head) kk++;
if(kk != r) return false;
//左右子树也必须是二叉搜索树
return dfs(sequence , l , k - 1) && dfs(sequence , k , r - 1);
}
};
第十一章 位运算
简单
[801. 二进制中1的个数 - AcWing题库]
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int n;
//获取二进制数中的最后一个1 , 例如 01100 得到 00100
int lowbit(int x) {
return x & -x;
}
int main() {
scanf("%d", &n);
while(n--) {
int x;
scanf("%d", &x);
int c = 0;
while(x > 0) {
x -= lowbit(x);
c++;
}
printf("%d ", c);
}
return 0;
}
中等
73. 数组中只出现一次的两个数字
两个相同的数异或为0,那么
2 ^ 2 ^ 3 ^3 ^ 5 = 5;
2 ^ 2 ^ 3 ^3 ^ 5 ^ 6 = 5 ^ 6;
class Solution {
public:
vector<int> findNumsAppearOnce(vector<int>& nums) {
int sum = 0;
//把所有数进行异或,得到答案两个数的异或的值
for(auto & t : nums) sum ^= t;
int k = 0;
while((sum >> k & 1) != 1) k++; //若(sum >> k & 1)=1;则找到两个数二进制不同的点
//依据这个不同点,我们把数组分成两个部分,则两个数据必不可能在一个部分
int first = 0;
for(auto & t : nums)
if(t >> k & 1) first ^= t;
int second = sum ^ first;//第二个数就用这种办法
return vector<int>{first , second};
}
};
第十二章 数学
简单
52. 数组中出现次数超过一半的数字
class Solution {
public:
int moreThanHalfNum_Solution(vector<int>& nums) {
int val = -1 , count = 0;
for(auto & t : nums){
if(count == 0) val = t;
if(t == val) count ++;
else count--;
}
return val;
}
};
86. 构建乘积数组
题解 : 例如 ans[2] = A[0] x A[1] x A[3] x A[4]
第一步计算左边的乘积 A[0] x A[1]
第二步计算右边的乘积 A[3] x A[4]
class Solution {
public:
vector<int> multiply(const vector<int>& A) {
if(!A.size()) return vector<int>{};
vector<int> ans(A.size() , 0);
//从左到右把每个B[i]左边所有数乘积算出来,存放到对应位置
//第一个元素左边没有任何数,赋值为1
for(int i = 0 , nextValue = 1 ; i < A.size() ; i++){
ans[i] = nextValue;
nextValue = nextValue * A[i];
}
//从左到右把每个B[i]右边所有数乘积算出来,存放到对应位置
//最后一个元素右边没有任何数,乘1,保持不变
for(int i = A.size() - 1 , preValue = 1 ; i >= 0 ; i--){
ans[i] = ans[i] * preValue;
preValue = preValue * A[i];
}
return ans;
}
};
补充:二叉树遍历基础
//前序遍历
void praTree(TreeCode * root)
{
if (root == NULL)
{
return;
}
cout << root->data << " ";
praTree(root->lchild);
praTree(root->rchild);
}
//中序遍历
void medTree(TreeCode * root)
{
if (root == NULL)
{
return;
}
medTree(root->lchild);
cout << root->data << " ";
medTree(root->rchild);
}
//后序遍历
void subTree(TreeCode * root)
{
if (root == NULL)
{
return;
}
subTree(root->lchild);
subTree(root->rchild);
cout << root->data << " ";
}
中等
25. 剪绳子
class Solution {
public:
int maxProductAfterCutting(int length) {
if(length <= 3) return 1 * (length - 1);
int ans = 1;
if(length % 3 == 2) ans *= 2 , length -= 2;
if(length % 3 == 1) ans *= 4 , length -= 4;
while(length){
ans *= 3;
length -=3 ;
}
return ans;
}
};
76. 和为S的连续正数序列
class Solution {
public:
vector<vector<int> > findContinuousSequence(int sum) {
vector<vector<int>> ans;
vector<int> temp;
if(sum == 0) return ans;
//滑动窗口:初始两个指针指向第一个元素,且元素的值就是指针的值
int i = 1 , j = 1 ;
int s = 1;//左右指针已经指向第一个元素,所以初始值为1
//滑动窗口:左指针最多可以移动到 sum/2 的位置
while(i <= sum/2){
if(s < sum){
j++;
s += j;
}
if(s > sum){
s -= i;
i++;
}
if(s == sum){
for(int a = i ; a <= j ; a++) temp.push_back(a);
ans.push_back(temp);
temp.clear();
//这个答案已经保存,所以左指针向后移动一位
s -= i;
i++;
}
}
return ans;
}
};
第十三章 模拟
简单
40. 顺时针打印矩阵
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
vector<int> ans;
if(matrix.empty()) return ans;
int n = matrix.size() , m = matrix[0].size();
int diy[5] = {0,1,0,-1,0};
vector<vector<bool>> visited(n , vector<bool>(m,false));//判断是否已经遍历过
int x= 0 , y = 0 , d = 0;
for(int i = 0 ; i < n * m ; i++){
ans.push_back(matrix[x][y]);
visited[x][y] = true;
int a = x + diy[d] , b = y + diy[d + 1];
if(a < 0 || a >= n || b < 0 || b >= m || visited[a][b]){
d = (d + 1) % 4;
a = x + diy[d] , b = y + diy[d + 1];
}
x = a , y = b;
}
return ans;
}
};
42. 栈的压入、弹出序列
class Solution {
public:
bool isPopOrder(vector<int> pushV,vector<int> popV) {
if(pushV.size()!=popV.size()) return false;
stack<int> sta;
int j = 0 ;
for(int i = 0 ; i < pushV.size() ; i++){
sta.push(pushV[i]);
//注意sta.empty()条件必须在前面,因为如果为空就不能访问栈顶
while(sta.empty() == false && sta.top() == popV[j]) sta.pop() , j++;
}
return sta.empty();
}
};
第十四章 全排列
模板
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
78. 子集
class Solution {
public:
vector<int> path;
vector<vector<int>> ans;
void dfs(vector<int>& nums , int startIndex){
//放在结束的前,以防漏掉子集,第一个子集为[]
ans.push_back(path);
if(startIndex >= nums.size()) return; //终止条件可以不加,如果不加后面的循环也不会执行
for(int i = startIndex ; i < nums.size() ; i++){
path.push_back(nums[i]);
dfs(nums , i + 1);
path.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
dfs(nums , 0);
return ans;
}
};
46. 全排列
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> permute(vector<int>& nums) {
vector<bool> used(nums.size() , false);
dfs(nums , used);
return ans;
}
void dfs(vector<int>& nums , vector<bool>& used){
if(nums.size() == path.size()) {
ans.push_back(path) ;
return;
}
for(int i = 0 ; i < nums.size() ; i++){
if(used[i] == true) continue;
used[i] = true;
path.push_back(nums[i]);
dfs(nums , used);
//回溯
used[i] = false;
path.pop_back();
}
}
};