尺取法和并查集在日常刷题过程中也经常可以看到,
尺取法
尺取法,对于孤陋寡闻的我来说,刷题之前没听说过,后来了解了一下这个方法,感觉好多题目都可以用这个方法解决,在leetcode上有一类题叫做Two Pointers,其中一部分题目就应该使用尺取法来解决。
在介绍尺取法的使用之前,先看一个典型的实例Leetcode209. Minimum Size Subarray Sum:
Given an array of n positive integers and a positive integer s, find the minimal length of a contiguous subarray of which the sum ≥ s. If there isn't one, return 0 instead.
For example, given the array [2,3,1,2,4,3] and s = 7,
the subarray [4,3] has the minimal length under the problem constraint.
题目要求在一个正数数组中寻找一个连续的子数组,使得这个子数组的和值大于等于给定的s,并要求这个子数组的长度最小。
再来看一下这个题的O(n)解法:
class Solution { public: int minSubArrayLen(int s, vector<int>& nums) { int n=nums.size(),l=0,r=0,sum=0,mlen=n+1; while(1){ while(r<n&&sum<s){ sum+=nums[r]; r++; } if(sum<s){ break; } mlen=min(mlen,r-l); sum-=nums[l]; l++; } mlen=(mlen==n+1)?0:mlen; return mlen; } };
代码很简单,主要的思想就是维护两个指针i和j(i<j),计算i和j之间值的和,如果大于s,说明子集中的数过多,则右移i指针j,并不断维护一个全局变量,计算满足条件的最小值。
尺取法的一个典型应用场景是一个数组,寻找连续子集,要求子集里面的数字满足某个条件。如果使用暴力法,复杂度为O(n^2),而尺取法则将复杂度降低到了O(n)。
举例:713. Subarray Product Less Than K
Your are given an array of positive integers nums.
Count and print the number of (contiguous) subarrays where the product of all the elements in the subarray is less than k.
Example 1:
Input: nums = [10, 5, 2, 6], k = 100 Output: 8 Explanation: The 8 subarrays that have product less than 100 are: [10], [5], [2], [6], [10, 5], [5, 2], [2, 6], [5, 2, 6]. Note that [10, 5, 2] is not included as the product of 100 is not strictly less than k.
Note:
0 < nums.length <= 50000.0 < nums[i] < 1000.0 <= k < 10^6.
解法:
class Solution { public: int numSubarrayProductLessThanK(vector<int>& nums, int k) { int n = nums.size(),i=0,j=0,tmppro = 1,ret = 0; while(i<n){ while(j<n&&tmppro<k){ tmppro*=nums[j]; j++; } ret+=max(j-i-1,0); if(tmppro<k) ret++; tmppro/=nums[i]; i++; if(i>=n) break; } return ret; } };
这个题和上面的题大同小异。个人感觉只要掌握了这个方法,认准应用场景,就基本可以解决问题。
另附上尺取法的介绍
并查集
并查集,据我理解,就是一个“分组”的方法,原本1 2 3 4 5 6各位一组,现在1和2合并,2和3合并,1和4合并,于是1 2 3 4就成了新的一组,如何能快速找到“组织”,并查集提供了一个很好的解决方案。
并查集介绍:http://blog.csdn.net/dm_vincent/article/details/7655764 ,可以看出来,对于一个并查集,包括查找,插入(对),统计个数等基本操作
看一个典型实例:Leetcode 547: Friend Circles
There are N students in a class. Some of them are friends, while some are not. Their friendship is transitive in nature. For example, if A is a direct friend of B, and B is a direct friend of C, then A is an indirect friend of C. And we defined a friend circle is a group of students who are direct or indirect friends.
Given a N*N matrix M representing the friend relationship between students in the class. If M[i][j] = 1, then the ithand jth students are direct friends with each other, otherwise not. And you have to output the total number of friend circles among all the students.
Example 1:
Input: [[1,1,0], [1,1,0], [0,0,1]] Output: 2 Explanation:The 0th and 1st students are direct friends, so they are in a friend circle.
The 2nd student himself is in a friend circle. So return 2.
Example 2:
Input: [[1,1,0], [1,1,1], [0,1,1]] Output: 1 Explanation:The 0th and 1st students are direct friends, the 1st and 2nd students are direct friends,
so the 0th and 2nd students are indirect friends. All of them are in the same friend circle, so return 1.
Note:
- N is in range [1,200].
- M[i][i] = 1 for all students.
- If M[i][j] = 1, then M[j][i] = 1.
题目的意思是几个学生有其直接的朋友,秉承着朋友的朋友就是朋友的原则,问当前一共有多少个朋友圈。
典型的并查集实例。直接的朋友即为并查集中需要添加的对,最后返回的朋友圈的个数即为并查集的个数。代码如下:
class Solution { public: int find(int x){ int k=x; while(k!=pre[k]){ k=pre[k]; } int p=x; while(p!=k){ int t=pre[p]; pre[p]=k; p=t; } return k; } void add(int x,int y){ int f1=find(x),f2=find(y); if(f1!=f2){ pre[f1]=f2; } } int findCircleNum(vector<vector<int>>& M) { if(M.empty())return 0; int m=M.size(); for(int i=0;i<=m;i++){ pre.push_back(i); } for(int i=0;i<m;i++){ for(int j=0;j<i;j++){ if(M[i][j]==1)add(i+1,j+1); } } set<int> s; for(int i=1;i<=m;i++){ s.insert(find(i)); } return s.size(); } private: vector<int> pre; };
套用模板即可。个人认为并查集最难的就是记住代码……