leetcode 刷题记录


链表

移除链表元素
在这里插入图片描述

思路:
挺简单的。首先判读链表的开头几个节点,是不是target值,是的话把这些节点也都忽视掉。
对于后面的节点也是同样的思路,如果某个节点的下一个节点是target值,那么就让它->next = 它->next ->next

class Solution 
{
public:
    ListNode* removeElements(ListNode* head, int val) 
    {
        if(head==nullptr)
        {
            return head;
        }

        ListNode* Pre = head;
        while(Pre!=nullptr && Pre->val==val)
        {
            ListNode* target = Pre;
            Pre = Pre->next;
            // delete target;
        }
        head = Pre;

        while(Pre!=nullptr)
        {
            if(Pre->next!=nullptr && Pre->next->val==val)
            {
                ListNode* target = Pre->next;
                Pre->next = Pre->next->next;
                delete target;
            }
                
            else
                Pre = Pre->next;
        }
        return head;
    }
};

贪心


最短无序连续子数组
题目描述:在这里插入图片描述

解决方案:
可以将整个数组分为三部分,numsa,numsb,numsc,其中a和c都是有序的,b是需要排序的部分,那么我们保证a和c最大,那么b部分就会最小,也就是长度最小。
同时需要知道最终的数组一定是被排序完的,且排序的结果唯一,那么我们可以先得到一个排序后的结果,然后先从前往后比较两个数组,看哪里先不一样,先不一样的地方就是b的起点,再从后往前比较,先不一样的地方就b的终点。

class Solution 
{
public:
    int findUnsortedSubarray(vector<int>& nums) 
    {
        if(is_sorted(nums.begin(), nums.end()))
        {
            return 0;
        }
        vector<int> numssorted(nums);
        sort(nums.begin(),nums.end());

        int left = 0;
        int n = nums.size();
        for(int i = 0;i<n;i++)
        {
            if(nums[i]==numssorted[left])
            {
                left++;
            }
            else
            {
                break;
            }
        }

        int right = n-1;
        for(int i = n-1;i>=0;i--)
        {
            if(nums[i]==numssorted[right])
            {
                right--;
            }
            else
            {
                break;
            }
        }

        return right-left+1;
    }
};

任务调度器
在这里插入图片描述

解决方案:
可以像填方块一样,解决这个问题。
首先整理每个字符出现的次数,并按次数出现的高低排序。
假设出现最多的字符是A,它出现了X次,那么我们画一个X*(n+1)的格子。前(x-1)个格子肯定会被用上,但是最后一行,要看其余字符出现的次数,还有没有和X一样多的,有多少个出现了X的字符,那么结果就是(x-1)*(n+1)+出现次数。
在这里插入图片描述

class Solution 
{
public:
    int leastInterval(vector<char>& tasks, int n) 
    {
        int len = tasks.size();
        vector<int> vec(26);

        for(auto a:tasks)
        {
            vec[a-'A']++;
        }

        sort(vec.begin(),vec.end(),greater<int>());

        int cnt = 1;
        while(cnt<vec.size() && vec[cnt]==vec[0])
        {
            cnt++;
        }

        return max(len,cnt+(vec[0]-1)*(n+1));
    }
};

面试题 10.11. 峰与谷
在这里插入图片描述

思路

题目要求是峰谷峰谷…这样排列
相当于大小大小…这样子排列
那么我们另nums数组排序,然后让最后的数字放到新数组第一个,最前面数组放新数组第二个… 依次类推

class Solution
{
public:
    void wiggleSort(vector<int>& nums) 
    {
        sort(nums.begin(),nums.end());

        vector<int> vec;
        
        for(auto num:nums)
        {
            vec.push_back(num);
        }

        nums.clear();

        int i = 0,j = vec.size()-1;
        while(i<=j)
        {
            if(i<=j)
            {
                nums.push_back(vec[j]);j--;
            }
            if(i<=j)
            {
                nums.push_back(vec[i]);i++;
            }
        }

        vec.clear();
    }
};

动态规划


最大正方形
在这里插入图片描述
思路:
首先题目说了,找只包含1的最大正方形,那么包含0的就不算数。
因此我们可以创建一个二维数组dp,这个二维数组中与原matrix一致,在是1的地方为1 ,为0的地方是0
同时创建一个res,如果原matrix中有1的话,res=1,否则res=0,可以直接返回res=0,因为原数组中没有1 。
我们从dp数组的第2行第2列开始,也就是dp[1][1]开始,dp[1][1]=min(dp[0][0],dp[1][0],dp[0][1])+1,如果这4个都是1的话,dp[1][1]=2,res = max(res,dp[1][1]),对于其它的也是这样。
那么我们的转移方差就是

                if(matrix[i][j]=='1'&& matrix[i][j]==matrix[i-1][j] && matrix[i][j]==matrix[i][j-1] && matrix[i][j]==matrix[i-1][j-1])
                {
                    dp[i][j] = min(dp[i-1][j],min(dp[i][j-1],dp[i-1][j-1]))+1;
                    res = max(res,dp[i][j]);
                }

最终结果返回res*res

class Solution 
{
public:
    int maximalSquare(vector<vector<char>>& matrix) 
    {
        int m = matrix.size(),n = matrix[0].size();
        vector<vector<int>> dp(m,vector<int>(n,0));
        int res = 0;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(matrix[i][j]=='1')
                {
                    dp[i][j] = 1;
                    res = 1;
                }
            }
        }


        for(int i = 1;i<m;i++)
        {
            for(int j =1;j<n;j++)
            {
                if(matrix[i][j]=='1'&& matrix[i][j]==matrix[i-1][j] && matrix[i][j]==matrix[i][j-1] && matrix[i][j]==matrix[i-1][j-1])
                {
                    dp[i][j] = min(dp[i-1][j],min(dp[i][j-1],dp[i-1][j-1]))+1;
                    res = max(res,dp[i][j]);
                }
            }
        }


        return res*res;
    }
};

跳跃游戏
在这里插入图片描述

思路:
思路很简单,首先创建dp数组,dp数组大小和nums的大小一致
另dp[0]=1
从位置0开始判断,如果dp[i]=1,那么就将从i出发到i+nums[i]的dp[i],都置为1
最后判断dp[n-1]是否为1

class Solution 
{
public:
    bool canJump(vector<int>& nums) 
    {
        int n = nums.size();

        vector<int> dp(n,0);
        dp[0] = 1;

        for(int i =0;i<n-1;i++)
        {
            if(dp[i]==1)
            {
                if(i+nums[i]<n)
                {
                    for(int j=i+1;j<=i+nums[i];j++)
                    {
                        dp[j] = 1;
                    }
                }
                else
                {
                    for(int j=i+1;j<n;j++)
                    {
                        dp[j] = 1;
                    } 
                }
            }

        }

            if(dp[n-1]==1)
            {
                return true;
            }
        return false;
    }
};

看到的另一种更好的答案

class Solution 
{
public:
    bool canJump(vector<int>& nums) 
    {
        int k = 0;

        for(int i = 0;i<nums.size();i++)
        {
            if(i>k) return false;
            k = max(k,i+nums[i]);
        }
        return true;
    }
};

最小路径和
在这里插入图片描述

思路:
对于第一行,如果想走到末尾,只能是grid[i][j]+=grid[i][j-1]
对于第一列 只能是grid[i][j]+=grid[i-1][j]
对于其它地方,就是grid[i][j]+=min(grid[i-1][j],grid[i][j-1]);

class Solution 
{
public:
    int minPathSum(vector<vector<int>>& grid) 
    {
        int m = grid.size(),n=grid[0].size();
        for(int i = 1;i<n;i++)
        {
            grid[0][i]+=grid[0][i-1];
        }

        for(int i = 1;i<m;i++)
        {
            grid[i][0]+=grid[i-1][0];
        }

        for(int i = 1;i<m;i++)
        {
            for(int j = 1;j<n;j++)
            {
                grid[i][j]+=min(grid[i-1][j],grid[i][j-1]);
            }
        }

        return grid[m-1][n-1];
    }
};

完全平方数
在这里插入图片描述

思路:
首先得承认,这个题自己做了好久。动态规划确实有意思哦
另f(i)表示,数字i可以最少用几个完全平方数表示
定义一个dp数组,数组大小为n+1,dp[i] = i;
其中dp[i] = min(dp[i],dp[i-j*j]+1)

class Solution 
{
public:
    int numSquares(int n) 
    {
        vector<int> dp(n+1,0);

        for(int i = 0;i<=n;i++)
        {
            dp[i] = i;
            for(int j = 1;i-j*j>=0;j++)
            {
                dp[i] = min(dp[i],dp[i-j*j]+1);
            }
        }

        return dp[n];
    }
};

回文子串
在这里插入图片描述

思路:
中心向外扩展法,判断共有多少字符串。
另每个字符作为中心,向两边延伸去判断当前子串是否为回文串。
判断字符串是否为回文串有两种情况:
如果字符串的个数为奇数,那么选中间的字符为中心即可
如果字符串的个数为偶数,选择中间的两个字符为中心

class Solution 
{
public:
    int countSubstrings(string s) 
    {
        int ans = 0,n = s.size();

        for(int i = 0;i<2*n-1;i++)
        {
            int l = i/2,r= i/2+i%2;
            while(l>=0&&r<n&&s[l]==s[r])
            {
                l--;
                r++;
                ans++;
            }
        }

        return ans;
    }
};

三步问题
在这里插入图片描述

思路:
定义一个n+3大小的数组vec
转移方程为vec[i] = vec[i-3]+vec[i-2]+vec[i-1] i>=3
vec[0] = 1;vec[1] = 1;vec[2] = 2;
题中又说vec[i]可能很大,所以要和1000000007取模 即 vec[i] = vec[i]%1000000007

class Solution 
{
public:
    int waysToStep(int n) 
    {
        vector<long int> vec(n+3);
        vec[0] = 1;
        vec[1] = 1;
        vec[2] = vec[0]+vec[1];
        vec[3] = vec[0]+vec[1]+vec[2];

        for(int i = 4;i<=n;i++)
        {
            vec[i] = vec[i-3]+vec[i-2]+vec[i-1];
            vec[i] = vec[i]%1000000007;
        }

        return vec[n];
    }
};

面试题 16.17. 连续数列
在这里插入图片描述

思路:
另part为当前元素或者当前元素+前一个元素中的最大值 另res 是 已有res和part的最大值

part = max(num,num+part); res = max(res,part);


class Solution 
{
public:
    int maxSubArray(vector<int>& nums) 
    {
        int res = nums[0],part = 0;
        for(auto num:nums)
        {
            part = max(num,num+part);
            res = max(part,res);
        }

        return res;
    }
};

面试题 17.09. 第 k 个数
在这里插入图片描述

思路

这里附上我对某个大神代码的理解

我们把题目要求的数字称为丑数,那么一个丑数一定是由前面的丑数 x3/x5/x7得来的

假如我们已经知道了丑数数列的前5个 nums[0] nums[1] nums[2] nums[3] nums[4]

那么后面的丑数一定是

nums[0]*3 nums[1]*3 nums[2]*3 nums[3]*3 nums[4]*3
nums[0]*5 nums[1]*5 nums[2]*5 nums[3]*5 nums[4]*5
nums[0]*7 nums[1]*7 nums[2]*7 nums[3]*7 nums[4]*7

将上面3个数组,合并起来,得到的有序数列。
那么这个过程其实可以用3个指针p3 p5 p7表示,
可以用min(nums[p3]*3,nums[p5]*5,nums[p7]*7),作为下一个丑数,用到了哪一个,那么对应的指针就加1


class Solution 
{
public:
    int getKthMagicNumber(int k) 
    {
        vector<int> vec(k+10);

        vec[1] = 1;
        int p3 = 1,p5 = 1,p7 = 1;

        for(int i = 2;i<=k;i++)
        {
            int num3 = vec[p3]*3,num5 = vec[p5]*5,num7 = vec[p7]*7;

            vec[i] = min(min(num3,num5),num7);

            if(vec[i]==num3)
            {
                p3++;
            }
            if(vec[i]==num5)
            {
                p5++;
            }
            if(vec[i]==num7)
            {
                p7++;
            }
        }

        return vec[k];

    }
};


深度优先搜索

翻转二叉树
在这里插入图片描述

思路:
如果root本身为空,那就返回nullptr
对于root->left而言,我们要root的右子树(右节点)
对于root->right而言,我们要root的左子树(左节点)

class Solution 
{
public:
    TreeNode* invertTree(TreeNode* root) 
    {
        if(root==nullptr)
        {
            return nullptr;
        }

        TreeNode* temp = root->left;
        root->left = invertTree(root->right);
        root->right = invertTree(temp);
        return root;
    }
};

二叉树展开为链表
在这里插入图片描述

思路:
通过一个vector容器,将二叉树的先序遍历保存起来,然后利用容器中的保存顺序,将节点串起来即可。但是缺陷在于内存空间占用较多。

class Solution 
{
public:
    void first_dp(TreeNode* root,vector<TreeNode*>& vec)
    {
        if(root==nullptr)
        {
            return;
        }

        vec.push_back(root);
        first_dp(root->left,vec);
        first_dp(root->right,vec);
    }
    void flatten(TreeNode* root) 
    {
        vector<TreeNode*> vec;
        first_dp(root,vec);

        TreeNode* res = root;
        for(auto a:vec)
        {
            if(a==root)
            {
                continue;
            }
            else
            {
                res->right = a;
                res->left = nullptr;
                res = res->right;
            }
        }
    }
};

打家劫舍III
在这里插入图片描述

思路:
对于每节点o,我们令f(o)是节点o,在选取它的情况下,最大的累计值。
令g(o)是在不选取它的情况下,最大的累计值
f(o)=g(o->left)+g(o->right)
g(o) = max(f(o->left),g(o->left))+max(f(o->right),g(o->right))
这个题我最开始没想明白,看了答案发现确实妙

class Solution 
{
public:
    unordered_map<TreeNode*,int>f,g;
    void dfs(TreeNode* node)
    {
        if(!node)
        {
            return;
        }

        dfs(node->left);
        dfs(node->right);
        f[node] = node->val+g[node->left]+g[node->right];
        g[node] = max(f[node->left],g[node->left])+max(f[node->right],g[node->right]);
    }
    int rob(TreeNode* root) 
    {
        dfs(root);
        return max(f[root],g[root]);
    }
};

把二叉搜索树转换为累加树
在这里插入图片描述

思路:
这个题的思路比较简单,先前序遍历整颗二叉树的节点值,存到一个vector中。然后获得vector中元素和。
接着对于每一个节点,计算小于它的节点和,另某一节点=vector所有元素和-小于它的节点和
当然这个比较简单,还有较大优化空间

class Solution 
{
public:
    void first_dfs(TreeNode* root,vector<int>& vec)
    {
        if(root==nullptr)
        {
            return;
        }

        vec.push_back(root->val);
        first_dfs(root->left,vec);
        first_dfs(root->right,vec);
    }

    void dfs(TreeNode* root,int sum_all,vector<int> vec)
    {
        if(root==nullptr)
        {
            return;
        }

        int temp = 0;
        for(auto a:vec)
        {
            if(a<root->val)
            {
                temp+=a;
            }
            else
            {
                break;
            }
        }

        root->val = sum_all-temp;
        dfs(root->left,sum_all,vec);
        dfs(root->right,sum_all,vec);
    }
    TreeNode* convertBST(TreeNode* root) 
    {
        vector<int> vec;
        first_dfs(root,vec);

        sort(vec.begin(),vec.end());

        int sum_all = accumulate(vec.begin(),vec.end(),0);

        dfs(root,sum_all,vec);

        return root;
    }
};

零钱兑换
在这里插入图片描述

思路:
这个题我自己做的时候,没做出来。看答案看懂的。下面把我对答案的理解阐述一遍。
amount表示总金额。我们用函数f(x)表示,对于金额x,我们使用最少硬币个数表示的情况
f(x) = min(f(x-coin1),f(x-coin2),f(x-coin3)…)+1;
如果最终的f(x),x=amount情况下,f(x)>amount的话,说明没有能任何一种硬币能组成总金额,则返回1.否则返回正常的结果。

class Solution 
{
public:
    int coinChange(vector<int>& coins, int amount) 
    {
        int Max = amount+1;
        vector<int> dp(amount+1,Max);
        dp[0] =0;

        for(int  i = 1;i<=amount;i++)
        {
            for(auto a:coins)
            {
                if(a<=i)
                {
                    dp[i] = min(dp[i],dp[i-a]+1);
                }
            }
        }
        
        return dp[amount]>amount? -1 : dp[amount];
    }
};

广度优先搜索

二叉树的层序遍历
在这里插入图片描述

思路:
比较简单,看代码就OK

class Solution 
{
public:

    vector<vector<int>> dp;
    vector<vector<int>> levelOrder(TreeNode* root) 
    {
        if(root==nullptr)
        {
            return dp;
        }

        queue<TreeNode* > vec;
        vec.push(root);

        while(vec.size()>0)
        {
            vector<int> temp;
            int n = vec.size();
            for(int i = 0;i<n;i++)
            {
                TreeNode* node = vec.front();
                vec.pop();
                temp.push_back(node->val);
                if(node->left!=nullptr)
                {
                    vec.push(node->left);
                }
                if(node->right!=nullptr)
                {
                    vec.push(node->right);
                }
            }           
            dp.push_back(temp); 
        }
        return dp;
    }
};

二叉树的最近公共祖先
在这里插入图片描述

思路:
这个题真是,一看就会,一做就错。

class Solution 
{
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        if(root==p||root==q||root==nullptr) return root;

        TreeNode* left = lowestCommonAncestor(root->left,p,q);
        TreeNode* right = lowestCommonAncestor(root->right,p,q);

        if(left!=nullptr && right!=nullptr) return root;
        if(left==nullptr) return right;
        return left;
    }
};

快速选择

前 K 个高频元素
在这里插入图片描述

思路:
挺简单的,但是本来想用map做排序,但是后来还是要用 vector<pair<int,int>>

class Solution 
{
public:
    vector<int> topKFrequent(vector<int>& nums, int k) 
    {
        unordered_map<int, int> m;
        vector<pair<int,int>>dp;
        for(auto a:nums)
        {
            m[a]++;
        }

        for(auto x:m)
        {
            dp.push_back(x);
        }

        sort(dp.begin(),dp.end(),[&](pair<int,int>a,pair<int,int>b)
        {
            return a.second>b.second;
        });
        vector<int> vec;

        for(int i =0;i<k;i++)
        {
            vec.push_back(dp[i].first);
        }

        return vec;
    }
};

二分查找

在排序数组中查找元素的第一个和最后一个位置
在这里插入图片描述

思路:
蛮简单的,感觉不像是中等题

class Solution 
{
public:
    vector<int> searchRange(vector<int>& nums, int target) 
    {
        int left = 0,right = nums.size()-1;
        vector<int> res;

        for(int i = 0;i<nums.size();i++)
        {
            if(nums[i]==target)
            {
                res.push_back(i);
                break;
            }
        }

        for(int i =nums.size()-1;i>=0;i--)
        {
            if(nums[i]==target)
            {
                res.push_back(i);
                break;
            }
        }
        if(res.empty()==true)
        {
            res.push_back(-1);
            res.push_back(-1);
        }
        return res;
    }
};

搜索旋转排序数组
在这里插入图片描述

思路:
数组中的数据可以分为两部分 partA partB,其中A、B内部都是有序的,如果把B和A调换位置整体数组有序。
假设数组是4,5,6,7,0,1,2,其实也可以用二分查找
另l = 0,r = nums.size()-1,mid = (l+r)/2
如果nums[l]<=nums[mid],那么说明在这段区域内数组是有序的,判断target 是否在改区域内

如果上面为否,那么nums[mid]到nums[r]这段就是有序的,判断target是否在改区域内

对于target不在有序数组中的情况,要么是对l = mid+1,要么是r = mid-1,

class Solution 
{
public:
    int search(vector<int>& nums, int target) 
    {
        int left = 0,right = nums.size()-1,n = nums.size();
        if(n==0)
        {
            return -1;
        }
        else if(n==1)
        {
            return nums[0]==target? 0:-1;
        }

        while(left<=right)
        {
            int mid = (left+right)/2;
            if(nums[mid]==target)
            {
                return mid;
            }

            if(nums[left]<=nums[mid])
            {
                if(nums[left]<=target && target<nums[mid])
                {
                    right=mid-1;
                }
                else
                {
                    left = mid+1;
                }
            }
            else
            {
                if(nums[mid]<target && target<=nums[right])
                {
                    left = mid+1;
                }
                else
                {
                    right = mid-1;
                }
            }
        }

        return -1;
    }
};

寻找重复数
在这里插入图片描述

思路:
这个题有点难,我从自己的个人理解,分析下我的思路。
首先题目中的数组的范围都在1到n之间,数组中共有n+1个数字,那么就是说数组中有一个数字被至少重复了2次。

那么我们另left = 0 right = n mid = (left+right)/2
我们统计数组中小于等于mid的数字个数cnt,如果cnt>mid,说明这个重复的数字在left到mid中,否咋就在mid到right之间
基于这个思想去寻找这个重复的数字。
可以看出来这里依然使用了二分查找法,但是二分查找的对象不是数组本身

class Solution 
{
public:
    int findDuplicate(vector<int>& nums) 
    {
        int n = nums.size();
        int left = 1,right = n;

        while(left<right)
        {
            int mid = (left+right)/2;

            int cnt = 0;

            for(auto num:nums)
            {
                if(num<=mid)
                {
                    cnt++;
                }
            }

            if(cnt>mid)
            {
                right = mid;
            }
            else
            {
                left = mid+1;
            }
        }

        return left;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值