3. 无重复字符的最长子串

A:

要找最长的无重复子串,所以用一个map保存出现过的字符,并且维持一个窗口,用le和ri指针标识。ri为当前要遍历的字符,如果ri字符在map中出现过,那么将le字符从map移除,le++。如果ri字符没出现过,那ri++,并更新最大无重复子串长度。全程利用map保持无重复的要求,每次循环要么le++,要么ri++。最差情况是n个一样的字符,那么ri++,le++,ri++,le++这样一直从0循环到n-1。时间复杂度和空间复杂度都是O(N)。

  1. class Solution {
  2. public:
  3. int lengthOfLongestSubstring(string s) {
  4. if(s.empty()){return ;}
  5. int le=,ri=;
  6. map<char,int> dic;
  7. dic[s[]]+=;
  8. int max_len=;
  9. while(ri<s.size()){
  10. if(dic[s[ri]]){
  11. dic[s[le]]-=;
  12. ++le;
  13. }
  14. else{
  15. dic[s[ri]]+=;
  16. ++ri;
  17. max_len=max(max_len,ri-le);
  18.  
  19. }
  20. }
  21. return max_len;
  22. }
  23. };

30. 串联所有单词的子串

A:

题目指定了所有单词是一样长度的,那么我们就可以按单词为单位进行查找。假如单词长度为3,那么从0、1、2分别按单词长度进行查找,一直查找到字符串结尾,就可以查找到所有满足条件的子串。

  1. class Solution {
  2. public:
  3. vector<int> findSubstring(string s, vector<string>& words) {
  4. if(s.empty() or words.empty()){
  5. return vector<int>();
  6. }
  7. int s_len=s.length(),one_word=words[].size(),all_word=words.size()*one_word,word_cnt=words.size();
  8. if(all_word>s_len){
  9. return vector<int>();
  10. }
  11. map<string,int> dic;
  12. for(const string& word:words){
  13. dic[word]+=;
  14. }
  15. vector<int> res;
  16. for(int i=;i<one_word;++i){
  17. int le=i,ri=i,cnt=;
  18. map<string,int> cur_dic;
  19. while(ri+one_word-<s_len){ //ri开头的单词结尾不能越界
  20. string cur=s.substr(ri,one_word); //当前单词
  21. if(dic[cur]==){ //无匹配
  22. ri+=one_word;
  23. le=ri;
  24. cnt=;
  25. cur_dic.clear();
  26. }
  27. else{ //匹配
  28. ri+=one_word;
  29. cur_dic[cur]+=;
  30. ++cnt;
  31. while(dic[cur]<cur_dic[cur]){
  32. string w=s.substr(le,one_word);
  33. cur_dic[w]-=;
  34. le+=one_word;
  35. --cnt;
  36. }
  37. if(cnt==word_cnt){
  38. res.push_back(le);
  39. }
  40. }
  41. }
  42. }
  43. return res;
  44. }
  45. };


但是稍微改动一下:

  1. class Solution {
  2. public:
  3. vector<int> findSubstring(string s, vector<string>& words) {
  4. if(s.empty() or words.empty()){
  5. return vector<int>();
  6. }
  7. int s_len=s.length(),one_word=words[].size(),all_word=words.size()*one_word,word_cnt=words.size();
  8. if(all_word>s_len){
  9. return vector<int>();
  10. }
  11. map<string,int> dic;
  12. for(const string& word:words){
  13. dic[word]+=;
  14. }
  15. vector<int> res;
  16. for(int i=;i<one_word;++i){
  17. int le=i,ri=i,cnt=;
  18. unordered_map<string,int> cur_dic;
  19. while(ri+one_word-<s_len){ //ri开头的单词结尾不能越界
  20. string cur=s.substr(ri,one_word); //当前单词
  21. if(dic.count(cur) and cur_dic[cur]<dic[cur]){
  22. ++cnt;
  23. cur_dic[cur]+=;
  24. ri+=one_word;
  25. }
  26. else{
  27. if(dic.count(cur)==){ //ri开头的单词不在单词表里
  28. ri+=one_word; //ri后移一个单词长度
  29. if(ri+one_word->=s_len){
  30. break;
  31. }
  32. //因为之前ri开头的单词gg了,那么移动le到新的ri重新开始
  33. le=ri;
  34. cur_dic.clear();
  35. cnt=;
  36. }
  37. else{ //cur_dic[cur]>=dic[cur],le一直右移直到cur_dic[cur]<dic[cur]使得cur能插入
  38. while(cur_dic[cur]>=dic[cur]){
  39. cur_dic[s.substr(le,one_word)]-=;
  40. le+=one_word;
  41. --cnt;
  42. }
  43. }
  44. }
  45. if(cnt==word_cnt){
  46. res.push_back(le);
  47. cur_dic[s.substr(le,one_word)]-=;
  48. le+=one_word;
  49. --cnt;
  50. }
  51. }
  52. }
  53. return res;
  54. }
  55. };


改动的地方只是把dic[cur]==0改成了dic.count(cur)==0。对于dic中不存在的cur,前者会自动假如加入 dic,默认值为0。所以内存用量一下从14M变成了26M,同时map的查询时间是log级别,所有的查map时间也会有相应增加,这一点一定要注意。

76. 最小覆盖子串

A:

写了两遍,代码基本的结构一致,唯一一点区别是第一种是先一直向右寻找直到找到覆盖t的子串,再尽量把左边界右移加入结果。
第二种是每次循环只向右跳一个字符,如果还没找到覆盖t的子串就记录并把左边界尽量右移,找到的话就加入结果。其实写到这我发现这俩解法没啥区别,就不说了吧。
另外看见题解里有一种解法是把s串中属于t串的字符挑出来记录成map,这样在遍历s寻找覆盖t的子串的时候就不用实际在s中从0到s.size()-1慢慢挪了,相当于把不属于t串的字符直接一下子跳过。在s串长度远大于t的时候能节省大量时间,这个写法有时间再补吧。。2019年11月13日 22:19:10

  1. class Solution {
  2. public:
  3. string minWindow(string s, string t) {
  4. if(s.empty() or t.empty()){
  5. return string();
  6. }
  7. map<char,int> dic,cur_dic;
  8. for(const char& c:t){
  9. dic[c]+=;
  10. }
  11. int le=,ri=,min_len=INT_MAX,cnt=,t_len=t.size();
  12. string res="";
  13. while(ri<s.size()){
  14. // cout<<le<<" "<<ri<<" "<<s.substr(le,ri-le)<<" "<<cnt<<endl;
  15. while(ri<s.size() and cnt<t_len){
  16. if(dic.count(s[ri])>){
  17. if(dic[s[ri]]>cur_dic[s[ri]]){
  18. cnt++;
  19. }
  20. cur_dic[s[ri]]++;
  21. }
  22. ++ri;
  23. }
  24. if(cnt!=t_len){
  25. return res;
  26. }
  27. while(dic.count(s[le])== or dic[s[le]]<cur_dic[s[le]]){
  28. if(dic.count(s[le])!=){
  29. cur_dic[s[le]]--;
  30. }
  31. ++le;
  32. }
  33. if(ri-le<min_len){
  34. min_len=ri-le;
  35. res=s.substr(le,ri-le);
  36. }
  37. cur_dic[s[le]]--;
  38. ++le;
  39. cnt--;
  40. }
  41. return res;
  42. }
  43. };
  1. class Solution {
  2. public:
  3. string minWindow(string s, string t) {
  4. if(s.empty() or t.empty()){
  5. return string();
  6. }
  7. map<char,int> dic,cur_dic;
  8. for(const char& c:t){
  9. dic[c]+=;
  10. }
  11. int le=,ri=,min_len=INT_MAX,cnt=,t_len=t.size();
  12. string res="";
  13. while(ri<=s.size()){
  14. // cout<<le<<" "<<ri<<" "<<s.substr(le,ri-le)<<" "<<cnt<<endl;
  15. char c=s[ri];
  16. ++ri;
  17. if(dic.count(c)){ //匹配
  18. cur_dic[c]+=;
  19. if(dic[c]>=cur_dic[c]){
  20. ++cnt;
  21. }
  22. while(){ //有重复匹配的,一直右移左边界
  23. while(dic.count(s[le])==){ //跳过开头多余字符
  24. ++le;
  25. }
  26. if(dic[s[le]]<cur_dic[s[le]]){
  27. cur_dic[s[le]]-=;
  28. ++le;
  29. }
  30. else{
  31. break;
  32. }
  33. }
  34. while(dic.count(s[le])==){
  35. ++le;
  36. }
  37. if(cnt==t_len and ri-le<min_len){
  38. cout<<s.substr(le,ri-le)<<endl;
  39. min_len=ri-le;
  40. res=s.substr(le,ri-le);
  41. }
  42. }
  43. else{ //不匹配
  44. ;
  45. }
  46. }
  47. return res;
  48. }
  49. };

可以看到两种解法时间并不算快,因为是遍历了s的全部字符。

159. 至多包含两个不同字符的最长子串

  1. class Solution {
  2. public:
  3. int lengthOfLongestSubstringTwoDistinct(string s) {
  4. map<char,int>dic;
  5. int le=,ri=,cnt=,s_len=s.size(),max_len=;
  6. while(ri<s_len){
  7. if(cnt==){
  8. if(dic.count(s[ri])){ //是之前的两个字符之一,直接添加
  9. dic[s[ri]]++;
  10. ++ri;
  11. }
  12. else{ //不属于之前的两个字符
  13. while(dic[s[le]]>){ //找最左侧元素出现次数为1的
  14. dic[s[le]]-=;
  15. ++le;
  16. }
  17. dic.erase(s[le++]); //把这个出现次数为1的删了
  18. dic[s[ri]]++;
  19. ++ri;
  20. }
  21. }
  22. else{ //cnt=0 or 1
  23. if(dic.count(s[ri])==){
  24. ++cnt;
  25. }
  26. dic[s[ri++]]+=;
  27. }
  28. max_len=max(max_len,ri-le);
  29. }
  30. return max_len;
  31. }
  32. };

209. 长度最小的子数组

这题不解释了阿,前面会了就会。

A:

  1. class Solution {
  2. public:
  3. int minSubArrayLen(int s, vector<int>& nums) {
  4. int le=,ri=,len=nums.size(),min_len=INT_MAX,cur_sum=;
  5. while(ri<len){
  6. cur_sum+=nums[ri++];
  7. if(cur_sum>=s){
  8. while(cur_sum-nums[le]>=s){
  9. cur_sum-=nums[le];
  10. ++le;
  11. }
  12. min_len=min(min_len,ri-le);
  13. }
  14. }
  15. return min_len!=INT_MAX?min_len:;
  16. }
  17. };

239. 滑动窗口最大值

这题没做出来,用的是一个新的东西:双端队列。
我先说一下我做时候的想法:由于这题的窗口是固定长度,每次要找窗口内的最大值。那最naive的方法就是维护长度k的窗口,遍历(n-k)*k次就行,复杂度O(nk)。然后我又考虑可以用堆(c++优先队列)维护窗口,这样复杂度可以减少为O(nlogk)。但问题是假如前一个窗口的最大值是首元素,窗口后移一位后,怎么把之前最大值删掉。更新:这里可以用索引堆,专门去研究了一下索引堆怎么写。 但这个写法就不写了。
下面只放一个双端队列的做法:
思路是维护一个双端队列(两边都可以push和pop的队列),其大小为k。然后关键要始终维持的性质是该队列为非递增序,即x1>=x2>=x3>=…>=xk。当然队列不存储值,而是存储对应nums数组中的下标。然后每次窗口右移一位,每次要考察队首(队列左侧)的值是否已经越界。即当前窗口右侧到达i位置,那么最左侧位置不能小于i+1-k,否则整个窗口长度就超过k了,这就不符题意了。而且每次滑动窗口只需要检验最左侧,原因有二:1. 我们push索引到窗口里的时候是从左到右遍历nums来的,故只要最左侧不越界,窗口其它位置一定不越界。2. 由于滑动窗口一次只挪一位。
上面一段写成代码就是:

  1. if(!window.empty() and window.front()<i-k+1){
  2. window.pop_front();
  3. }

考察最左侧索引后,剩下的工作就是push新移进窗口的右边界(新的边界)。
同最开始建立初始窗口一样,我们要保持非增序。小于右边界的都pop,最后把右边界push进来,就完成了一轮窗口的移动。对应的代码如下:

  1. while(!window.empty() and nums[window.back()]<nums[i]){
  2. window.pop_back();
  3. }
  4. window.push_back(i);

这样完成一轮移动后,窗口内最大的元素就是左边界作为索引p对应的nums中的元素nums[p]。
下面我简单证明一下这个算法的有效性。
1.对于任意长度k的子数组,我们对左边界是否越界的考察保证了当前窗口不会包含比该长度k子数组更多的元素。
2.既然我们的窗口包含元素比实际长度k的子数组更少,那有没有可能会略掉某个该子数组中的最大值没有统计呢?假设实际子数组是i到i+k-1,那么上次的窗口遍历的是i-1到i+k-2的子数组,并假设上次的窗口window1中的元素都满足不越界(相当于数学归纳法,假设i-1的情况成立,只要推得i的情况成立,并且i==1时情况成立结论就成立,而这题我们第一轮初始的窗口显然是正确的)。1. 假设上次的窗口是window1。若window1包含i-1,则当前这轮遍历开始时,会考察左边界是否越界,此时会把i-1剔除掉,再push进来i+k-1号。故这种情况当前窗口会输出正确结果。
2. 假设上次的窗口window1不包含i-1,那么显然这轮遍历开始时,左边界肯定不会越界,因为左边界最多也就是i,i本来就应该在当前窗口内的。故这种情况也会输出正确结果。

  1. class Solution {
  2. public:
  3. vector<int> maxSlidingWindow(vector<int>& nums, int k) {
  4. if(k< or nums.empty()){
  5. return vector<int>();
  6. }
  7. deque<int> window;
  8. int siz=nums.size();
  9. vector<int> res;
  10. //初始化双端队列
  11. for(int i=;i<k;++i){
  12. while(!window.empty() and nums[window.back()]<nums[i]){
  13. window.pop_back();
  14. }
  15. window.push_back(i);
  16. }
  17. res.push_back(nums[window.front()]);
  18. for(int i=k;i<siz;++i){
  19. if(!window.empty() and window.front()<i-k+){
  20. window.pop_front();
  21. }
  22. while(!window.empty() and nums[window.back()]<nums[i]){
  23. window.pop_back();
  24. }
  25. window.push_back(i);
  26. res.push_back(nums[window.front()]);
  27. }
  28. return res;
  29. }
  30. };

567. 字符串的排列

这题其实只要前面题都做了,就一模一样的解法套就完事了。然后我看评论中很多用vector代替map来做字符记录的。这种做法是利用char对应ascii码0到128的特点,开一个128大小的vector来模拟map,比如当前有一个字母a(ascii好像是97吧),那就把vector[97]+1,到时候查询字母a的频数也直接来查vector[97]就好了。滑窗还是正常的滑,并且这题还是固定大小的窗口,所以比较简单。用map会慢一些,因为首先map查询O(logn),比不上数组下标的O(1)。另外遍历到非s1中字符的时候,我的做法将当前记录的map清零,这个可能也比较费时。用vector模拟map的解法我偷懒没写,想看的就点进评论区看看吧。。。

  1. class Solution {
  2. public:
  3. bool checkInclusion(string s1, string s2) {
  4. if(s2.size()<s1.size()){
  5. return false;
  6. }
  7. int s2_siz=s2.size(),s1_siz=s1.size();
  8. int le=,ri=,cnt=;
  9. map<char,int> dic;
  10. for(char c:s1){
  11. dic[c]+=;
  12. }
  13. map<char,int>cur_dic;
  14. while(ri<s2_siz){
  15. char c=s2[ri++];
  16. if(dic.count(c)){ //匹配
  17. ++cnt;
  18. cur_dic[c]++;
  19. while(dic[c]<cur_dic[c]){
  20. cur_dic[s2[le]]--;
  21. ++le;
  22. --cnt;
  23. }
  24. if(cnt==s1_siz){
  25. return true;
  26. }
  27. }
  28. else{ //不匹配
  29. cnt=;
  30. cur_dic.clear();
  31. le=ri;
  32. }
  33. }
  34. return false;
  35. }
  36. };

727. 最小窗口子序列

这题我一开始是这样思考的:要找S的最短子串满足T中字符按序存在于该子串内。那显然就不能像之前的题目那样用map了,因为此题多了出现顺序的要求。那么就先找第一个符合条件的子串再看看怎么继续下去。假如目前找到了0开始到i结束的子串包含T的子序列,那么从0开始的最短子串就是这个了。那下面把左边界右移一位再从1开始找?这样的复杂度不就是n^2了吗。然后我就卡在这了。
憋了半天还是去看评论区了,大致看到了两种解法:
第一种:评论链接和我的基本思路是一致的,但作者用了一种特别的优化。关键思路:如果当前我们找到了一个满足条件的子串,索引i到j,即i处对应T[0],j处对应T[-1]。
那么在该子串中满足条件的最短子串就是从右到左倒序依次查找T[-1],T[-2],T[-2]…到T[0]的子串(因为这个子串肯定不比j-i+1长,所以是最短的没毛病)。咋证明呢,很简单:对于i到j的串,因为最后一个元素是T[-1],而且这个元素只出现了这一次,那么最终的子串必须包含它。所以我们从末尾倒着往前查找T中的字符,最终找到的子串就一定是当前i到j串中满足条件的最短子串。比较遗憾的是,作者的代码格式不是很清晰,故我又自己写了一份在下面,思路完全参照作者。这个时间应该是最优O(n),最差O(n^2),比如S是100个字符a,T是aa,每次循环相当于只移动了一位。

  1. class Solution {
  2. public:
  3. string minWindow(string S, string T) {
  4. //找S的最短子串w,w中要包含T中的全部字符,并按顺序出现。
  5. if(S.size()<T.size()){
  6. return "";
  7. }
  8. int s_size=S.size(),t_size=T.size(),le=,ri=,cnt=,_start=,_end=INT32_MAX;
  9. while(ri<s_size){
  10. if(S[ri]==T[cnt]){//匹配
  11. ++cnt;
  12. if(cnt==t_size){//找到符合条件的子串[le,ri]
  13. le=ri;
  14. while(cnt){//从ri倒序查找T[-1],T[-2]..到T[0]
  15. if(S[le]==T[cnt-]){
  16. --cnt;
  17. }
  18. --le;
  19. }
  20. //此时cnt==0,S[le+1]==T[0]
  21. ++le;
  22. if(ri-le+<_end-_start){
  23. _start=le;
  24. _end=ri+;
  25. }
  26. ++le;
  27. ri=le-;
  28. }
  29. ++ri;
  30. }
  31. else{
  32. ++ri;
  33. }
  34. }
  35. return _end==INT32_MAX?"":S.substr(_start,_end-_start);
  36. }
  37. };


然后我又看到一种动态规划解法,tql,没想到。
思路是二维dp,dp[i][j]存储T截止到j的字符串t1,S截止到i包含t1子序列的左边界索引,这个时间是稳定的O(n^2)。

  1. class Solution {
  2. public:
  3. string minWindow(string S, string T) {
  4. if(S.size()<T.size()){
  5. return "";
  6. }
  7. int s_size=S.size(),t_size=T.size();
  8. vector<vector<int>>dp(t_size,vector<int>(s_size,-));
  9. int le=,ri=INT32_MAX;
  10. //dp[i][j]存储S截止j的字符串s1,T截止i的字符串t1,s1包含t1的子序列的左索引
  11. if(S[]==T[]){
  12. dp[][]=;
  13. }
  14. for(int i=;i<s_size;++i){
  15. if(S[i]==T[]){
  16. dp[][i]=i;
  17. }
  18. else if(dp[][i-]!=-){
  19. dp[][i]=dp[][i-];
  20. }
  21. }
  22. for(int i=;i<t_size;++i){
  23. for(int j=i;j<s_size;++j){
  24. if(S[j]==T[i]){
  25. dp[i][j]=dp[i-][j-];
  26. }
  27. else{
  28. dp[i][j]=dp[i][j-];
  29. }
  30. }
  31. }
  32. // for(auto row:dp){
  33. // for(auto col:row){
  34. // cout<<col<<" ";
  35. // }cout<<endl;
  36. // }
  37. for(int i=t_size;i<s_size;++i){
  38. if(dp[t_size-][i]!=- and i-dp[t_size-][i]<ri-le){
  39. le=dp[t_size-][i];
  40. ri=i;
  41. }
  42. }
  43. return ri==INT32_MAX?"":S.substr(le,ri-le+);
  44. }
  45. };


完结撒花!2019年11月17日 00:46:57

leetcode全部滑动窗口题目总结C++写法(完结)的更多相关文章

  1. Leetcode 480.滑动窗口中位数

    滑动窗口中位数 中位数是有序序列最中间的那个数.如果序列的大小是偶数,则没有最中间的数:此时中位数是最中间的两个数的平均数. 例如: [2,3,4],中位数是 3 [2,3],中位数是 (2 + 3) ...

  2. leetcode 239. 滑动窗口最大值(python)

    1. 题目描述 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧.你只可以看到在滑动窗口内的 k 个数字.滑动窗口每次只向右移动一位. 返回滑动窗口中的最大值. 示 ...

  3. LeetCode(239.滑动窗口的最大值

    题目: 给定一个数组nums,有一个大小为k的滑动窗口从数组的最左侧移动到最右侧,你只可以看到滑动窗口内的k个数字.滑动窗口每次只向右移动一位. 返回滑动窗口中的最大值. 示例: 输入: nums = ...

  4. 【leetcode 239. 滑动窗口最大值】解题报告

    思路:滑动窗口的思想,只要是求连续子序列或者子串问题,都可用滑动窗口的思想 方法一: vector<int> maxSlidingWindow(vector<int>& ...

  5. Leetcode 239.滑动窗口最大值

    滑动窗口最大值 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧.你只可以看到在滑动窗口 k 内的数字.滑动窗口每次只向右移动一位. 返回滑动窗口最大值. 示例: ...

  6. Java实现 LeetCode 480 滑动窗口中位数

    480. 滑动窗口中位数 中位数是有序序列最中间的那个数.如果序列的大小是偶数,则没有最中间的数:此时中位数是最中间的两个数的平均数. 例如: [2,3,4],中位数是 3 [2,3],中位数是 (2 ...

  7. Java实现 LeetCode 239 滑动窗口最大值

    239. 滑动窗口最大值 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧.你只可以看到在滑动窗口内的 k 个数字.滑动窗口每次只向右移动一位. 返回滑动窗口中的最 ...

  8. P1886 滑动窗口(单调队列)

    P1886 滑动窗口 题目描述 现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口.现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值. 例如: ...

  9. P1886 滑动窗口&&P1440 求m区间内的最小值

    声明:下面这两个题就不要暴力了,学一学单调队列吧 推荐博文:https://www.cnblogs.com/tham/p/8038828.html 单调队列入门题 P1440 求m区间内的最小值 题目 ...

随机推荐

  1. Hibernate注释

    Hibernate注释映射一.PO类的基本注释1.@Entity:将pojo类标记成实体,可以指定一个name属性,指定实体类的名称.默认一该类的类名作为实体类的名称 2.@Table:注释改持久化类 ...

  2. 04_TypeScript类

    1.类的定义 //ts定义类和ES6相似,不同的是属性需要修饰符并定义数据类型 class Person{ public name:string; constructor(n:string){ thi ...

  3. 关于牛客网C语言结构体位域(bit-fields)的一道题

    题目链接地址: https://www.nowcoder.com/questionTerminal/f4e20747a2dd4649bac0c028daa234f4 来源:牛客网 低地址字节 Byte ...

  4. HDU1163 - Eddy's digital Roots

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1163 九余数:一个数除于9所得到的余数,即模9得到的值 求九余数: 求出一个数的各位数字之和,如果是两 ...

  5. babel环境安装与编译

    babel:将浏览器不支持的ES6语法转为javascript 查看node是否安装: npm -v node -v 实例演示:在桌面新建part5目录在cmd命令行中 cd desktop cd p ...

  6. CTF长久练习平台

    0x01 XCTF(攻防世界) 攻防世界是ctf爱好者很喜欢的一个平台,不仅是界面风格像大型游戏闯关,里面的各类题目涵盖的ctf题型很广,还分为新手区和进阶区两块: 并且可以在里面组队,做一道题还有相 ...

  7. RabbitMQ的五种工作方式详细

    在了解之前得先有个RabbitMQ客户端.官网: https://www.rabbitmq.com/getstarted.html connections:无论生产者还是消费者,都需要与RabbitM ...

  8. 浅谈C#委托的用法-delegate[转]

    一.委托的概念 委托和类一样是一种用户自定义类型,它存储的就是一系列具有相同签名和返回类型的方法的地址,调用委托的时候,它所包含的所有方法都会被执行. 借用百度上的一句话概括:委托是一个类,它定义了方 ...

  9. 【笔记0-开篇】面试官系统精讲Java源码及大厂真题

    背景 开始阅读 Java 源码的契机,还是在第一年换工作的时候,被大厂的技术面虐的体无完肤,后来总结大厂的面试套路,发现很喜欢问 Java 底层实现,即 Java 源码,于是我花了半年时间,啃下了 J ...

  10. Java对MongoDB进行分组操作并统计各个分组的数量

    最近在检索MongoDB的数据时需要用到分组操作,由于没有现成的说明文档可参考,只能是在代码中不断调试.摸索前进:目前已现实了Java对MongoDB的分组操作,并统计各个分组的数量.现通过示例详细解 ...