题目所在链接:
1. 栈与队列
剑指 Offer 09. 用两个栈实现队列
class CQueue {
public:
stack<int> a,b;
CQueue() {
// 维护两个栈,第一个栈支持插入操作,第二个栈支持删除操作
while(!a.empty()){
a.pop();
}
while(!b.empty()){
b.pop();
}
}
void appendTail(int value) {
a.push(value);
}
int deleteHead() {
if(b.empty()){
// 如果b是空的
while(!a.empty()){
// 容易忘记这个条件
b.push(a.top());
a.pop();
}
}
if(b.empty()){
return -1;
}else{
int res = b.top();
b.pop();
return res;
}
}
};
/**
* Your CQueue object will be instantiated and called as such:
* CQueue* obj = new CQueue();
* obj->appendTail(value);
* int param_2 = obj->deleteHead();
*/
剑指 Offer 30. 包含min函数的栈
class MinStack {
public:
stack<int> data, min_s;
/** initialize your data structure here. */
MinStack() {
// 使用一个辅助栈,该辅助栈负责保存目前操作下前面所有数据中最小的值
min_s.push(INT_MAX);
}
void push(int x) {
data.push(x);
if(x < min_s.top()){
min_s.push(x);
}else{
min_s.push(min_s.top());
}
}
void pop() {
data.pop();
min_s.pop();
}
int top() {
return data.top();
}
int min() {
return min_s.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->min();
*/
2. 链表
剑指 Offer 06. 从尾到头打印链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
// 利用栈来保存链表中的值
stack<int> sta;
vector<int> res;
while(head != NULL){
sta.push(head->val);
head = head -> next;
}
while(!sta.empty()){
res.push_back(sta.top());
sta.pop();
}
return res;
}
};
剑指 Offer 35. 复杂链表的复制
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
map<Node*, Node*> listmap;
Node* copyRandomList(Node* head) {
// 本题中因为随机指针的存在,当我们拷贝节点时,「当前节点的随机指针指向的节点」可能还没创建,因此我们需要变换思路。
// 递归+map
if(head == NULL){
return NULL;
}
if(!listmap.count(head)){
Node* newone = new Node(head->val);
listmap.emplace(head, newone);
newone->next = copyRandomList(head->next);
newone->random = copyRandomList(head->random);
}
return listmap[head];
}
};
3.字符串
剑指 Offer 05. 替换空格
class Solution {
public:
string replaceSpace(string s) {
// 循环遍历放进一个新的string即可
string res;
for(int i = 0;i<s.size();i++){
if(s[i] == ' '){
res = res + "%20";
}
else{
res = res + s[i];
}
}
return res;
}
};
剑指 Offer 58 - II. 左旋转字符串
class Solution {
public:
string reverseLeftWords(string s, int n) {
string res;
// 先放[n...length-1]部分的字符串,再放[0...n-1]的字符串
int length = s.size();
for(int i = n;i<length;i++){
res = res + s[i];
}
for(int i = 0;i<n;i++){
res = res + s[i];
}
return res;
}
};
4.查找算法
剑指 Offer 03. 数组中重复的数字
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
// 用set
set<int> numtimes;
for(int i =0;i<nums.size();i++){
if(numtimes.count(nums[i])){
return nums[i];
}
else{
numtimes.insert(nums[i]);
}
}
return -1;
}
};
剑指 Offer 53 - I. 在排序数组中查找数字 I
class Solution {
public:
int search(vector<int>& nums, int target) {
// 二分查找
int left = 0;
int right = nums.size() - 1;
int res = 0;
while(left <= right){
int mid = left + (right - left)/2;
if(nums[mid] == target){
left = mid;
right = mid;
while(left >=0 && nums[left] == target){
left--;
}
while(right<nums.size() && nums[right] == target){
right++;
}
res = right - left -1;
return res;
}
else if(nums[mid] > target){
right = mid - 1;
}
else if(nums[mid] < target){
left = mid + 1;
}
}
return 0;
}
};
剑指 Offer 53 - II. 0~n-1中缺失的数字
class Solution {
public:
int missingNumber(vector<int>& nums) {
// 二分查找
int left = 0;
int right = nums.size() - 1;
while(left <= right){
int mid = left + (right - left)/2;
if(left == right && nums[left] != left){
return left;
}
// 考虑有可能是缺少最后一个数
else if(left == right && nums[left] == left){
return left + 1;
}
if(nums[mid] == mid){
left = mid + 1;
}
else if(nums[mid] != mid){
right = mid - 1;
}
}
return left;
}
};
剑指 Offer 04. 二维数组中的查找
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
// 二分查找 只能想到nlog(n)级别的
// 答案最好是初始值在右上角 只用考虑row++和col--
if(matrix.size() <= 0){
return false;
}
for(int i=0;i<matrix.size();i++){
int left = 0;
int right = matrix[i].size() - 1;
while(left <= right){
int mid = left + (right - left)/2;
if(matrix[i][mid] == target){
return true;
}
else if(matrix[i][mid] < target){
left = mid + 1;
}else{
right = mid - 1;
}
}
}
return false;
}
};
剑指 Offer 11. 旋转数组的最小数字
class Solution {
public:
int minArray(vector<int>& numbers) {
// 遍历
if(numbers.size() <= 0){
return -1;
}
if(numbers.size() == 1){
return numbers[0];
}
int length;
for(int i = 0;i<numbers.size()-1;i++){
int j = i + 1;
if(numbers[j] < numbers[i]){
return numbers[j];
}
length++;
}
if(length+1 == numbers.size()){
return numbers[0];
}
return 0;
}
};
剑指 Offer 50. 第一个只出现一次的字符
class Solution {
public:
char firstUniqChar(string s) {
// 两个26位的数组,一个记录次数,一个记录该字母的位置
// 普通数组放INT_MAX会溢出,所以用尽量用vector
vector<int> index = vector<int>(26, INT_MAX);
int number[26];
memset(number, 0, sizeof(number));
for(int i=0;i<s.size();i++){
int cur = s[i] - 'a';
index[cur] = min(index[cur], i);
number[cur] = number[cur] + 1;
}
int res_index = INT_MAX;
for(int j=0;j<26;j++){
if(number[j] == 1){
res_index = min(index[j], res_index);
}
}
if(res_index == INT_MAX){
return ' ';
}
return s[res_index];
}
};
5. 搜索与回溯算法
剑指 Offer 32 - I. 从上到下打印二叉树(*)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> levelOrder(TreeNode* root) {
// 二叉树的层序遍历 队列来做
vector<int> res;
queue<TreeNode*> que;
if(root == NULL){
return res;
}
que.push(root);
while(!que.empty()){
TreeNode* node = que.front();
res.push_back(node->val);
que.pop();
if(node->left != NULL){
que.push(node->left);
}
if(node->right != NULL){
que.push(node->right);
}
}
return res;
}
};
剑指 Offer 32 - II. 从上到下打印二叉树 II
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
//i的值初始化赋值queue.length,循环开始时执行那一次,判断时不受queue变化影响 一定要注意
// 层序遍历 队列
vector<vector<int>> res;
if(root == NULL){
return res;
}
queue<TreeNode*> que;
que.push(root);
while(!que.empty()){
vector<int> sub_res;
int length = que.size();
// 没有想到,当前que的长度就是当前层的节点数
for(int i=0;i<length;i++){
TreeNode* node = que.front();
sub_res.push_back(node->val);
que.pop();
if(node->left != NULL){
que.push(node->left);
}
if(node->right != NULL){
que.push(node->right);
}
}
res.push_back(sub_res);
}
return res;
}
};
剑指 Offer 32 - III. 从上到下打印二叉树 III
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
// 层序遍历 BFS 队列 需要考虑是第几层
// 0. 错误版本,一边遍历一遍根据层数换顺序,有可能产生换了2遍次序,就又换回去了,
// 1. 因此,从左到右顺序得到一层的结果后,最后再根据层数换顺序
vector<vector<int>> res;
if(root == NULL){
return res;
}
int depth = 0;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()){
vector<int> sub_res;
depth = depth + 1;
int length = que.size();
//cout<<"length"<<length<<endl;
//cout<<"depth"<<depth<<endl;
for(int i=0;i<length;i++){
TreeNode* node = que.front();
sub_res.push_back(node->val);
que.pop();
if(node->left != NULL){
que.push(node->left);
}
if(node->right != NULL){
que.push(node->right);
}
}
cout<<"depth"<<depth<<endl;
if(depth % 2 == 0){
reverse(sub_res.begin(), sub_res.end());
res.push_back(sub_res);
}else{
res.push_back(sub_res);
}
}
return res;
}
};
剑指 Offer 27. 二叉树的镜像
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
// 递归的思路
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
if(root==NULL) return NULL;
TreeNode* tmp = root->left;
root->left = mirrorTree(root->right);
root->right = mirrorTree(tmp);
return root;
}
};
剑指 Offer 28. 对称的二叉树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isSymmetric(TreeNode* root) {
bool index = true;
if(root != NULL){
index = isgoodtree(root->left, root->right);
}
return index;
}
bool isgoodtree(TreeNode* leftnode, TreeNode* rightnode){
if(leftnode == NULL && rightnode == NULL){
return true;
}
if(leftnode == NULL || rightnode == NULL){
return false;
}
if(leftnode->val != rightnode->val){
return false;
}
return isgoodtree(leftnode->right, rightnode->left) && isgoodtree(leftnode->left, rightnode->right);
}
};
剑指 Offer 26. 树的子结构(*)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isSubStructure(TreeNode* A, TreeNode* B) {
// 1.先序遍历树 AA 中的每个节点 n_A(对应函数 isSubStructure(A, B))
// 2.判断树 AA 中 以 n_A为根节点的子树 是否包含树 BB 。(对应函数 recur(A, B))
if(A == NULL && B == NULL){
return true;
}
if(A == NULL || B == NULL){
return false;
}
return recur(A,B) || isSubStructure(A->left, B) || isSubStructure(A->right, B);
}
bool recur(TreeNode* A, TreeNode* B) {
if(B == NULL){
return true;
}
if(A == NULL){
return false;
}
if(A->val != B->val){
return false;
}
return recur(A->left, B->left) && recur(A->right, B->right);
}
};
6. 动态规划
剑指 Offer 10- I. 斐波那契数列
class Solution {
public:
int fib(int n) {
// 动态规划
vector<int> memo(n+1, 0);
if(n == 0){
return 0;
}
if(n == 1){
return 1;
}
memo[0] = 0;
memo[1] = 1;
for(int i=2;i<=n;i++){
memo[i] = memo[i-1] + memo[i-2];
memo[i] = memo[i] % 1000000007;
}
return memo[n];
}
};
剑指 Offer 10- II. 青蛙跳台阶问题
class Solution {
public:
int numWays(int n) {
// 动态规划
// dp[n] = dp[n-1] + dp[n-2]
if(n == 0){
return 1;
}
if(n == 1){
return 1;
}
if(n == 2){
return 2;
}
vector<int> memo(n+1);
memo[1] = 1;
memo[2] = 2;
for(int i=3;i<=n;i++){
memo[i] = memo[i-1] + memo[i-2];
memo[i] = memo[i] % 1000000007;
}
return memo[n];
}
};
剑指 Offer 63. 股票的最大利润
/*
class Solution {
public:
int res = 0;
void find_maxprofit(vector<int>& prices, int index, vector<int>& pre){
if(pre.size() == 2){
res = max(res, pre[1] - pre[0]);
return;
}
for(int i = index; i<prices.size();i++){
pre.push_back(prices[i]);
find_maxprofit(prices, i+1, pre);
pre.pop_back();
}
return;
}
int maxProfit(vector<int>& prices) {
// 1. 暴力回溯法,最后一个测试用例会超时,但现实中可以解决该问题
// 2. 直接双重循环也可以
if(prices.size() <= 1){
return 0;
}
vector<int> pre;
find_maxprofit(prices, 0, pre);
return res;
}
};
*/
class Solution {
public:
int maxProfit(vector<int>& prices) {
// 动态规划
// 一个数组用来存dp[i]
// 一个数用来存上一轮的成本
// dp[i]=max(dp[i−1],prices[i]−min(prices[0:i]))
if(prices.size() <= 1){
return 0;
}
vector<int> dp(prices.size()+1);
dp[0] = 0;
dp[1] = 0;
int cost = prices[0];
for(int i=2;i<=prices.size();i++){
cost = min(cost, prices[i-1]);
dp[i] = max(dp[i-1], prices[i-1] - cost);
}
return dp[prices.size()];
}
};
剑指 Offer 42. 连续子数组的最大和
class Solution {
public:
int maxSubArray(vector<int>& nums) {
//1. 同样可以用暴力回溯法
// 2. 动态规划
// dp[i] = max(dp[i-1], dp[i-1]+nums[i-1])没有体现连续
// dp[i] = max(nums[i-1], dp[i-1]+nums[i-1])
// 为何定义最大和 dp[i]中必须包含元素 nums[i] :保证 dp[i] 递推到 dp[i+1] 的正确性;如果不包含 nums[i],递推时则不满足题目的 连续子数组 要求
vector<int> memo(nums.size()+1);
if(nums.size() == 1){
return nums[0];
}
memo[1] = nums[0];
int res = INT_MIN;
for(int i=2;i<=nums.size();i++){
if(memo[i-1]>0){
memo[i] = memo[i-1]+nums[i-1];
}
if(memo[i-1] <=0){
memo[i] = nums[i-1];
}
res = max(memo[i], res);
}
return max(res, memo[1]);
}
};
剑指 Offer 47. 礼物的最大价值
class Solution {
public:
int maxValue(vector<vector<int>>& grid) {
// 动态规划
// dp[i][j] = max(dp[i-1][j-1],dp[i-1][j], dp[i][j-1]) + grid[i][j];
int m = grid.size();
int n = grid[0].size();
vector<vector<int>> dp(m,vector<int>(n));
dp[0][0] = grid[0][0];
// 对于第0行和第0列,只能有一个方向,首先初始化
for(int j=1;j<n;j++){
dp[0][j] = grid[0][j] + dp[0][j-1];
}
for(int i=1;i<m;i++){
dp[i][0] = grid[i][0] + dp[i-1][0];
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
dp[i][j] = max(dp[i-1][j-1], max(dp[i-1][j], dp[i][j-1])) + grid[i][j];
}
}
return dp[m-1][n-1];
}
};
剑指 Offer 46. 把数字翻译成字符串
class Solution {
public:
int translateNum(int num) {
string nums = to_string(num);
vector<int> dp(nums.size()+1);
// 动态规划
// if(10<= pre <=25) dp[i] = dp[i-2] +dp[i-1]
// else dp[i] = dp[i-1]
if(nums.size() <= 1){
return 1;
}
dp[0] = 1;
dp[1] = 1;
for(int i=2;i<=nums.size();i++){
auto pre = nums.substr(i-2, 2);
cout<<pre<<endl;
if(pre <="25" && pre >= "10"){
dp[i] = dp[i-1] + dp[i-2];
}else if(pre>"25" || pre<"10"){
dp[i] = dp[i-1];
}
}
return dp[nums.size()];
}
};
剑指 Offer 48. 最长不含重复字符的子字符串
class Solution {
public:
int lengthOfLongestSubstring(string s) {
// 滑动窗口可以用
/*
int left =0;
int right = 0;
int res = 0;
set<char> substring;
while(left <= right && right <s.size()){
if(!substring.count(s[right])){
substring.insert(s[right]);
right ++;
}else{
res = max<int>(res, substring.size());
substring.erase(s[left]);
left++;
}
}
return max<int>(res, substring.size());
*/
// 动态规划
// 一定包含s[i]才能保证连续
// 1.if dp[i-1] ==0 dp[i] = 1;
// 2.if dp[i-1] >0
// 2.1 如果前面包含了这个字符位置是j dp[i] = i-1-j;
// 2.2 如果前面没有这个字符dp[i] = dp[i-1] +1
vector<int> dp(s.size()+1);
int res = 0;
if(s.size()==0){
return 0;
}
if(s.size() ==1){
return 1;
}
dp[1] = 1;
for(int i=2;i<=s.size();i++){
if(dp[i-1] == 0){
dp[i] = 1;
}
else if(dp[i-1] >0){
// 需要考虑前面的dp[i-1]里面是否包含这个字符
bool flag = false;
for(int j = i-2;j>=i-1-dp[i-1];j--){
if(s[j] == s[i-1]){
dp[i] = i - 1 - j;
flag = true;
}
}
if(!flag){
dp[i] = dp[i-1] +1;
}
}
res = max(dp[i], res);
}
return max(res, dp[1]);
}
};
剑指 Offer 49. 丑数(*)
class Solution {
public:
int nthUglyNumber(int n) {
// 动态规划
vector<int> dp(n);
dp[0] = 1;
int a=0, b=0, c=0;
for(int i=1;i<n;i++){
int n2 = dp[a] * 2;
int n3 = dp[b] * 3;
int n5 = dp[c] * 5;
dp[i] = min(n2, min(n3, n5));
if(dp[i] == n2) a++;
if(dp[i] == n3) b++;
if(dp[i] == n5) c++;
}
return dp[n-1];
}
};
剑指 Offer 60. n个骰子的点数(*)
class Solution {
public:
vector<double> dicesProbability(int n) {
// 动态规划
// 逆向思维
vector<vector<double>>dp(n+1, vector<double>(6*n+1));
vector<double>res(5*n+1);
for(int i=1;i<=6;i++){
dp[1][i] = 1.0/6;
}
if(n == 1){
for(int i=0;i<=5;i++){
res[i] = (1.0/6);
}
return res;
}
for(int i=2;i<=n;i++){
for(int j=i;j<=6*n;j++){
for(int k=1;k<=6;k++){
if(j-k>0){
dp[i][j] = dp[i][j] + dp[i-1][j-k]/6;
}
else{
break;
}
}
}
}
for(int i=0;i<=5*n;i++){
res[i] = dp[n][n+i];
}
return res;
}
};
剑指 Offer 19. 正则表达式匹配(*)
class Solution {
public:
bool isMatch(string s, string p) {
// 动态规划 脑子在打铁
int m = s.size() + 1;
int n = p.size() + 1;
vector<vector<bool>> dp(m, vector<bool>(n, false));
dp[0][0] = true;
// 初始化第一行
for(int j=2;j<n;j+=2){
dp[0][j] = dp[0][j-2] && p[j-1] == '*';
}
// 状态转移
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
if(p[j-1] == '*'){
if(dp[i][j-2]) dp[i][j] = true;
else if(dp[i-1][j] && s[i-1] == p[j-2]) dp[i][j] = true;
else if(dp[i-1][j] && p[j-2] == '.') dp[i][j] = true;
}
else{
if(dp[i-1][j-1] && s[i-1] == p[j-1]) dp[i][j] = true;
else if(dp[i-1][j-1] && p[j-1] == '.') dp[i][j] = true;
}
}
}
return dp[m-1][n-1];
}
};
7. 双指针
剑指 Offer 18. 删除链表的节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
// 双指针
ListNode* fast=head;
ListNode* slow=head;
// 注意如果就是头节点
if(head->val == val){
return head->next;
}
while(fast !=NULL){
if(fast->val == val){
ListNode* fina = fast->next;
slow->next = fina;
return head;
}
else{
slow = fast;
fast = fast->next;
}
}
return head;
}
};
剑指 Offer 22. 链表中倒数第k个节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
ListNode* slow = head;
ListNode* fast = head;
// 双指针,一定要注意2个while循环分开
while(fast!=NULL && k>0){
fast = fast->next;
k--;
}
while(fast != NULL){
fast = fast->next;
slow = slow->next;
}
return slow;
}
};
剑指 Offer 25. 合并两个排序的链表(*)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(l1 == NULL){
return l2;
}
if(l2 == NULL){
return l1;
}
if(l1->val > l2->val){
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
else if(l1->val <= l2->val){
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
return l1;
}
};
剑指 Offer 52. 两个链表的第一个公共节点(*)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
// 双指针太绝了,根本想不到
// 还可以利用hashset
// 注意公共节点是地址一样,并不是只比较节点中的val一样就行
if(headA == NULL && headB == NULL){
return NULL;
}
if(headA == NULL || headB == NULL){
return NULL;
}
ListNode* a = headA;
ListNode* b = headB;
while(a != b){
a = a == NULL?headB:a->next;
b = b== NULL?headA:b->next;
}
return a;
}
};
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
class Solution {
public:
vector<int> exchange(vector<int>& nums) {
// 双指针 O(n) O(1)
int length = nums.size();
if(length <=1){
return nums;
}
int i = 0;
int j = length -1;
while(i <= j){
if(nums[i]%2!=0 && nums[j]%2==0){
i++;
j--;
}
else if(nums[i]%2==0 && nums[j]%2!=0){
swap(nums[i], nums[j]);
i++;
j--;
}
else if(nums[i]%2==0 && nums[j]%2==0){
j--;
}
else if(nums[i]%2!=0 && nums[j]%2!=0){
i++;
}
}
return nums;
}
};
剑指 Offer 57. 和为s的两个数字
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
// 双指针,头尾指针
vector<int> res;
if(nums.size() == 1){
return res;
}
int left = 0;
int right = nums.size() - 1;
while(left < right){
if(nums[left] + nums[right] == target){
res.push_back(nums[left]);
res.push_back(nums[right]);
return res;
}
else if(nums[left] + nums[right] > target){
right--;
}
else if(nums[left] + nums[right] < target){
left++;
}
}
return res;
}
};
剑指 Offer 58 - I. 翻转单词顺序
class Solution {
public:
string reverseWords(string s) {
// 时间复杂度 O(n) 空间复杂度:O(n)
if(s.size() == 0){
return "";
}
// 删除多余空格,将s存储进vector里面
string res;
vector<string> ss;
s.erase(s.find_last_not_of(" ") + 1);
s.erase(0, s.find_first_not_of(" "));
vector<int> index;
index.push_back(-1);
for(int i =0;i<s.size();i++){
if(s[i] == ' '){
index.push_back(i);
}
}
index.push_back(s.size());
string sub_string;
for(int i=0;i<index.size()-1;i++){
sub_string = s.substr(index[i]+1, index[i+1] - index[i]);
// 就算中间夹了多少个空格,最后截取下来的sub_string还是一个空格
if(sub_string == " ") continue;
sub_string.erase(sub_string.find_last_not_of(" ") + 1);
sub_string.erase(0, sub_string.find_first_not_of(" "));
ss.push_back(sub_string);
cout<<sub_string<<endl;
}
// 对vector用双指针
int left = 0;
int right = ss.size() - 1;
while(left < right){
swap(ss[left], ss[right]);
left++;
right--;
}
for(int i=0;i<ss.size();i++){
res = res+ss[i] + ' ';
}
res.erase(res.find_last_not_of(" ") + 1);
res.erase(0, res.find_first_not_of(" "));
return res;
}
};
8. 搜索和回溯算法
剑指 Offer 12. 矩阵中的路径
class Solution {
public:
const int dx[4] = {1,-1,0,0};
const int dy[4] = {0,0,1,-1};
bool res = false;
vector<vector<bool>> flag;
int m;
int n;
bool exist(vector<vector<char>>& board, string word) {
// dfs无返回值,dfs就类似于回溯中全排列的形式
m = board.size();
n = board[0].size();
flag = vector<vector<bool>>(m, vector<bool>(n, false));
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(board[i][j] == word[0]){
dfs(board, word, i, j, 0);
}
}
}
return res;
}
void dfs(vector<vector<char>>& board, string word, int i, int j, int k){
if(res) return;
if(k == word.size()-1) res = true;
flag[i][j] = true;
for(int f=0;f<4;f++){
int mx = i + dx[f];
int my = j + dy[f];
if(mx>=0 && mx<m && my>=0 && my<n && !flag[mx][my] && board[mx][my]==word[k+1]){
dfs(board, word, mx, my, k+1);
}
}
flag[i][j] = false;
}
};
剑指 Offer 13. 机器人的运动范围
BFS解法:
class Solution {
public:
const int dx[4] = {1,-1,0,0};
const int dy[4] = {0,0,1,-1};
int sums(int x){
int s = 0;
while(x != 0) {
s += x % 10;
x = x / 10;
}
return s;
}
int movingCount(int m, int n, int k) {
// bfs广度优先遍历
vector<vector<bool>> flag = vector<vector<bool>>(m, vector<bool>(n, false));
queue<pair<int, int>> que;
int res = 0;
if(k<0){
return res;
}
que.emplace(0,0);
res++;
flag[0][0] = true;
while(!que.empty()){
int x = que.front().first;
int y = que.front().second;
que.pop();
for(int i=0;i<4;i++){
int mx = x + dx[i];
int my = y + dy[i];
if(mx>=0 && mx<m && my>=0 && my<n && !flag[mx][my] && sums(mx)+sums(my)<=k){
que.emplace(mx, my);
flag[mx][my] = true;
res++;
}
}
}
return res;
}
};
DFS解法:
class Solution {
public:
const int dx[4] = {1,-1,0,0};
const int dy[4] = {0,0,1,-1};
vector<vector<bool>> flag;
int res;
int hang;
int lie;
int sums(int x){
int s = 0;
while(x != 0) {
s += x % 10;
x = x / 10;
}
return s;
}
int movingCount(int m, int n, int k) {
// bfs广度优先遍历
/*
vector<vector<bool>> flag = vector<vector<bool>>(m, vector<bool>(n, false));
queue<pair<int, int>> que;
int res = 0;
if(k<0){
return res;
}
que.emplace(0,0);
res++;
flag[0][0] = true;
while(!que.empty()){
int x = que.front().first;
int y = que.front().second;
que.pop();
for(int i=0;i<4;i++){
int mx = x + dx[i];
int my = y + dy[i];
if(mx>=0 && mx<m && my>=0 && my<n && !flag[mx][my] && sums(mx)+sums(my)<=k){
que.emplace(mx, my);
flag[mx][my] = true;
res++;
}
}
}
return res;
*/
// dfs+回溯思想
hang = m;
lie = n;
flag = vector<vector<bool>>(hang, vector<bool>(lie, false));
res = 0;
dfs(0, 0, k);
return res;
}
void dfs(int x, int y, int k){
int sub = sums(x)+sums(y);
if(flag[x][y] == true || sub > k) return;
if(flag[x][y] == false && sub <= k) res++;
flag[x][y] = true;
for(int i=0;i<4;i++){
int mx = x + dx[i];
int my = y + dy[i];
if(mx>=0 && mx<hang && my>=0 && my<lie && !flag[mx][my]){
dfs(mx, my, k);
}
}
}
};
剑指 Offer 34. 二叉树中和为某一值的路径
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> pathSum(TreeNode* root, int target) {
// 回溯 dfs
if(root == nullptr){
return res;
}
vector<int> pre;
dfs(root, target, pre);
return res;
}
void dfs(TreeNode* node, int target, vector<int>& pre){
if(node == nullptr) return;
pre.push_back(node->val);
target = target - node->val;
if(node->left == nullptr && node->right == nullptr && target == 0){
res.push_back(pre);
}
dfs(node->left, target, pre);
dfs(node->right, target, pre);
pre.pop_back();
}
};
剑指 Offer 36. 二叉搜索树与双向链表(*没理解题意)
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node() {}
Node(int _val) {
val = _val;
left = NULL;
right = NULL;
}
Node(int _val, Node* _left, Node* _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
public:
Node* pre;
Node* cur;
Node* head;
Node* treeToDoublyList(Node* root) {
// 二叉搜索树 中序遍历是递增数据 左根右
if(root == nullptr) return nullptr;
dfs(root);
pre->right = head;
head->left = pre;
//pre->right = head;
return head;
}
void dfs(Node* cur){
if(cur == nullptr) return;
dfs(cur->left);
if(pre != nullptr){
pre->right = cur;
}else{
head = cur;
}
cur->left = pre;
pre = cur;
dfs(cur->right);
}
};
剑指 Offer 54. 二叉搜索树的第k大节点
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> num_res;
int kthLargest(TreeNode* root, int k) {
// 笨办法,前序遍历每一个值,排序后找到第k大
// 聪明方式,二叉搜索树中序遍历是从小到大的
if(root == NULL){
return -1;
}
int res = -1;
binali(root);
sort(num_res.begin(), num_res.end());
res = num_res[num_res.size() - k];
return res;
}
void binali(TreeNode* node){
if(node == NULL){
return;
}
num_res.push_back(node->val);
binali(node->left);
binali(node->right);
}
};
剑指 Offer 55 - I. 二叉树的深度
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int res = 0;
int maxDepth(TreeNode* root) {
// 递归 想好递归终止的条件 以及递归过程
if(root == NULL){
return 0;
}
if(root->left != NULL && root->right == NULL){
res = 1 + maxDepth(root->left);
}
else if(root->left == NULL && root->right != NULL){
res = 1 + maxDepth(root->right);
}
else{
res = 1 + max(maxDepth(root->left), maxDepth(root->right));
}
return res;
}
};
剑指 Offer 55 - II. 平衡二叉树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool flag = false;
int res = 0;
bool isBalanced(TreeNode* root) {
//1. 递归 算每一个节点的左右子树深度 符合条件即可
if(root == NULL){
return true;
}
flag = abs(depthtree(root->right) - depthtree(root->left))<=1?true:false;
return flag && isBalanced(root->left) && isBalanced(root->right);
}
int depthtree(TreeNode* node){
if(node == NULL){
return 0;
}
if(node->left == NULL && node->right != NULL){
res = 1 + depthtree(node->right);
}
else if(node->left != NULL && node->right == NULL){
res = 1 + depthtree(node->left);
}
else{
res = 1 + max(depthtree(node->right), depthtree(node->left));
}
return res;
}
};
剑指 Offer 64. 求1+2+…+n
class Solution {
public:
int sumNums(int n) {
// 递归
if(n == 1){
return 1;
}
return sumNums(n-1) + n;
}
};
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
//vector<TreeNode*> trees;
vector<TreeNode*> p_path;
vector<TreeNode*> q_path;
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
/*
// 1. 前序遍历后两点前距离前面一点最近的一点或者这点本身就是
// 先判断是否该点本身就是另一点的父节点,如果是直接返回
// 不对,并不是前面最近一点就是祖先,例如[3,1,4,null,2] 2 4
if(isancestor(p, q)) return p;
if(isancestor(q, p)) return q;
cout<<isancestor(p, q)<<endl;
qianxu(root);
for(int i=0;i<trees.size();i++){
cout<<trees[i]<<endl;
}
int index_p, index_q;
for(int i=0;i<trees.size();i++){
if(p->val == q->val && trees[i]->val == p->val){
index_p = i;
index_q = i;
break;
}
if(p->val != q->val && trees[i]->val == p->val){
index_p = i;
cout<<index_p<<endl;
}
if(p->val != q->val && trees[i]->val == q->val){
index_q = i;
cout<<index_q<<endl;
}
}
if(index_p < index_q){
return trees[index_p - 1];
}
else if(index_p > index_q){
return trees[index_q - 1];
}
return p;
*/
// 2.比较到p节点的祖先路径, 和到q节点的祖先路径,然后其中最后一个相等的数字就是最近公共祖先
p_path = get_path(root, p);
q_path = get_path(root, q);
TreeNode* res;
for(int i=0;i<p_path.size() && i<q_path.size();i++){
if(p_path[i] == q_path[i]){
res = p_path[i];
}else{
return res;
}
}
return res;
}
vector<TreeNode*> get_path(TreeNode* node, TreeNode* p){
vector<TreeNode*> path;
while(node != p){
path.push_back(node);
cout<<node->val<<endl;
if(p->val < node->val){
node = node->left;
}else{
node = node->right;
}
}
path.push_back(node);
return path;
}
/*
void qianxu(TreeNode* node){
if(node == NULL){
return;
}
trees.push_back(node);
qianxu(node->left);
qianxu(node->right);
}
bool isancestor(TreeNode* father, TreeNode* son){
if(father == NULL){
return false;
}
if(father == son){
return true;
}
return isancestor(father->left, son) || isancestor(father->right, son);
}
*/
};
剑指 Offer 68 - II. 二叉树的最近公共祖先
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<TreeNode*> p_path;
vector<TreeNode*> q_path;
vector<vector<TreeNode*>> res;
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
// 1.dfs找到这两个节点的路径
// 2. 一一对应 最后一个相同的节点就是最近的公共祖先
get_path(root, p, p_path);
get_path(root, q, q_path);
TreeNode* res_node;
for(int i=0;i<res[0].size() && i<res[1].size();i++){
if(res[0][i] == res[1][i]){
res_node = res[0][i];
}
else{
return res_node;
}
}
return res_node;
}
void get_path(TreeNode* node, TreeNode* target, vector<TreeNode*>& path){
if(node == NULL){
return;
}
path.push_back(node);
if(node == target){
// 重要!如果只用path作为储存,随着递归path指向的空间会一直改变的
res.push_back(path);
return;
}
get_path(node->left, target, path);
get_path(node->right, target, path);
path.pop_back();
}
};
剑指 Offer 38. 字符串的排列
class Solution {
public:
set<string> res;
vector<bool> used;
vector<string> permutation(string s) {
// 回溯,全排列
// 然后对结果进行去重
string pre;
used = vector<bool>(s.size(), false);
permuta(s, used, 0, pre);
vector<string> final_res;
for(auto string1:res){
final_res.push_back(string1);
}
return final_res;
}
void permuta(string s, vector<bool>& used,int index, string pre){
if(s.size() == pre.size()){
res.emplace(pre);
return;
}
for(int i=0;i<s.size();i++){
if(!used[i]){
pre.push_back(s[i]);
used[i] = true;
permuta(s, used, index+1, pre);
pre.pop_back();
used[i] = false;
}
}
}
};
剑指 Offer 37. 序列化二叉树(*)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Codec {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
// 层序遍历 BFS
// string a = a+b {a +=b == a.append(b)}
if(root == NULL) return "[]";
string res = "[";
queue<TreeNode*> que;
que.push(root);
while(!que.empty()){
TreeNode* node = que.front();
que.pop();
if(node != NULL){
res += to_string(node->val) + ",";
que.push(node->left);
que.push(node->right);
}
else{
res += "null,";
}
}
res += "]";
return res;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
if(data == "[]"){
return NULL;
}
queue<TreeNode*> deseque;
stringstream iss(data.substr(1, data.size() - 2));
string str;
getline(iss, str, ',');
TreeNode* root = new TreeNode(atoi(str.c_str()));
deseque.push(root);
while(!deseque.empty()){
TreeNode* node = deseque.front();
deseque.pop();
getline(iss, str, ',');
if(str != "null"){
TreeNode* left = new TreeNode(atoi(str.c_str()));
node->left = left;
deseque.push(left);
}
getline(iss, str, ',');
if(str != "null"){
TreeNode* right = new TreeNode(atoi(str.c_str()));
node->right = right;
deseque.push(right);
}
}
return root;
}
};
// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));
9. 排序
剑指 Offer 61. 扑克牌中的顺子(*)
class Solution {
public:
bool isStraight(vector<int>& nums) {
//此 5 张牌是顺子的 充分条件 如下:
// 除大小王外,所有牌 无重复 ;设此 5 张牌中最大的牌为 max ,最小的牌为 min (大小王除外),则需满足:max - min < 5
// 利用set 以及遍历找出最大最小值
set<int> norepeat;
int min_num = 14;
int max_num = -1;
for(int i = 0;i<nums.size();i++){
if(nums[i] == 0){
continue;
}
if(norepeat.count(nums[i])) return false;
norepeat.insert(nums[i]);
min_num = min(min_num, nums[i]);
max_num = max(max_num, nums[i]);
}
if(max_num - min_num < 5){
return true;
}
return false;
}
};
剑指 Offer 45. 把数组排成最小的数(*)
class Solution {
public:
string minNumber(vector<int>& nums) {
/*
此题求拼接起来的最小数字,本质上是一个排序问题。设数组 numsnums 中任意两数字的字符串为x和y ,则规定 排序判断规则 为:
若拼接字符串 x + y > y + x ,则 x “大于” y;
反之,若 x + y < y + x ,则 x “小于” y ;
x “小于” y 代表:排序完成后,数组中 x 应在 y 左边;“大于” 则反之。
*/
vector<string> strs;
string res;
for(int i=0;i<nums.size();i++){
strs.push_back(to_string(nums[i]));
}
sort(strs.begin(), strs.end(), [](string& x, string& y){return x+y < y+x;});
for(int i =0;i<strs.size();i++){
res.append(strs[i]);
}
return res;
}
};
剑指 Offer 40. 最小的k个数
class Solution {
public:
void qsort(vector<int>& arr, int low, int high){
if(low < high){
int pivot = partition(arr, low, high);
qsort(arr, low, pivot - 1);
qsort(arr, pivot + 1, high);
}
}
int partition(vector<int>& arr, int low, int high){
int pivot = arr[low];
while(low < high){
while(low < high && arr[high] >= pivot) high--;
arr[low] = arr[high];
while(low < high && arr[low] <= pivot) low++;
arr[high] = arr[low];
}
arr[low] = pivot;
return low;
}
vector<int> getLeastNumbers(vector<int>& arr, int k) {
// 快排
vector<int> res;
if(k < 0){
return res;
}
qsort(arr, 0, arr.size()-1);
for(int i=0;i<k;i++){
res.push_back(arr[i]);
}
return res;
}
};
/*
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
// 维护一个k长度的堆,大根堆
vector<int> vec(k, 0);
if (k == 0) { // 排除 0 的情况
return vec;
}
priority_queue<int> Q;
for (int i = 0; i < k; i++) {
Q.push(arr[i]);
}
for (int i = k; i < arr.size(); i++) {
if (Q.top() > arr[i]) {
Q.pop();
Q.push(arr[i]);
}
}
for (int i = 0; i < k; i++) {
vec[i] = Q.top();
Q.pop();
}
return vec;
}
};
*/
剑指 Offer 41. 数据流中的中位数(*)
class MedianFinder {
public:
/** initialize your data structure here. */
// 用一个小根堆维持大的数,用一个大根堆维持小的数
// 大根堆,存前半部分
priority_queue<int, vector<int>, less<int>> maxheap;
// 小根堆,存后半部分
priority_queue<int, vector<int>, greater<int>> minheap;
MedianFinder() {
}
// 维持两个堆保持数量平衡,并且保证左边堆的最大值小于右边堆的最小值
void addNum(int num) {
/*
* 当两堆的数据个数相等时候,左边堆添加元素。
* 采用的方法不是直接将数据插入左边堆,而是将数据先插入右边堆,算法调整后
* 将堆顶的数据插入到左边堆,这样保证左边堆插入的元素始终是右边堆的最小值。
* 同理左边数据多,往右边堆添加数据的时候,先将数据放入左边堆,选出最大值放到右边堆中。
*/
if(maxheap.size() == minheap.size()){
minheap.push(num);
int top = minheap.top();
minheap.pop();
maxheap.push(top);
}else{
maxheap.push(num);
int top = maxheap.top();
maxheap.pop();
minheap.push(top);
}
}
double findMedian() {
if(maxheap.size() == minheap.size()){
return (maxheap.top() + minheap.top()) / 2.0;
}
else{
return maxheap.top() / 1.0;
}
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
10. 分治算法(*)
剑指 Offer 07. 重建二叉树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
unordered_map<int,int> map;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
//hashmap inorder方便在中序遍历中确定root的位置
for(int i = 0;i< inorder.size();i++){
map[inorder[i]] = i;
}
// 传入参数:前序,中序,前序序列根节点,中序序列左边界,中序序列右边界
return build(preorder, inorder, 0, 0, inorder.size() - 1);
}
TreeNode* build(vector<int>& preorder, vector<int>& inorder, int pre_root, int index_left, int index_right){
if(index_left>index_right){
return NULL;
}
TreeNode* root = new TreeNode(preorder[pre_root]);
int index_root = map[preorder[pre_root]];
root->left = build(preorder, inorder, pre_root+1, index_left, index_root-1);
root->right = build(preorder, inorder, pre_root+index_root-index_left+1, index_root+1, index_right);
return root;
}
};
剑指 Offer 16. 数值的整数次方
class Solution {
public:
double myPow(double x, int n) {
if(n == 0){
return 1;
}
// 递归,区分一下奇偶数情况就不超时了
else if(n > 0 && n%2==0){
return myPow(x*x, n/2);
}
else if(n>0 && n%2!=0){
return x * myPow(x*x,n/2);
}
else{
return 1.0 / (myPow(x, abs(n)-1) *x);
}
}
};
剑指 Offer 33. 二叉搜索树的后序遍历序列
class Solution {
public:
bool verifyPostorder(vector<int>& postorder) {
// 递归分治
return recur(postorder, 0, postorder.size()-1);
}
bool recur(vector<int>& postorder, int left, int root){
if(left >= root){
return true;
}
int p = left;
while(postorder[p] <postorder[root]) p++;
int right = p;
while(postorder[p] > postorder[root]) p++;
return p==root && recur(postorder, left, right-1) && recur(postorder, right, root - 1);
}
};
剑指 Offer 17. 打印从1到最大的n位数
class Solution {
public:
vector<int> printNumbers(int n) {
// 暴力法
vector<int> res;
string ans;
ans = "1";
for(int i=0;i<n;i++){
ans += "0";
}
int a = atoi(ans.c_str());
for(int i=1;i<a;i++){
res.push_back(i);
}
return res;
}
};
剑指 Offer 51. 数组中的逆序对
class Solution {
public:
/*
vector<vector<int>> res;
int final_res;
int reversePairs(vector<int>& nums) {
// 回溯得到所有数组对 组合问题
// 严重超时
if(nums.size() <= 1){
return 0;
}
vector<int> pre;
permute(nums, 0, pre);
final_res = res.size();
return final_res;
}
void permute(vector<int>& nums, int depth, vector<int>& pre){
if(pre.size() == 2){
if(pre[0] > pre[1]){
res.push_back(pre);
return;
}
return;
}
for(int i=depth;i<nums.size();i++){
pre.push_back(nums[i]);
permute(nums, i+1, pre);
pre.pop_back();
}
}
*/
//归并排序的过程,恰好是跟逆顺序对,息息相关
int count = 0;
int reversePairs(vector<int>& nums) {
mergesort(nums, 0, nums.size() - 1);
return count;
}
void mergesort(vector<int>& nums, int low, int high){
int mid = (high - low)/2 + low;
if(low < high){
mergesort(nums, low, mid);
mergesort(nums, mid+1, high);
merge(nums, low, mid, high);
}
}
void merge(vector<int>& nums, int low, int mid, int high){
vector<int> temparr(high-low+1);
int left = low;
int right = mid + 1;
int k = 0;
while(left<=mid && right<=high){
if(nums[left] <= nums[right]){
temparr[k++] = nums[left++];
}else{
count += (mid - left + 1);
temparr[k++] = nums[right++];
}
}
// 把左边的数放进数组中
while(left <= mid){
temparr[k++] = nums[left++];
}
// 把右边剩余的数放进数组中
while(right <= high){
temparr[k++] = nums[right++];
}
// 把临时数组中的值覆盖到原数组nums中
for(int k1=0;k1<temparr.size();k1++){
nums[low + k1] = temparr[k1];
}
}
};
11. 位运算
剑指 Offer 15. 二进制中1的个数
class Solution {
public:
int hammingWeight(uint32_t n) {
int res = 0;
for(int i=0;i<32;i++){
if(n & (1<<i)){
res++;
}
}
return res;
}
};
剑指 Offer 65. 不用加减乘除做加法
class Solution {
public:
int add(int a, int b) {
while(b != 0){
int c = (unsigned int) (a&b) << 1;
a = a^b;
b = c;
}
return a;
}
};
剑指 Offer 56 - I. 数组中数字出现的次数
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int x=0, y=0, n=0, m=1;
vector<int> res;
// 1. 遍历异或
for(int num:nums){
n = n ^ num;
}
//2. 循环左移,计算m
while((n & m) == 0){
m <<= 1;
}
//3. 遍历nums分组
for(int num:nums){
if(num & m) x = x^num;
else y = y^num;
}
res.push_back(x);
res.push_back(y);
return res;
}
};
剑指 Offer 56 - II. 数组中数字出现的次数 II
class Solution {
public:
int singleNumber(vector<int>& nums) {
// map
unordered_map<int, int> num_map;
for(int num:nums){
if(num_map.count(num)){
int temp = num_map[num];
num_map[num] = temp + 1;
}
else{
num_map.emplace(num,1);
}
}
for(auto& v :num_map){
if(v.second == 1){
return v.first;
}
}
return 0;
}
};
12. 数学(*)
剑指 Offer 39. 数组中出现次数超过一半的数字
class Solution {
public:
int majorityElement(vector<int>& nums) {
// 排序 中间的数就是这个数字
sort(nums.begin(), nums.end());
return nums[nums.size() /2];
}
};
剑指 Offer 66. 构建乘积数组
class Solution {
public:
vector<int> constructArr(vector<int>& a) {
// 本质就是两个dp数组,分别维护 i 左侧、右侧的乘积和
if(a.size() <=0){
return a;
}
int length = a.size();
vector<int> left(length);
vector<int> right(length);
vector<int> res(length);
left[0] = 1;
right[length - 1] = 1;
for(int i=1;i<length;i++){
left[i] = left[i-1] * a[i-1];
}
for(int i=length-2;i>=0;i--){
right[i] = right[i+1] * a[i+1];
}
for(int i=0;i<length;i++){
res[i] = left[i] * right[i];
}
return res;
}
};
剑指 Offer 62. 圆圈中最后剩下的数字
class Solution {
public:
int lastRemaining(int n, int m) {
/* K神牛逼!约瑟夫环
状态定义: 设「i, m问题」的解为 dp[i] ;
转移方程: 通过以下公式可从 dp[i - 1]递推得到 dp[i] ;
dp[i]=(dp[i−1]+m)%i
初始状态:「1, m 问题」的解恒为 0 ,即 dp[1] = 0;
返回值: 返回「n, m问题」的解 dp[n];
*/
vector<int> dp(n+1);
dp[1] = 0;
if(n == 1){
return dp[1];
}
for(int i=2;i<=n;i++){
dp[i] = (dp[i-1] + m)%i;
}
return dp[n];
}
};
剑指 Offer 57 - II. 和为s的连续正数序列
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> findContinuousSequence(int target) {
// 暴力回溯法 100就超时了
/*
if(target <= 2){
return res;
}
int length = target/2 +1;
vector<int> nums(length);
for(int i=0;i<nums.size();i++){
nums[i] = i+1;
}
vector<int> pre;
int sum = 0;
findcombine(nums, target, 0, pre, sum);
return res;
}
void findcombine(vector<int>& nums, int target, int index, vector<int>& pre, int sum){
if(pre.size()>=2 && sum == target && islianxu(pre)){
res.push_back(pre);
return;
}
for(int i=index;i<nums.size();i++){
pre.push_back(nums[i]);
sum = sum+nums[i];
findcombine(nums, target, i+1, pre, sum);
pre.pop_back();
sum = sum-nums[i];
}
return;
}
bool islianxu(vector<int>& pre){
for(int i=0;i<pre.size()-1;i++){
if(pre[i]+1 != pre[i+1]){
return false;
}
}
return true;
}
*/
// 滑动窗口
int left = 1;
int right = 2;
int sum = 3;
while(left < right){
if(sum == target){
vector<int> arr;
for(int i=left;i<=right;i++){
arr.push_back(i);
}
res.push_back(arr);
}
if(sum >= target){
sum = sum - left;
left++;
}
else{
//此处的顺序很重要
right++;
sum = sum + right;
}
}
return res;
}
};
剑指 Offer 14- I. 剪绳子
class Solution {
public:
int cuttingRope(int n) {
// 动态规划
// dp[i]
if(n <=1){
return 0;
}
if(n==2){
return 1;
}
vector<int> dp(n+1, 0);
dp[2] = 1;
dp[3] = 2;
for(int i=4;i<=n;i++){
for(int j = 1;j<i;j++){
// 剩下的i-j长度还剪不剪,如果不剪就是j*(i-j),如果剪就是j*dp[i-j]
// 对于每一个j,哪个dp[i]最大
dp[i] = max(dp[i], max(j*(i-j), j*dp[i-j]));
}
}
return dp[n];
}
};
剑指 Offer 14- II. 剪绳子 II
class Solution {
public:
long p = 1000000007;
int cuttingRope(int n) {
// 本题唯一区别在于存在大数越界问题
// 无法使用动态规划,只能利用数学+贪心算法
if(n <= 1){
return 0;
}
if(n == 2){
return 1;
}
if(n == 3){
return 2;
}
if(n == 4){
return 4;
}
long res = 1;
int mod = 1000000007;
while(n > 4){
res = res * 3 % mod;
n -= 3;
}
return (int)(res * n % mod);
}
};
剑指 Offer 43. 1~n 整数中 1 出现的次数(*)
class Solution {
public:
int countDigitOne(int n) {
long digit = 1;
int res = 0;
int high = n/10;
int cur = n%10;
int low = 0;
// 当 high 和 cur 同时为 0 时,说明已经越过最高位,因此跳出
while(high != 0 || cur != 0){
if(cur==0) res += high * digit;
else if(cur == 1) res += high * digit + low + 1;
else res += (high + 1) * digit;
// 将 cur 加入 low ,组成下轮 low
low += cur * digit;
// 下轮 cur 是本轮 high 的最低位
cur = high % 10;
// 将本轮 high 最低位删除,得到下轮 high
high /= 10;
// 位因子每轮 × 10
digit *= 10;
}
return res;
}
};
剑指 Offer 44. 数字序列中某一位的数字
class Solution {
public:
int findNthDigit(int n) {
long start = 1;
int digit = 1;
long count = 9;
while(n>count){
n -=count;
start *= 10;
digit ++;
count = start* digit* 9;
}
int num = start +(n-1)/digit;
string s = to_string(num);
return s[(n-1)%digit] -'0';
}
};
13. 模拟(*)
剑指 Offer 29. 顺时针打印矩阵
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
// 递归yyds
int hang = matrix.size();
vector<int> res;
if(hang <= 0){
return res;
}
int lie = matrix[0].size();
res = vector<int>(hang * lie);
//四个角
recur(matrix, res, hang-1, lie-1, 0, 0, 0);
return res;
}
void recur(vector<vector<int>>& matrix, vector<int>& res, int hang, int lie, int i, int j, int k){
if( k>=res.size() || i>hang || j>lie){
return;
}
// 从左到右
for(int m=j; m<=lie&&k<res.size() ;m++){
res[k++] = matrix[i][m];
}
// 从上到下
for(int n=i+1; n<=hang&&k<res.size() ;n++){
res[k++] = matrix[n][lie];
}
// 从右到左
for(int p=lie-1; p>=j&&k<res.size(); p--){
res[k++] = matrix[hang][p];
}
// 从下到上
for(int q=hang-1; q>=i+1&&k<res.size(); q--){
res[k++] = matrix[q][j];
}
recur(matrix, res, hang-1, lie-1, i+1, j+1, k);
}
};
剑指 Offer 31. 栈的压入、弹出序列
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
// 用到一个辅助栈来模拟整个过程
stack<int> help_stack;
int i = 0;
for(int num:pushed){
help_stack.push(num);
while(!help_stack.empty() && help_stack.top() == popped[i]){
help_stack.pop();
i++;
}
}
return help_stack.empty();
}
};
有限状态自动机(牛掰)
剑指 Offer 20. 表示数值的字符串
class Solution {
public:
bool isNumber(string s) {
// 有限状态自动机
/*
状态定义:按照字符串从左到右的顺序,定义以下 9 种状态。
1开始的空格
2幂符号前的正负号
3小数点前的数字
4小数点、小数点后的数字
5当小数点前为空格时,小数点、小数点后的数字
6幂符号
7幂符号后的正负号
8幂符号后的数字
9结尾的空格
结束状态:
合法的结束状态有 2, 3, 7, 8 。
//0:规定0是初值,字符串表示数值,有4种起始状态,开头空格、符号、数字、前面没有数的小数点
//其中 开头空格 还是指向states[0],上一位是 开头空格,下一位可以是 空格、符号、数字、前面没有数的小数点
new HashMap<>() {{ put(' ', 0); put('s', 1); put('d', 2); put('.', 4); }},
//1:上一位是符号,符号位后面可以是 数字、前面没有数的小数点
new HashMap<>() {{ put('d', 2); put('.', 4); }},
//2:上一位是数字,数字的下一位可以是 数字、前面有数的小数点、e、结尾空格
new HashMap<>() {{ put('d', 2); put('.', 3); put('e', 5); put(' ', 8); }},
//3:上一位是前面有数的小数点,下一位可以是 数字、e(8.e2 = 8e2,和2的情况一样)、结尾空格
new HashMap<>() {{ put('d', 3); put('e', 5); put(' ', 8); }},
//4:上一位是前面没有数的小数点,下一位只能是 数字(符号肯定不行,e得前面有数才行)
new HashMap<>() {{ put('d', 3); }},
//5:上一位是e,下一位可以是 符号、数字
new HashMap<>() {{ put('s', 6); put('d', 7); }},
//6::上一位是e后面的符号,下一位只能是 数字
new HashMap<>() {{ put('d', 7); }},
//7:上一位是e后面的数字,下一位可以是 数字、结尾空格
new HashMap<>() {{ put('d', 7); put(' ', 8); }},
//8:上一位是结尾空格,下一位只能是 结尾空格
new HashMap<>() {{ put(' ', 8); }}
*/
vector<unordered_map<char,int>> states = {
{{' ', 0},{'s', 1},{'d', 2},{'.', 4}}, //0
{{'d', 2},{'.', 4}}, //1
{{'d', 2},{'.', 3},{'e', 5},{' ', 8}}, //2
{{'d', 3},{'e', 5},{' ', 8}}, //3
{{'d', 3}}, //4
{{'s', 6},{'d', 7}}, //5
{{'d', 7}}, //6
{{'d', 7},{' ', 8}}, //7
{{' ', 8}}, //8
};
int p = 0;
char t;
for(char c : s) {
if(c >= '0' && c <= '9') t = 'd';
else if(c == '+' || c == '-') t = 's';
else if(c == 'e' || c == 'E') t = 'e';
else if(c == '.' || c == ' ') t = c;
else t = '?';
if(!states[p].count(t)) return false;
p = states[p][t];
}
return p == 2 || p == 3 || p == 7 || p == 8;
}
};
剑指 Offer 67. 把字符串转换成整数
class Solution {
public:
int strToInt(string str) {
// 有限状态自动机
/*
状态:0:起始状态,1:符号+/-状态,2:数字状态,3:终止状态
转移条件:空格、符号、数字、其他
*/
vector<unordered_map<char, int>> states = {
{{' ',0}, {'s',1}, {'d', 2}, {'e', 3}}, //0
{{'d',2}, {' ',3}, {'s',3}, {'e',3}}, //1
{{'e',3}, {'d',2}, {' ',3}, {'s',3}}, //2
{{' ',3}, {'d',3}, {'s',3}, {'e',3}}, //3
};
long res = 0;
int p = 0;
int flag = 1;
char t;
for(char c:str){
if(c == ' ') t = ' ';
else if(c == '+' || c=='-') t = 's';
else if(c >= '0' && c <= '9') t = 'd';
else{
t = 'e';
}
p = states[p][t];
if(p == 1){
if(c == '+'){
flag = 1;
}else{
flag = -1;
}
}
else if(p == 2){
res = 10 * res + (c - '0');
if(flag == 1 && res >= INT_MAX){
return INT_MAX;
}
if(flag == -1 && res > INT_MAX){
return INT_MIN;
}
}
}
return flag * res;
}
};
14. 栈与队列
剑指 Offer 59 - II. 队列的最大值
class MaxQueue {
public:
deque<int> que;
deque<int> fuzhu;
// 利用一个辅助的双端队列
MaxQueue() {
}
int max_value() {
if(fuzhu.empty()){
return -1;
}
int index = fuzhu.front();
return index;
}
void push_back(int value) {
while(!fuzhu.empty() && fuzhu.back() < value){
fuzhu.pop_back();
}
fuzhu.push_back(value);
que.push_back(value);
}
int pop_front() {
if (que.empty())
return -1;
int deq_front = que.front();
if(fuzhu.front() == deq_front){
fuzhu.pop_front();
}
que.pop_front();
return deq_front;
}
};
/**
* Your MaxQueue object will be instantiated and called as such:
* MaxQueue* obj = new MaxQueue();
* int param_1 = obj->max_value();
* obj->push_back(value);
* int param_3 = obj->pop_front();
*/
剑指 Offer 59 - I. 滑动窗口的最大值
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
// 维护一个单调双端队列
if(nums.size() <=0){
return res;
}
if(k == 1){
return nums;
}
deque<int> fuzhu;
for(int i=0;i<k;i++){
while(!fuzhu.empty() && fuzhu.back() < nums[i]){
fuzhu.pop_back();
}
fuzhu.push_back(nums[i]);
}
res.push_back(fuzhu.front());
for(int i=k;i<nums.size();i++){
if(nums[i-k] == fuzhu.front()){
fuzhu.pop_front();
}
while(!fuzhu.empty() && fuzhu.back() < nums[i]){
fuzhu.pop_back();
}
fuzhu.push_back(nums[i]);
res.push_back(fuzhu.front());
}
return res;
}
};