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; } }; 不同的二叉搜索树 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; } }; 分割数组的最大值(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; } };