刷题思路

第一遍:

  1. 使用c++独立解。
    • 20分钟以上无思路再看他人题解,且标记。
  2. 解题完毕,记录自己的题解。
  3. 看他人题解的其他办法

字符串

剑指 Offer 05. 替换空格 简单

我的题解

class Solution {
public:
    string replaceSpace(string s) {
        string r;
        for(int i=0; i<s.length();i++){
            char c = s[i];
            if(c==' ')
            {
                r.push_back('%');
                r.push_back('2');
                r.push_back('0');
            }
            else
                r.push_back(c);
        }
        return r;
    }
};

剑指 Offer 58 - II. 左旋转字符串 简单

我的题解

//切片法
class Solution {
public:
    string reverseLeftWords(string s, int n) {
        return s.substr(n,s.length()-n) + s.substr(0,n);
    }
};
//切片法过于暴力
//遍历法取余
class Solution {
public:
    string reverseLeftWords(string s, int n) {
        string r = s; 
        int len = s.length();
        for(int i=0; i<len;i++)
        {
            r[(i+len-n)%len] = s[i];
        }
        return r;
    }
};

剑指 Offer 67. 把字符串转换成整数 中等

class Solution {
public:
    int strToInt(string str) {
        int len = str.length();
        if(len == 0) return 0; //字符串长度为o
        int res = 0;//结果
        int sign = 1;//符号,默认为无符号正数
        int i = 0;//序号
        while(str[i]==' ') i++; //索引到第一个无空格的字符
        if(str[i]=='-') sign = -1; //负号
        if(str[i]=='-' || str[i]=='+') ++i; // 有符号则向后移一位
        for(;i<len;++i)
        {
            if(str[i]<'0'||str[i]>'9') break;
            if (res > INT_MAX / 10 || (res == INT_MAX / 10 && str[i] - '0' > 7)) //溢出判定
                return sign == 1 ? INT_MAX : INT_MIN;
            res = res * 10 + (str[i] - '0');
        }
        return sign * res;
    }
};

链表

剑指 Offer 06. 从尾到头打印链表 简单

//回溯
//依次存入temp,再反向存入输出数组
class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> temp, res;
        ListNode *p = head;
        while(p)
        {
            temp.push_back(p->val);
            p = p->next;
        }
        for(int i = temp.size()-1; i>=0; --i)
        {
            res.push_back(temp[i]);
        }
        return res;
    }
};

顺便复习STL中的vector

剑指 Offer 24. 反转链表

//头插法
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* res=NULL, *cur=head;
        while(cur!=NULL)
        {
            ListNode* temp = cur->next;
            cur->next = res;
            res = cur;
            cur = temp;
        }
        return res;
    }
};

双指针

剑指 Offer 18. 删除链表的节点

class Solution {
public:
    ListNode* deleteNode(ListNode* head, int val) {
        if(head == NULL) return head;//判断空
        ListNode *pre=head,*cur=head->next;
        if(pre->val == val) return cur; // 判断第一个节点
        while(cur!=NULL) //判断后面的阶段
        {
            if(cur->val == val)
            {
                pre->next = cur->next;
                cur = cur->next;
                break;
            }
            pre = pre->next;
            cur = cur->next;
        }
        return head;
    }
};

剑指 Offer 22. 链表中倒数第k个节点

//统计法
class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        ListNode *p = nullptr;
        int cnt = 0;
        for(p=head;p!=NULL;p=p->next)
            cnt++;
        for(p=head;cnt>k;--cnt)
            p = p->next;
        return p;
    }
};
//双指针
class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        ListNode* p=head, *q = head;
        for(int n=0; n<k; ++n)
            p = p->next;
        while(p!=NULL)
        {
            p = p->next;
            q = q->next;
        }
        return q;
    }
};

剑指 Offer 25. 合并两个排序的链表

//迭代
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* head = new ListNode(0);
        ListNode* res = head;
        while(l1 && l2)
        {
            if(l1->val < l2->val)
            {
                res->next = l1;
                l1 = l1->next;
            }
            else
            {
                res->next = l2;
                l2 = l2->next;
            }
            res = res->next;
        }
        if(l1)
            res->next = l1;
        else if(l2) 
            res->next = l2;

        return head->next;
    }
};
//递归
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
       if(!l1) return l2;
       if(!l2) return l1;
       if(l1->val < l2->val)
       {
           l1->next = mergeTwoLists(l1->next,l2);
           return l1;
       }
       else
       {
            l2->next = mergeTwoLists(l1,l2->next);
            return l2;
       }
    }
};

剑指 Offer 52. 两个链表的第一个公共节点

//双指针遍历相遇
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *a = headA;
        ListNode *b = headB;
        while(a != b )
        {
            a = a != nullptr ? a->next : headB;
            b = b != nullptr ? b->next : headA;
        }
        return a;
    }
};

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

class Solution {
public:
    vector<int> exchange(vector<int>& nums) {
        int left = 0, right = nums.size()-1;
        while(left < right)
        {
            if(nums[left]%2==0||nums[left]==0)//偶数
            {
                while(nums[right]%2==0||nums[right]==0)//从后往前直到找到第一个奇数
                {
                    if(right < 0 || right<=left) break; //right不能在left左边且不能小于0
                    right--;
                } 
                int temp = nums[left];
                nums[left] = nums[right];
                nums[right] = temp;
            }
            left++;
        }
        return nums;
    }
};

其他思路,可以使用双向队列deque进行实现,但是要求结果是vector,实现deque之后需要再依次赋值给新的vector,效率很低,所以在题目要求的情况下,不建议使用deque

剑指 Offer 57. 和为s的两个数字

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> res;
        int left = 0, right = nums.size()-1;
        while(left<right)
        {
            if(target-nums[left] < nums[right]) right--;
            else if (target-nums[left] > nums[right]) left++;
            else
            {
                res.push_back(nums[left]);
                res.push_back(nums[right]);
                return res;
            }
        }
        return res;
    }
};

剑指 Offer 58 - I. 翻转单词顺序

class Solution {
public:
    string reverseWords(string s) {
        string res = "";
        int len = s.length();
        int right = len-1;
        int left = right;
        while(left>=0)
        {
            //首尾空格
            if(s[right] == ' ')
            {
                right--;
                left--;
                continue;
            }
            //正常
            if(s[left]==' '||left == 0)
            {
                int temp = right;
                if(left==0 &&s[left]!=' ') right = left;//第0个字符问题
                else right = left+1;
                while(right<=temp)
                {
                    res +=s[right++];
                }
                res += ' ';
                right = left-1;
            }
            left--;
        }
        res.pop_back();
        return res;
    }
};

栈和队列

剑指 Offer 09. 用两个栈实现队列

class CQueue {
public:
    stack<int> s1,s2;
    CQueue() {

    }
    
    void appendTail(int value) {
        s1.push(value);
    }
    
    int deleteHead() {
        if(s1.empty()) return -1;
        
        int len = s1.size();

        while(!s1.empty())
        {
            s2.push(s1.top());
            s1.pop();
        }
        int res = s2.top();
        s2.pop();//删除栈顶
        while(!s2.empty())
        {
            s1.push(s2.top());
            s2.pop();
        }
        return res;
    }
};

剑指 Offer 30. 包含min函数的栈

class MinStack {
public:
    /** initialize your data structure here. */
    stack<int> s,s_min;
    int m=0;
    MinStack() {

    }
    
    void push(int x) {
        if(s.empty())
        {
            m = x;
            s.push(x);
            s_min.push(x);
        }
        else
        {
            s.push(x);
            if(x<m)
                m=x;
            s_min.push(m);
        }
    }
    
    void pop() {
        s.pop();
        s_min.pop();
      	//剔除已经删除的元素,这里是易错易忽略点
        if(!s_min.empty()) m = s_min.top(); 
    }
    
    int top() {
        return s.top();
    }
    
    int min() {
        return s_min.top();
    }
};

剑指 Offer 59 - II. 队列的最大值

class MaxQueue {
public:
    queue<int> q;
    deque<int> dq;
    int max;
    MaxQueue() {

    }
    
    int max_value() {
        return dq.empty() ? -1 : dq.front();
    }
    
    void push_back(int value) {
        q.push(value);
        while(!dq.empty()&&dq.back()<value)
            dq.pop_back();
        dq.push_back(value);
        
    }
    
    int pop_front() {
        if(q.empty()) return -1;
        int max = q.front();
        if(max == dq.front())
        {
            dq.pop_front();
        }
        q.pop();
        return max;
    }
};

使用一个双端队列保存最大数的序列,尾部放入数之前,先推出所有比该数小的数,然后放入该数。

模拟

剑指 Offer 29. 顺时针打印矩阵

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        //空的情况
        if(matrix.size()==0 || matrix[0].size()==0) return {};
        //
        vector<int> ans;
        int top = 0;
        int bottom = matrix.size()-1;
        int left = 0;
        int right = matrix[0].size()-1;
        while(true)
        {
            //从左到右
            for(int i=left; i<=right; ++i) ans.push_back(matrix[top][i]);
            if(++top > bottom) break; // 每次从左到右,都要下移一行
            //从上到下
            for(int i=top; i<=bottom; ++i) ans.push_back(matrix[i][right]);
            if(--right < left) break; // 每次从上到下都要,左移一列
            //从右到左
            for(int i=right; i>=left; --i) ans.push_back(matrix[bottom][i]);
            if(--bottom < top) break;// 每次从右到左,都要上移一行
            //从下到上
            for(int i=bottom; i>=top; --i) ans.push_back(matrix[i][left]);
            if (++ left > right) break;//每次从下到上,都要右移一列
        }
        return ans;
    }
};

剑指 Offer 31. 栈的压入、弹出序列 ✨中等

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> stack;
        int cnt = pushed.size();
        for(int i=0,j=0; i<cnt; ++i)
        {
            stack.push(pushed[i]);
            while(!stack.empty()&& stack.top()==popped[j])
            {
                stack.pop();
                j++;
            }
        }
        if(stack.empty()) return true;
        else return false;
    }
};

查找算法

剑指 Offer 03. 数组中重复的数字

使用hash set的唯一性进行遍历,一旦遇到存在的hash set则返回该数。

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        std::unordered_map<int,bool> map;
        for(int num:nums){
            if(map[num]) return num;
            map[num] = true;
        }
        return 0;
    }
};

剑指 Offer 53 - I. 在排序数组中查找数字 I

使用哈希查找,遍历数组,key为数字,value为出现次数。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        std::unordered_map<int, int> map;
        for(int num:nums)
        {
            if(map[num]) map[num]++;
            else map[num] = 1;
        }
        if(map[target]) return map[target];
        else return 0;
    }
};

剑指 Offer 53 - II. 0~n-1中缺失的数字

常规使用顺序遍历,这里使用二分法。当nums[mid] = mid时,说明前一半数字(左边)并没有缺少,所以继续查找右边即可。

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int left = 0;
        int right = nums.size()-1;
        while(left<=right)
        {
            int mid = (left + right) / 2;
            if(nums[mid]==mid) left = mid+1;
            else right = mid - 1;
        }
        return left;
    }
};

剑指 Offer 04. 二维数组中的查找✨中等

把二维举行想象成菱形(树的思想演变) https://leetcode.cn/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/solutions/95306/mian-shi-ti-04-er-wei-shu-zu-zhong-de-cha-zhao-zuo/?envType=study-plan-v2&envId=coding-interviews

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        if(matrix.size()==0) return false;
        int i = matrix.size()-1, j=0;
        int max_col = matrix[0].size();
        while(i>=0 && j< max_col)
        {
            if(target<matrix[i][j]) i--;
            else if (target > matrix[i][j]) j++;
            else return true;
        }
        return false;
    }
};