leetcode刷题7月

leetcode378 有序矩阵中第k小的元素
给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素。

matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,
思路:二分查找
主函数:
统计小于当前mid的数量,>k,说明我们找的数<=mid,令right=mid,反之亦然,
check函数:
1.初始位置在 matrix[n - 1][0]matrix[n−1][0](即左下角);
2.设当前位置为 matrix[i][j]。若 matrix[i][j]<=mid,则将当前所在列的不大于 mid的数的数量(即 i + 1)累加到答案中,并向右移动,否则向上移动;
3.不断移动直到走出格子为止。

在这里插入图片描述

//时间复杂度:O(n\log(r-l)),二分查找进行次数为 O(log(r−l)),每次操作时间复杂度为 O(n)。
class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        int n=matrix.size();
        int left=matrix[0][0],right=matrix[n-1][n-1];
        while(left<right){
            int mid=left+(right-left)/2;
            if(check(matrix,k,n,mid)){
                right=mid;
            }
            else{
                left=mid+1;
            }
        }
        return left;
        

    }
    int check(vector<vector<int>>& matrix, int k,int n,int mid){
        int i=n-1,j=0;
        int num=0;
        while(i>=0&&j<n){
            if(matrix[i][j]<=mid){
                num+=i+1;  //表明这一列的元素都比当前的mid元素小,累加
                j++;
            }
            else{
                i--;
            }
        }
        return num>=k;
    }
};

leetcode23 最长有效括号
给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。

示例 1:

输入: “(()”
输出: 2
解释: 最长有效括号子串为 “()”
示例 2:

输入: “)()())”
输出: 4
解释: 最长有效括号子串为 “()()”

class Solution {
public:
    int longestValidParentheses(string s) {
        stack<int> st;
        int count=0;
        st.push(-1);
        for(int i=0;i<s.size();i++){
            if(s[i]=='('){
                st.push(i);
            }
            else{
                st.pop();
                if(st.empty()){
                    st.push(i); //记录最后一个不被匹配的右括号
                }
                else{
                    count=max(count,i-st.top());

                }
            }
        }
        return count;

        
    }
};

leetcode 11 盛水最多的容器(medium)
给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

思想:贪心+双指针
对O(n)的算法写一下自己的理解,一开始两个指针一个指向开头一个指向结尾,此时容器的底是最大的,接下来随着指针向内移动,会造成容器的底变小,在这种情况下想要让容器盛水变多,就只有在容器的高上下功夫。 那我们该如何决策哪个指针移动呢?我们能够发现不管是左指针向右移动一位,还是右指针向左移动一位,容器的底都是一样的,都比原来减少了 1。这种情况下我们想要让指针移动后的容器面积增大,就要使移动后的容器的高尽量大,所以我们选择指针所指的高较小的那个指针进行移动,这样我们就保留了容器较高的那条边,放弃了较小的那条边,以获得有更高的边的机会。

class Solution {
public:
    int maxArea(vector<int>& heights) {
       int i=0;
       int j=heights.size()-1;
       int ans=0;
       while(i<j){
           ans=max(ans,(j-i)*min(heights[i],heights[j]));
           if(heights[i]>heights[j]){
               j--;
            }
            else{
                i++;
            }
       }
            return ans;
       }
};

leetcode4. 寻找两个正序数组的中位数(hard)
给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。

请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

class Solution {
public:
    int find(vector<int>& nums1, int i, vector<int>& nums2, int j, int k){//从nums1中的i和nums2的j开始 找第k大的数
        if(nums1.size() - i > nums2.size() - j) return find(nums2, j, nums1, i, k);//保证第二个大于等于第一个
        if(nums1.size() == i) return nums2[j+k-1];//第一个数组为空时 
        if(k==1) return min(nums1[i],nums2[j]);//递归边界 即为两个最小值
        int si = min(int(nums1.size()),i + k/2), sj = j + k - k/2;//每个数组从起点开始的第k/2大数的后一个数的下标
        if(nums1[si-1] > nums2[sj-1]){//若前者的该数大于后者的该数 则后者的前部分就没用了
         return find(nums1, i, nums2, sj, k - (sj-j));//sj-j即为删去的数的个数
        }
        else return find(nums1, si, nums2, j, k - (si - i));//同理 此时 前者的前部分就没用了
}
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int tot = nums1.size() + nums2.size();
        if(tot % 2) {//总数为奇数时的中位数
         return find(nums1, 0, nums2, 0, tot/2+1);
     }
        else {
            int l = find(nums1, 0, nums2, 0, tot/2), r = find(nums1, 0, nums2, 0, tot/2 + 1);
            return (l + r)/2.0;
    }
}
};


leetcode42 接雨水(hard)
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

在这里插入图片描述

上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

//单调递减栈,O(N) O(N)
class Solution {
public:
    int trap(vector<int>& height) {
        stack<int> s;
        int area=0;
        for(int i=0;i<height.size();i++){
            while(!s.empty()&&height[i]>height[s.top()]){
                    int curr=s.top();
                    s.pop();
                    if(s.empty()){
                        break;
                    }
                    int j=s.top();
                    int h=min(height[i],height[j])-height[curr];
                    area+=(i-j-1)*h;
                }
                s.push(i);
            }
        
        return area;
        
    }
};
  1. 不同的二叉搜索树 II
    给定一个整数 n,生成所有由 1 … n 为节点所组成的 二叉搜索树 。
    示例:

输入:3
输出:
[
[1,null,3,2],
[3,2,null,1],
[3,1,null,null,2],
[2,1,3],
[1,null,2,null,3]
]
解释:
以上的输出对应以下 5 种不同结构的二叉搜索树:

1 3 3 2 1
\ / / / \
3 2 1 1 3 2
/ / \
2 1 2 3

/**
 * 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<TreeNode*> generateTrees(int n) {
        return helper(1,n);
        
    }
    vector<TreeNode*> helper(int left,int right){
        vector<TreeNode*> ans;
        if(left>right){
            return {};
        }
        if(left==right){
          return {new TreeNode(left)};  
        }
        for(int i=left;i<=right;i++){
            auto left_child=helper(left,i-1);
            auto right_child=helper(i+1,right);
            if(right_child.empty()){
                for(auto l:left_child){
                    auto root=new TreeNode(i);
                    root->left=l;
                    ans.push_back(root);
                }
            }
             else if(left_child.empty()){
                for(auto r:right_child){
                    auto root=new TreeNode(i);
                    root->right=r;
                    ans.push_back(root);
                }
        }
             else{
                 for(auto l:left_child){
                    for(auto r:right_child){
                     auto root=new TreeNode(i);
                        root->left=l;
                        root->right=r;
                        ans.push_back(root);
                }
                 }
                 }
        }
        return ans;
    }
};
  1. 分割数组的最大值(hard)
    给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。
    注意:
    数组长度 n 满足以下条件:
    1 ≤ n ≤ 1000
    1 ≤ m ≤ min(50, n)
    示例:
    输入:
    nums = [7,2,5,10,8]
    m = 2

输出:
18

思路:对于每个解,他们的各个子数组和的最大值肯定是固定的,而且肯定在left和right之间;其中left=nums数组中的最大值,right=nums的所有元素之和,这样使用二分查找确定各个子数组的最大和,然后判断每个确定的最大值是否满足。

当满足的时候,我们考虑把最大值变小,判断是否还满足
如果还满足,那么我们就继续变小,
如果不满足,那么我们需要把最大值变大,然后再找。

class Solution {
public:
    int splitArray(vector<int>& nums, int m) {
        long low=0,high=0;
        for(auto num:nums){
            high+=num;   //sum(nums)
            low=num>low?num:low; //max(nums)
        }
        while(low<high){
            int count=1;
            int mid=low+(high-low)/2;
            long temp=0;
            for(auto num:nums){
                temp+=num;
                if(temp>mid){
                    temp=num;
                    count++;
                }
            }
            if(count>m){
                low=mid+1;
            }
            else{
                high=mid;
            }
        }
        return low;

    }
};

约瑟夫环:

//模拟循环链表
class Solution {
public:
    struct listnode{
        int val;
        listnode* next;
        listnode(int x):val(x),next(NULL){}
    };
    int LastRemaining_Solution(int n, int m)
    {
        if(n==0||m==0){return -1;}
        listnode* root=new listnode(0);
        listnode* head=root;
        for(int i=1;i<n;i++){
            head->next=new listnode(i);
            head=head->next;
        }
        head->next=root;
        int count=0;
        listnode* curr=root;
        listnode* pre;
        while(curr->next!=curr){
            
            count++;
            if(count==m){
                pre->next=curr->next;
                curr=curr->next;
                count=0;    
            }
            else{
                pre=curr;
                curr=curr->next;
            }
        }
        return curr->val;
        
    }
};

leetcode93.复原IP地址(medium)
给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
有效的 IP 地址正好由四个整数(每个整数位于 0 到 255 之间组成),整数之间用 ‘.’ 分隔。
示例:

输入: “25525511135”
输出: [“255.255.11.135”, “255.255.111.35”]

class Solution {
private:
    vector<string> res;
    string path;
public:
    vector<string> restoreIpAddresses(string s) {
        if(s.size() < 4 || s.size() > 12)
        return {};
        dfs(0, s, 0, path);
        return res;             
    }

    void dfs(int cur_count, string s, int start, string path){
        if(cur_count == 4){   //当达到4个分组,且s已经遍历完时,将结果输出到res中
            if(start == s.size())
            res.push_back(path);
            return;
        }
        
        for(int i = 1; i < 4 && start + i <= s.size(); ++i){  //遍历+回溯,每次尝试1、2、3个元素为当前节
            string tmp = s.substr(start, i);                  //必须加上start + i <= s.size(),因为遍历到s末尾后会越界
            if(check(tmp)){
                if(cur_count < 3)   //注意'.'的输出次数
                tmp += '.';
                dfs(cur_count + 1, s, start + i, path + tmp); //遍历、回溯
                if(s[start] == '0') //此处排除0x和0xx的情况
                return;
            }
            else{
                return;   //当前所选节是无效的,就得返回
            }
        }

    }

    bool check(string str){  //判断是否符合0-255
        int tmp = stoi(str);
        if(tmp >= 0 && tmp <= 255)
        return true;
        return false;
    }
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值