剑指offer刷题笔记(一)

03 数组中重复的数字

在这里插入图片描述

法一:用set

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        set<int> s;
        for(int i=0;i<nums.size();i++){
            if(s.count(nums[i])) return nums[i];
            else s.insert(nums[i]);
        }
        return -1;

    }
};

法二:排序

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
       sort(nums.begin(),nums.end());
       for(int i=0;i<nums.size()-1;i++){
           if(nums[i]==nums[i+1]){
               return nums[i];
           }
       }
       return -1;
    }
};

法三:鸽巢原理
因为出现的元素值 < nums.size(); 所以我们可以将见到的元素 放到索引的位置,如果交换时,发现索引处已存在该元素,则重复 O(N) 空间O(1)

class Solution {
public:
   int findRepeatNumber(vector<int>& nums) {
        for(int i=0;i<nums.size();i++){
            while(nums[i]!=i){
                if(nums[nums[i]] == nums[i]) return nums[i];
                int tmp = nums[i];
                nums[i] = nums[tmp];
                nums[tmp] = tmp;
            }
        }
        return -1;
    }
};

04查找矩阵中一个数

在这里插入图片描述
法一:直接搜索

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

法二:线性查找,需要满足这个列递增和行递增的特点

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        if(matrix.size()==0||matrix[0].size()==0) return false;

        int i=0,j=matrix[0].size()-1;
        while(i<matrix.size() && j>=0){//注意这里j>=0保证0也要判断
            if(matrix[i][j]==target) return true;
            if(matrix[i][j]<target) i++;
            else j--;
        }
        return false;
    }
};

05替换空格

在这里插入图片描述

class Solution {
public:
    string replaceSpace(string s) {
        string result;
        for(int i=0;i<s.size();i++){
            if(s[i]==' '){
                result.push_back('%');
                result.push_back('2');
                result.push_back('0');

            }
            else{
                result.push_back(s[i]);
            }
        }
        return result;
    }
};

06从尾到头打印链表

在这里插入图片描述
法一:弄到数组里再逆序打印

class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> re;
        ListNode * pnode=head;
        while(pnode){
            re.push_back(pnode->val);
            pnode=pnode->next;
        }
        for(int i=0;i<re.size()/2;i++){
            swap(re[i],re[re.size()-1-i]);
        }
        return re;
    }
};

法二:递归

class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
       if(head==nullptr) return{};
       vector<int>a=reversePrint(head->next);
       a.push_back(head->val);
       return a;
    }
};

07重建二叉树

在这里插入图片描述

class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        return build(preorder,inorder,0,0,inorder.size()-1);
    }
    TreeNode* build(vector<int>& preorder,vector<int>& inorder,int root,int start,int end)//前序root 中序start和end
    {
        if(start>end) return NULL;
        TreeNode *tree = new TreeNode(preorder[root]);//根据前序得到根节点
        int i=start;
        while(i<end && preorder[root]!=inorder[i]) i++;//找到中序遍历中的根节点
        tree->left=build(preorder,inorder,root+1,start,i-1);//前序遍历,每次都是一个子树的根节点
        tree->right=build(preorder,inorder,root+1+i-start,i+1,end);//i-start得到左子树的数量
        return tree;
    }
};

09用两个栈实现队列

在这里插入图片描述

class CQueue {
public:
    stack<int> a;
    stack<int> b;
    CQueue() {

    }
    
    void appendTail(int value) {
        a.push(value);
    }
    
    int deleteHead() {
        if(a.empty() && b.empty()) return -1;
        else if(b.empty() && !a.empty()){
            while(!a.empty()){
                b.push(a.top());
                a.pop();
            }
        }
        int temp=b.top();
        b.pop();
        return temp;
    }
};

10 -1斐波那契数列

在这里插入图片描述
简单dp 注意取模

class Solution {
public:
    int fib(int n) {
        if(n==0) return 0;
        if(n==1) return 1;
        vector<int> result;
        result.push_back(0);
        result.push_back(1);
        for(int i=2;i<=n;i++){
            int temp=(result[i-1]+result[i-2])%1000000007;
            result.push_back(temp);
        }
        return result[result.size()-1];
    }
};

递归:

class Solution {
public:
    int numWays(int n) {
        if(n == 0 || n == 1)
            return 1;
        return numWays(n-1) + numWays(n-2);
    }
};

10-2青蛙跳台

在这里插入图片描述

class Solution {
public:

    int numWays(int n) {
        if(n==0) return 1;
        if(n==1) return 1;
        vector<int> dp;
        dp.push_back(1);
        dp.push_back(1);
        for(int i=2;i<=n;i++){
            int temp=(dp[i-1]+dp[i-2])%1000000007;
            dp.push_back(temp);
        }
        return dp[n];
    }
};

11旋转数组的最小值

在这里插入图片描述

class Solution {
public:
    int minArray(vector<int>& numbers) {
        int low=0,high=numbers.size()-1;
        while(low<high){
            int mid=low+(high-low)/2;
            if(numbers[mid]>numbers[high]) low=mid+1;//34512mid在左,转折点在mid+1-high
            else if(numbers[mid]<numbers[high]) high=mid;//mid在右边,转折点在low-mid
            else high--;//无法确定左还是右,缩小范围
        }
        return numbers[high];//or low
    }
};

12矩阵中的路径

在这里插入图片描述
搜索 回溯

class Solution {
public:
    vector<int> dx={0,0,1,-1};
    vector<int> dy={1,-1,0,0};

    bool find(vector<vector<char>>& board,string &word,int x,int y,int index){

        if(index==word.size()){
           return true;
        }

        if(x>board.size()-1||y>board[0].size()-1||x<0||y<0) return false;
        
        
        if(board[x][y]==word[index] && board[x][y]!='*'){
            board[x][y]='*';
            for(int i=0;i<4;i++){
                if(find(board,word,x+dx[i],y+dy[i],index+1)) return true;
            }
           board[x][y]=word[index];
        }
        return false;
        
    }

    bool exist(vector<vector<char>>& board, string word) {
        
        for(int i=0;i<board.size();i++){
            for(int j=0;j<board[0].size();j++){
               if(find(board,word,i,j,0)) return true;
            }
        }
        return false;
    }
};

注意一个点,为什么下面这样会超时?因为find那里四种情况无论如何都走了一遍,上面的写法一旦有true就可以返回了,相当于没有剪枝。

class Solution {
public:
    bool hasroute=false;
    vector<int> dx={0,0,1,-1};
    vector<int> dy={1,-1,0,0};

    void find(vector<vector<char>>& board,string word,int x,int y,int index,vector<vector<int>>& visit){

         if(index==word.size()){
            hasroute=true;
            return;
        }

        if(x>board.size()-1||y>board[0].size()-1||x<0||y<0) return;
        
       
        if(board[x][y]==word[index] && visit[x][y]==0){
            visit[x][y]=1;
            for(int i=0;i<4;i++){
                find(board,word,x+dx[i],y+dy[i],index+1,visit);
            }
            visit[x][y]=0;
        }
        
    }

    bool exist(vector<vector<char>>& board, string word) {
        vector<vector<int>> visit(board.size(),vector<int>(board[0].size(),0));
        for(int i=0;i<board.size();i++){
            for(int j=0;j<board[0].size();j++){
                find(board,word,i,j,0,visit);
            }
        }
        return hasroute;
    }
};

13机器人的运动范围

在这里插入图片描述
dfs 不回溯

class Solution {
public:

    vector<int> dx={0,0,1,-1};
    vector<int> dy={1,-1,0,0};
    
    int result=0;

    bool judge(int x,int y,int k){
        int sum=0;
        while(x>0){
            sum+=x%10;
            x/=10;
        }
        while(y>0){
            sum+=y%10;
            y/=10;
        }
        if(sum>k) return false;
        else return true;
    }

   

    int move(int x,int y,int m,int n,int k,vector<vector<int>> & visit){
        if(x>m-1 || y>n-1 || x<0 || y<0) return result;
        if(!judge(x,y,k)||visit[x][y]==1) return result;
        visit[x][y]=1;
        result++;
        int maxn=0;
        for(int i=0;i<4;i++){
            maxn=max(maxn,move(x+dx[i],y+dy[i],m,n,k,visit));
        }
        //visit[x][y]=0;
        return maxn;
        
    }

    int movingCount(int m, int n, int k) {
        vector<vector<int>> visit(m,vector<int>(n,0));
        return move(0,0,m,n,k,visit);
    }
};

注意这里为啥不把visit还原?因为求的是范围,去过的就算,是多条路线求和,不是仅仅算一条路径。

14-1割绳子(未做)

在这里插入图片描述

15二进制中1的个数

在这里插入图片描述
技巧就是n&(n-1)可以消除最低位的1

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int re=0;
        while(n>0){
            re++;
            n=n&(n-1);
        }
        return re;
    }
};

18 删除链表的节点

在这里插入图片描述
O(n)方法 注意这里的dummynode是为了让头节点也能通过p->next得到

class Solution {
public:
    ListNode* deleteNode(ListNode* head, int val) {
       ListNode* dummy=new ListNode(1);
       dummy->next=head;
       ListNode * p=dummy;
       while(p && p->next){
           if(p->next->val==val){
               p->next=p->next->next;
               break;
           }else{
               p=p->next;
           }
       }
       return dummy->next;
        
    }
};

22链表中倒数第k个节点

用vector存放指针值,再返回

class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        vector<ListNode*> res;
        while(head){
            res.push_back(head);
            head=head->next;
        }
        return res[res.size()-k];
    }
};

24翻转链表

在这里插入图片描述

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* cur = NULL, *last = head;
        while (last != NULL) {
            ListNode* t = last->next;//保存用于更新cur
            last->next = cur;
            cur = last;
            last = t;//cur和last同时移动
        }
        return cur;
    }
};

25合并两个排序链表

在这里插入图片描述

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
            ListNode *dummy=new ListNode(1);
            ListNode* p1=l1,*p2=l2,*pr=dummy;
            
            while(p1 && p2){
                if(p1->val>p2->val){
                    pr->next=p2;
                    pr=pr->next;
                    p2=p2->next;
                } else{
                    pr->next=p1;
                    pr=pr->next;
                    p1=p1->next;
                }
            }
             if(p1==nullptr){
                    pr->next=p2;
            }
                else{
                    pr->next=p1;
            }
            return dummy->next;
    }
};

26树的子结构

在这里插入图片描述

class Solution {
public:
    bool isSubStructure(TreeNode* A, TreeNode* B) {//这一步是遍历A树,写的时候先不管跟B比较
        if(A==nullptr||B==nullptr) return false;
        if(isPartSame(A,B)) return true;
        else return(isSubStructure(A->left,B)||isSubStructure(A->right,B));
    }

    bool isPartSame(TreeNode* A,TreeNode* B){
        if(B==nullptr)return true;//B中节点可以为空,因为B是子部分
        if(A==nullptr)return false;//B不为空但是A为空,不行

        if(A->val==B->val){
            return isPartSame(A->left,B->left)&&isPartSame(A->right,B->right);
        }
        else{
            return false;
        }
    }
};

27二叉树的镜像

在这里插入图片描述
递归

class Solution {
public:
    TreeNode* mirrorTree(TreeNode* root) {
        if(root==nullptr) return nullptr;
        swap(root->left,root->right);
        mirrorTree(root->left);
        mirrorTree(root->right);
        return root;
    }
};

栈实现二叉树先序遍历

class Solution {
public:
    TreeNode* mirrorTree(TreeNode* root) {
        stack<TreeNode*>s;
        s.push(root);
        while(!s.empty()){
            TreeNode* node=s.top();
            s.pop();
            if(node==nullptr){
                continue;
            }
            swap(node->left,node->right);
            s.push(node->left);
            s.push(node->right);
        }
        return root;
    }
};

队列实现二叉树层次遍历

class Solution {
public:
    TreeNode* mirrorTree(TreeNode* root) {
       queue<TreeNode*> q;
       q.push(root);
       while(!q.empty()){
           TreeNode* node=q.front();
           q.pop();
           if(node==nullptr) continue;
           swap(node->left,node->right);
           q.push(node->right);
           q.push(node->left);
        
       }
       return root;
    }
};

28 对称二叉树

在这里插入图片描述

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if(root==NULL) return true;
        return recur(root->left,root->right);
    }
    bool recur(TreeNode* ltree,TreeNode* rtree){
        if(ltree==NULL && rtree==NULL) return true;
        if(ltree==NULL || rtree==NULL || ltree->val!=rtree->val) return false;
        return recur(ltree->left,rtree->right) && recur(ltree->right,rtree->left);
    }
};

30包含min的栈

class MinStack {
public:
    /** initialize your data structure here. */
    stack <int> A;
    stack<int> B;
    MinStack() {
        
    }
    
    void push(int x) {
        A.push(x);
        if(B.empty()||x<=B.top()){//这里注意顺序
            B.push(x);
        }

    }
    
    void pop() {
        if(A.top()==B.top()){
            B.pop();
        }
        A.pop();

    }
    
    int top() {
        return A.top();
    }
    
    int min() {
        return B.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();
 */

32 从上到下打印二叉树

在这里插入图片描述
拓展
在这里插入图片描述
转:https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-iii-lcof/solution/cli-yong-dequeshuang-duan-dui-lie-hao-shi-0ms-ji-b/

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        if (root==NULL)
            return res;
        bool flag = true; //从左向右打印为true,从右向左打印为false
        deque<TreeNode*> q;
        q.push_back(root);
        while (!q.empty())
        {
            int n = q.size();
            vector<int> out;
            TreeNode* node;
            while (n>0)
            {
                if (flag) // 前取后放:从左向右打印,所以从前边取,后边放入
                {
                    node = q.front();
                    q.pop_front();
                    if (node->left)
                        q.push_back(node->left);  // 下一层顺序存放至尾
                    if (node->right)
                        q.push_back(node->right);
                }
                else  //后取前放: 从右向左,从后边取,前边放入
                {
                    node = q.back();
                    q.pop_back();
                    if (node->right)
                        q.push_front(node->right);  // 下一层逆序存放至首
                    if (node->left)
                        q.push_front(node->left);
                }
                out.push_back(node->val);
                n--;
            }
            flag = !flag;
            res.push_back(out);
        }
        return res;
    }
};

33二叉搜索树的后序遍历序列

在这里插入图片描述

class Solution {
public:
    bool verifyPostorder(vector<int>& postorder) {
        return recur(postorder,0,postorder.size()-1);
    }
    bool recur(vector<int> postorder,int i,int j){
        if(i>=j)return true;
        int p=i;
        while(postorder[p]<postorder[j]) p++;//j是根节点,左子树都小于根节点
        int m=p;//找到m是右子树根节点
        while(postorder[p]>postorder[j])p++;//右子树都大于根节点
        return p==j&& recur(postorder,i,m-1)&&recur(postorder,m,j-1);
    }
};

40最小的k个数

法一:排序取k个

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        sort(arr.begin(),arr.end());
        vector<int> re(k,0);
        for(int i=0;i<k;i++){
            re[i]=arr[i];
        }
        return re;

    }
};

时间:O(nlogn) 空间:O(logn)

法二:大根堆

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        vector<int> vec(k,0);
        if(k==0)return vec;
        priority_queue<int> q;
        for(int i=0;i<k;i++)q.push(arr[i]);
        for(int i=k;i<(int)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;

    }
};

时间:O(nlogk) 空间 O(k)
法三:快速排序:

class Solution {
public:

    int partition(vector<int> &nums,int low,int high){
        
        int p= rand()%(high-low+1)+low;
        swap(nums[p],nums[high]); 
        int i=low,j=low;
        for(;j<high;j++){
            if(nums[j]<nums[high]){
                swap(nums[i++],nums[j]);
            }
        }
        swap(nums[high],nums[i]);
        return i;
    }
    void findPart(vector<int> &nums,int low,int high,int k){
        if(low>high)return;
        int p=partition(nums,low,high);
        if(p==k)return;
        if(p>k)findPart(nums,low,p-1,k);
        else findPart(nums,p+1,high,k);
    }
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        findPart(arr,0,arr.size()-1,k);
        vector<int> re(k,0);
        for(int i=0;i<k;i++){
            re[i]=arr[i];
        }
        return re;
    }
};

时间:O(n) 最坏情况为O(n^2) 空间:O(logn)

49 丑数

在这里插入图片描述
自己写的dp,,时间复杂度有点高

class Solution {
public:
    int nthUglyNumber(int n) {
        vector<long> dp(n,0);
        dp[0]=1;
        for(int i=1;i<n;i++){
            long amin=dp[i-1]*2;
            for(int k=0;k<i;k++){
                long temp=0;
                if((temp=dp[k]*2)>dp[i-1] || (temp=dp[k]*3)>dp[i-1] || (temp=dp[k]*5)>dp[i-1]){
                    amin=min(temp,amin);
                    dp[i]=amin;
                }
            }
        }
        return dp[n-1];
    }
};

三指针:
https://leetcode-cn.com/problems/chou-shu-lcof/comments/250364

int nthUglyNumber(int n) {
        if(!n)return 0;
        vector<int>ugly(n,0);
        ugly[0] = 1;     //基础丑数为1
        int i=0,j=0,k=0;  //初始分别指向三个有序链表第一个元素,这三个有序链表是想象出来的,分别就是ugly数组元素分别乘以2,3,5得到的
        for(int idx=1;idx<n;idx++)
        {
            int tmp =  min(ugly[i]*2,min(ugly[j]*3,ugly[k]*5));    
            //三个链表可能有相同元素,所以只要是最小的,都要移动指针
            if(tmp == ugly[i]*2)i++;
            if(tmp == ugly[j]*3)j++;
            if(tmp == ugly[k]*5)k++;
            ugly[idx] = tmp;
        }
        return ugly[n-1];
    }

55二叉树深度

法一:dfs递归

class Solution {
public:
    int maxDepth(TreeNode* root) {
        
        if(root){
            return max(1+maxDepth(root->right),1+maxDepth(root->left));
        }
        else return 0;
    }
};

法二:利用队列BFS

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if(root==NULL)return 0;
        queue<TreeNode*> q;
        q.push(root);
        int res=0;
        while(!q.empty()){
            queue <TreeNode*>temp;//用于存放下一层节点
            while(!q.empty()){
                TreeNode* cur=q.front();
                q.pop();
                if(cur->left) temp.push(cur->left);
                if(cur->right) temp.push(cur->right);
            }
            res++;
            q=temp;
        }
        return res;
    }
};

时间复杂度都是O(n)要遍历所有节点

猜你喜欢

转载自blog.csdn.net/weixin_42189888/article/details/105577663
今日推荐