剑指OfferII-Leetcode刷题笔记C++版本

剑指OfferII-Leetcode刷题笔记


更新ing…第一遍刷LeetCode剑指offerII笔记记录。

栈与队列

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

栈模拟队列:
两个栈,一个入栈只接受,另一个出栈

class CQueue {
public:
    CQueue() {

    }
    
    void appendTail(int value) {
        stackpush.push(value);
    }
    
    int deleteHead() {
        if(stackpop.empty()){
            while(!stackpush.empty()){
                stackpop.push(stackpush.top());
                stackpush.pop();
            }
        }
        if(!stackpop.empty()){
            int temp = stackpop.top();
            stackpop.pop();
            return temp;
        }else{
            return -1;
        }
    }
private:
    stack<int>stackpop;
    stack<int>stackpush;
};

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

两个栈一个维护数据,一个维护最小值的索引。

class MinStack {
public:
    /** initialize your data structure here. */
    MinStack() {
        memset(stack,0,sizeof(int)*length);
        memset(minstack,0,sizeof(int)*length);
        index = minindex = -1;
    }
    
    void push(int x) {
        if(index<length){
            stack[++index]=x;
            if(minindex>-1){
                if(stack[minstack[minindex]]>=x)
                    minstack[++minindex]=index;
            }
            else minstack[++minindex]=index;
        }
    }
    
    void pop() {
        if(index>-1){
            if(index==minstack[minindex]){
                --minindex;
            }
            index--;
        }
    }
    
    int top() {
        if(index>-1)return stack[index];
        else return -1;
    }
    
    int min() {
        if(minindex>-1)return stack[minstack[minindex]];
        else return -1;
    }
private:
    static const int length = 20000;
    int stack[length];
    int index;
    int minstack[length];
    int minindex;
};

链表

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

class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        ListNode node,* p = NULL;
        node.next = NULL;
        while(head!=NULL){//头插法逆序链表
            p = head;
            head = head->next;
            p->next  = node.next;
            node.next = p;
        }
        head = node.next;
        vector<int> nums;
        while(head!=NULL){//遍历链表装入数组
            nums.push_back(head->val);
            head = head->next;
        }
        return nums;
    }
};

剑指 Offer 24. 反转链表

  1. 头插法逆序时间On 空间O1
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode node,*p=NULL;
        node.next = NULL;
        while(head!=NULL){
            p = head;
            head =  head->next;
            p->next = node.next;
            node.next = p;

        }
        return node.next;
    }
};
  1. 官方双指针法可省去方法一中的ListNode局部变量

剑指 Offer 35. 复杂链表的复制

  1. 思路:数组存新链表各个节点的指针,map存旧链表中指针和第几个节点的映射关系。时间On空间On(较差
class Solution {
public:
    Node* copyRandomList(Node* head) {
        vector<Node *> point(1000);
        map<Node*,int>mp;
        Node h(-1) , *tail=&h,*temp;
        int cnt = -1;
        Node *temphead = head;
        while(head!=NULL){//构建新链表仅维护next指针
            temp = new Node(head->val);
            point[++cnt] = temp;
            mp[head] = cnt;
            tail->next = temp;
            tail = tail->next;
            head = head->next;
        }
        head = temphead;
        tail = h.next;
        while(head!=NULL){//维护random指针
            if(head->random!=NULL){
                tail->random = point[mp[head->random]];
            }
            head=head->next;
            tail=tail->next;
        }
        return h.next;
        
    }
};

  1. leetcode官方哈希表映射指针进行复制,时间On空间On 优于自己写的方法一
  2. leetcode官方拼接 + 拆分,先拼接后拆分,时间On空间O1(最优
    第一步,copy原节点
    第二步,copyrandom
    第三步,剪切

字符串

剑指 Offer 05. 替换空格

class Solution {
public:
    string replaceSpace(string s) {
        int index =-1;
        while(s.find(' ')!=string::npos){
            s.replace(s.find(' '),1,"%20");
        }
        return s;
    }
};

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

1.利用转置可以解决,时间On空间O1,(c++reverse函数默认为原地翻转)
( A B ) T = A T B T (AB)^T = A^TB^T (AB)T=ATBT

class Solution {
public:
    string reverseLeftWords(string s, int n) {
        reverse(s.begin(),s.begin()+n);
		reverse(s.begin()+n,s.end());
		reverse(s.begin(),s.end());
        return s;
    }
};
  1. 字符串拼接
class Solution {
public:
    string reverseLeftWords(string s, int n) {
        return (s+s).substr(n,s.size());
    }
};

作者:yzwz
链接:https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/solution/yi-xing-jiu-gou-liao-by-yzwz-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

查找算法

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

  1. 不打乱顺序,使用map记录,时间On空间On
class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        unordered_map<int,int> mp;
        int cnt = 0;
        for(int i=nums.size()-1;i>=0;--i){
            if(mp.find(nums[i])==mp.end())
                mp.insert({nums[i],-1});
            else {
                cnt = nums[i];
                break;
            }
        }
        return  cnt; 
    }
};
  1. 更改数组,先排序再遍历时间Onlogn空间O1
  2. 更改数组,原地交换,时间On空间O1
class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        int N = nums.size();
        for(int i=0; i<N; i++){
            while(nums[i] != i){              //发现这个坑里的萝卜不是自己家的
                int temp = nums[i];           //看看你是哪家的萝卜
                if(nums[temp] == temp)        //看看你家里有没有和你一样的萝卜
                    return temp;            //发现你家里有了和你一样的萝卜,那你就多余了,上交国家
                else                        //你家里那个萝卜和你不一样    
                    swap(nums[temp], nums[i]);  //把你送回你家去,然后把你家里的那个萝卜拿回来
            }
        }
        return -1;
    }
};

作者:Dean-98543
链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/solution/yuan-di-jiao-huan-yi-jiao-huan-luo-bu-bi-gh5c/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

二分查找,二分上界和下界

class Solution {
public:
    int search(vector<int>& nums, int target) {
        return 
        upper_bound(nums.begin(),nums.end(),target)-lower_bound(nums.begin(),nums.end(),target);
    }
};

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

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

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

  1. 思路:对于每一行进行二分查找,时间O(nlogm)空间O1
class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        for(int i=matrix.size()-1 ;i>=0;--i){
            int l=0,r = matrix[0].size()-1;
            while(l<=r){
                int mid = (r-l)/2+l;
                //printf("%d %d   %d\n",i,mid,matrix[i][mid]);
                if(matrix[i][mid]==target){
                    return true;
                }else if(matrix[i][mid]>target){
                    r = mid -1;
                }else l = mid+1;
            }
        }
        return false;
    }
};
  1. 旋转45度,进行查找,时间O(m+n)空间O1
class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        int i = matrix.size() - 1, j = 0;
        while(i >= 0 && j < matrix[0].size())
        {
            if(matrix[i][j] > target) i--;
            else if(matrix[i][j] < target) j++;
            else return true;
        }
        return false;
    }
};

作者:jyd
链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/solution/mian-shi-ti-04-er-wei-shu-zu-zhong-de-cha-zhao-zuo/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer 50. 第一个只出现一次的字符

  1. 思路,第一遍统计,第二遍判断。时间O(n)空间O(m),m为字符数。
class Solution {
public:
    char firstUniqChar(string s) {
        int visit[26]={0};
        for(int i=0;i<s.length();i++){
            visit[s[i]-'a']++;
        }
        for(int i=0;i<s.length();i++){
            if(1 == visit[s[i]-'a'])return s[i];
        }
        return ' ';
    }
};

剑指 Offer 11. 旋转数组的最小数字

思路:没做会,差十几个例子,参考leetcode官方解析。

class Solution {
public:
    int minArray(vector<int>& numbers) {
        int low = 0;
        int high = numbers.size() - 1;
        while (low < high) {
            int pivot = low + (high - low) / 2;
            if (numbers[pivot] < numbers[high]) {
                high = pivot;
            }
            else if (numbers[pivot] > numbers[high]) {
                low = pivot + 1;
            }
            else {
                high -= 1;
            }
        }
        return numbers[low];
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/solution/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-by-leetcode-s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

搜索与回溯算法

剑指 Offer 32 - I. 从上到下打印二叉树

思路:二叉树层次遍历,时间On空间On

class Solution {
public:
    vector<int> levelOrder(TreeNode* root) {
        vector<int>v;
        if(root==NULL)return v;
        queue<TreeNode *>q;
        q.push(root);
        while(!q.empty()){
            TreeNode * temp = q.front();
            q.pop();
            v.push_back(temp->val);
            if(temp->left!=NULL)q.push(temp->left);
            if(temp->right!=NULL)q.push(temp->right);
        }
        return v;
    }
};

剑指 Offer 32 - II. 从上到下打印二叉树 II

思路:广度优先搜索时对于每一层加len标记。时间On空间On

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> v;
        if(root == NULL)return v;
        queue<TreeNode*>q;
        q.push(root);
        int cnt =0;
        while(!q.empty()){
            int len=q.size();//每一层长度标记
            vector<int>vec;
            while(len--){
                TreeNode* temp = q.front();
                q.pop();
                vec.push_back(temp->val);
                if(temp->left!=NULL)q.push(temp->left);
                if(temp->right!=NULL)q.push(temp->right);
            }
            v.push_back(vec);
        }
        return v;
    }
};

剑指 Offer 32 - III. 从上到下打印二叉树 III

  1. 思路:两个队列分别维护从左到右和从右到左的层次遍历,通过标记isodd判断偶数列和奇数列。时间On空间On。
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>>v;
        if(root==NULL)return v;
        queue<TreeNode*>oddq;
        queue<TreeNode*>evenq;
        oddq.push(root);
        evenq.push(root);
        int isodd = 1;//指示当前应该添加奇数列还是偶数列的标记
        while(!oddq.empty()){
            int len = oddq.size();
            vector<int>vec;
            while(len--){
                TreeNode* tempodd = oddq.front();
                TreeNode* tempeven = evenq.front();
                oddq.pop();
                evenq.pop();
                if(tempodd->left!=NULL)oddq.push(tempodd->left);
                if(tempodd->right!=NULL)oddq.push(tempodd->right);
                if(tempeven->right!=NULL)evenq.push(tempeven->right);
                if(tempeven->left!=NULL)evenq.push(tempeven->left);
                if(isodd){
                    vec.push_back(tempodd->val);
                }else{
                    vec.push_back(tempeven->val);
                }
            }
            isodd^=1;
            v.push_back(vec);
        }
        return v;
    }
};

  1. 看题解有这样思路:(优化掉方法一中的一个队列)时间On空间On
    采用双端队列:
    当为偶数层时,子节点从尾部压入,先压入左节点,后压入右节点。并从头部取出,
    当为奇数层是,子节点从头部压入,先压入右节点,后压入左节点。并从尾部取出

剑指 Offer 28. 对称的二叉树

思路:isSymmetric 处理root为空的特殊情况以及接收遍历结果。isduichen递归判断是否对称,通过flag值带出结果。时间On空间On

class Solution {
public:
    void isduichen(TreeNode* root,TreeNode * roottemp,bool &flag){
        if(root==NULL&&roottemp==NULL){
        }else if(root!=NULL&&roottemp!=NULL){
            if(roottemp->val!=root->val){
                //printf("%d %d\n",root->val,roottemp->val);
                flag = false;
            }else{
                isduichen(root->right,roottemp->left,flag);
                isduichen(root->left,roottemp->right,flag);
            }
        }else{
            flag = false;
        }
    }
    bool isSymmetric(TreeNode* root) {
        if(root==NULL)return true;
        bool flag = true;
        isduichen(root->left,root->right,flag);
        return flag;
    }
};

剑指 Offer 26. 树的子结构

思路:回溯过程中,A中有值和B的根节点值相等就传入递归函数进行判断。时间O(n*m)空间我认为是O(max(n,m))

class Solution {
public:
    bool isSub(TreeNode *A,TreeNode *B){
        if(B!=NULL&&A!=NULL){
            printf("%d %d\n",A->val,B->val);
            return B->val==A->val&& isSub(A->left,B->left) && isSub(A->right,B->right);
        }else if(B!=NULL && A==NULL)return false;
        return true;
    }
    bool isSubStructure(TreeNode* A, TreeNode* B) {
        if(A!=NULL&&B!=NULL){
            bool flag = isSubStructure(A->left,B) || isSubStructure(A->right,B);
            if(A->val == B->val){
                return isSub(A,B) || flag;
            }else{
                return flag;
            }
        }
        return false;
    }
};

  1. 更加简洁的方法一写法
    class Solution {
    public:
        bool isSubStructure(TreeNode* A, TreeNode* B) {
            return (A && B) && (recur(A, B) || isSubStructure(A->left, B) || isSubStructure(A->right, B));
        }
    
        bool recur(TreeNode* A, TreeNode* B) {
            if(!B) return true;
            if(!A || A->val != B->val) return false;
            return recur(A->left, B->left) && recur(A->right, B->right);
        }
    };
    
    作者:guizimo
    链接:https://leetcode-cn.com/problems/shu-de-zi-jie-gou-lcof/solution/cshuang-di-gui-by-guizimo-ng2q/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    

剑指 Offer 27. 二叉树的镜像

  1. 思路:递归,交换左右子树,时间On空间On
    class Solution {
    public:
        TreeNode* mirrorTree(TreeNode* root) {
            if(root!= NULL){
                swap(root-> left,root-> right);
                mirrorTree(root-> left);
                mirrorTree(root-> right);      
            }
            return root;
        }
    };
    
  2. 思路:广搜,交换左右子树,时间On空间On
class Solution {
public:
    TreeNode* mirrorTree(TreeNode* root) {
        if (root == nullptr) 
            return root;
        queue<TreeNode*> que;
        que.push(root);
        while (!que.empty()){
            int size = que.size();
            for (int i = 0; i < size; i ++){
                TreeNode* node = que.front();
                que.pop();
                TreeNode* swap = node->left;
                node->left = node->right;
                node->right = swap;
                if (node->left != nullptr) 
                    que.push(node->left);
                if (node->right != nullptr) 
                    que.push(node->right);
            }
        }
        return root;
    }
};

作者:master_xue
链接:https://leetcode-cn.com/problems/er-cha-shu-de-jing-xiang-lcof/solution/cdai-ma-fen-xiang-yan-du-you-xian-shen-d-gl4h/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

动态规划

剑指 Offer 10- I. 斐波那契数列

时间On空间O1

class Solution {
public:
    int fib(int n) {
        if(n==0)return 0;
        if(n==1)return 1;
        int f1 = 0,f2 = 1,temp=0;
        //0 1 1 2 3 5
        while(--n){
            //printf("%d  %d  %d\n",temp,f1,f2);
            temp = (f1+f2)%1000000007;
            f1 = f2;
            f2 = temp;
        }
        return f2;
    }
};

剑指 Offer 10- II. 青蛙跳台阶问题

思路:此题天然符合斐波那契数列。例如对于3级台阶,可以分为从2级台阶走一步到3级,也可以从1级台阶走两步到3级,恰好为f(3) = f(1)+f(2)。只是起始数字不同。时间On空间O1。

class Solution {
public:
    int numWays(int n) {
        if(n==0)return 1;
        if(n==1)return 1;
        int f1=1,f2=1,temp=0;
        while(--n){
            temp = (f1+f2)%1000000007;
            f1=f2;
            f2=temp;
        }
        return f2;
    }
};

剑指 Offer 63. 股票的最大利润

思路:给定区间内,1次买卖获得最大利润。即这个区间内最大差值。时间On空间O1

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size()<=1)return 0;//不能卖出
        int maxpro=0,minprice = 0x3f3f3f3f;
        for(int i=0;i<prices.size();++i){
            maxpro = max(maxpro,prices[i]-minprice);
            minprice = min(minprice,prices[i]);
        }
        return maxpro;
    }
};

剑指 Offer 42. 连续子数组的最大和

思路:对于f(n)来讲,有两个来源,一是f(n-1)与第n个元素之和,二是从第n个元素从新计算。

//f(n) = max(f(n-1)+nums[n],nums[n])
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int pre=0,ans=-0x3f3f3f3f;
        for(const auto&x:nums){
            pre=max(pre+x,x);
            //cout<<pre<<endl;
            ans=max(ans,pre);
        }
        return ans;
    }
};

剑指 Offer 47. 礼物的最大价值

思路:f(n,m)有两个来源,一是f(n-1,m)+grid[n][m],二是f(n,m-1)+grid[n][m]。

//f[n][m] = max(f[n-1][m],f[n][m-1])+grid[n][m]
class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
        int row=grid.size(),col=grid[0].size();
        for(int i=0;i<row;++i)
        for(int j=0;j<col;++j){
            grid[i][j]=
            	max(j>0?grid[i][j-1]:0,i>0?grid[i-1][j]:0)+grid[i][j];
        }
		return grid[row-1][col-1];
    }
};

剑指 Offer 46. 把数字翻译成字符串

没想出来,题解如下两种方法。

class Solution {
public:
    int translateNum(int num) {
        if (num < 10) return 1;
        return (num%100 < 10 || num%100 > 25) ? translateNum(num/10) : translateNum(num/10) + translateNum(num/100);
    }
};

作者:OrangeMan
链接:https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof/solution/cjian-ji-dai-ma-shuang-bai-by-orangeman/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
    int translateNum(int num) {

        //记dp[i]表示从倒数第i个数位开始往右的数字对应的情况(注意个位对应i=1)
        //那么dp[i]只取决于dp[i-1]和dp[i-2]
        //若数字num倒数第i位与其后面一位(即倒数第i-1位)无法组合为0~25之间的数字(不能含前导0)
        //那么dp[i]=dp[i-1]
        //否则dp[i]=dp[i-1]+dp[i-2],因为针对倒数第i位有两种翻译方式,分别对应已求出的两种子情况

        int dp0=1, dp1=1;//分别表示只考虑最右边0个和1个数位时的情况数(滚动数组形式更新)
        int last_num=num%10;//记录"后面一位"

        while(num/=10){//运算结果就是运算表达式的值
        
            int n=num%10;//当前数位
            int dp0_backup=dp0;//备份一下dp0
            dp0=dp1;//无论时1种还是2种翻译方式,dp0都会更新为dp1

            //有两种翻译方式时(注意前导0)
            if(n>0&&n*10+last_num<26) dp1+=dp0_backup;

            last_num=n;//更新"后面一位"
        }
        return dp1;
    }
};

剑指 Offer 48. 最长不含重复字符的子字符串

思路:维护一个字符标记数组,判断是否出现该字符,然后利用双指针维护当前不重复字符长度,当出现重复字符时,将左指针移到上一个字符出现位置的下一个字符上。维护ans记录最大长度。时间On空间Om,m为字符种类。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        vector<bool>visit(256,false);
        int length =  s.length();
        int ans = 0;
        int temp = 0;
        int j=0;
        for(int i=0;i<length;i++){
            if(visit[s[i]]==false){
                temp++;
                visit[s[i]]=true;
                ans = max(ans,temp);
            }else{
                while(s[j]!=s[i]){
                    visit[s[j]]=false;
                    temp--;
                    j++;
                }
                j++;
            }
        }
       return ans;
    }
};

双指针

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

思路:双指针,一个指针先跑k次,然后两个指针一起走,直到第一个指针走到头。特殊情况:头指针为空,链表长度不够k。
时间On空间O1。

class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        if(head==NULL)return NULL;
        ListNode node;//头结点 方便链表操作
        node.next = head;
        ListNode *p = node.next,*q = node.next;
        while(k-- && p!=NULL){
            p = p->next;
        }
        // if(p)
        // cout<<p->val<<endl;
        // cout<<k<<endl;
        if(p==NULL && k>=0)return NULL;
        while(p!=NULL){
            p = p->next;
            q = q->next;
        }
        return q;
    }
};

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

思路:遍历,找到第一个节点,删除。特殊情况:头指针为空,删除第一个节点。时间On空间O1

class Solution {
public:
    ListNode* deleteNode(ListNode* head, int val) {
        if(head ==NULL)return head;
        ListNode node;
        node.next = head;
        ListNode * pre = &node,*q = node.next;
        while(q!=NULL){
            if(q->val==val){
                pre->next = q->next;
                break;
            }
            pre = q;
            q = q->next;
        }
        return node.next;
    }
};

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

思路:
构造一个头结点简化对于头指针是否为空的判断。然后双指针遍历构造有序链表。时间O(n+m)空间O1

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode node(0);
        ListNode * tail = &node;
        while(l1!=NULL && l2!=NULL){
            if(l1->val<l2->val){
                tail->next = l1;
                l1 = l1->next;
            }else{
                tail->next = l2;
                l2 = l2->next;
            }
            tail = tail->next; tail->next =NULL;
        }
        if(l1!=NULL)tail->next = l1;
        if(l2!=NULL)tail->next = l2;
        return node.next;
    }
};

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

  1. 做出来但代码没有官方简洁,官方思路分析很好,这是双指针遍历,时间On+m 空间O1
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (headA == nullptr || headB == nullptr) {
            return nullptr;
        }
        ListNode *pA = headA, *pB = headB;
        while (pA != pB) {
            pA = pA == nullptr ? headB : pA->next;
            pB = pB == nullptr ? headA : pB->next;
        }
        return pA;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/solution/liang-ge-lian-biao-de-di-yi-ge-gong-gong-pzbs/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

自己写的代码:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode * p = headA,*q = headB;
        bool flagp=true,flagq = true;
        while(p!=NULL&&q!=NULL){
            if(p == q){
                return p;
            }
            p = p->next;
            q = q->next;
            if(p == NULL&&flagp){
                p = headB;flagp = false;
            }
            if(q == NULL){
                q = headA;
                flagq = false;
            }
        } 
        return NULL;
    }
};

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

思路:题目没描述清楚,应是数组中的奇数在前偶数在后,并非奇数位和偶数位。
双指针从数组两端遍历,从左至右找到一个偶数和从右至左找到一个奇数进程互换。时间On空间O1。

class Solution {
public:
    vector<int> exchange(vector<int>& nums) {
        int l=0,r=nums.size()-1;
        while(l<r){
            while(l<r && (nums[l]&1))++l;
            while(l<r && (nums[r]&1)==0)--r;
            swap(nums[l++],nums[r--]);
        }
        
        return nums;
    }
};

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

  1. 用无序map,对遍历过的元素进行映射,时间On空间On
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,bool>mp;
        for(int i = nums.size()-1;i>=0;--i){
            mp.emplace(nums[i],true);
        }
        for(int i = nums.size()-1;i>=0;--i){
            int temp = nums[i];
            if(mp.find(target-temp)!=mp.end()){
                vector<int>ans;
                ans.emplace_back(target-temp);
                ans.emplace_back(temp);
                return ans;
            }
        }
        return vector<int>();
    }
};
  1. 利用数组有序进行双指针从两端向里查找(非二分查找)时间On空间O1
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int l = 0,r = nums.size()-1;
        while(l<r){
            int temp = nums[l]+nums[r];
            if(temp==target)
                return {nums[l],nums[r]};
            else if(temp>target){
                --r;
            }else{
                ++l;
            }
        }
        return {};
    }
};

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

  1. 思路:利用转置进行逆序。处理好边界条件。时间On2空间O n
    ( A B ) T = A T B T (AB)^T = A^TB^T (AB)T=ATBT
class Solution {
public:
    void reverse(string &s,int l,int r){
        while(l<r){
            swap(s[l++],s[r--]);
        }
    }
    string reverseWords(string s) {
        if(s.empty())return {};
        string ans;
        int l = 0;
        s.append(" ");//填一个空格好操作
        int len = s.length()-1;
        while(l<len && s[len]==' ')--len;//去除首尾空格
        while(l<len && s[l]==' ')++l;
        for(int r=0;r<=len;r++){
            while(s[l]==' ')++l;
            if(s[r]!=' ' && s[r+1]==' '){
                reverse(s,l,r);
                ans.append(s.begin()+l,s.begin()+r+1);
                ans.append(" ");
                l = r+1;
            }
            
        }
        ans.pop_back();//去除最后一个ans.append(" ")多余空格。
        reverse(ans,0,ans.length()-1);
        return ans;
    }
};
  1. 思路:从后往前便利字符串。
class Solution {
public:
    string reverseWords(string s) {
        string ans;
        //i,j用于确定跳过首尾空格的下标范围
        int i=0,j=s.size()-1;
        //跳过s的首部空格
        while(i<=j&&s[i]==' ') i++;
        //跳过s的尾部空格
        while(i<=j&&s[j]==' ') j--;

        //k,w为用于确定每个单词范围的双指针,从非空格尾部开始往前扫描,i为前边界,j为后边界
        int k=j,w=j;
        //当输入全为空格时,跳过首尾空格后i>j
        while(i<=j&&k>=i){
            //k往前扫描直到遇到空格停下,或者超出i前边界停下
            while(k>=i&&s[k]!=' ') k--;
            //k+1到w为一个单词的范围,将每个字符按序加入string ans即可
            for(int idx=k+1;idx<=w;idx++) ans+=s[idx];
            //没超出前边界i时,k停下遇到的肯定是空格,可能是一个或多个,跳过
            if(k>=i&&s[k]==' '){
                while(k>=i&&s[k]==' ') k--;
                //跳过一个或多个空格后,ans加一个必要的空格
                ans+=' ';
            }
            //w跳到k位置继续扫描下一个单词范围
            w=k;
        }
        return ans;
    }
};

作者:rong-ma-ma-C1XvG4c2gy
链接:https://leetcode-cn.com/problems/fan-zhuan-dan-ci-shun-xu-lcof/solution/cshuang-zhi-zhen-mo-ni-bu-shi-yong-api-b-5dp7/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

搜索与回溯算法

剑指 Offer 12. 矩阵中的路径

思路:如果矩阵中字符与字符串首字符相等则进行dfs遍历。
时间O(n2m) 空间On2

class Solution {
public:
    int dx[4]={1,-1,0,0};
    int dy[4]={0,0,1,-1};
    int row=0;
    int col=0;
    bool dfs(vector<vector<char>>& board,vector<vector<bool>>& visit,const string &word,int index,int x,int y){
        if(index==word.length()-1){
            //printf("****");
            //printf("%c %c\n",board[x][y],word[index]);
            return board[x][y]==word[index];
        }
        bool flag=false;
        //printf("%c %c\n",board[x][y],word[index]);
        for(int i=0;i<4;i++){
            int xx=x+dx[i];
            int yy=y+dy[i];
        if(0<=xx&&0<=yy&&xx<row&&yy<col&&board[xx][yy]==word[index+1]&&visit[xx][yy]==false){
               visit[xx][yy]=true;
               flag=flag || dfs(board,visit,word,index+1,xx,yy);
               //printf("**\t%c %c\n",board[xx][yy],word[index]);
               visit[xx][yy]=false;
            }
        }
        return flag;
    }
    bool exist(vector<vector<char>>& board, string word) {
        row = board.size();
        col= board[0].size();
        vector<vector<bool>> visit(row,vector<bool>(col,false));
        for(int i=0;i<row;++i){
            for(int j=0;j<col;++j){
                if(board[i][j]==word[0]){
                    visit[i][j]=true;
                 if(dfs(board,visit,word,0,i,j)){
                     return true;
                 }
                    visit[i][j]=false;
                }
            }
        }
        return false;

    }
};

剑指 Offer 13. 机器人的运动范围

思路:广搜的同时,控制边界判断。时间On2空间On2

class Solution {
public:
    inline bool istrue(int x,int y,int sum){
        //printf("%d\n",x%10+y%10+x/10+y/10);
        return (x%10+x/10+y%10+y/10)>sum?false:true;
    }
    int movingCount(int m, int n, int k) {
        int dx[] = {1,-1,0,0};
        int dy[] = {0,0,1,-1};
        queue<int> qx;
        queue<int> qy;
        vector<vector<bool>> visit(m,vector<bool>(n,false));//模版特化节省空间
        qx.push(0);
        qy.push(0);
        visit[0][0] = true;
        int count = 1;
        while(!qx.empty()){
            int x=qx.front();
            qx.pop();
            int y=qy.front();
            qy.pop();
            //printf("%d %d\n",x,y);
            visit[x][y] = true;
            for(int i=0;i<4;i++){
                int xx = x+dx[i];
                int yy = y+dy[i];
                if(0<=xx&&0<=yy&&xx<m&&yy<n&&istrue(xx,yy,k)&&visit[xx][yy]==false){  
                    count++;
                    visit[xx][yy] = true;
                    qx.push(xx);
                    qy.push(yy);
                }

            }           
        }
        return count;
    }
};

剑指 Offer 34. 二叉树中和为某一值的路径

思路:维护路径和sum以及路径path,进行深度优先搜索。利用vector的push_back方法为拷贝的特性,符合要求就加入答案中。时间On 空间On。注意是根到叶子节点的路径。

class Solution {
public:
    vector<vector<int>> ans;
    void dfs(TreeNode* root,int sum,const int target,vector<int>&path){
            if(root->left==nullptr&&root->right==nullptr && sum == target){
                ans.push_back(path);
            }
            if(root->left!=nullptr){
                int temp = root->left->val;
                path.emplace_back(temp);
                dfs(root->left,sum+temp,target,path);
                path.pop_back();
            }
            if(root->right){
                int temp = root->right->val;
                path.emplace_back(temp);
                dfs(root->right,sum+temp,target,path);
                path.pop_back();
            }
    }
    vector<vector<int>> pathSum(TreeNode* root, int target) {
        if(root==nullptr)return ans;
        vector<int>path;
        path.emplace_back(root->val);
        dfs(root,root->val,target,path);
        return ans; 
    }
};

剑指 Offer 36. 二叉搜索树与双向链表

思路:对于一颗二叉搜索树来说,根节点的前驱为左子树最右下节点。根节点的后继是右子树的最左下节点。故深度搜索时需要返回这两个信息与根节点连接起来。如何知道本层是上层的左子树还是右子树?通过istrue标记来指示,为真则表示为右子树。时间On空间O1

class Solution {
public:
    Node * leftnode(Node * root){
        if(root==NULL)return root;
        while(root->left!=NULL){
            root = root->left;
        }
        return root;
    }
    Node * rightnode(Node * root){
        if(root==NULL)return root;
        while(root->right!=NULL){
            root = root->right;
        }
        return root;
    }
    Node * gengrate(Node * root,bool istrue){
        if(root->left!=NULL){
            root->left = gengrate(root->left,false);
            root->left->right = root;
        }
        if(root->right!=NULL){
            root->right = gengrate(root->right,true);
            root->right->left = root;
        }
        return istrue?leftnode(root):rightnode(root);
    }
    Node* treeToDoublyList(Node* root) {
        if(root==NULL)return NULL;
        Node *l = leftnode(root),*r = rightnode(root);
        //printf("%d %d\n",l==NULL?-1:l->val,r==NULL?-1:r->val);
        gengrate(root,false);
        l->left = r;
        r->right = l;
        return l;
    }
};

剑指 Offer 54. 二叉搜索树的第k大节点

思路:二叉搜索树在中序遍历时是一个递增序列。故镜像二叉搜索树中序遍历是递减序列。由此先镜像再在中序遍历中记录当前遍历节点为第几个即可。时间On 空间O1

class Solution {
public:
    void reverse(TreeNode *root){
        if(root){
            swap(root->left,root->right);
            reverse(root->left);
            reverse(root->right);
        }
    }
    void bianli(TreeNode * root,int &k,int &val){
        if(root){
            bianli(root->left,k,val);
            if((--k)==0){
                val = root->val;
                return ;
            }
            bianli(root->right,k,val);
        }
    }
    int kthLargest(TreeNode* root, int k) {
       int val=0;
       reverse(root);
       bianli(root,k,val);
       return val; 
    }
};

排序

剑指 Offer 61. 扑克牌中的顺子

起初不知道什么是顺子,题解如下:

https://leetcode-cn.com/problems/bu-ke-pai-zhong-de-shun-zi-lcof/solution/mian-shi-ti-61-bu-ke-pai-zhong-de-shun-zi-ji-he-se/

class Solution {
public:
    bool isStraight(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int minindex=0;
        for(int i=0;i<4;++i){
            if(nums[i]==0)minindex++;
            else if(nums[i]==nums[i+1]){
                return false;
            }
        }
        return nums[4]-nums[minindex]<5;
    }
};

剑指 Offer 45. 把数组排成最小的数

思路:转化为字符串,利用字符串进行比大小。
时间 O(nlogn)空间On

class Solution {
public:
    static bool cmp(string &a,string &b){
        return a+b<b+a;
    }
    string minNumber(vector<int>& nums) {
        vector<string>numstring(nums.size());
        for(int i=nums.size()-1;i>=0;--i){
            numstring[i] = to_string(nums[i]);
        }
        string ans;
        sort(numstring.begin(),numstring.end(),cmp);
        
        for(string s:numstring)ans.append(s);
        return ans;
    }
};

剑指 Offer 40. 最小的k个数

思路:利用快速排序的特性,每轮确定一个数的位置可得,当一个数是数组第k个时,其前面的元素就为最小的k个数

class Solution {
public:
    void quicksort(vector<int>&arr,int left,int right,int k,bool &isfind){
        if(left>=right||isfind) return ;
        int l =left,r = right;
        int target = arr[l];
        //printf("%d %d\n",l,r);
        while(l<r){
            while(l<r && arr[r]>=target)r--;
            swap(arr[l],arr[r]); 
            while(l<r && arr[l]<=target)l++;
            swap(arr[r],arr[l]);
        } 
        arr[l] = target;
        if(l+1==k)isfind=true;
        quicksort(arr,left,l-1,k,isfind);
        quicksort(arr,l+1,right,k,isfind);
    }
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        bool isfind = false;
        quicksort(arr,0,arr.size()-1,k,isfind);
        
        return vector<int>(arr.begin(),arr.begin()+k);
    }
};

剑指 Offer 41. 数据流中的中位数

思路1:用vector保持有序数组,但插入时时间复杂度很高,解出来复杂度很高
思路2:看题解知利用堆,

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) {
        /*
        * 注意:两个堆的元素个数之差要小于等于1
        * 1、若大顶堆元素数目与小顶堆元素数目相等:
        * 先将数字存入大顶堆,再将大顶堆堆顶元素存入小顶堆
        * 保证小顶堆中的数永远大于等于大顶堆的堆顶元素
        * 2、若大顶堆元素数目与小顶堆元素数目不相等:
        * 先将数字存入小顶堆,再将小顶堆堆顶元素存入大顶堆
        * 保证大顶堆中的数永远小于等于小顶堆的堆顶元素
        */
        if (maxHeap.size() == minHeap.size()) {
            maxHeap.push(num);
            minHeap.push(maxHeap.top());
            maxHeap.pop();
        } else {
            minHeap.push(num);
            maxHeap.push(minHeap.top());
            minHeap.pop();
        }
    }
    
    double findMedian() {
        /*
        * 1、若两个堆的元素个数相等:
        * 中位数就是两个堆顶元素的平均值
        * 2、若两个堆的元素个数不相等:
        * 由于当两个堆元素个数相等时,
        * 插入元素是先插入大顶堆,再将大顶堆堆顶元素插入小顶堆
        * 这就相当于多出来的这个数最终插入的是小顶堆
        * 所以小顶堆的堆顶元素就是数据流的中位数
        */
        return maxHeap.size() == minHeap.size() ? (maxHeap.top() + minHeap.top()) / 2.0 : minHeap.top();
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

作者:RyanWangllng
链接:https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof/solution/zhu-shi-xing-ti-jie-da-ding-dui-xiao-din-txvk/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值