怎么屏蔽优酷网站的广告一个完美的网站怎么做

张小明 2025/12/29 15:55:09
怎么屏蔽优酷网站的广告,一个完美的网站怎么做,湖北建站方案,艺商网站建议每道题目都只用时30分钟#xff0c;三十分钟一过#xff0c;直接开始看答案。不然太浪费时间#xff0c;很没有必要 #xff08;虽然本人还是每道题目都请趋向于自己做#xff0c;有思路自己做#xff0c;没思路看题解思路再自己做#xff0c;当然最后会学习题解中…建议每道题目都只用时30分钟三十分钟一过直接开始看答案。不然太浪费时间很没有必要虽然本人还是每道题目都请趋向于自己做有思路自己做没思路看题解思路再自己做当然最后会学习题解中的解法49字母异位词分组解法class Solution { public ListListString groupAnagrams(String[] strs) { ListListString arrsnew ArrayList(); int[]signalnew int[strs.length]; for (int i0;istrs.length;i){ if(signal[i]1){ continue; } int l1strs[i].length(); ListString arrnew ArrayList(); arr.add(strs[i]); char[] c1strs[i].toCharArray(); Arrays.sort(c1); for(int ji1;jstrs.length;j){ int l2strs[j].length(); if(l1!l2) continue; char[] c2strs[j].toCharArray(); Arrays.sort(c2); if(Arrays.equals(c1,c2)){ arr.add(strs[j]); signal[j]1; } } arrs.add(arr); } return arrs; } }其中用到的我不是很熟悉的知识增强for的用法public ListListString groupAnagrams(String[] strs) { for(String str : strs){ }字符串的遍历①字符串的遍历 // 方法一:使用charAt(i)遍历字符串的每个元素每个元素是字符的形式 for(int i0;i str.length();i) { System.out.println(str.charAt(i)); } //方法二截取字符串中的每个字符 for(int i0;i str.length();i) { System.out.println(str.substring(i,i1)); } //方法三将字符串转变成字符数组 char[] c str.toCharArray(); for(int i0;i c.length;i) { System.out.println(c[i]); } ②字符串元素的取代 str.replace(A,B);取代字符串中的为A的元素换成B。 str.replaceAll(A,B);取代str所有的字符串中有A的元素换成B。128最长连续序列解法下面是解法只在存在的连续序列的最大数才查询此连续序列的个数别的直接跳过。比如1234这一组分散在数组各个位置的数字只在4的位置查询个数在123的位置的时候都直接跳过。如何实现这一点呢遍历数组遍历到那个数据假如是x的时候检查x1是否存在如果存在说明如果他是一个比较长的序列的一员的话他不是最大的我们直接跳过它因为我们只在序列最大值的时候去查询连续序列的个数。如果x1不存在说明其就是最大的进行查询。、如何查询数组中连续序列的个数呢前面我们确定了一个连续序列的最大值那么我们只需要确定x-1是否存在就可以了然后计数循环直到x-1不存在。为什么只在最大的数字进行查询呢?序列中的任意一个数字我们对他进行左右两边的查询都可以找到这个序列的长度但是会做很多无用功这些无用功会让时间复杂度涨到O(n2),就不满足题目要求了。class Solution { public int longestConsecutive(int[] nums) { SetInteger set new HashSet(); for (int num : nums) { set.add(num); } int longestNum0; for (int num : set) { if (!set.contains(num 1)) { int currentNum1; int currentnum; while (set.contains(current-1)){ currentcurrent-1; currentNum1; } if (currentNumlongestNum){ longestNumcurrentNum; } } } return longestNum; } }283移动零解法双指针法这个解法跟值得借鉴我目前还没有直接写出这种档次的算法的能力看的出来这种解法还挺难的。class Solution { public void moveZeroes(int[] nums) { int nnums.length; int left0; int right0; while(rightn){ if (nums[right]!0) { int tmp nums[left]; nums[left] nums[right]; nums[right] tmp; left; } right; } } }11盛最多水的容器解法双指针法左右两边各一个指针每次选择左右两边的指针中较小的那一个左移或右移在移动两者中较小的那一个之前这两个构成的面积肯定是比那个较小的值和别的值相乘更大的宽乘高宽最大高选的是两者中间较小的那一个。这就是这个方法在算法层面的优势。我之前还用了一些别的方法时间复杂度都是On2,不是内存超限制就是运行时间超限制。后面我也将我的在力扣中点运行成功但点提交成功不了的代码一个内存超出一个运行时间超出的代码都附在本文中。正确代码class Solution { public int maxArea(int[] height) { int n height.length; int left0; int rightn-1; int max0; while(left!right){ int hMath.min(height[left],height[right]); int wright-left; int areah*w; if(areamax) maxarea; if (height[left]height[right]){ right--; }else{ left; } } return max; } }历史代码报错内存超限制的代码class Solution { public int maxArea(int[] height) { int nheight.length; int num(n-1)*n/2; int[] allSizenew int[num]; int poi0; for(int i0;in-1;i){ for(int ji1;jn-1;j){ int hMath.min(height[i],height[j]); int wj-i; allSize[poi]h*w; poi; } } int m0; for(int i0;inum;i){ if(allSize[i]m){ mallSize[i]; } } return m; } }报错超出时间限制的代码class Solution { public int maxArea(int[] height) { int nheight.length; int max0; int[] allSizenew int[n]; for(int in-1;i0;i--){ for(int ji1;jn-1;j){ int hMath.min(height[i],height[j]); int wj-i; allSize[j]h*w; } for(int p0;pn;p){ if(allSize[p]max){ maxallSize[p]; } } } return max; } }15三数之和解法这道题目用到双指针的方法双指针的思想会使得算法的时间复杂度降一个档次为了确保我真的理解了这个算法我专门和题解里的代码写的不一样但是思想是一样的。简单讲一下这道题我被卡住的点。我不太理解那个第二个指针和第三个指针移动的逻辑。123指针指到的数字我们分别记作a,b,c。从小到大排好序的a从左到右为最外层循环b从左到右为a的内层循环c从右到左为a的内层循环。首先外层a是定的其次b是定的我们看c当abc0的时候c--因为比c小的c说不定有可以令其为0的。当abc0的时候就记录。当abc0的时候b指针右移。注意全程b在c的左边。然后还有就是不能让bc也就是不能让b和c指向同一个数字因为可能会造成nums数组中只出现了一次但是在最后的结果中却出现了两次的情况。class Solution { public ListListInteger threeSum(int[] nums) { Arrays.sort(nums); int nnums.length; ListListIntegerarrsnew ArrayList(); for (int i 0; i n; i) { if (i0||nums[i]!nums[i-1]){ int thirdn-1; for (int j i1; j n; j) { if(ji1||nums[j]!nums[j-1]){ while(jthirdnums[i]nums[j]nums[third]0){ third--; } if(jthirdnums[i]nums[j]nums[third]0){ ListIntegerarrnew ArrayList(); Collections.addAll(arr,nums[i],nums[j],nums[third]); arrs.add(arr); } } } } } return arrs; }}历史错误的解法报错少全零的解法我在验证新加入的元素是否在集合中已经存在重复的时候选择使用HashSet的方式因为哈希表找到元素的时间复杂度最低但是这是没有必要的因为我们要验证的只有三个数字要是数目特别多的话倒是可以考虑这个方法当然还要再改进一下。我的这个验证的方法在这里是行不通的完全行不通的。比如例子中全零因为在前面的[-4,0,4]中存在0。所以后面全零直接就无法通过肯定是有很多例子都无法通过的。class Solution { public ListListInteger threeSum(int[] nums) { int nnums.length; ListListIntegerallThreenew ArrayList(); for(int i0;in-2;i){ for(int ji1;jn-1;j){ for(int kj1;kn;k){ int n1nums[i]; int n2nums[j]; int n3nums[k]; if(n1n2n30){ boolean flagfalse; for(int p0;pallThree.size();p){ SetIntegernum_setnew HashSetInteger(); num_set.add(allThree.get(p).get(0)); num_set.add(allThree.get(p).get(1)); num_set.add(allThree.get(p).get(2)); if(num_set.contains(n1)num_set.contains(n2)num_set.contains(n3)){ flagtrue; break; } } if(!flag){ ListIntegerthreenew ArrayList(); three.add(n1); three.add(n2); three.add(n3); allThree.add(three); } } } } } return allThree; } }报错超出时间限制的解法这一次优化了验证将要新加入集合的符合条件的集合的方法出现这个报错说明我的算法已经跑通了。出现这个报错的原因是我这个算法的时间复杂度是On3我知道这道题目要用双指针的方法来做但这是我唯一能想到的方法在磨了一个点20分钟后我准备开始看题解了。上一道双指针的方法的题目真的是让我不禁感叹算法的美妙。希望这次的也可以。class Solution { public ListListInteger threeSum(int[] nums) { int nnums.length; ListListIntegerallThreenew ArrayList(); for(int i0;in-2;i){ for(int ji1;jn-1;j){ for(int kj1;kn;k){ int n1nums[i]; int n2nums[j]; int n3nums[k]; int[]nn{n1,n2,n3}; if(n1n2n30){ boolean flagfalse; for(int p0;pallThree.size();p){ int count0; int[]f1new int[3]; int[]f2new int[3]; for(int q0;q3;q){ for(int x0;x3;x) if(allThree.get(p).get(q)nn[x]f1[q]0f2[x]0){ f1[q]1; f2[x]1; count; } } if(count3)flagtrue; } if(!flag){ ListIntegerthreenew ArrayList(); three.add(n1); three.add(n2); three.add(n3); allThree.add(three); } } } } } return allThree; } }42接雨水解法我的解法我的算法思想是两个指针一个指针遍历记作a一个指针寻找合适的值记作b。找a后面比a大的值找到了就算面积面积低*高-中间的黑色小方块。找不到的情况判断标准当b到最后n-1的时候也没有找到就说明a后面没有比他大的了操作找a后面最大的数字算面积方法和上面的一样。遍历a后面的数字找到最大的数字并记下他的下标然后算面积。并且让a指针直接指到下标的位置在代码中采用ipoi-1是因为循环中会i这就是我的解法。class Solution { public int trap(int[] height) { int nheight.length; int sum0; for(int i0;in;i){ boolean flagfalse; boolean fnullfalse; for(int ji1;jn;j){ if(height[j]height[i]){ int b0; for(int pi1;pj;p){ bbheight[p]; } int areaheight[i]*(j-i-1)-b; sumsumarea; ij-1; if(jn-1)flagtrue; break; } if(jn-1height[j]height[i]){ fnulltrue; } } if(fnull){ int imax0; int poi0; int b0; for(int ji1;jn;j){ if(height[j]imax||imax0height[j]0){ imaxheight[j]; poij; } } for(int pi1;ppoi;p){ bbheight[p]; } int areaheight[poi]*(poi-i-1)-b; sumsumarea; ipoi-1; } if(flagtrue)break; } return sum; } }历史解法现在还没开始写代码标着困难我现在稍有眉目。一直找找到两个数他们中间的数字都比他们都小并且后面的数字要大于前面的数字然后低乘高减去中间侵占的部分的就可以了具体可以是这样的遍历数组第二个指针寻找比第一个指针大的数字找到了就按上面说的算面积找不到就定位道剩下的里面最大的。报错结果偏大的解法这次错误我感觉是当第二个指针指到最后的时候没有及时清理掉。class Solution { public int trap(int[] height) { int nheight.length; int sum0; for(int i0;in;i){ boolean flagfalse; for(int ji1;jn;j){ if(height[j]height[i]){ int b0; for(int pi1;pj;p){ bbheight[p]; } int areaheight[i]*(j-i-1)-b; sumsumarea; break; } } } return sum; } }报错超出时间限制的方法出现这个报错说明~哎不对通过了4个案例那还有很大的改进空间。经过检查我发现是我的imax这个遇到全局最大的值在他后面找不道比他更大的值的时候寻找后面最大的数的临时变量我设为0是不合适的当最后一个数是0的时候就会死循环。我准备将其改为-1。后来发现不是这里的问题是我在这个位置将下标和数字搞混了。class Solution { public int trap(int[] height) { int nheight.length; int sum0; for(int i0;in;i){ boolean flagfalse; boolean fnullfalse; for(int ji1;jn;j){ if(height[j]height[i]){ int b0; for(int pi1;pj;p){ bbheight[p]; } int areaheight[i]*(j-i-1)-b; sumsumarea; ij-1; if(jn-1)flagtrue; break; } if(jn-1height[j]height[i]){ fnulltrue; } } if(fnull){ int imax0; int poi0; int b0; for(int ji1;jn;j){ if(height[j]imax){ imaxheight[j]; poij; } } for(int pi1;pimax;p){ bbheight[p]; } int areaheight[poi]*(poi-i-1)-b; sumsumarea; ipoi-1; } if(flagtrue)break; } return sum; } }3无重复字符的最长字串解法这道题用到了滑动窗口的方法,我的代码和官方给的稍有不同思想是一样的class Solution { public int lengthOfLongestSubstring(String s) { int ns.length(); int sum0; int j0; SetCharacterallnew HashSet(); for (int i 0; i n; i) { if(i!0){ all.remove(s.charAt(i-1)); } int tsumall.size(); while(jn!all.contains(s.charAt(j))){ all.add(s.charAt(j)); j; tsum; } sumMath.max(tsum,sum); } return sum; } }历史解法没写出来的一个解法这道题我真的没有思路浑浑噩噩地写了两个小时还是没有任何的头绪。class Solution { public int lengthOfLongestSubstring(String s) { int numMax0; int ns.length(); char []alls.toCharArray(); for(int i0;in;i){ boolean flagtrue; int ji1; int num1; ListCharacterchecknew ArrayList(); check.add(all[i]); while(flag){ for(int x0;xcheck.size();x){ if(jn){ if(check.get(x)all[j]){ if(numMaxnum)numMaxnum; num0; flagfalse; break; } check.add(all[j]); num; } } j; } if(!flag)continue; } return numMax; } }438找到字符串中所有字母异位词解法历史解法报错的例子没通过的历史解法我的这个解法是在判断s和p是否一样的这个地方搞错了。我用的是哈希表哈希表就无法通过这个p中有重复值的这种例子。然后在让AI帮我排错的时候不小心将看到答案了知道了另一种方法。class Solution { public ListInteger findAnagrams(String s, String p) { ListInteger result new ArrayList(); int np p.length(); int ns s.length(); boolean flag false; SetCharacter pc new HashSet(); SetCharacter pcc new HashSet(); for (int i 0; i np; i) { pc.add(p.charAt(i)); } for (int i 0; i np; i) { pcc.add(p.charAt(i)); } for (int i 0; i ns - np 1; i) { if(flag){ if(s.charAt(i-1)s.charAt(inp-1)){ result.add(i); }else { flagfalse; pc.add(s.charAt(i-1)); pc.add(s.charAt(i)); i; } }else{ int countpc.size(); while(count0){ count--; int nhpc.size(); if(pc.contains(s.charAt(inp-nh))){ pc.remove(s.charAt(inp-nh)); if(pc.size()0){ flagtrue; result.add(i); } }else{ if(!pcc.contains(s.charAt(inp-nh))){ iinp-nh; } } } } } return result; } }560和位K的子数组解法这道题我直接做出来了就两个循环遍历每次遍历都要找遍所有的class Solution { public int subarraySum(int[] nums, int k) { int sum0; int nnums.length; for(int i0;in;i){ int s0; int ji; while(jn){ ssnums[j]; j; if(sk){ sum; } } } return sum; } }239滑动窗口最大值解法解法1此解法在 历史解法\报错超出时间限制的解法2 的基础上做了一些改动。我观察发现上次的报错超出时间限制的那个例子是一个有很多重复数字的由大到小的一个递减的数列。当每次的上一组的最大值实在上一组的第一个的时候那这个时间复杂度就还是Onk我就在代码上将变为了,就解决了这个问题。同时我有另一个想法我准备再试一试。class Solution { public int[] maxSlidingWindow(int[] nums, int k) { int nnums.length; int[]resultnew int[n-k1]; int min0; for(int i0;in;i){ if(nums[i]min)minnums[i]; } int fmaxmin; int poi0; for(int j0;jk;j){ if(nums[j]fmax){ fmaxnums[j]; poij; } } result[0]fmax; int maxfmax; for(int i1;in-k1;i){ if(poii-1poiik-1){ if(nums[ik-1]max){ maxnums[ik-1]; poiik-1; } result[i]max; }else{ maxmin; for(int ji;jik;j){ if(nums[j]max){ maxnums[j]; poij; } result[i]max; } } } return result; } }历史解法报错超出时间限制的解法2这次我优化了比较的部分我发现当k比较大的时候当然当k比较小的时候也是成立的如果上一组的最大值不是在第一个位置那么后面那一组只需要拿最后一个数字和上一组最大的数字比较就可以了比如1231k3有两组第一组最大是3第二组只需要拿1和3比较一下就可以了 但还是报错确实有优化的空间我再优化一下。class Solution { public int[] maxSlidingWindow(int[] nums, int k) { int nnums.length; int[]resultnew int[n-k1]; int min0; for(int i0;in;i){ if(nums[i]min)minnums[i]; } int fmaxmin; int poi0; for(int j0;jk;j){ if(nums[j]fmax){ fmaxnums[j]; poij; } } result[0]fmax; int maxfmax; for(int i1;in-k1;i){ if(poii-1poiik-1){ if(nums[ik-1]max){ maxnums[ik-1]; poiik-1; } result[i]max; }else{ maxmin; for(int ji;jik;j){ if(nums[j]max){ maxnums[j]; poij; } result[i]max; } } } return result; } }报错超出最大运行时间的解法先找到所有的数中最小的这样就不会在找局部最大数的时候造成不好的影响了。遍历在局部中找到最大值。class Solution { public int[] maxSlidingWindow(int[] nums, int k) { int nnums.length; int[]resultnew int[n-k1]; int min0; for(int i0;in;i){ if(nums[i]min)minnums[i]; } for(int i0;in-k1;i){ int maxmin; for(int ji;jki;j){ if(nums[j]max)maxnums[j]; } result[i]max; } return result; } }76最小覆盖字串解法这道题目我是看的题解的思路做的。讲一下我加工过的思路两个map分别记录t和s的含t中字母的个数。检查操作如果在s的map中发现某个字母小于t的map说明不满足情况。如果不满足情况就让右指针右移并且添加到sc的map中如果满足情况左指针右移并减去sc的map中的相应的数量。这就是这道题目的思路。这道题目我做了三天最后在算法里l和r的指针的移动上也耽误了好久。终于结束了。class Solution { MapCharacter,Integerscnew HashMap(); MapCharacter,Integertcnew HashMap(); public String minWindow(String s, String t) { int l0; int r-1; int ansl0; int ansr-1; int sns.length(); int tnt.length(); int lensn; for(int i0;itn;i){ char ct.charAt(i); tc.put(c,tc.getOrDefault(c,0)1); } while(rsn){ if(!check()){ r; if(rsn){ char cs.charAt(r); if(tc.containsKey(c)){ sc.put(c,sc.getOrDefault(c,0)1); } } }else{ if(lenr-l1){ ansrr; ansll; lenr-l1; } char cs.charAt(l); if(tc.containsKey(c)){ sc.put(c,sc.getOrDefault(c,0)-1); } l; } } return ansr-1?:s.substring(ansl,ansr1); } public boolean check(){ Iterator ittc.entrySet().iterator(); while(it.hasNext()){ Map.Entry m(Map.Entry)it.next(); Character key(Character)m.getKey(); Integer value(Integer)m.getValue(); if(valuesc.getOrDefault(key,0)){ return false; } } return true; } }历史解法报错超出时间限制的解法思路将t变为数组52位分别对应大小写的26个字母前26是小写后26是大写。数值代表个数当数组清零后说明找到了一个匹配的子串。筛选判断.class Solution { public String minWindow(String s, String t) { //先小写后大写 int[]scnew int[52]; int[]tccnew int[52]; int[]zeroArrnew int[52]; StringBuilder sbnew StringBuilder(); String minStr; int sns.length(); int tnt.length(); //整理tc的特征数组 for(int i0;itn;i){ if(t.charAt(i)a){ tcc[t.charAt(i)-a]; }else{ tcc[t.charAt(i)-A26]; } } int minsn; for(int i0;isn-tn;i){ int ji; int []tctcc.clone(); while(jsn!Arrays.equals(zeroArr,tc)){ int poi; if(s.charAt(j)97){ pois.charAt(j)-a; }else{ pois.charAt(j)-A26; } if(tc[poi]!0){ tc[poi]--; } j; } if(Arrays.equals(zeroArr,tc)j-imin){ minj-i; minStrs.substring(i,j); } } return minStr; } }报错超出时间限制的解法2我在上次的基础上添加了一个指针pass。比图tabc,sfhghjabc这个例子中当i0第一次遍历寻找的时候显然前面5位永远都不可能有用用pass记录下来直接加到i上面这样就跳过了中间无用的部分省了好几步。但最后还是报错超出时间限制我想这是时间复杂度方面的问题。我得好好看看这些东西了。经过两分钟思考我想到了为什么还是报同样的错我的这个方法在这个错误上没有用处这个报错的例子t很长一定是包含了所有的52的大小写字母的所以我的pass会一直是0。所以我的方法确实被优化了但是对t很大的情况是没用的。class Solution { public String minWindow(String s, String t) { //先小写后大写 int[]scnew int[52]; int[]tccnew int[52]; int[]zeroArrnew int[52]; StringBuilder sbnew StringBuilder(); String minStr; int sns.length(); int tnt.length(); //整理tc的特征数组 for(int i0;itn;i){ if(t.charAt(i)a){ tcc[t.charAt(i)-a]; }else{ tcc[t.charAt(i)-A26]; } } int minsn; for(int i0;isn-tn;i){ int ji; int []tctcc.clone(); boolean f1false; //上一轮直到pass的位置都没有t中的字母 int pass0; while(jsn!Arrays.equals(zeroArr,tc)){ int poi; if(s.charAt(j)97){ pois.charAt(j)-a; }else{ pois.charAt(j)-A26; } if(tc[poi]!0){ tc[poi]--; f1true; } if(!f1){ pass; } if(Arrays.equals(zeroArr,tc)jsn-1){ f1false; iipass; } j; } if(Arrays.equals(zeroArr,tc)j-imin){ minj-i; minStrs.substring(i,j); } } return minStr; } }报错测试没通过但我发现问题的解法看了题解没看代码知道了题解的解题思路之后想自己实现这个地方但是一直报错刚刚我发现了我是错在哪里了。题解思路是左右指针当包含t中所有指针的时候左指针右移当不包含所有的时候右指针右移。代码就体现这个但我发现一直报错当不包含所有的时候右指针右移的时候可能会有里面有的数据我没有算导致一直没有得到正确的结果。这道题我纠结的时间太久了我决定不看了直接看答案。class Solution { public String minWindow(String s, String t) { //先小写后大写 int[]scnew int[52]; int[]tccnew int[52]; int[]zeroArrnew int[52]; StringBuilder sbnew StringBuilder(); String minStr; int sns.length(); int tnt.length(); SetCharactertchnew HashSet(); //整理tc的特征数组 for(int i0;itn;i){ tch.add(t.charAt(i)); if(t.charAt(i)a){ tcc[t.charAt(i)-a]; }else{ tcc[t.charAt(i)-A26]; } } int minsn; int a0; int b0; int[]tctcc.clone(); while(bsnab){ if(Arrays.equals(zeroArr,tc)){ if(b-amin){ minb-a; minStrs.substring(a,b); } if(tch.contains(s.charAt(a))){ int poi; if(s.charAt(a)97){ pois.charAt(a)-a; }else{ pois.charAt(a)-A26; } tc[poi]; } a; }else{ int poi; if(s.charAt(b)97){ pois.charAt(b)-a; }else{ pois.charAt(b)-A26; } if(tc[poi]!0){ tc[poi]--; } b; } } return minStr; } }看了答案的算法后仍然出错的解法class Solution { MapCharacter,Integertcnew HashMap(); MapCharacter,Integerscnew HashMap(); public String minWindow(String s, String t) { int sns.length(); int tnt.length(); int l0; int r0; int ansl0; int ansr0; int lensn; boolean flagfalse; for(int i0;itn;i){ char ct.charAt(i); tc.put(c,tc.getOrDefault(c,0)1); } while(rsn){ if(check()){ flagtrue; if(r-1len){ ansll; ansrr; lenr-l; } if(lsn)break; char ccs.charAt(l); if(sc.containsKey(cc)){ sc.put(cc,sc.getOrDefault(cc,0)-1); } l; }else{ if(rsn)break; char ccs.charAt(r); if(tc.containsKey(cc)){ sc.put(cc,sc.getOrDefault(cc,0)1); } r; } } if(flag){ String strs.substring(ansl,ansr); return str; }else{ String str; return str; } } public boolean check(){ IteratorMap.EntryCharacter,Integeritsc.entrySet().iterator(); while(it.hasNext()){ Map.EntryCharacter,Integermit.next(); Character key(Character)m.getKey(); Integer value(Integer)m.getValue(); if(valuetc.getOrDefault(key,0)){ return false; } } return true; } }class Solution { MapCharacter,Integertcnew HashMap(); MapCharacter,Integerscnew HashMap(); public String minWindow(String s, String t) { int sns.length(); int tnt.length(); int l0; int r0; int ansl0; int ansr0; int lensn; boolean flagfalse; for(int i0;itn;i){ char ct.charAt(i); tc.put(c,tc.getOrDefault(c,0)1); } while(rsn){ if(check()){ flagtrue; if(r-llen){ ansll; ansrr; lenr-l; } if(lsn){ char ccs.charAt(l); if(tc.containsKey(cc)){ sc.put(cc,sc.getOrDefault(cc,0)-1); } l; } }else{ char ccs.charAt(r); if(tc.containsKey(cc)){ sc.put(cc,sc.getOrDefault(cc,0)1); } r; } } if(flag){ String strs.substring(ansl,ansr); return str; }else{ String str; return str; } } public boolean check(){ IteratorMap.EntryCharacter,Integeritsc.entrySet().iterator(); while(it.hasNext()){ Map.EntryCharacter,Integermit.next(); Character key(Character)m.getKey(); Integer value(Integer)m.getValue(); if(valuetc.getOrDefault(key,0)){ return false; } } return true; } }53最大子数组和解法动态规划遍历数组当前若干位的和小于当前位的数的时候丢弃前面的和。因为你都比我小了我用你为什么不直接用我当前的这个呢。当最大值比新加入了当前位的数后的前若干位的和小的时候最大值变为加入了最新的数据后的前若干位的和class Solution { public int maxSubArray(int[] nums) { int nnums.length; int sum0; int maxsumnums[0]; for(int num:nums){ if(sumnumnum){ sumsumnum; }else{ sumnum; } if(summaxsum){ maxsumsum; } } return maxsum; } }贪心算法之前和小于0就丢弃class Solution { public int maxSubArray(int[] nums) { int nnums.length; int maxnums[0]; int sum0; for(int num:nums){ if(sum0){ sumnum; }else{ sumnum; } if(summax){ maxsum; } } return max; } }历史解法报错超出时间限制的解法我的算法是定义一个遍历指针ii的元素大于0的时候开始往后如果到他后面的若干位的和仍然大于零接着往下走,并且记录其中的最大值如果出现从i往后若干位的和小于零的情况开始从i。走到超出时间限制这个程度说明我的算法跑通了。只是他本身不太行。看了官方题解之后我发现我的算法是不完善的贪心算法我的算法如果用贪心算法一定不会报这个错误的。class Solution { public int maxSubArray(int[] nums) { int nnums.length; int smaxnums[0]; for(int i0;in;i){ if(nums[i]0){ if(nums[i]smax){ smaxnums[i]; } }else{ int ji; int tsumnums[j]; int tmsumnums[j]; j; while(jntsum0||ij-1jn){ if(jn){ tsumnums[j-1]; }else{ tsumtsumnums[j]; } if(tmsumtsum){ tmsumtsum; } j; if(smaxtmsum){ smaxtmsum; } } } } return smax; } }56合并区间解法先排序再遍历遍历到的元素的前项和列表中的最后一个元素的后项做比较大就往列表中添加一个新的小说明可以和前面那个连成一片更新列表中的最后一项的前项和后项。这是看了题解的做法做的。class Solution { public int[][] merge(int[][] intervals) { if (intervals.length 0) { return new int[0][]; } Arrays.sort(intervals, new Comparatorint[]() { Override public int compare(int[] o1, int[] o2) { return o1[0] - o2[0]; } }); Listint[] list new ArrayList(); for (int i 0; i intervals.length; i) { int l intervals[i][0]; int r intervals[i][1]; if (list.size() 0 || l list.get(list.size()-1)[1]) { list.add(new int[]{l, r}); } else { int l1 list.get(list.size() - 1)[0]; int r1 list.get(list.size() - 1)[1]; list.set(list.size() - 1, new int[]{Math.min(l, l1), Math.max(r1, r)}); } } return list.toArray(new int[list.size()][]); } }历史解法报错解答错误的历史解法我的这个方法我感觉很乱哪里少东西我补哪里就是感觉怪怪的。我准备直接看答案。不在这里纠结了。class Solution { public int[][] merge(int[][] intervals) { int nintervals.length; int[]endsnew int[n]; int eMintervals[0][1]; int sMintervals[0][0]; int[]startsnew int[n]; for(int i0;in;i){ ends[i]intervals[i][1]; if(eMintervals[i][1]){ eMintervals[i][1]; } } for(int i0;in;i){ starts[i]intervals[i][0]; if(sMintervals[i][0]){ sMintervals[i][0]; } } int leneM-sM; int []allnew int[len]; for(int i0;in;i){ int sintervals[i][0]; int eintervals[i][1]; while(se){ all[s-sM]1; s; } } int rlen0; int psM; while(peM){ if(all[p-sM]1){ rlen; while(prlenall[p-sM]1){ p; } }else{ while(all[p-sM]0){ p; } } } int [][]resultnew int[rlen][2]; int i0; int qsM; while(qeMirlen){ if(all[q-sM]1){ result[i][0]all[q-sM]; while(qrlenall[q-sM]1){ q; } }else{ result[i][1]all[q-sM]; while(qrlenall[q-sM]0){ q; } i; } } return result; } }189轮转数组解法这道题我用了四个半点的时间做出来。最开始找的那种方法不到一个点就做出来了后面的思路有些难以理清。从这道题目中我受益匪浅没有掌握的知识点有dowhile的用法还有找到两个数的最大公约数的方法。class Solution { public void rotate(int[] nums, int k) { int n nums.length; kk%n; int cgcd(n,k); for(int i0;ic;i){ int curri; int prenums[i]; do{ int next(currk)%n; int tmpnums[next]; nums[next]pre; pretmp; currnext; }while(curr!i); } } public int gcd(int x,int y){ return y0?gcd(y,x%y):x; } }找到两个数的最大公约数的方法代码如下之后可以直接定义这个方法然后直接用就可以后面有解释的地方可以帮助理解public int gcd(int x,int y){ return y0?gcd(y,x%y):x; }1. 算法历史与基本思想​欧几里得算法是历史上最古老的算法之一出现在欧几里得的《几何原本》公元前300年左右。它的目的是求两个整数的最大公约数。​最大公约数​能同时整除两个数的最大正整数。例如gcd(12, 18) 6因为6是能整除12和18的最大整数。2. 算法核心原理​关键定理​如果a b * q r其中q是商r是余数且0 ≤ r b那么gcd(a, b) gcd(b, r)​为什么成立​​设d gcd(a, b)那么d能整除a和b因为r a - b * q所以d也能整除r因此d是b和r的公约数同理任何能整除b和r的数也能整除a所以gcd(a, b) gcd(b, r)3. 数学推导示例​手动计算​gcd(48, 18)48 ÷ 18 2 余 12 → gcd(48, 18) gcd(18, 12) 18 ÷ 12 1 余 6 → gcd(18, 12) gcd(12, 6) 12 ÷ 6 2 余 0 → gcd(12, 6) 6另一个例子​gcd(1071, 462)1071 ÷ 462 2 余 147 → gcd(1071, 462) gcd(462, 147) 462 ÷ 147 3 余 21 → gcd(462, 147) gcd(147, 21) 147 ÷ 21 7 余 0 → gcd(147, 21) 21历史解法分别开辟了两个总长为n的空间的解法首先关于这道题的理解当kn的时候也就是题目描述中给出的那些情况将后半段和前半段分别存储到一个第三方空间中再分别复制到nums数组中。当kn的时候kk%n他是这么算的这个题目中也没有明说感觉要是这点他明说的话可以省去好多时间。class Solution { public void rotate(int[] nums, int k) { int nnums.length; if(n!1nk){ int []tmpnew int[k]; int[]tnew int[n-k]; for(int in-k;in;i){ tmp[i-nk]nums[i]; } for(int i0;in-k;i){ t[i]nums[i]; } for(int i0;ik;i){ nums[i]tmp[i]; } for(int i0;in-k;i){ nums[ik]t[i]; } }else{ kk%n; int []tmpnew int[k]; int[]tnew int[n-k]; for(int in-k;in;i){ tmp[i-nk]nums[i]; } for(int i0;in-k;i){ t[i]nums[i]; } for(int i0;ik;i){ nums[i]tmp[i]; } for(int i0;in-k;i){ nums[ik]t[i]; } } } }优化上面的那个方法我发现那个条件判断语句可以单独列在前面。class Solution { public void rotate(int[] nums, int k) { int nnums.length; if(n!1nk){ kk; }else{ kk%n; } int []tmpnew int[k]; int[]tnew int[n-k]; for(int in-k;in;i){ tmp[i-nk]nums[i]; } for(int i0;in-k;i){ t[i]nums[i]; } for(int i0;ik;i){ nums[i]tmp[i]; } for(int i0;in-k;i){ nums[ik]t[i]; } } }目标空间复杂度为O1但失败的方法这个方法我想通过每次直接找到一个数据的准确位置下一次直接找到被替换的数据的正确位置这样就可以直接找到所有值的正确位置了。但是最后我发现有一个问题我没有解决就是当满足一些条件比如n2k再比如n6,k2可能是当n可以整除k的时候会来回就更新那几个数字跑不到别的数字上面就搞得很难受。我觉得再有一些时间我肯定能解决这个问题但是这道题我已经看了两个半点了再看有些不划算了。所以我准备直接看答案。看这种最优解法。class Solution { public void rotate(int[] nums, int k) { int n nums.length; if (n ! 1 n k) { k k; } else { k k % n; } int count 0; int i 0; int tmp; int t nums[0]; if (n ! 2 * k) { while (count n) { if (n 2 k 2) { break; } int ln-k-1; if (i l) { tmp nums[i k]; nums[i k] t; t tmp; i i k; } else { tmp nums[i - n k]; nums[i - n k] t; t tmp; i i - n k; } count; } } else { for (int j 0; j k; j) { tmp nums[j]; nums[j] nums[j k]; nums[j k] tmp; } } } }对上面方法的优化我看题解看到了找下一个的一个好方法(ik)%n。这个方法可以直接少了很多条件判断的语句代码量少了一半效果还差不多但是那个陷入局部走不出来的方法还是没有解决。class Solution { public void rotate(int[] nums, int k) { int n nums.length; if (n ! 1 n k) { k k; } else { k k % n; } int count 0; int i 0; int tmp; int t nums[0]; if(n!2*k){ while (count n) { tmp nums[(i k)%n]; nums[(i k)%n] t; t tmp; i (i k)%n; count; } }else{ for (int j 0; j k; j) { tmp nums[j]; nums[j] nums[j k]; nums[j k] tmp; } } } }因为while()和do...while()的区别而报错的方法我看了题解但是一直做不出来我总是想如果我完全理解了题解中的方法那么我不要跟他的方法完全一样。class Solution { public void rotate(int[] nums, int k) { int n nums.length; kk%n; int cgcd(n,k); for(int i0;ic;i){ int curri; int prenums[i]; while(curr!i){ int next(currk)%n; int tmpnums[next]; nums[next]pre; pretmp; currnext; } } } public int gcd(int x,int y){ return y0?gcd(y,x%y):x; } }238除自身以外数组的乘积解法解法1构建左右两边的乘积数组l[0]1,r[n-1]1;然后 l[i]l[i-1]*nums[i-1]r[j]r[j1]*nums[j1]。class Solution { public int[] productExceptSelf(int[] nums) { int nnums.length; int[]lnew int[n]; int[]rnew int[n]; int[]resultnew int[n]; l[0]1; r[n-1]1; int i1; int jn-2; while(in){ l[i]l[i-1]*nums[i-1]; i; } while(j0){ r[j]r[j1]*nums[j1]; j--; } for(int p0;pn;p){ result[p]l[p]*r[p]; } return result; } }解法2空间复杂度为O(1)的解法将result数组输出答案的数组作为L数组先将l数组求出来然后定义一个变量r一直更新来代替r数组的功能。class Solution { public int[] productExceptSelf(int[] nums) { int nnums.length; int[]resultnew int[n]; int i1; result[0]1; while(in){ result[i]result[i-1]*nums[i-1]; i; } int jn-2; int rnums[n-1]; while(j0){ result[j]result[j]*r; rr*nums[j]; j--; } return result; } }历史解法报错超出预期时间的解法遍历数组当不是当前遍历的元素的时候找到其他元素的乘积。这种方法的时间复杂度是O(n2)不符合题意。class Solution { public int[] productExceptSelf(int[] nums) { int nnums.length; int[]resultnew int[n]; int[]mulsnew int[n-1]; for(int i0;in;i){ int mul1; for(int j0;jn;j){ if(j!i){ mulmul*nums[j]; } if(jn-1||in-1jn-2){ result[i]mul; } } } return result; } }41缺失的第一个正数解法题解中的置换法将数组中的每个元素都找到他应该在的位置比如1的位置应该在0,2的位置应该在1。然后再遍历数组位置上没有那个数字的就是缺少的最大正整数。class Solution { public int firstMissingPositive(int[] nums) { int nnums.length; for(int i0;in;i){ while(nums[i]0nums[i]nnums[i]!nums[nums[i]-1]){ int tmpnums[i]; nums[i]nums[tmp-1]; nums[tmp-1]tmp; } } for(int i0;in;i){ if(i1!nums[i]){ return i1; } } return n1; } }题解中的标记法题解中的标记法首先将数组中所有的负数还有0都置为n1因为最小出现的正整数只会是1~n或者n1的情况。然后再遍历数组将大于0的元素的值作为下标将下标的值置为负的这个值前面我们将本来为负的数全部变成了n1本来为负的数组就不会干扰到我们了。下面这张图片很好地诠释了这个方法。class Solution { public int firstMissingPositive(int[] nums) { int nnums.length; for(int i0;in;i){ if(nums[i]0)nums[i]n1; } for(int i0;in;i){ int tmpMath.abs(nums[i]); if(tmpn)nums[tmp-1]0-Math.abs(nums[tmp-1]); } for(int i0;in;i){ if(nums[i]0)return i1; } return n1; } }自己写的用哈希表的解法按理说这个方法不能通过的啊这种方法的空间复杂度是O(n但是官方给我通过了那我就没有办法了。这道题目我的思路是首先遍历数组找到最大值的同时将数组中的每个元素都放到哈希表中然后再从1开始找到最大值的所有自然数那个没有再哈希表中哪个就是我们要找的值。class Solution { public int firstMissingPositive(int[] nums) { HashSetIntegerhnew HashSet(); int nnums.length; int max0; for(int i0;in;i){ h.add(nums[i]); if(nums[i]max)maxnums[i]; } for(int i1;imax;i){ if(!h.contains(i))return i; } return max1; } }73矩阵置零解法先遍历一遍所有的数组定义一个行记录数组和一个列记录数组如果一个元素为0将他的行和列数组对应的值变为1再遍历数组将行和列数组中为1的地方变为0。题解中优化的解法1将第一列和第一行作为标记的行和列再定义两个变量分别标记第一行和第一列原来是否有0的标记2只记录第一列的第一列的第一个元素可以记录第一行中是否有0。这种方法感觉纯纯没事找事差这一点空间吗差这一个变量的空间吗class Solution { public void setZeroes(int[][] matrix) { int mlmatrix.length; int nlmatrix[0].length; int[]rownew int[ml]; int[]colnumnew int[nl]; for(int i0;iml;i){ for(int j0;jnl;j){ if(matrix[i][j]0){ row[i]1; colnum[j]1; } } } for(int i0;iml;i){ for(int j0;jnl;j){ if(row[i]1){ matrix[i][j]0; } if(colnum[j]1){ matrix[i][j]0; } } } } }历史解法这道题有点简单并且题解的做法也还好不难想到一下子就做出来了。54螺旋矩阵解法直接将四个边界定义出来然后 不断缩小这四个边界的范围就可以得到正确的答案了。class Solution { public ListInteger spiralOrder(int[][] matrix) { ListIntegerresultnew ArrayList(); int mmatrix.length; int nmatrix[0].length; int top0;int bottonm-1; int left0;int rightn-1; while(topbottonleftright){ for(int ileft;iright;i){ result.add(matrix[top][i]); } top; for(int itop;ibotton;i){ result.add(matrix[i][right]); } right--; if(topbotton){ for(int iright;ileft;i--){ result.add(matrix[botton][i]); } botton--; } if(leftright){ for(int ibotton;itop;i--){ result.add(matrix[i][left]); } left; } } return result; } }历史解法报错边界问题的解法思路右下左上这个顺序依次遍历但是这个边界的问题我一直找不清楚。class Solution { public ListInteger spiralOrder(int[][] matrix) { ListIntegerresultnew ArrayList(); int mmatrix.length; int nmatrix[0].length; boolean [][]flagnew boolean[m][n]; int r0; int c0; int pre1; int count0; int pr1; int pun2; int pl3; int pup4; while(countm*n){ if(pre1cn-1flag[r][c1]false||pre4flag[r-1][c]){ flag[r][c]true; result.add(matrix[r][c]); c; count; }else if(pre2rm-1!flag[r1][c]||pre1(flag[r][c1]||cn-1)){ flag[r][c]true; result.add(matrix[r][c]); r; count; }else if(pre3c0!flag[r][c-1]||pre2(flag[r1][c]rm-1)){ flag[r][c]true; result.add(matrix[r][c]); c--; count; }else if(pre4r0!flag[r-1][c]||pre3(flag[r][c-1]||c0)){ flag[r][c]true; result.add(matrix[r][c]); r--; count; }else{ result.add(matrix[r][c]); count; } } return result; } }48旋转图像解法通过观察我发现旋转后的矩阵和旋转前的矩阵的是前x,y——后y,n-1-x。copy一个矩阵然后在原来的矩阵上按着这个规律改就可以了。class Solution { public void rotate(int[][] matrix) { int nmatrix.length; int[][]copynew int[n][n]; for(int i0;in;i){ for(int j0;jn;j){ copy[i][j]matrix[i][j]; } } for(int i0;in;i){ for(int j0;jn;j){ matrix[j][n-1-i]copy[i][j]; } } } }160相交链表题目给你两个单链表的头节点headA和headB请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点返回null。图示两个链表在节点c1开始相交题目数据保证整个链式结构中不存在环。注意函数返回结果后链表必须保持其原始结构。自定义评测评测系统的输入如下你设计的程序不适用此输入intersectVal- 相交的起始节点的值。如果不存在相交节点这一值为0listA- 第一个链表listB- 第二个链表skipA- 在listA中从头节点开始跳到交叉节点的节点数skipB- 在listB中从头节点开始跳到交叉节点的节点数评测系统将根据这些输入创建链式数据结构并将两个头节点headA和headB传递给你的程序。如果程序能够正确返回相交节点那么你的解决方案将被视作正确答案。示例 1输入intersectVal 8, listA [4,1,8,4,5], listB [5,6,1,8,4,5], skipA 2, skipB 3输出Intersected at 8解释相交节点的值为 8 注意如果两个链表相交则不能为 0。从各自的表头开始算起链表 A 为 [4,1,8,4,5]链表 B 为 [5,6,1,8,4,5]。在 A 中相交节点前有 2 个节点在 B 中相交节点前有 3 个节点。— 请注意相交节点的值不为 1因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说它们在内存中指向两个不同的位置而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点B 中第四个节点) 在内存中指向相同的位置。示例 2输入intersectVal 2, listA [1,9,1,2,4], listB [3,2,4], skipA 3, skipB 1输出Intersected at 2解释相交节点的值为 2 注意如果两个链表相交则不能为 0。从各自的表头开始算起链表 A 为 [1,9,1,2,4]链表 B 为 [3,2,4]。在 A 中相交节点前有 3 个节点在 B 中相交节点前有 1 个节点。示例 3输入intersectVal 0, listA [2,6,4], listB [1,5], skipA 3, skipB 2输出No intersection解释从各自的表头开始算起链表 A 为 [2,6,4]链表 B 为 [1,5]。由于这两个链表不相交所以 intersectVal 必须为 0而 skipA 和 skipB 可以是任意值。这两个链表不相交因此返回 null 。提示listA中节点数目为mlistB中节点数目为n1 m, n 3 * 1041 Node.val 1050 skipA m0 skipB n如果listA和listB没有交点intersectVal为0如果listA和listB有交点intersectVal listA[skipA] listB[skipB]解法两个指针会在第二遍遍历的时候相遇。/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { * val x; * next null; * } * } */ public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { if(headAnull||headBnull){ return null; } ListNode aheadA,bheadB; while(a!b){ aanull? headB : a.next; bbnull? headA : b.next; } return a; } }206反转链表解法在原链表的基础上将链表反转的解法不创建新的节点直接在原有的基础上反转链表我画了一张表。这张表清晰地诠释了这个算法的思想。希望可以帮到你/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * ListNode(int val, ListNode next) { this.val val; this.next next; } * } */ class Solution { public ListNode reverseList(ListNode head) { ListNode prenull; ListNode currhead; while(curr!null){ ListNode nextcurr.next; curr.nextpre; precurr; currnext; } return pre; } }遍历取出所有元素再重新创建新的链表的解法先遍历链表将所有的值都收集到一个List集合之中然后再遍历数组将一个一个创建ListNode对象然后跟在结果链表的后面就可以了。/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * ListNode(int val, ListNode next) { this.val val; this.next next; } * } */ class Solution { public ListNode reverseList(ListNode head) { if(headnull){ return null; } ListNode resultnew ListNode(); ListNode phead; ListIntegerlnew ArrayList(); int n0; while(p!null){ l.add(p.val); n; pp.next; } if(n0){ ListNode qresult; n--; q.vall.get(n--); while(n0){ ListNode tmpnew ListNode(); tmp.vall.get(n--); q.nexttmp; qtmp; }} return result; } }234回文链表解法将链表复制到list列表中再列表判断的方法先将链表复制到list列表中再两头挤如果有不一样的返回false。否则返回true感觉这道题肯定有更好的方法来做但是我感觉我的这个方法已经够了并且这个方法很容易就想到了所以这道题没什么看的。/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * ListNode(int val, ListNode next) { this.val val; this.next next; } * } */ class Solution { public boolean isPalindrome(ListNode head) { ListNode phead; ListIntegerlinew ArrayList(); while(p!null){ li.add(p.val); pp.next; } int nli.size(); int f0; int rn-1; while(fr){ if(li.get(f)!li.get(r--)){ return false; } } return true; } }141环形链表解法题解中快慢指针的方法定义一个快指针一个慢指针快指针指向head慢指针指向head.next快指针一次移动两个位置慢指着一次移动一个位置这样如果存在环快指针就一定会跟上慢指针。/** * Definition for singly-linked list. * class ListNode { * int val; * ListNode next; * ListNode(int x) { * val x; * next null; * } * } */ public class Solution { public boolean hasCycle(ListNode head) { if(headnull||head.nextnull)return false; ListNode phead; ListNode qhead.next; while(p!nullq!null){ if(pq)return true; pp.next; if(q.next!null){ qq.next.next; }else{ return false; } } return false; } }题解中用哈希表判断元素是否已经访问过的方法遍历链表访问一个元素往哈希表里面加一个元素同时判断这个元素是否访问过(add()这个方法本来就有判断元素是否添加成功的功能。/** * Definition for singly-linked list. * class ListNode { * int val; * ListNode next; * ListNode(int x) { * val x; * next null; * } * } */ public class Solution { public boolean hasCycle(ListNode head) { SetListNodeseennew HashSet(); ListNode pnew ListNode(); phead; while(p!null){ if(!seen.add(p)){ return true; } headp.next; } return false; } }利用链表数目的上限判断利用链表的数目上限观察发现链表最多只有10000个元素如果所以比10000大的数值就可以作为一个判断标准然后加以判断但是我觉得这个方法肯定不是正规的方法应该有更好的方法。/** * Definition for singly-linked list. * class ListNode { * int val; * ListNode next; * ListNode(int x) { * val x; * next null; * } * } */ public class Solution { public boolean hasCycle(ListNode head) { int count0; ListNode pnew ListNode(); phead; while(count10500){ if(pnull)return false; pp.next; count; if(count10400){ return true; } } return false; } }21.合并两个有序链表解法定义两个指针如果哪个指针指的值小将哪个指针指的值放到新的链表中去。看了一眼题解有一个更好的方法但是我没耐心看了。/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * ListNode(int val, ListNode next) { this.val val; this.next next; } * } */ class Solution { public ListNode mergeTwoLists(ListNode list1, ListNode list2) { ListNode plist1; ListNode qlist2; ListNode rnew ListNode(); ListNode resultnew ListNode(); if(pnull){ return q; } if(qnull){ return p; } if(p!nullq!null){ if(p.valq.val){ result.valp.val; pp.next; }else{ result.valq.val; qq.next; } } rresult; while(p!null||q!null){ ListNode tmpnew ListNode(); if(p!nullq!null){ if(p.valq.val){ tmp.valp.val; pp.next; result.nexttmp; resulttmp; }else{ tmp.valq.val; qq.next; result.nexttmp; resulttmp; } }else if(pnull){ tmpq; result.nexttmp; resulttmp; qq.next; }else if(qnull){ tmpp; result.nexttmp; resulttmp; pp.next; } } return r; } }2两数相加解法题解中的方法看了题解中的方法我发现我写代码太乱了一点都不简便。题解中的代码的思想和我一摸一样但是代码中的逻辑是非常明显的这段代码有很多值得借鉴值得学习的地方。/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * ListNode(int val, ListNode next) { this.val val; this.next next; } * } */ class Solution { public ListNode addTwoNumbers(ListNode l1, ListNode l2) { ListNode headnull,tailnull; int carry0; while(l1!null||l2!null){ int n1l1null?0:l1.val; int n2l2null?0:l2.val; int sumn1n2carry; if(headnull){ headtailnew ListNode(sum%10); }else{ tail.nextnew ListNode(sum%10); tailtail.next; } carrysum/10; if(l1!null){ l1l1.next; } if(l2!null){ l2l2.next; } } if(carry!0){ tail.nextnew ListNode(1); tailtail.next; } return head; } }参考了题解的自己做出来的用每一位相的解法每一位直接相加就像那个加法器两个底数相加和进位相加然后记录下进位和底数。/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * ListNode(int val, ListNode next) { this.val val; this.next next; } * } */ class Solution { public ListNode addTwoNumbers(ListNode l1, ListNode l2) { if(l1null)return l2; if(l2null)return l1; ListNode resultnew ListNode((l1.vall2.val)%10); ListNode rresult; boolean flagfalse; int ni(l1.vall2.val)/10; if((l1nulll2!nulll2.nextnull||l2nulll1!nulll1.nextnull||l1!nulll2!nulll1.nextnulll2.nextnull)ni1){ flagtrue; } l1l1.next; l2l2.next; while(l1!null||l2!null){ int s0; if(l1!nulll2!null){ sl1.vall2.valni; }else if(l1nulll2!null){ sl2.valni; }else if(l2nulll1!null){ sl1.valni; } int values; if(s10){ values%10; nis/10; }else{ ni0; } ListNode tmpnew ListNode(value); r.nexttmp; rr.next; if((l1nulll2!nulll2.nextnull||l2nulll1!nulll1.nextnull||l1!nulll2!nulll1.nextnulll2.nextnull)ni1){ flagtrue; } if(l1!null)l1l1.next; if(l2!null)l2l2.next; } if(flag){ ListNode tmpnew ListNode(1); r.nexttmp; rr.next; } return result; } }历史解法报错大数溢出的历史解法经过观察发现逆序刚好是一个数字从个位到高位的顺序先求每个链表代表的数字再求两个链表的和再将其从数字变为链表。但是这个过程出现了一个问题就是大数溢出遇到的例子是有10个元素的链表要是100个更表示不了了。/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * ListNode(int val, ListNode next) { this.val val; this.next next; } * } */ class Solution { public ListNode addTwoNumbers(ListNode l1, ListNode l2) { int s10; int s20; int s0; int c11;int c21; if(l1null)return l2; if(l2null)return l1; while(l1!null){ int mul1; for(int i1;ic1;i){ mul10*mul; } s1s1mul*l1.val; c1; l1l1.next; } while(l2!null){ int mul1; for(int i1;ic2;i){ mul10*mul; } s2s2mul*l2.val; c2; l2l2.next; } ss1s2; ListNode resultnew ListNode(); ListNode rresult; r.vals%10; ss/10; while(s!0){ ListNode tmpnew ListNode(s%10); r.nexttmp; rtmp; ss/10; } return result; } }19删除链表的倒数第N个结点解法题解的方法看了一下题解我发现我们的思路很像然后有一点值得借鉴就是因为删除的只有一个结点所以不需要将节点后面的部分也加在循环里面。/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * ListNode(int val, ListNode next) { this.val val; this.next next; } * } */ class Solution { public ListNode removeNthFromEnd(ListNode head, int n) { int count0; ListNode dummynew ListNode(0,head); ListNode phead; while(p!null){ pp.next; count; } int poicount-n; phead; ListNode predummy; for(int i1;icount-n1;i){ pp.next; prepre.next; } pre.nextp.next; return dummy.next; } }自己做的方法先遍历一遍得到链表长度再根据倒数第几个找到确切的坐标然后定义pre和p指针一个指向前面的节点一个指向后面一个的节点如果到要删除的节点就删掉具体看代码。/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * ListNode(int val, ListNode next) { this.val val; this.next next; } * } */ class Solution { public ListNode removeNthFromEnd(ListNode head, int n) { int count0; ListNode phead; while(p!null){ pp.next; count; } int poicount-n; ListNode prehead; ppre.next; count0; if(poi0){ return head.next; } while(p!null){ if(countpoi-1){ pre.nextp.next; ppre.next; }else{ prep; pp.next; pre.nextp; } count; } return head; } }24两两交换链表中的节点解法自己的解法无敌时间超过100%空间超过99.99%先定义一个dimmy节点next指向head为了方便后面的代码书写这样可以少很多要考虑的情况pre前面的节点p后面的节点s上一轮的后面的节点。pre.nextp.next; p.nextpre; s.nextp;/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * ListNode(int val, ListNode next) { this.val val; this.next next; } * } */ class Solution { public ListNode swapPairs(ListNode head) { if(headnull||head.nextnull)return head; ListNode prehead,ppre.next; ListNode dimmynew ListNode(0,head); ListNode sdimmy; while(p!null){ pre.nextp.next; p.nextpre; s.nextp; if(pre.nextnull)break; if(pre.next.next!null){ spre; prepre.next; ppre.next; }else{ pnull; } } return dimmy.next; } }94二叉树的中序遍历解法参考题解的递归方式本来以为自己可以直接做出来后来发现做不出来看了答案发现要在方法外面再写一个用来递归的方法怪不得我写不出来。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public ListInteger inorderTraversal(TreeNode root) { ListIntegerresultnew ArrayList(); inOrder(root,result); return result; } public void inOrder(TreeNode root,ListIntegerresult){ if(rootnull)return; inOrder(root.left,result); result.add(root.val); inOrder(root.right,result); } }自己的非递归方法最后还有30分钟做道简单题目撤了之前就想做这道题目了但是当时链表这一块不很清晰现在链表那块题刷了不少感觉可以做这道题了一做。我就想起来了之前做考研辅导这道题目我给学生讲了好多遍当时是用c语言写的当然用的是非递归方式递归方式太简单了。然后第一次用java写这个算法。中序遍历我们让遍历指针一直向左移动定义一个存放栈第一次不访问第二次从栈里弹出的时候再访问然后让指针向右转。这就是算法思想这道题我不用看题解我就是答案25.K个一组翻转链表解法题解中的方法这里要回头过来重新再看一遍我现在没有耐心了这道题目我隔了很久才看现在有点不想看了。自己的使用回退逻辑的解法我发现我在判断循环或者条件分支的边界的时候总是会出一些问题。这道题目我的解法直接遍历全部节点在一个交换轮回里定义一个p指针指向要反转的值一个r指针指向要反转的值的前一个元素这样就可以反转了。然后发现凑不够k个的时候直接最后不够的几个再重新反转回来。/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * ListNode(int val, ListNode next) { this.val val; this.next next; } * } */ class Solution { public ListNode reverseKGroup(ListNode head, int k) { ListNode dismmynew ListNode(0,head); if(headnull)return head; if(head.nextnull)return head; ListNode pre dismmy; ListNode rpre.next; ListNode pr.next; int flag0; int count0; while(p!nullflag0){ count0; for(int i0;ik-1;i){ count; r.nextp.next; p.nextpre.next; pre.nextp; if(r.next!null||r.nextnullik-2){ pr.next; }else{ flag1; rp.next; for(int j0;jcount;j){ p.nextr.next; r.nextpre.next; pre.nextr; rp.next; } break; } } if(flag1)break; prer; if(r.next!null){ rr.next; if(r.next!null){ pr.next; }else{ pnull; } }else{ pnull; } } return dismmy.next; } }138随机链表的复制解法题解解法迭代节点拆分这个解法我懒得看了跳过要是以后有机会就看看。题解解法回溯加哈希表只考虑一个回合的事情然后用递归的方式。/* // Definition for a Node. class Node { int val; Node next; Node random; public Node(int val) { this.val val; this.next null; this.random null; } } */ class Solution { MapNode,NodehashNodenew HashMap(); public Node copyRandomList(Node head) { if(headnull)return null; if(!hashNode.containsKey(head)){ Node headNewnew Node(head.val); hashNode.put(head,headNew); headNew.nextcopyRandomList(head.next); headNew.randomcopyRandomList(head.random); } return hashNode.get(head); } }自己写的解法先遍历链表将所有的链表的值都复制出来再 再次遍历一遍将每个节点的random值补上去。/* // Definition for a Node. class Node { int val; Node next; Node random; public Node(int val) { this.val val; this.next null; this.random null; } } */ class Solution { public Node copyRandomList(Node head) { Node phead; if(headnull)return null; Node resultnew Node(head.val); Node dismmynew Node(0,result); Node qdismmy; while(p!null){ Node tmpnew Node(p.val); q.nexttmp; if(p.next!null){ qq.next; } pp.next; } phead; Node rhead; qdismmy.next; while(p!nullq!null){ rhead; int count 0; while(r!nullr!p.random){ rr.next; count; } //这里的q和p是对应的 Node qqdismmy.next; while(qq!nullcount!0){ qqqq.next; count--; } if(q!null){ q.randomqq; } pp.next; qq.next; } return dismmy.next; } }23合并K个升序链表解法自己写的通过直接遍历的解法直接遍历每个链表的最小的也就是第一个元素先找到最小的元素和最小的元素的下标在lists数组中的位置然后更新结果链表。/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * ListNode(int val, ListNode next) { this.val val; this.next next; } * } */ class Solution { public ListNode mergeKLists(ListNode[] lists) { if(listsnull)return null; int nlists.length; if(n1lists[0]null)return null; ListNode dismmynew ListNode(0); ListNode rdismmy; int poi0; while(check(lists)){ int minInteger.MAX_VALUE; for(int i0;in;i){ if(lists[i]!null){ if(minlists[i].val){ poii; minlists[i].val; } } } lists[poi]lists[poi].next; ListNode tmpnew ListNode(min); r.nexttmp; rtmp; } return dismmy.next; } public boolean check(ListNode[]lists){ int nlists.length; for(int i0;in;i){ if(lists[i]!null)return true; } return false; } }146LRU缓存解法看题解中的答案class LRUCache { class dlinknode{ int key; int value; dlinknode pre; dlinknode next; public dlinknode(){} public dlinknode(int k,int v){ keyk; valuev; } } private int size; private MapInteger,dlinknodecachenew HashMap(); private int capacity; private dlinknode head; private dlinknode tail; public LRUCache(int capacity) { this.capacity capacity; this.size0; headnew dlinknode(); tailnew dlinknode(); head.nexttail; tail.prehead; } public int get(int key) { dlinknode nodecache.get(key); if(nodenull){ return -1; }else{ moveth(node); return node.value; } } public void put(int key, int v) { dlinknode f cache.get(key); if(f!null){ f.valuev; moveth(f); }else{ dlinknode nodenew dlinknode(key,v); addHead(node); cache.put(node.key,node); if(sizecapacity){ dlinknode tail removeTail(); cache.remove(tail.key); } } } public void moveth(dlinknode node){ remove(node); addHead(node); } public void addHead(dlinknode node){ head.next.prenode; node.nexthead.next; node.prehead; head.nextnode; size; } public void remove(dlinknode node ){ node.pre.nextnode.next; node.next.prenode.pre; size--; } public dlinknode removeTail(){ dlinknode restail.pre; remove(res); return res; } } /** * Your LRUCache object will be instantiated and called as such: * LRUCache obj new LRUCache(capacity); * int param_1 obj.get(key); * obj.put(key,value); */历史解法报错不能用数组来承载的解法class LRUCache { public LRUCache(int capacity) { int[]capnew int[3*capacity]; for(int i0;i3*capacity;i){ if(i%3!2){ cap[i]Integer.MAX_VALUE; }else{ cap[i]0; } } } public int get(int key) { for(int i0;i3*capacity;i){ if(cap[i]key){ cap[i2]0; return cap[i1]; } i; i; } return -1; } public void put(int key, int value) { boolean flagfalse; int poi0; int nullpoiInteger.MAX_VALUE; int count0; for(int i0;i3*capacity;i){ if(cap[i]key){ flagtrue; poii; } if(cap[i]!Integer.MAX_VALUE){ nullpoii; } i; i; } if(flag){ //找到了 cap[poi1]value; //最近使用时间更新 for(int j0;j3*capacity;j){ if(cap[j]!Integer.MAX_VALUE){ cap[j2]; } } }else{ //没有找到 //没有爆满 if(nullpoi!Integer.MAX_VALUE){ cap[nullpoi]key; cap[nullpoi1]value; //最近使用时间更新 for(int j0;j3*capacity;j){ if(cap[j]!Integer.MAX_VALUE){ cap[j2]; } } } //爆满 int poimax2; int max0; for(int j0;j3*capacity;j){ if(cap[i]!Integer.MAX_VALUE){ if(cap[i2]max){ maxcap[i2]; poimaxi; } } i; i; } cap[poimax]key; cap[poimax1]value; cap[poimax2]0; } } } /** * Your LRUCache object will be instantiated and called as such: * LRUCache obj new LRUCache(capacity); * int param_1 obj.get(key); * obj.put(key,value);104二叉树的最大深度解法题解中的广度优先搜索方法好长懒得看回头有机会再看。题解中的使用递归的解法题解中的递归解法和我的几乎一样不一样的是我用的是if...else判断。题解中直接用Math.max来判断显然题解中的更加简洁。值得学习/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public int maxDepth(TreeNode root) { if(root!null){ int l maxDepth( root.left); int r maxDepth(root.right); return Math.max(l,r)1; }else{ return 0; } } }自己写的用递归的思想将每一个节点和他的左右孩子作为一个系统如果左孩子的最大深度比右孩子的大就返回左孩子的最大深度为自己的最大深度如果右孩子的最大深度更大就返回右孩子的。如果节点为null直接返回0。这样就不影响了。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public int maxDepth(TreeNode root) { if(root!null){ int l maxDepth( root.left); int r maxDepth(root.right); if(lr){ return l1; }else{ return r1; } }else{ return 0; } } }本题目中值得学习的点这行代码可以替代下面的好多行代码return Math.max(l,r)1;if(lr){ return l1; }else{ return r1; }148排序链表解法题解中归并排序的方法归并排序的方法我之前一直没太搞明白。/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * ListNode(int val, ListNode next) { this.val val; this.next next; } * } */ class Solution { public ListNode sortList(ListNode head) { return sort(head,null); } public ListNode sort(ListNode head,ListNode tail){ if(headnull)return head; if(head.nexttail){ head.nextnull; return head; } ListNode phead; ListNode qhead; while(q!tail){ pp.next; qq.next; if(q!tail)qq.next; } ListNode midp; ListNode l1sort(head,p); ListNode l2sort(p,tail); return merge(l1,l2); } public ListNode merge(ListNode l1,ListNode l2){ ListNode pl1; ListNode ql2; ListNode rnew ListNode(); ListNode dismmynew ListNode(0,r); if(l1null)return l2; if(l2null)return l1; while(p!null||q!null){ ListNode tmpnew ListNode(); if(pnull){ tmp.valq.val; qq.next; }else if(qnull){ tmp.valp.val; pp.next; }else{ if(p.valq.val){ tmp.valp.val; pp.next; }else{ tmp.valq.val; qq.next; } } r.nexttmp; rtmp; } return dismmy.next.next; } }历史解法超出时间限制的历史解法这种解法时间复杂度On2每次将后面序列中的最小值和第一个值的位置进行交换好吧我的代码是将每一个比当次第一个小的都进行一次交换。/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * ListNode(int val, ListNode next) { this.val val; this.next next; } * } */ class Solution { public ListNode sortList(ListNode head) { if(headnull)return head; ListNode qhead; ListNode phead; ListNode resultnew ListNode(0); ListNode rresult; while(q!null){ int minq.val; ListNode tmpnew ListNode(min); while(p!null){ if(p.valmin){ minp.val; p.valq.val; q.valmin; } pp.next; } tmp.valmin; r.nexttmp; rr.next; qq.next; pq; } return result.next; } }ListNode resultnew ListNode(0); ListNode rresult; int n0; ListNode phead; while(p!null){ n; pp.next; } phead; ListNode qhead; for(int i0;in/2;i){ qq.next; } ListNode poiq; while(p!poi||q!null){ ListNode tmpnew ListNode(); if(qnull){ tmp.valp.val; } if(ppoi){ tmp.valq.val; } if(p!poiq!null){ if(p.valq.val){ tmp.valp.val; pp.next; }else{ tmp.valq.val; qq.next; } } r.nexttmp; rr.next; } return result.next; }226翻转二叉树解法自己的递归法对左子树采用方法对右子树采用方法。让左子树右子树让右子树左子树。这样就可以了。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public TreeNode invertTree(TreeNode root) { if(rootnull){ return null; }else{ invertTree(root.left); invertTree(root.right); } TreeNode tmproot.left; root.leftroot.right; root.righttmp; return root; } }101对称二叉树解法看了题解的思想后自己写的迭代的解法这种方法用到了二叉树遍历中的层序遍历我之前也想过这种方法但是我当时想的是先序遍历或者中序遍历不是很适合这道题目。层序遍历完美适配这道题目。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public boolean isSymmetric(TreeNode root) { boolean flagtrue; ListTreeNodequeuelnew ArrayList(); ListTreeNodequeuernew ArrayList(); TreeNode proot; queuel.add(p); queuer.add(p); while(!queuel.isEmpty()||!queuer.isEmpty()){ TreeNode tlqueuel.get(0); TreeNode trqueuer.get(0); if(tlnulltr!null||tl!nulltrnull){ flagfalse; } if(tl!nulltr!null){ if(tl.val!tr.val){ flagfalse; } } queuel.remove(0); queuer.remove(0); if(tl!null){ queuel.add(tl.left); queuel.add(tl.right); } if(tr!null){ queuer.add(tr.right); queuer.add(tr.left); } } return flag; } }题解中的递归解法我一直没搞明白那个是左边子树和右边子树是怎么递归的然后现在明白了。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { boolean flagtrue; public boolean isSymmetric(TreeNode root) { return check(root.left,root.right); } public boolean check(TreeNode p,TreeNode q){ if(pnullqnull){ return true; } if(pnullq!null||p!nullqnull){ return false; } return p.valq.valcheck(p.left,q.right)check(p.right,q.left); } }历史解法这个错误是因为将题意理解为左右子树一样但实际上是左右对称的一样才对。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { boolean flagtrue; public boolean isSymmetric(TreeNode root) { if(rootnull){ return false; }else{ isSymmetric(root.left); isSymmetric(root.right); int lInteger.MIN_VALUE; int rInteger.MIN_VALUE; if(root.left!null) lroot.left.val; if(root.right!null) rroot.right.val; if(l!r){ flagfalse; return false; } } return flag; } }543二叉树的直径解法题解中的深度优先的解法这道题我不会做这个递归我没整明白。现在倒是整明白了。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { int ans0; public int diameterOfBinaryTree(TreeNode root) { ans1; depth(root); return ans-1; } public int depth(TreeNode p){ if(pnull)return 0; int ldepth(p.left); int rdepth(p.right); ansMath.max(ans,lr1); return Math.max(l,r)1; } }102二叉树的层序遍历解法自己写的解法因为要按层输出层放到一个列表中最后再放到一个列表中一块输出。所以我创建两个列表list和list1来放两层的元素这样就可以实现按层输出。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public ListListInteger levelOrder(TreeNode root) { ListTreeNodelistnew ArrayList(); ListTreeNodelist1new ArrayList(); ListListIntegerresultnew ArrayList(); if(rootnull)return result; ListIntegerrnew ArrayList(); list.add(root); int flag1; while(list.size()!0||list1.size()!0){ if(flag1list.size()!0){ TreeNode tmplist.get(0); if(tmp.left!null){ list1.add(tmp.left); } if(tmp.right!null){ list1.add(tmp.right); } r.add(tmp.val); list.remove(0); if(list.size()0){ flag0; result.add(r); rnew ArrayList(); } } if(flag0list1.size()!0){ TreeNode tmplist1.get(0); if(tmp.left!null){ list.add(tmp.left); } if(tmp.right!null){ list.add(tmp.right); } r.add(tmp.val); list1.remove(0); if(list1.size()0){ flag1; result.add(r); rnew ArrayList(); } } } return result; } }108将有序数组转换为二叉搜索树解法自己写的解法递归从中间砍两半分别找左边和右边的中间节点然后就可以了。这道题目我在递归的出口那里的判断出了好几次错误。关于递归我觉得我在判断出界条件这块还要提升提升。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public TreeNode sortedArrayToBST(int[] nums) { int nnums.length; return balance(nums,0,n-1); } public TreeNode balance(int []nums,int start,int end){ int mid(startend)/2; if(startend){ return null; } TreeNode l balance(nums,start,mid-1); TreeNode rbalance(nums,mid1,end); TreeNode mnew TreeNode(nums[mid],l,r); return m; } }历史解法判断边界搞错的解法/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public TreeNode sortedArrayToBST(int[] nums) { int nnums.length; return balance(nums,0,n-1); } public TreeNode balance(int []nums,int start,int end){ int mid(startend)/2; if(midstart){ return new TreeNode(nums[start],null,null); } TreeNode l balance(nums,start,mid); TreeNode rbalance(nums,mid1,end); TreeNode mnew TreeNode(nums[mid],l,r); return m; } }98验证二叉搜索树解法题解中的递归的做法从上往下不断缩小递归的范围这样就可以照顾到右子树的左孩子小于根节点的情况还有另一种情况这个方法很妙。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public boolean isValidBST(TreeNode root) { return check(root,Long.MIN_VALUE,Long.MAX_VALUE); } public boolean check(TreeNode p,long l,long r){ if(pnull)return true; if(p.vall||p.valr)return false; return check(p.left,l,p.val)check(p.right,p.val,r); } }自己写的中序遍历的方法中序遍历刚好是左中右只需要确定中序序列中一个数字和他前面的那个数字大就可以了我写的这个感觉不够好我准备再看看题解中的方法。我看了和我的方法一样就是他专门用了栈这个数据结构而我是通过列表的添加和删除来实现的栈的结构的。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public boolean isValidBST(TreeNode root) { ListTreeNodelistnew ArrayList(); TreeNode proot; long preLong.MIN_VALUE; while(list.size()!0||p!null){ if(p!null){ list.add(p); pp.left; }else{ plist.get(list.size()-1); if(p.valpre){ return false; } prep.val; pp.right; list.remove(list.size()-1); } } return true; } }自己写的对每个节点都遍历左子树和遍历右子树的方法写了两个方法一个是验证以某个节点为根的节点的树的每个节点的值是否都比给的值小另一个相反然后遍历整棵树我用的是层序遍历。代码行数稍微有些多。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public boolean isValidBST(TreeNode root) { ListTreeNodelistnew ArrayList(); list.add(root); while(list.size()!0){ TreeNode plist.get(0); if(p.left!null){ list.add(p.left); if(checksmall(p.left,p.val)false){ return false; } } if(p.right!null){ list.add(p.right); if(!checkbig(p.right,p.val)){ return false; } } list.remove(0); } return true; } public boolean checksmall(TreeNode p,int r){ ListTreeNodeqnew ArrayList(); q.add(p); while(q.size()!0){ TreeNode tmpq.get(0); if(tmp.left!null){ q.add(tmp.left); } if(tmp.right!null){ q.add(tmp.right); } if(tmp.valr){ return false; } q.remove(0); } return true; } public boolean checkbig(TreeNode p,int r){ if(pnull)return true; ListTreeNodeqnew ArrayList(); q.add(p); while(q.size()!0){ TreeNode tmpq.get(0); if(tmp.left!null){ q.add(tmp.left); } if(tmp.right!null){ q.add(tmp.right); } if(tmp.valr){ return false; } q.remove(0); } return true; } }历史解法没能检测出来下面节点和根节点关系的解法用递归判断节点和他的左右孩子的关系是否满足条件不满足就返回false/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { boolean flagtrue; public boolean isValidBST(TreeNode root) { check(root); return flag; } public boolean check(TreeNode p){ if(pnull)return true; check(p.left); check(p.right); if(p.left!nullp.valp.left.val||p.right!nullp.valp.right.val){ flagfalse; return false; } return true; } }230二叉搜索树中第K小的元素解法自己写的中序遍历的方法就是正常的中序遍历的过程然后有一点就是在遍历指针向左边移的时候不能用if要用while不然直接给pop出来了。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public int kthSmallest(TreeNode root, int k) { DequeTreeNodestacknew LinkedListTreeNode(); ListIntegerlistnew ArrayList(); TreeNode proot; while(p!null||!stack.isEmpty()){ while(p!null){ stack.push(p); pp.left; } pstack.pop(); list.add(p.val); pp.right; } return list.get(k-1); } }历史解法报错索引溢出但是AI告诉我没问题的解法就神奇。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public int kthSmallest(TreeNode root, int k) { DequeTreeNodestacknew LinkedList(); ListIntegerlistnew ArrayList(); TreeNode proot; while(p!null||!stack.isEmpty()){ if(p!null){ stack.push(p); pp.left; } pstack.pop(); list.add(p.val); pp.right; } return list.get(k-1); } }199二次树的右视图解法自己的层序遍历的解法层序遍历两个队列轮着将同一层的元素放到同一个队列中然后只有当最后一个元素的时候才将其加入到结果列表中去。官方题解看起来也挺长的我就不看了。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public ListInteger rightSideView(TreeNode root) { ListIntegerresultnew ArrayList(); if(rootnull)return result; QueueTreeNodeq1new LinkedList(); QueueTreeNodeq2new LinkedList(); q1.offer(root); TreeNode proot; while(!q1.isEmpty()||!q2.isEmpty()){ while(!q1.isEmpty()){ TreeNode tmpq1.peek(); if(tmp.left!null){ q2.offer(tmp.left); } if(tmp.right!null){ q2.offer(tmp.right); } if(q1.size()1){ result.add(q1.poll().val); }else{ q1.poll(); } } while(!q2.isEmpty()){ TreeNode tmpq2.peek(); if(tmp.left!null){ q1.offer(tmp.left); } if(tmp.right!null){ q1.offer(tmp.right); } if(q2.size()1){ result.add(q2.poll().val); }else{ q2.poll(); } } } return result; } }114二叉树展开为链表解法自己的先序遍历的方法创建一个列表先序遍历树将其存放到列表中去然后再将其一个一个找出来就可以了。这道题感觉不用看题解直接就做出来了。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public void flatten(TreeNode root) { ListTreeNodelistnew ArrayList(); DequeTreeNodestacknew LinkedList(); TreeNode proot; while(!stack.isEmpty()||p!null){ while(p!null){ list.add(p); stack.push(p); pp.left; } pstack.pop(); pp.right; } for(int i1;ilist.size();i){ root.leftnull; root.rightlist.get(i); rootroot.right; } } }105从前序与中序遍历序列构造二叉树解法自己借助AI找边界的递归的方法思想在先序序列中找第一个为根节点在中序遍历中找到和他一样的分为左右子树再在左子树和右子树中分别重复就可以了。递归方法三个参数sp:先序序列中的指针定位跟节点。si中序序列中指向开始位置的指针ei中序序列中指向结束位置的指针后两个指针将子树的范围圈起来第一个指针在这个子树的范围内找到其根节点。递归就解决了这个问题。要注意递归的出口还有左右子树的遍历的边界问题。这道题目最让我头疼的地方在边界的问题什么时候该返回以及向左子树遍历和向右子树遍历的边界。因为开始我是用的两个参数后来改为三个参数在改的时候没有改完全。我看了一眼题解感觉代码量好像我的更少哈哈哈那就先偷个懒不看答案了。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { DequeIntegerstacknew LinkedList(); public TreeNode buildTree(int[] preorder, int[] inorder) { return createtree(preorder,inorder,0,0,inorder.length-1); } public TreeNode createtree(int[] preorder, int[] inorder,int sp,int si,int ei){ int ninorder.length; if(eisi){ return new TreeNode(inorder[ei]); } if(siei)return null; //sp表示preorder中的开始。ei表示inorder中的结束 TreeNode rootnew TreeNode(preorder[sp]); //rp找相同的 int rpsi; while(rpninorder[rp]!preorder[sp])rp; if(rpsi){ TreeNode lcreatetree(preorder,inorder,sp1,si,rp-1); root.leftl; } if(rpei){ TreeNode rcreatetree(preorder,inorder,sp(rp-si)1,rp1,ei); root.rightr; } return root; } }历史解法边界报错没有正确输出的方法/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { DequeIntegerstacknew LinkedList(); public TreeNode buildTree(int[] preorder, int[] inorder) { return createtree(preorder,inorder,0,0,inorder.length-1); } public TreeNode createtree(int[] preorder, int[] inorder,int sp,int si,int ei){ int ninorder.length; if(eisi){ return new TreeNode(inorder[ei]); } //sp表示preorder中的开始。ei表示inorder中的结束 TreeNode rootnew TreeNode(preorder[sp]); //rp找相同的 int rp0; while(rpninorder[rp]!preorder[sp])rp; if(rp0){ TreeNode lcreatetree(preorder,inorder,sp1,si,rp-1); root.leftl; } if(rpsprp!n-1){ TreeNode rcreatetree(preorder,inorder,rp1,rp1,ei); root.rightr; } return root; } }未完成的递归我找不到出口这个方法我找不好递归的出口这道题目我已经看了很久了我准备直接看答案。不再看别的了。我的思路是先看先序遍历找到根节点再看中序遍历找出左子树和右子树再分别在上面找。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { DequeIntegerstacknew LinkedList(); public TreeNode buildTree(int[] preorder, int[] inorder) { return createtree(preorder,inorder,0,inorder.length-1); } public TreeNode createtree(int[] preorder, int[] inorder,int sp,int ei, stack){ int ninorder.length; if(!stack.isEmpty()spstack.peek()){ } //sp表示preorder中的开始。ei表示inorder中的结束 TreeNode rootnew TreeNode(preorder[sp]); //rp找相同的 int rpsp; while(inorder[rp]!preorder[sp]rpstack.peek()!stack.isEmpty())rp; stack.push(rp); TreeNode lcreatetree(preorder,inorder,sp1,rp-1); TreeNode rcreatetree(preorder,inorder,rp1,ei); root.leftl; root.rightr; return root; } }437路径总和三解法题解中的递归法遍历整棵树每次都从一个节点找他的所有的子节点看有没有符合条件的。递归传下去的后面的目标值可以做差这样就可以完美传递下去了。还有一个题解我没有看要是有缘可以看一看/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public int pathSum(TreeNode root, int targetSum) { if(rootnull)return 0; int rsum(root,targetSum); rpathSum(root.left,targetSum); rpathSum(root.right,targetSum); return r; } public int sum(TreeNode root,long targetSum){ if(rootnull)return 0; int ret0; int vroot.val; if(vtargetSum){ ret; } retsum(root.left,targetSum-v); retsum(root.right,targetSum-v); return ret; } }历史解法没跑完的遍历两次的解法/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public int pathSum(TreeNode root, int targetSum) { int sum0; DequeTreeNodesnew LinkedList(); TreeNode proot; while(p!null||!s.isEmpty()){ while(p!null){ s.push(p); pp.left; } ps.pop(); int countcheck(p,targetSum); sumsumcount; pp.right; } return sum; } public int check(TreeNode root,int targetSum){ int count0; ListTreeNodestacknew ArrayList(); TreeNode proot; while(p!null||!stack.isEmpty()){ while(p!null){ stack.add(p); int sum0; for(int i0;istack.size();i){ sumsumstack.get(i).val; if(sumtargetSum){ count; } } pp.left; } // int sum0; // for(int i0;istack.size();i){ // sumsumstack.get(i).val; // if(sumtargetSum){ // count; // } // } pstack.get(stack.size()-1); stack.remove(stack.size()-1); pp.right; } return count; } }236二叉树的最近公共祖先解法题解中的存储父节点解法我的这个方法和题解中的代码的区别还是挺大的中间有一部分我借鉴了链表那一块的一道题的解法好像是寻找公共节点的那个地方。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val x; } * } */ class Solution { MapTreeNode,TreeNodemapnew HashMap(); public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { makehash(root); TreeNode Ap; TreeNode Bq; while(A!B){ Amap.containsKey(A)?map.get(A):q; Bmap.containsKey(B)?map.get(B):p; } return A; } public void makehash(TreeNode root){ if(root!null){ if(root.left!null){ map.put(root.left,root); makehash(root.left); } if(root.right!null){ map.put(root.right,root); makehash(root.right); } } } }题解中的递归解法一个满足条件的节点只有两种情况左右子树分别包含pq。或者此节点就是p或q另一个节点在他的左右子树上面。递归方法检查节点状态看是否满足上面的情况满足返回答案不满足接着往下递归返回值为左子树包含pq的情况右子树包含pq的情况本节点和Pq的情况的一个交叉的反应情况。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val x; } * } */ class Solution { TreeNode ansnew TreeNode(0); public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { check(root,p,q); return ans; } public boolean check(TreeNode root,TreeNode p,TreeNode q){ if(rootnull)return false; boolean lcheck(root.left,p,q); boolean rcheck(root.right,p,q); if(lr||(root.valp.val||root.valq.val)(l||r)){ ansroot; } return l||r||root.valp.val||root.valq.val; } }自己的解法遍历树分别找到两个节点将其父节点分别存储到两个列表中然后按顺序比较就可以了。额这道题目没耐心了直接看题解了做了个半成品放这里了/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val x; } * } */ class Solution { ListTreeNodel1new ArrayList(); ListTreeNodel2new ArrayList(); public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { } public TreeNode find(TreeNode root,TreeNode target){ if(rootnull{ return null; } if(roottarget){ l1.add(root); return root; } if(root!target){ l1.add(root); } } }124二叉树中的最大路径和解法自己的递归解法计算以每个节点为根节点的树的最大路径和然后向上传递左右子树中路径和值比较大的加上本身传上去如果节点的路径和值小于零返回0.如果大于零直接返回。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { int maxPInteger.MIN_VALUE; public int maxPathSum(TreeNode root) { int vcheck(root); return maxP; } public int check(TreeNode root){ if(rootnull){ return 0; } int vroot.val; int lcheck(root.left); int rcheck(root.right); if(lrvmaxP)maxPlrv; if(lv0||rv0){ int mMath.max(rv,lv); return m; }else{ return 0; } } }历史解法第一次写通过三分之二的例子的解法递归法递归判断一个节点的值。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { int maxP0; public int maxPathSum(TreeNode root) { int vcheck(root); return maxP; } public int check(TreeNode root){ if(rootnull){ return 0; } int vroot.val; int lcheck(root.left); int rcheck(root.right); if(lrvmaxP)maxPlrv; if(lv0||rv0){ int mMath.max(rv,lv); return m; } return v; } }200岛屿的数量解法题解中的深度优先搜索方法定义一个dfs方法递归地将一个连成片的1全部变为0通过将一个1的上下左右都递归一下来实现。然后在主方法中遍历所有节点每次遍历到一个岛屿就可以直接将所有的岛屿的节点变为1这样就不会重复了。class Solution { public int numIslands(char[][] grid) { int count 0; int row grid.length; int col grid[0].length; for(int i0;irow;i){ for(int j0;jcol;j){ if(grid[i][j]1){ count; dfs(grid,i,j); } } } return count; } public void dfs(char[][]grid,int r,int c){ int row grid.length; int col grid[0].length; if(r0||c0||rrow||ccol||grid[r][c]0){ return; } grid[r][c]0; dfs(grid,r-1,c); dfs(grid,r1,c); dfs(grid,r,c1); dfs(grid,r,c-1); } }历史错误解法下面的方法都是我想通过直接检查一个节点的上下左右的节点来判断是否加1或者减一。class Solution { public int numIslands(char[][] grid) { int count0; int rowgrid.length; int colgrid[0].length; for(int i0;irow;i){ for(int j0;jcol;j){ boolean flagfalse; //可加1的条件 //为1且为行第一或前一个不为1且列第一或者上面一个不为1 if(grid[i][j]1(j0||grid[i][j-1]!1)(i0||grid[i-1][j]0)(j!col-1grid[i][j1]0)){ count; flagtrue; } } } return count; } }class Solution { public int numIslands(char[][] grid) { int count0; int rowgrid.length; int colgrid[0].length; for(int i0;irow;i){ boolean flagfalse; for(int j0;jcol;j){ //可加1的条件 //为1且为行第一或前一个不为1且列第一或者上面一个不为1 if(grid[i][j]1(j0||grid[i][j-1]!1)(i0||grid[i-1][j]0)(!flag||irow-1||i!row-1grid[i1][j]0)(!flag||jcol-1||j!col-1grid[i][j1]0)){ count; flagtrue; } if(flaggrid[i][j]1(j!0i!0grid[i][j-1]1grid[i-1][j]1grid[i-1][j-1]0)){ count--; } } } return count; } }994腐烂的橘子解法自己的解法今天下午脑袋比较昏沉本来打算放弃了但是看了一眼答案感觉答案没有那种让我焕然一新的思路而且过程也很冗长我决定还是自己写吧。最后写出来了。思路首先将所有初始2找到两个列表一个记录没腐蚀完的节点一个记录被正在腐蚀的节点的临近节点也就是下一轮要遍历的节点我在这里用的是一个轮换一直用l列表感觉中间检查上下左右节点的按个部分可以抽象出来。class Solution { int count -1; ListInteger l new ArrayList(); ListInteger r new ArrayList(); int ctl 1; public int orangesRotting(int[][] grid) { if(checkfrash(grid))return 0; for (int i 0; i grid.length; i) { for (int j 0; j grid[0].length; j) { if (grid[i][j] 2) { l.add(i); l.add(j); } } } change(grid); if (checkfrash(grid)) { return count; } else { return -1; } } public void change(int[][] grid) { boolean flag false; while (!l.isEmpty() || !r.isEmpty()) { if (l.isEmpty()) { ListInteger tmp l; l r; r tmp; } while (!l.isEmpty()) { int i l.get(0); l.remove(0); int j l.get(0); l.remove(0); if (i ! 0 grid[i - 1][j] 1) { flag true; grid[i - 1][j] 2; r.add(i - 1); r.add(j); } if (i ! grid.length - 1 grid[i 1][j] 1) { flag true; grid[i 1][j] 2; r.add(i 1); r.add(j); } if (j ! 0 grid[i][j - 1] 1) { flag true; grid[i][j - 1] 2; r.add(i); r.add(j - 1); } if (j ! grid[0].length - 1 grid[i][j 1] 1) { flag true; grid[i][j 1] 2; r.add(i); r.add(j 1); } } if (flag) { count; } } } public boolean checkfrash(int[][] grid) { for (int i 0; i grid.length; i) { for (int j 0; j grid[0].length; j) { if (grid[i][j] 1) return false; } } return true; } }优化后代码少了13行class Solution { int count -1; ListInteger l new ArrayList(); ListInteger r new ArrayList(); int ctl 1; public int orangesRotting(int[][] grid) { if(checkfrash(grid))return 0; for (int i 0; i grid.length; i) { for (int j 0; j grid[0].length; j) { if (grid[i][j] 2) { l.add(i); l.add(j); } } } change(grid); if (checkfrash(grid)) { return count; } else { return -1; } } public void change(int[][] grid) { boolean flag false; while (!l.isEmpty() || !r.isEmpty()) { if (l.isEmpty()) { ListInteger tmp l; l r; r tmp; } while (!l.isEmpty()) { int i l.get(0); l.remove(0); int j l.get(0); l.remove(0); int[][]dirs{{-1,0},{1,0},{0,-1},{0,1}}; for(int[]d:dirs){ int iiid[0]; int jjjd[1]; if(ii0iigrid.lengthjj0jjgrid[0].lengthgrid[ii][jj]1){ r.add(ii); r.add(jj); grid[ii][jj]2; flagtrue; } } } if (flag) { count; } } } public boolean checkfrash(int[][] grid) { for (int i 0; i grid.length; i) { for (int j 0; j grid[0].length; j) { if (grid[i][j] 1) return false; } } return true; } }历史错误解法通过127个用例的解法遍历网格找到所有初始腐烂橘子值为 2的坐标存入 l1横纵坐标交替存入。一旦找到一个腐烂橘子立即调用 change扩散腐烂扩散完检查是否还有新鲜橘子如果没有就返回分钟数。change方法中用两个列表 l1和 l2交替作为当前层和下一层的容器模拟 BFS 扩散。这道题目我不想再看了直接看答案。如果后面又看到这个部分倒是可以尝试着将这个思路实现出来。class Solution { int count0; ListIntegerl1new ArrayList(); ListIntegerl2new ArrayList(); public int orangesRotting(int[][] grid) { for(int i0;igrid.length;i){ for(int j0;jgrid[0].length;j){ if(grid[i][j]2){ l1.add(i); l1.add(j); change(grid); if(checkfrash(grid)){ return count; } } } } if(checkfrash(grid)){ return count; }else{ return -1; } } public void change(int[][] grid){ boolean flagfalse; ListIntegerll1.isEmpty()?l2:l1; ListIntegerrl1.isEmpty()?l1:l2; while(!l.isEmpty()){ int il.get(0); l.remove(0); int jl.get(0); l.remove(0); if(i!0grid[i-1][j]1){ flagtrue; grid[i-1][j]2; r.add(i-1); r.add(j); } if(i!grid.length-1grid[i1][j]1){ flagtrue; grid[i1][j]2; r.add(i1); r.add(j); } if(j!0grid[i][j-1]1){ flagtrue; grid[i][j-1]2; r.add(i); r.add(j-1); } if(j!grid[0].length-1grid[i][j1]1){ flagtrue; grid[i][j1]2; r.add(i); r.add(j1); } } if(flag){ count; } } public boolean checkfrash(int[][]grid){ for(int i0;igrid.length;i){ for(int j0;jgrid[0].length;j){ if(grid[i][j]1)return false; } } return true; } }207课程表解法题解中的广度优先算法直接运用拓扑排序中的找入度为0的点的方法就可以了。非常地清晰。class Solution { public boolean canFinish(int numCourses, int[][] prerequisites) { ListListIntegeredgsnew ArrayList(); int []flagnew int[numCourses]; for(int i0;inumCourses;i){ edgs.add(new ArrayListInteger()); } for(int[]nums:prerequisites){ edgs.get(nums[1]).add(nums[0]); flag[nums[0]]; } QueueIntegerqueuenew LinkedList(); for(int i0;inumCourses;i){ if(flag[i]0){ queue.offer(i); } } int visited0; while(!queue.isEmpty()){ visited; int tmpqueue.poll(); for(int v:edgs.get(tmp)){ flag[v]--; if(flag[v]0){ queue.offer(v); } } } return visitednumCourses; } }题解中的深度优先搜索用图的数据结构来解题将数组变成有向图。ListListInteger外面是头结束是尾。三种状态未访问0正在访问1访问过2正在访问的时候发现访问到了正在访问的节点有点绕但想一下就说明有环访问到自己了。直接从栈里面弹出去return。如果已经知道存在环flagfalse就直接弹出去return,如果没有发现环就正常地让节点进入到“访问过的状态2”.我在做这道题的时候遇到的一个问题就是我开始没有搞明白题解中的return 是什么意思dfs这个方法不需要返回什么东西如果他走完了可以将节点的状态变为访问过的状态只有出现异常发现了环才会中间强制返回。class Solution { ListListIntegeredgs; boolean flagtrue; int []visited; public boolean canFinish(int numCourses, int[][] prerequisites) { int n prerequisites.length; visitednew int[numCourses]; edgsnew ArrayListListInteger(); for(int i0;inumCourses;i){ edgs.add(new ArrayListInteger()); } for(int []num:prerequisites){ edgs.get(num[1]).add(num[0]); } for(int i0;inumCoursesflag;i){ if(visited[i]0){ dfs(i); } } return flag; } public void dfs(int i){ visited[i]1; for(int v:edgs.get(i)){ if(visited[v]0){ dfs(v); if(!flag){ return; } }else if(visited[v]1){ flagfalse; return; } } visited[i]2; } }历史解法报错超出时间限制的解法这个方法时间复杂度On2。每次都遍历整个数组看能不能找到让序列连续下去我想过用哈希表来做但是哈希表一次只能存储一个键比如1312只能留下一个。所以我还是直接让复杂度高一些先做出来再说。我又想了一下先决条件可以继承啊所以采用MapInteger,ListInteger这种数据结构后面是前面的先决条件这样就完美解决了这个问题选择正确的数据结构的重要性在这一刻体现出来了在做的过程中我又发现MapInteger,SetIntegermapnew HashMap();这种数据结构好像更合适。用MapInteger, SetInteger存储数据方便找。然后遍历集合class Solution { public boolean canFinish(int numCourses, int[][] prerequisites) { int n prerequisites.length; if (n numCourses * (numCourses - 1) / 2) return false; MapInteger, SetInteger map new HashMap(); ListInteger kk new ArrayList(); for (int i 0; i n; i) { int k prerequisites[i][0]; int v prerequisites[i][1]; if (!map.containsKey(k)) { kk.add(k); } map.computeIfAbsent(k, key - new HashSet()).add(v); } for (int i 0; i kk.size(); i) { boolean flag true; SetInteger s map.get(kk.get(i)); while (flag) { SetInteger toAdd new HashSet(); for (int num : s) { if (num kk.get(i)) return false; if (map.containsKey(num)) { SetInteger s1 map.get(num); toAdd.addAll(s1); } } if(s.containsAll(toAdd))flagfalse; s.addAll(toAdd); } } for (int i 0; i kk.size(); i) { SetInteger s map.get(kk.get(i)); for (int num : s) { if (num kk.get(i)) return false; } } return true; } }通过了52个例子的解法class Solution { public boolean canFinish(int numCourses, int[][] prerequisites) { int nprerequisites.length; if(nnumCourses*(numCourses-1)/2)return false; MapInteger,SetIntegermapnew HashMap(); ListIntegerkknew ArrayList(); for(int i0;in;i){ int kprerequisites[i][0]; int vprerequisites[i][1]; if(!map.containsKey(k)){ kk.add(k); } map.computeIfAbsent(k,key-new HashSet()).add(v); } for(int i0;ikk.size();i){ SetIntegersmap.get(kk.get(i)); SetIntegertoAddnew HashSet(); for(int num:s){ if(numkk.get(i))return false; if(map.containsKey(num)){ SetIntegers1map.get(num); toAdd.addAll(s1); } } s.addAll(toAdd); } for(int i0;ikk.size();i){ SetIntegersmap.get(kk.get(i)); SetIntegertoAddnew HashSet(); for(int num:s){ if(numkk.get(i))return false; if(map.containsKey(num)){ SetIntegers1map.get(num); toAdd.addAll(s1); } } s.addAll(toAdd); } for(int i0;ikk.size();i){ SetIntegersmap.get(kk.get(i)); for(int num:s){ if(numkk.get(i))return false; } } return true; } }208实现Trie前缀树解法题解中的解法没什么说的我有点懵逼我准备直接背答案。class Trie { private Trie[]children; private boolean isEnd; public Trie() { childrennew Trie[26]; isEndfalse; } public void insert(String word) { Trie nodethis; for(int i0;iword.length();i){ char chword.charAt(i); int indexch-a; if(node.children[index]null){ node.children[index]new Trie(); } nodenode.children[index]; } node.isEndtrue; } public boolean search(String word) { Trie nodeprefixNode(word); return nodenull?false:truenode.isEnd; } public boolean startsWith(String prefix) { Trie nodeprefixNode(prefix); return nodenull?false:true; } public Trie prefixNode(String prefix){ Trie node this; for(int i0;iprefix.length();i){ char chprefix.charAt(i); int indexch-a; if(node.children[index]null)return null; nodenode.children[index]; } return node; } } /** * Your Trie object will be instantiated and called as such: * Trie obj new Trie(); * obj.insert(word); * boolean param_2 obj.search(word); * boolean param_3 obj.startsWith(prefix); */46全排列解法题解中的递归回溯解法所谓回溯就是将之前改变过的状态再改变回来。在这道题目中也是如此这道题目可以看作是n个不同数字的不同的排列方式很容易就可以看出来是n!怎么求出所有的组合呢本题的算法可以这么理解每个位置都可以有n-1种情况但是其值要收到别的位置的影响因为不能有重复的。为了保证不重复本算法采用交换两个值的位置来产生新的情况同样也是采用交换两个值的位置来回复原来的状态。采用递归的方式使得从后往前不断地更新所有的情况。为了帮助理解我画了一张图片class Solution { public ListListInteger permute(int[] nums) { int nnums.length; ListListIntegerrnew ArrayList(); ListIntegeroutputnew ArrayList(); for(int i0;in;i){ output.add(nums[i]); } back(n,r,output,0); return r; } public void back(int n,ListListIntegerr,ListIntegeroutput,int first){ if(firstn){ r.add(new ArrayList(output)); } for(int ifirst;in;i){ Collections.swap(output,first,i); back(n,r,output,first1); Collections.swap(output,first,i); } } }自己做的通过将所有索引都列出来的解法2将所有的索引的情况都列举出来通过将从1到十的n次方的数遍历一遍其中一定包含我们所需要的所有的索引然后再将所有的索引都遍历出来就可以了。遍历出来以后再根据索引情况将对应的数据收集起来就可以了。class Solution { public ListListInteger permute(int[] nums) { int nnums.length; ListListIntegerrnew ArrayList(); int sum10; for(int i1;in;i){ sumsum*10; } Listint []allnew ArrayList(); for(int i0;isum;i){ int []ldiss(i,n); SetIntegersetnew HashSet(); boolean flagtrue; for(int j0;jn;j){ if(l[j]n)flagfalse; set.add(l[j]); } if(flagfalse)continue; if(set.size()n){ all.add(l); } } for(int i0;iall.size();i){ int []lall.get(i);//l为不同的索引号 ListIntegerlistnew ArrayList(); for(int j0;jn;j){ list.add(nums[l[j]]); } r.add(list); } return r; } public int[] diss(int n, int nn){ int []rnew int [nn]; for(int inn-1;i0;i--){ r[i]n%10; nn/10; } return r; } }自己做的通过将所有索引情况都列出来的方法首先将所有的索引情况都列出来在列出所有的索引情况的过程中我借鉴复制粘贴了热题100道中倒数第二道题目的解法这道题目刚好可以解决本解法中的关键问题。所有索引情况都列出来之后直接根据索引就可以找到所有的方案了。class Solution { public ListListInteger permute(int[] nums) { int nnums.length; ListListIntegerrnew ArrayList(); Listint []allcreateAll(n); for(int i0;iall.size();i){ int []lall.get(i); ListIntegerllnew ArrayList(); for(int j0;jn;j){ int poil[j]; ll.add(nums[poi]); } r.add(ll); } return r; } public Listint [] createAll(int n){ Listint []rnew ArrayList(); int []lnew int [n]; int mul1; l[0]0; for(int i1;in;i){ mulmul*i; if(i!n){ l[i]i; } } r.add(l.clone()); int count 1; while(countmul){ r.add(nextPermutation(l).clone()); count; } return r; } public int [] nextPermutation(int[] nums) { int nnums.length; int indexn-1; while(index0nums[index]nums[index-1]){ index--; } if(index0){ sort(nums,0); }else{ for(int jn-1;jindex;j--){ if(nums[j]nums[index-1]){ swap(nums,index-1,j); sort(nums,index); break; } } } return nums; } public void sort(int []nums,int i){ for(int ki;knums.length;k){ int mink; for(int sk;snums.length;s){ if(nums[min]nums[s])mins; } swap(nums,min,k); } } public void swap(int []nums,int i,int j){ int tmpnums[i]; nums[i]nums[j]; nums[j]tmp; } }78子集解法题解中的迭代的解法利用索引的二进制来标记当次应该哪几位。所有的子集一共有2的n次方种。刚好对应所有的索引的二进制其中索引的二进制中的第i位为1表示第i位在此子集中。比如例子nums[1,2,3]其中n3,7可以表示为111意思是123这三个数都在这个子集中。再比如3的二进制是011,那么这个子集中的数据有23。这样就将2的n次方种子集情况全部都表示出来了。class Solution { public ListListInteger subsets(int[] nums) { ListListIntegerrnew ArrayList(); ListIntegeronew ArrayList(); int nnums.length; for(int m0;m(1n);m){ o.clear(); for(int i0;in;i){ if((m(1i))!0){ o.add(nums[i]); } } r.add(new ArrayList(o)); } return r; } }17电话号码的字母组合解法题解中的回溯解法用索引k记录现在访问到了第几个数字如果k等于n(数字字符串的长度)可以输出了。否则用i记录循环到了k个数字的第几个字符如果后面那一层递归结束了当前i就往后挪如果当前的i超出范围限制本层递归结束回到前面那一层的递归中前面那一层的递归中的i往后挪一个。class Solution { public ListString letterCombinations(String digits) { ListStringrnew ArrayList(); MapCharacter,Stringmapnew HashMap(); map.put(2,abc); map.put(3,def); map.put(4,ghi); map.put(5,jkl); map.put(6,mno); map.put(7,pqrs); map.put(8,tuv); map.put(9,wxyz); b(r,digits,0,map,new StringBuffer()); return r; } public void b(ListStringr,String d,int k,MapCharacter,Stringmap, StringBuffer sb){ int nd.length(); if(kn){ r.add(sb.toString()); }else{ char cd.charAt(k); String smap.get(c); for(int i0;is.length();i){ sb.append(s.charAt(i)); b(r,d,k1,map,sb); sb.deleteCharAt(k); } } } }看了题解后在自己之前的做法的基础上写的解法我之前的做法是先创建一个数字字母映射表我使用的是一个ListListInteger集合其中外层索引对应数字内层的存储的是字母。然后我在如何回溯这块被卡住了。参考题解中的回溯的方法在递归函数的参数中定义一个索引k当k等于输入的digits的长度的时候说明已经凑齐了n个字符将StringBuffer输出。当k小于输入的digits的长度的时候定义一个for循环一次指向数字对应的那些字符将此刻i指向的数字加入到StringBuffer中然后递归地找下一个数字对应的字符。当后面的找完了之后复原将这个字符删掉然后此刻的i指针会自动指向此数字的下一个元素。如果i指针超出了它的界限也就到了这个层递归的尽头这个数字的前一个数字的i指针开始往后面挪。过程比较复杂时间有限就解释这么多吧。class Solution { public ListString letterCombinations(String digits) { ListStringrnew ArrayList(); //num为数字到字母的索引表 ListListCharacterdcreateNum(); int ndigits.length(); char []chdigits.toCharArray(); //nums里面放着索引表里面的索引 int []numsnew int [n]; for(int i0;in;i){ nums[i]ch[i]-2; } f(r,d,nums,0,new StringBuffer ()); return r; } public void f(ListStringr,ListListCharacterd,int []nums,int k,StringBuffer sb){ int nnums.length; if(kn){ r.add(sb.toString()); }else{ int indexnums[k]; ListCharacterld.get(index); for(int i0;il.size();i){ sb.append(l.get(i)); f(r,d,nums,k1,sb); sb.deleteCharAt(k); } } } //建立数字到字母的对应关系 public ListListCharacter createNum(){ ListListCharacternumnew ArrayList(); for(int i0;i8;i){ if(i7){ ListCharacterlnew ArrayList(); l.add((char)(i*3a1)); l.add((char)(i*3b1)); l.add((char)(i*3c1)); l.add((char)(i*3d1)); num.add(l); }else if(i6){ ListCharacterlnew ArrayList(); l.add((char)(i*3a1)); l.add((char)(i*3b1)); l.add((char)(i*3c1)); num.add(l); }else if(i5){ ListCharacterlnew ArrayList(); l.add((char)(i*3a)); l.add((char)(i*3b)); l.add((char)(i*3c)); l.add((char)(i*3d)); num.add(l); }else{ ListCharacterlnew ArrayList(); l.add((char)(i*3a)); l.add((char)(i*3b)); l.add((char)(i*3c)); num.add(l); } } return num; } }39组合总和解法题解中的回溯解法定义一个遍历指针k从第一个数字遍历到最后一个数字每次都有两个选择用现在指针指着的数字和不用此指针指着的数字不用就是直接递归到指针指着的下一个数字的情况如果用判断此时的t-candidates[k]t为target减去此时的列表中已有的数字后的剩余的数字)是否大于或者等于0如果是的说明可以用递归调用递归函数其中参数t要更新为t-candidates[k]参数k不变。整个递归函数的出口有两个一个是指针k指向n超出了数组的索引上限。一个是参数t变为0说明target已经被减完了因为每次都是两种情况用第k个数字更新t的值和不用第k个数字更新指针往后面移动。所以整个过程就像是一个二叉树。题解中的那个二叉树就很生动形象。class Solution { public ListListInteger combinationSum(int[] candidates, int target) { int ncandidates.length; ListIntegeronew ArrayList(); ListListIntegerrnew ArrayList(); b(r,o,0,target,candidates); return r; } public void b(ListListIntegerr,ListIntegero,int k,int t,int[] nums){ int nnums.length; if(kn)return; if(t0){ r.add(new ArrayList(o)); return; } b(r,o,k1,t,nums); if(t-nums[k]0){ o.add(nums[k]); b(r,o,k,t-nums[k],nums); o.remove(o.size()-1); } } }自己写的未完成的解法class Solution { public ListListInteger combinationSum(int[] candidates, int target) { int ncandidates.length; ListIntegeronew ArrayList(); ListListIntegerallnew ArrayList(); for(int m1;m(1n);m){ o.clear(); for(int i0;in;i){ if(m(1i)){ o.add(candidates[i]); } } all.add(new ArrayList(o)); } for(int i0;iall.size();i){ ListIntegerlall.get(i); } } }22括号生成解法题解中的递归回溯的解法利用递归函数来解决这道题目定义五个参数res是结果l表示左括号有多少个r表示右括号有多少个sb表示现在在目标里的括号的情况。当右括号的数量等于n的时候左括号肯定已经到n了此时是一种目标情况我们将这种情况加入到结果中去。先加入左括号和先加入右括号不影响的因为不管是哪种都需要将状态还原。class Solution { public ListString generateParenthesis(int n) { ListStringresnew ArrayList(); b(res,0,0,n,new StringBuffer()); return res; } public void b(ListStringres,int l,int r,int n,StringBuffer sb){ if(rn){ res.add(sb.toString()); } if(ln){ sb.append((); b(res,l1,r,n,sb); sb.deleteCharAt(sb.length()-1); } if(rl){ sb.append()); b(res,l,r1,n,sb); sb.deleteCharAt(sb.length()-1); } } }自己写的递归回溯的解法定义两个变量l表示左括号的数量r表示右括号的数量。用StringBuffer来记录已经添加到字符串的元素。递归函数的出口是当左括号的数量等于右括号的数量等于n将StringBuffer的值转换为String类型添加到结果中去。否则如果左括号的数量比右括号多且左括号的数量等于n说明左括号已经用光了。将所有的右括号都添加进去然后通过递归函数的下一层递归将结果输出。然后在本层递归中将StringBuffer还原到原来的样子。如果左括号的数量不等于n那么左括号和右括号都可以顺序在这里不重要反正最后状态都是要还原的。class Solution { public ListString generateParenthesis(int n) { int l0,r0; ListStringresnew ArrayList(); b(n,res,l,r,new StringBuffer()); return res; } public void b(int n,ListStringres,int l,int r,StringBuffer sb){ if(lrln){ res.add(sb.toString()); }else{ if(lr){ sb.append((); b(n,res,l1,r,sb); sb.deleteCharAt(sb.length()-1); }else{//lr if(ln){//只能加右括号了 for(int i0;in-r;i){ sb.append()); } b(n,res,n,n,sb); for(int i0;in-r;i){ sb.deleteCharAt(sb.length()-1); } }else{//左右括号都可以加 sb.append((); b(n,res,l1,r,sb); sb.deleteCharAt(sb.length()-1); sb.append()); b(n,res,l,r1,sb); sb.deleteCharAt(sb.length()-1); } } } } }79单词搜索解法题解中的递归迭代解法题解中比较新颖的思路在将上下左右那块代码用一个二维数组加上for循环给简化了。我自己做的时候是直接一个一个调的。还有更新标记数组这块的功能也是这块的方法也是比我的简洁。归其原因我感觉是我对这个过程理解的不够深入题解中如果遇到返回false的情况是放到最后才所有的标记数组返回重置的。而我是直接重置当然这也可能正是我的解法的运行时间更短的原因class Solution { public boolean exist(char[][] board, String word) { int mboard.length; int nboard[0].length; if(m*nword.length())return false; boolean[][] pnew boolean[m][n]; for(int i0;im;i){ for(int j0;jn;j){ if(b(board,i,j,word,0,p)){ return true; } } } return false; } public boolean b(char[][] board,int i,int j,String word,int k,boolean[][] p){ if(board[i][j]!word.charAt(k)){ return false; } if(kword.length()-1)return true; p[i][j]true; int [][] dirs{{-1,0},{1,0},{0,-1},{0,1}}; boolean flagfalse; for(int []dir:dirs){ int niidir[0],njjdir[1]; if(0niniboard.length0njnjboard[0].length!p[ni][nj]){ if(b(board,ni,nj,word,k1,p)){ flagtrue; break; } } } p[i][j]false; return flag; } }自己写的递归迭代的解法首先遍历数组找到所有等于word的第一个字符的元素记录下它的索引。然后递归根据索引进行递归介绍一下我的递归方法的六个参数中的特殊的i横坐标的索引j纵坐标的索引k表示目前找的word中的字符的索引p标记这个位置是否访问过。在递归函数中如果遍历到了第n-1个元素可以返回true这是此递归方法的出口。先找现在的位置的上下左右看有没有和word中的下一个字符相同的如果有将标记数组p中的对应值标记为已访问true。判断往那个方向的递归是否是true如果不是将标记数组重新置为false。注意标记数组p一定要在往那个方向递归前就更新不然标记数组是不会变的这也是为什么没有直接将判断下一个方向是否真的递归函数放在if的判断语句中。坐这个方法的时候我最开始是直接将判断下一个方向是否真的递归函数放在if的判断语句中的这样无法发挥标记数组的作用。class Solution { public boolean exist(char[][] board, String word) { int mboard.length; int nboard[0].length; if(m*nword.length())return false; for(int i0;im;i){ for(int j0;jn;j){ if(board[i][j]word.charAt(0)){ boolean [][]pnew boolean[m][n]; if(b(board,i,j,word,0,p)){ return true; } } } } return false; } public boolean b(char[][] board,int i,int j,String word,int k,boolean[][] p){ if(kword.length()-1)return true; boolean f1,f2,f3,f4; if(i!0!p[i-1][j]board[i-1][j]word.charAt(k1)){ p[i][j]true; f1b(board,i-1,j,word,k1,p); if(f1){ return true; }else{ p[i][j]false; } } if(i!board.length-1!p[i1][j]board[i1][j]word.charAt(k1)){//p2:不能向下 p[i][j]true; f2b(board,i1,j,word,k1,p); if(f2){ return true; }else{ p[i][j]false; } } if(j!0!p[i][j-1]board[i][j-1]word.charAt(k1)){//p3:不能向左 p[i][j]true; f3b(board,i,j-1,word,k1,p); if(f3){ return true; }else{ p[i][j]false; } } if(j!board[0].length-1!p[i][j1]board[i][j1]word.charAt(k1)){//p4:不能向右 p[i][j]true; f4b(board,i,j1,word,k1,p); if(f4){ return true; }else{ p[i][j]false; } } return false; } }自己写的忽略环的报错我的思路是这样的首先遍历数组找到若干个等于word的第一个字母的字母的索引然后再用递归的方法每次都递归他的三个方向写了四个但是有一个方向是不行的不能访问来时的方向比如上一层递归是往右那么这一层就不可以往左边走。这个方法在一般的情况下是可以的但是当出现大量的相同的元素特别是当四个元素刚好凑了一个四方格那么这个限制其不能走之前的路的方法就失效了。因此需要一个更好的方法来递归我准备采用数组试一试。class Solution { public boolean exist(char[][] board, String word) { int mboard.length; int nboard[0].length; if(m*nword.length())return false; for(int i0;im;i){ for(int j0;jn;j){ if(board[i][j]word.charAt(0)){ if(b(board,i,j,word,0,-1)){ return true; } } } } return false; } public boolean b(char[][] board,int i,int j,String word,int k,int p){ if(kword.length()-1)return true; if(i!0board[i-1][j]word.charAt(k1)b(board,i-1,j,word,k1,1)p!2){//p1:不能向上 return true; } if(i!board.length-1board[i1][j]word.charAt(k1)b(board,i1,j,word,k1,2)p!1){//p2:不能向下 return true; } if(j!0board[i][j-1]word.charAt(k1)b(board,i,j-1,word,k1,3)p!4){//p3:不能向左 return true; } if(j!board[0].length-1board[i][j1]word.charAt(k1)b(board,i,j1,word,k1,4)p!3){//p4:不能向右 return true; } return false; } }131分割回文串解法题解中的回溯动态代理的解法用一个二维数组来记录从i到j是否是一个回文串二维数组通过动态代理的方式来记录回文串。f[i][j]f[i1][j-1]s.charAt(i)s.charAt(j),注意i要从高到低遍历。然后用回溯完成剩下的工作在递归函数中如果kn说明ans中已经全都是回文串了并且还能完好地拼成s。以k为起点遍历后面所有的值以后面的每一个值为终点一个一个找。class Solution { int n; boolean[][] f; ListStringansnew ArrayList(); ListListStringresnew ArrayList(); public ListListString partition(String s) { ns.length(); fnew boolean[n][n]; for(int i0;in;i){ Arrays.fill(f[i],true); } for(int in-1;i0;i--){ for(int ji1;jn;j){ f[i][j]f[i1][j-1]s.charAt(i)s.charAt(j); } } dfs(s,0); return res; } public void dfs(String s,int k){ if(kn){ res.add(new ArrayList(ans)); return; } for(int ik;in;i){ if(f[k][i]){ ans.add(s.substring(k,i1)); dfs(s,i1); ans.remove(ans.size()-1); } } } }自己写的递归回溯的解法记录字符串的长度为n 然后按照字符串的长度分成1到n分别递归求出。递归函数的各个参数介绍l记录所有的满足条件的情况n:当前遍历的是将字符串分成n组的情况k当前遍历到了第几块。d后面的组最多可以一共有多少个字符ns一个数组记录着每个组有多少个字符。class Solution { public ListListString partition(String s) { int ns.length(); ListListStringrnew ArrayList(); for(int i1;in;i){//i表示将s分成i份 b(r,s,i,0,n,new int[i]); } return r; } public void b(ListListStringl, String s,int n,int k,int d,int []ns){ if(kn){ // 添加总长度检查 int totalLength 0; for(int len : ns){ totalLength len; } if(totalLength ! s.length()){ return; // 长度不匹配直接返回 } ListStringllnew ArrayList(); boolean flagtrue; int start0; for(int i0;in;i){ int endstartns[i]; if(!check(s.substring(start,end))){ flagfalse; } startend; } if(flag){ start0; for(int i0;in;i){ int endstartns[i]; ll.add(s.substring(start,end)); startend; } } if(ll.isEmpty()){ return; } l.add(ll); return ; } int maxs.length()-n1; for(int i1;imax;i){ dd-i; ns[k]i; if(d0)break; b(l,s,n,k1,d,ns); ddi; } } public boolean check(String s){ StringBuffer sbnew StringBuffer(); for(int is.length()-1;i0;i--){ sb.append(s.charAt(i)); } String sssb.toString(); return ss.equals(s); } }51N皇后解法题解中的运用集合的回溯解法题解中的解法逻辑非常清晰用三个集合记录相关的点是否不可以放皇后列集合,yx斜线集合y-x斜线集合。递归回溯函数每次以行号为递进关系当行号为n的时候将数据收集到结果中返回。当行号不为n的时候遍历列号如果当前的列号在列集合中或者当前行和列构成的点在两个斜线集合中的时候中断。找到一个不在里面的点往下一行递归。递归结束后将三个标记作用的集合的状态全部都恢复原状class Solution { public ListListString solveNQueens(int n) { int []queuenew int[n]; Arrays.fill(queue,-1); ListListStringresnew ArrayList(); SetIntegercsnew HashSet(); SetIntegerd1new HashSet();//yx SetIntegerd2new HashSet(); backtrace(res,queue,0,cs,d1,d2); return res; } public void backtrace(ListListStringres,int[]queue,int row,SetIntegercs,SetIntegerd1,SetIntegerd2){ int nqueue.length; if(rown){ ListStringlnew ArrayList(); for(int i0;in;i){ char[] chnew char[n]; Arrays.fill(ch,.); ch[queue[i]]Q; String snew String(ch); l.add(s); } res.add(l); } for(int i0;in;i){//表示列号 if(cs.contains(i))continue; int x1irow;//yx if(d1.contains(x1))continue; int x2row-i;//y-x if(d2.contains(x2))continue; cs.add(i); d1.add(x1); d2.add(x2); queue[row]i; backtrace(res,queue,row1,cs,d1,d2); cs.remove(i); d1.remove(x1); d2.remove(x2); queue[row]-1; } } }自己写的递归回溯的解法这道题目我做了一整天中间断断续续的。首先定义一个int型的flag[n][n]作为标记数组如果有女王在相应的位置标记就加一同时当我们回溯出来要回复数组原来的状态的时候就减一调整标记数组的这部分我出过很多问题甚至这道题目做这么久就是因为标记数组没搞明白应该怎么调整。经过观察发现所有的女王都在不同行不同列所以只需要记录每行的列号就可以了。先遍历第一行的元素让他们一个一个变成女王再递归地找别的行的女王如果最后一行可以有女王就将结果整出来。如果最后一行不能有女王第一行中就换下一个元素来当女王。其中要注意的点有在使用递归函数前要改变标记数组的状态使用后要将标记数组的状态变回来。在我的代码中第一行的代码也是这样有一次的报错就是因为第一行的情况中没有更新数据没有将第一行的列号添加到该有的写入结果的数组中去。class Solution { int [][]flag; public ListListString solveNQueens(int n) { ListListStringresnew ArrayList(); ListIntegerlnew ArrayList();//l记录每一行的第皇后的纵坐标 flagnew int [n][n]; for(int c0;cn;c){ l.add(c); b(res,l,0,c); l.remove(l.size()-1);//表示第0行的第c个位置现在是皇后。 } return res; } public void b(ListListStringres,ListIntegerl,int r,int c){ int nflag.length; if(l.size()n){ ListStringrsnew ArrayList(); for(int i0;il.size();i){ StringBuffer sbnew StringBuffer(); for(int j0;jn;j){ if(jl.get(i)){ sb.append(Q); }else{ sb.append(.); } } rs.add(sb.toString()); } res.add(rs); return; } changeFlag(r,c,1); for(int i0;in;i){ if(flag[r1][i]0){ l.add(i); b(res,l,r1,i); l.remove(l.size()-1); } } changeFlag(r,c,-1); } public void changeFlag(int r,int c,int f){//f可以是1也可以是-1 flag[r][c]f; for(int j0;jflag.length;j){ if(j!c){//横着的 flag[r][j]f; } if(j!r){//纵着的 flag[j][c]f; } } int irc;//计算yxi为最下边的 int jiflag.length-1?i-flag.length1:0; iiflag.length-1?flag.length-1:i; while(i0jflag.length){ if(i!r){ flag[i--][j]f; }else{ i--; j; } } //y-x jc-r; ij0?0:r-c; jj0?j:0; while(iflag.lengthjflag.length){ if(i!r){ flag[i][j]f; }else{ i; j; } } } }35搜索插入位置解法二分查找的经典方法两个指针low和high,一次缩减一半的检索范围。class Solution { public int searchInsert(int[] nums, int target) { int low0; int highnums.length-1; int mid0; while(lowhigh){ mid(lowhigh)/2; if(nums[mid]target){ return mid; }else if(nums[mid]target){ highmid-1; }else{ lowmid1; } } return low; } }74搜索二维矩阵解法自己的二分查找的方法先查找每组的第一个元素找到目标值在的那一行再在这一行里面再用二分查找找到对应的值。class Solution { public boolean searchMatrix(int[][] matrix, int target) { int lowo0; int highomatrix.length-1; int mido0; while(lowohigho){ mido(lowohigho)/2; if(matrix[mido][0]target){ lowomido1; }else{ highomido-1; } } int low0; int highmatrix[0].length-1; int mid0; if(higho0)return false; while(lowhigh){ mid(lowhigh)/2; if(matrix[higho][mid]target){ return true; }else if(matrix[higho][mid]target){ lowmid1; }else{ highmid-1; } } return false; } }34在排序数组中查找元素的第一个和最后一个位置解法自己写的二分查找法首先用正常的二分查找法找到与目标值一样的值的位置再向前向后找到与目标值一样的一些值。就可以了。class Solution { public int[] searchRange(int[] nums, int target) { int n nums.length; int low0; int highnums.length-1; int f0; int r0; if(numsnull||nums.length0||targetnums[low]||targetnums[high])return new int[]{-1,-1}; while(lowhigh){ int mid(lowhigh)/2; if(nums[mid]target){ highmid-1; }else if(nums[mid]target){ lowmid1; }else{ lowmid; break; } } if(nums[low]target){ flow; rlow; while(f0nums[f]target){ f--; } f; while(rn-1nums[r]target){ r; } r--; }else{ return new int[]{-1,-1}; } return new int[]{f,r}; } }33搜索旋转排序数组解法题解中的二分查找的方法先判断分界点在中间值的前面还是后面总之分四种情况所有的情况都找完覆盖掉之后就可以了class Solution { public int search(int[] nums, int target) { int nnums.length; if(n0||n1target!nums[0])return -1; int l0; int rn-1; while(lr){ int mid(lr)/2; if(nums[mid]target)return mid; //分界点在数组中间点的后半段前半段是有序的 if(nums[0]nums[mid]){ if(nums[0]targettargetnums[mid]){ rmid-1; }else{ lmid1; } //分界点在数组中间点的后半段前半段是有序的 }else{ if(nums[mid]targettargetnums[n-1]){ lmid1; }else{ rmid-1; } } } return -1; } }自己的线性查找的方法直接一个一个比较观察发现第一个和目标值一样的的下表就是我们要输出的。但是这个我自己写的方法的时间复杂度是On我不知道为什么通过了class Solution { public int search(int[] nums, int target) { int result-1; for(int i0;inums.length;i){ if(nums[i]target){ resulti; break; } } return result; } }153寻找旋转排列数组中的最小值解法题解中的二分查找法二分查找每次取中间值。如何中间值比最右边high的值要大说明最小值在中间值的右边如果比最右边的值小说明最小值在中间值的左边。class Solution { public int findMin(int[] nums) { int nnums.length; int low0,highn-1; while(lowhigh){ int mid(lowhigh)/2; if(nums[mid]nums[high]){ //说明最小值在中间值的右边 lowmid1; }else{ highmid; } } return nums[low]; } }自己写的找到第一个比前面小的数字的解法由题意知原来所有的元素都是递增的只是有些交换了位置。观察发现第一个比前面小的元素就是我们要找的。比后一个元素小的元素的个数如果刚好是n-1说明元素刚好反转了n次最小的元素在第0个位置否则就在count1个位置。class Solution { public int findMin(int[] nums) { int nnums.length; int count0; for(int i0;in-1;i){ if(nums[i]nums[i1]){ count; }else{ break; } } if(countn-1){ return nums[0]; }else{ return nums[count1]; } } }4寻找两个正序数组的中位数解法题解中的二分查找的方法两个数组的长度分别是m,n总共为t如果t是奇数我们要找到第t/21个数字索引为t/2如果是偶数我们要找到第t/2和第t/21个数字相加除以2。定义一个找到两个数组中第k个元素的方法每次都抛弃前k/2个元素因为两个数组都是单调递增的前k/2个元素一定都小于我们要找的元素一定都是目标元素。class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int mnums1.length; int nnums2.length; int mid(mn)/2; if((mn)%20){ return (getmid(nums1,nums2,mid)getmid(nums1,nums2,mid1))/2.0; }else{ return getmid(nums1,nums2,mid1); } } public int getmid(int []n1,int []n2,int k){ int mn1.length, nn2.length; int i0,j0; while(true){ if(im){ return n2[jk-1]; } if(jn){ return n1[ik-1]; } if(k1){ return Math.min(n1[i],n2[j]); } int hk/2; int niMath.min(hi,m)-1; int njMath.min(hj,n)-1; if(n1[ni]n2[nj]){ kk-(ni-i1); ini1; }else{ kk-(nj-j1); jnj1; } } } }题解中的O(mn)的解法使用归并的方法创建一个新的总的数组将两个数组中所有的值都按照从小到大的顺序插入到新数组中去。因为原来的数组本来就是按照从小到大的顺序排列的定义三个指针分别指向三个数组的最左边的元素比较第一个和第二个数组的指针所指的值将更小的值插入到新数组中去指针右移动。class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int mnums1.length; int nnums2.length; int i0,j0,k0,totalmn; int []tnew int [total]; while(imjn){ if(nums1[i]nums2[j]){ t[k]nums1[i]; }else{ t[k]nums2[j]; } } while(im)t[k]nums1[i]; while(jn)t[k]nums2[j]; int midtotal/2; if(total%20){ return (t[mid]t[mid-1])/2.0; }else{ return t[mid]; } } }自己实现错误的代码class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int mnums1.length; int nnums2.length; int i0,j0; int count 0; while(im||jn){ if(i!mj!n){ if(nums1[i]nums2[j]){ i; }else{ j; } }else if(j!n){ j; }else{ i; } count ; if(count (mn-1)/2)break; } if((mn)%20){ if(imj!n){ float r(nums2[j]nums2[j1])/2; return r; }else if(jni!m){ float r(nums1[i]nums1[i1])/2; return r; }else{ float r(nums1[i]nums2[j])/2; return r; } }else{ return Math.min(nums1[i],nums2[j]); } } }自己写的没跑通的解法首先计算mn如果为奇数说明我们最后要留下一个数字如果为偶数说明我们最后要留下两个数字。还要注意两个数组的长度问题可能一个数组的个数比较少然后数值也刚好偏向于一边在比较的过程中很可能消失了这种情况也要排除掉比如123和456789。我计划采用每次取两个数组的中间值比较较大的那个将其右边的全部删掉较小的那个将其左边的全部删掉记录左边和右边各删掉了多少个元素少的那边两个数组比较再接着删除。比如123和456789。第一次2和6比较左边删掉1右边删掉789然后左边再删两个2和4中删掉23和4中删掉3然后要解决两个数组其中一个在比较过程中消失的情况我计划用一个条件分支直接将其分开讨论时间有限我觉得我在这道题目上已经浪费了太多的时间很有可能我做不出来是我因为我还没有掌握正确的方法我准备直接去看答案如果以后有时间并且刚好我还对这道题目感兴趣或许我可以再把这个没那么好的思路实现一下。天有点像谈了一段没有结果的恋爱然后要分开去找版本答案的感觉虽然遗憾但是再拖下去对我没好处。class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int mnums1.length; int nnums2.length; int l10,l20,h1m,h2n; int n1m,n2n; int r0,l0; while(n1!0n2!0(l1h1||l2h2)){ int m1(l1h1)/2,m2(l2h2)/2; r0;l0; if(nums1[m1]nums2[m2]){ lm1-l1; n1n1-l; l1m11; rh2-m2; h2m2; n2n2-r; }else if(nums1[m1]nums2[m2]){ rh1-m1; h1m1; n1n1-r; lm2-l2; l2m21; n2n2-l; } if(lr){ int dr-l; for(int i0;id;i){ if(n1!0n2!0){ if(nums1[h1]nums2[h2]){ h1--; n1--; r--; }else{ h2--; n2--; r--; } } } }else if(lr){ int dl-r; for(int i0;id;i){ if(n1!0n2!0){ if(nums1[l1]nums2[l2]){ l1; n1--; l; }else{ l2; n2--; l; } } } } } //从while循环里出来先检查左边和右边删除的元素是否一样 if(n1!0){ if(lr){ int dl-r; for(int i0;id;i){ l1; } }else if(rl){ int dr-l; for(int i0;id;i){ h1--; } } if((l1h1)%20){ int mid(l1h1)/2; return (nums1[mid]nums1[mid1])/2; }else{ return nums1[(l1h1)/2]; } }else if(n20){ if(lr){ int dl-r; for(int i0;id;i){ l2; } }else if(rl){ int dr-l; for(int i0;id;i){ h2--; } } if((l2h2)%20){ int mid(l2h2)/2; return (nums2[mid]nums2[mid1])/2; }else{ return nums2[(l2h2)/2]; } } return -1; } }20有效的括号解法自己做的方法遍历字符串如果是前括号就入栈如果是后括号就和出栈的元素进行不比较如果出现不一样的返回false。注意可能先入栈的是后括号这种栈会是空的所以在判断的时候可以加上判断栈是否为空的这个东西来判断class Solution { public boolean isValid(String s) { DequeCharacterstacknew LinkedList(); for(int i0;is.length();i){ if(s.charAt(i)(||s.charAt(i){||s.charAt(i)[){ stack.push(s.charAt(i)); }else{ char cl; char cc s.charAt(i); if(cc))c(; if(cc])c[; if(cc})c{; if(stack.isEmpty()||c!stack.pop())return false; } } if(!stack.isEmpty())return false; return true; } }155最小栈解法自己写的用列表的解法定义一个int型的列表还有一个int型的变量来记录最小值的索引。然后就按照方法来解决。没什么难度。class MinStack { private ListIntegerstack; private int min; public MinStack() { stacknew ArrayList(); min-1; } public void push(int val) { stack.add(val); if(min-1){ min0; }else{ if(valstack.get(min)){ minstack.size()-1; } } } public void pop() { if(!stack.isEmpty()){ if(minstack.size()-1){ min0; for(int i0;istack.size()-1;i){ if(stack.get(i)stack.get(min)){ mini; } } } stack.remove(stack.size()-1); } } public int top() { return stack.get(stack.size()-1); } public int getMin() { return stack.get(min); } } /** * Your MinStack object will be instantiated and called as such: * MinStack obj new MinStack(); * obj.push(val); * obj.pop(); * int param_3 obj.top(); * int param_4 obj.getMin(); */394字符串解码解法自己写的一个栈的解法从左到右遍历字符串一个一个记录如果是数字判断其是否是第一个如果有两个数字连在一起要处理一下如果是前中括号就将前中括号数量加一。如果是后中括号就开始更新将之前的都结束掉。看了一眼题解感觉题解的代码量也好长我还是不看了吧不一定有我的好。class Solution { public String decodeString(String s) { DequeCharacterstacknew LinkedList(); StringBuilder resultnew StringBuilder(); int count0; int countNum-1; for(int i0;is.length();i){ char cs.charAt(i); if(c[||0cc9){ stack.push(c); if(0cc9){ //我发现问题了问题在下面计数的时候的地方100无法被识别出来 if(countNum!-1){ countNumcountNum*10c-0; stack.pop(); stack.pop(); stack.push((char)(countNum0)); }else{ countNumc-0; } } if(c[){ count; countNum-1; } } if(accz){ if(count0){ result.append(c); }else{ stack.push(c); } } if(c]){ count--; char ccstack.pop(); StringBuilder sbnew StringBuilder(); sb.append(Character.toString(cc)); while(accccz){ ccstack.pop(); if(accccz){ sb.append(Character.toString(cc)); }else{ //是[ break; } } //反转sb String ssbsb.toString(); StringBuilder sbNewnew StringBuilder(); for(int jssb.length()-1;j0;j--){ sbNew.append(ssb.charAt(j)); } String sbNewSsbNew.toString(); if(!stack.isEmpty()){ int numstack.pop()-0; if(num0){ String rsbNewS.repeat(num); if(count0){ for(int x0;xr.length();x){ stack.push(r.charAt(x)); } }else{ for(int x0;xr.length();x){ result.append(r.charAt(x)); } } } } } } return result.toString(); } }差两个例子就通过的解法除了下面的这个例子别的都通过了。class Solution { public String decodeString(String s) { DequeCharacterstacknew LinkedList(); StringBuilder resultnew StringBuilder(); int count0; int time0; int countNum-1; for(int i0;is.length();i){ char cs.charAt(i); if(c[||0cc9){ stack.push(c); if(0cc9){ //我发现问题了问题在下面计数的时候的地方100无法被识别出来 if(countNum!-1){ countNumcountNum*10c-0; stack.pop(); if(time!0)stack.pop(); time; stack.push((char)(countNum0)); }else{ countNumc-0; } } if(c[){ count; countNum-1; time0; } } if(accz){ if(count0){ result.append(c); }else{ stack.push(c); } } if(c]){ count--; char ccstack.pop(); StringBuilder sbnew StringBuilder(); sb.append(Character.toString(cc)); while(accccz){ ccstack.pop(); if(accccz){ sb.append(Character.toString(cc)); }else{ //是[ break; } } //反转sb String ssbsb.toString(); StringBuilder sbNewnew StringBuilder(); for(int jssb.length()-1;j0;j--){ sbNew.append(ssb.charAt(j)); } String sbNewSsbNew.toString(); if(!stack.isEmpty()){ int numstack.pop()-0; if(num0){ String rsbNewS.repeat(num); if(count0){ for(int x0;xr.length();x){ stack.push(r.charAt(x)); } }else{ for(int x0;xr.length();x){ result.append(r.charAt(x)); } } } } } } return result.toString(); } }历史解法一个栈的解法class Solution { public String decodeString(String s) { DequeCharacterstacknew LinkedList(); StringBuilder resultnew StringBuilder(); int count0; int countNum-1; for(int i0;is.length();i){ char cs.charAt(i); if(c[||0cc9){ stack.push(c); if(0cc9){ if(countNum!-1){ countNumcountNum*10c-0; stack.pop(); stack.push((char)(countNum0)); }else{ countNumc-0; } } if(c[){ count; countNum-1; } } if(accz){ if(count0){ result.append(c); }else{ stack.push(c); } } if(c]){ count--; char ccstack.pop(); StringBuilder sbnew StringBuilder(); sb.append(Character.toString(cc)); while(accccz){ ccstack.pop(); if(accccz){ sb.append(Character.toString(cc)); }else{ //是[ break; } } //反转sb String ssbsb.toString(); StringBuilder sbNewnew StringBuilder(); for(int jssb.length()-1;j0;j--){ sbNew.append(ssb.charAt(j)); } String sbNewSsbNew.toString(); if(!stack.isEmpty()){ int numstack.pop()-0; if(0numnum9){ String rsbNewS.repeat(num); if(count0){ for(int x0;xr.length();x){ stack.push(r.charAt(x)); } }else{ for(int x0;xr.length();x){ result.append(r.charAt(x)); } } } } } } return result.toString(); } }三个栈的解法我的想法是创建三个栈数字括号字母但是在写的过程中我想到了一个更好的方法。直接一个栈不就好了吗还省事情。class Solution { public String decodeString(String s) { DequeIntegernumSnew LinkedList(); DequeCharacterkhnew LinkedList(); StringBuilder resultnew StringBuilder(); DequeListCharacterchnew LinkedList(); boolean flagfalse; for(int i0;is.length();i){ char cs.charAt(i); if(0cc9){ numS.push(c-0); } if(accz){ if(!flag){ ListCharacterlistnew ArrayList(); list.add(c); ch.push(list); }else{ ListCharacterlistch.pop(); list.add(c); ch.push(list); } } if(c[){ kh.push([); flagtrue; } if(c]){ flagfalse; kh.pop(); StringBuilder sbnew StringBuilder(); ListCharacterlistch.pop(); for(char cc:list){ String saString.valueOf(cc); sb.append(sa); } String sssb.toString(); int num numS.pop(); String sssss.repeat(num); if(!ch.isEmpty()){ ListCharacterlch.pop(); for(int j0;jsss.length();j){ l.add(sss.charAt(j)); } ch.push(l); }else{ result.append(sss); } } } return result.toString(); } }739每日温度解法题解中的用栈的解法如果栈不为空且现在遍历到的元素大于栈顶元素弹出栈顶元素将用现在遍历到的下标和栈顶做差。然后将现在的这个值得下表入栈这样就可以了。注意栈中存储的是数组中的下标不是值。class Solution { public int[] dailyTemperatures(int[] temperatures) { int ntemperatures.length; int [] answernew int[n]; DequeIntegerstacknew LinkedList(); for(int i0;in;i){ while(!stack.isEmpty()temperatures[i]temperatures[stack.peek()]){ int prestack.pop(); answer[pre]i-pre; } stack.push(i); } return answer; } }自己写的暴力法进化版遍历整个数组每次都遍历然后遇到比自己大的就返回这样有一个例子通不过然后我优化了一下直接在内遍历之前先判断看正在找的元素和他前一位是否相同如果相同就不用再找一遍了然后就全部都通过了。感觉没有体现出来他的算法思想 我还是看看题解是怎么说的吧。class Solution { public int[] dailyTemperatures(int[] temperatures) { int ntemperatures.length; int [] answernew int[n]; for(int i0;in-1;i){ if(i!0answer[i-1]0temperatures[i]temperatures[i-1]){ answer[i]answer[i-1]-1; }else{ for(int ji1;jn;j){ if(temperatures[j]temperatures[i]){ answer[i]j-i; break; } } } } return answer; } }历史解法报错超出时间限制的解法直接遍历暴力法class Solution { public int[] dailyTemperatures(int[] temperatures) { int ntemperatures.length; int [] answernew int[n]; for(int i0;in-1;i){ for(int ji1;jn;j){ if(temperatures[j]temperatures[i]){ answer[i]j-i; break; } } } return answer; } }84柱状图中最大的矩阵解法单调栈的做法AI的解法确保栈里的元素是单调递增的如果遍历到的元素比栈顶元素不小将目前的元素的索引入栈。当比栈顶元素小的时候出栈开始结算栈里的数据如果此刻栈为空经历了刚刚的出栈那么说明此时遍历的元素就是前面所有元素的最小元素宽度widthi高度等于现在的这个元素。如果此时栈不为空高度等于栈顶元素的高宽度等于i-栈顶元素索引-1计算面积如果比之前的最大面积还要大就取代他重复这个过程直到最后。class Solution { public int largestRectangleArea(int[] heights) { int n heights.length; DequeInteger stack new LinkedList(); int max0; for(int i0;in;i){ int hin?0:heights[i]; while(!stack.isEmpty()hheights[stack.peek()]){ int heightheights[stack.pop()]; int widthstack.isEmpty()?i:i-stack.peek()-1; maxMath.max(max,height*width); } stack.push(i); } return max; } }核心思想以每个柱子为高度它能向两边扩展多远对于输入[2,1,5,6,2,3]我们想知道每个柱子能形成的最大矩形第一步理解问题本质我们要找的是由相邻柱子组成的最大矩形矩形的高度由最矮的柱子决定矩形的宽度由左右边界决定第二步单调栈的作用单调栈帮我们快速找到每个柱子的左右边界左边界左边第一个比当前柱子矮的柱子位置右边界右边第一个比当前柱子矮的柱子位置第三步具体过程演示以 [2,1,5,6,2,3] 为例索引: 0 1 2 3 4 5高度: 2 1 5 6 2 3我们用栈来存储索引保持栈内对应的高度是递增的步骤1i0高度2栈空入栈stack [0]步骤2i1高度11 2破坏递增弹出索引0高度 heights[0] 2左边界 栈空 ? -1 : stack.peek() -1右边界 i 1宽度 1 - (-1) - 1 1面积 2 × 1 2入栈1stack [1]步骤3i2高度55 1保持递增入栈stack [1,2]步骤4i3高度66 5保持递增入栈stack [1,2,3]步骤5i4高度22 6弹出索引3高度 6左边界 stack.peek() 2右边界 i 4宽度 4 - 2 - 1 1面积 6 × 1 62 5弹出索引2高度 5左边界 stack.peek() 1右边界 i 4宽度 4 - 1 - 1 2面积 5 × 2 10 ←找到当前最大值2 1入栈4stack [1,4]步骤6i5高度33 2入栈stack [1,4,5]步骤7处理栈中剩余元素虚拟右边界n6弹出索引5高度3宽度6-4-11面积3弹出索引4高度2宽度6-1-14面积8弹出索引1高度1宽度6-(-1)-16面积6历史解法另一个错误的做法不做了看答案class Solution { public int largestRectangleArea(int[] heights) { int nheights.length; DequeIntegerstacknew LinkedList(); int i0; int j1; int maxheights[i]*1; while(in){ if(jnheights[j]heights[i]){ int areaheights[i]*(j-i); if(areamax)maxarea; if(jn-1){ i; } j; }else{ i; ji1; } } return max; } }算法错误的做法想错了先排序后计算的方法是行不通的。会改变整个数组的情况。class Solution { public int largestRectangleArea(int[] heights) { int nheights.length; int max0; Arrays.sort(heights); for(int i0;in;i){ int areaheights[i]*(n-i); if(areamax)maxarea; } return max; } }报错超出时间限制的暴力法直接遍历所有情况得出结果。class Solution { public int largestRectangleArea(int[] heights) { int nheights.length; int max0; for(int i0;in;i){ int minheights[i]; for(int ji;jn;j){ if(heights[j]min)minheights[j]; int areamin*(j-i1); if(areamax)maxarea; } } return max; } }215数组中的第K个最大元素解法题解中的堆的解法这个解法用到了我不会的一种数据结构PriorityQueue还有匿名内部类这种方法得学别的思路倒是跟我的挺像class Solution { public int[] topKFrequent(int[] nums, int k) { MapInteger,Integermapnew HashMap(); for(int num:nums){ map.put(num,map.getOrDefault(num,0)1); } PriorityQueueint[]queuenew PriorityQueueint[](new Comparatorint [](){ public int compare(int []m,int []n){ return m[1]-n[1]; } }); for(Map.EntryInteger,Integerentry:map.entrySet){ int numentry.getKey() int countentry.getValue(); if(queue.size()k){ if(queue.peek()[1]count){ queue.poll(); } queue.offer(new int[]{num,count}); }else{ queue.offer(new int []{nums,count}); } } int retnew int[k]; for(int i0;ik;i){ ret[i]queue.poll()[0]; } return ret; } }借鉴了题解思想的自己写出来的解法题解中的一个思想感觉非常不错虽然最后跑出来没有我第一次单纯用堆排序跑出来的效率高但是也很不错了下面是思路首先用HashMap将所有的数字和数字的数量都收集起来再用一个数组all将其全部都变成可以直接索引的。然后分析题目我们可以构建一个数量为k的小根堆当数量不够k的时候就直接往小根堆数组里面加当数量够了之后直接开始构建小根堆。然后后面的数据就跟小根堆的第一个元素比较目前最小的值如果比小根堆里最小的值还小就直接将其替换掉然后让其下沉到他该在的位置保证小根堆里的第一个元素是最小的。遍历完all数组记录着nums数组的数字和个数的数组后再将从结果数组中提取出我们要的答案在本代码中我的是直接找到所有奇数索引的值。记录做这个思路时的一个关键报错在遇到像是这道题目需要用数组来记录一对数据为一组比如这道题目中需要记录数字本身还有数字对应的个数在遍历的时候一定要直接在外层for遍历的时候直接写ii2不要写i然后在循环体里面再i;这样很容易报错切记切记for(int i0;i2*size;i){ if(countk){ int numcount*2; r[num]all[i]; r[num1]all[i]; swap(r,0,num); swap(r,1,num1); beHeap(r,0,count1); count; }else{ if(all[i1]r[1]){ r[0]all[i]; r[1]all[i]; swap(r,0,2*k-2); swap(r,1,2*k-1); beHeap(r,0,k); } } } int c0; for(int i0;i2*k;i){ result[c]r[i]; } return result; }自己做的大根堆的解法首先用HashMap来将所有的数据的值和数量统计下来在用一个数组all将所有的值和数量放进去一个数字一个数量然后构建大根堆只不过这些东西都需要稍微调整一下。别的倒是都还好。刚刚看了一眼题解我发现题解也是将数字和数量都存储到同一个数组里面我感觉我的做法更好理解。然后我看到了一种优化的点大概是这样的我的方法是构建大根堆然后除了最初构建大根堆后面还需要不断地交换第一个和最后一个元素但是如果构建的是小根堆就不需要了小根堆的叶子节点一定是大于他的非叶子结点的所以如果K的值大于叶子节点的数量的时候叶子节点就可以直接抄到结果的数组中去。剩下的只需要在非叶子节点上再稍微拍个序就好了。我刚刚试了一下这个方法不行。是我理解错了题解中的思想。此思路是失败的思考前面我自己做的是正确的我想优化但优化错了class Solution { MapInteger,Integermapnew HashMap(); public int[] topKFrequent(int[] nums, int k) { int nnums.length; int []resultnew int [k]; for(int i0;in;i){ int valuemap.getOrDefault(nums[i],0)1; map.put(nums[i],value); } //size表示有多少对 int sizemap.size(); int [] allnew int [2*size]; SetIntegerkeysmap.keySet(); int index0; for(Integer key:keys){ Integer valuemap.get(key); all[index]key; all[index]value; } buildHeap(all); for(int i0;ik;i){ result[i]all[0]; swap(all,0,2*(size-1-i)); swap(all,1,2*(size-1-i)1); beHeap(all,0,size-i-1); } return result; } public void buildHeap(int []nums){ int nnums.length/2; for(int in/2-1;i0;i--){ beHeap(nums,i,n); } } //nums两个值看作是一个前面的是键后面的是值n表示有多少对 public void beHeap(int [] nums,int k,int n ){ int ik; while(in/2){ int li2*i1; int ri2*i2; int larili; if(rin-1nums[2*ri1]nums[2*li1])lariri; if(nums[2*lari1]nums[2*i1]){ swap(nums,2*lari1,2*i1); swap(nums,2*lari,2*i); }else{ break; } ilari; } } public void swap(int []a ,int i,int j){ int tmpa[i]; a[i]a[j]; a[j]tmp; } }347前K个高频元素解法题解中的堆的解法这个解法用到了我不会的一种数据结构PriorityQueue还有匿名内部类这种方法得学别的思路倒是跟我的挺像。这个数据结构直接解决了我的那个直接用数组会导致在使用堆排序的时候索引混乱的问题混乱所以想要理清楚要费很大的力气class Solution { public int[] topKFrequent(int[] nums, int k) { MapInteger,Integermapnew HashMap(); for(int num:nums){ map.put(num,map.getOrDefault(num,0)1); } PriorityQueueint[]queuenew PriorityQueueint[](new Comparatorint [](){ public int compare(int []m,int []n){ return m[1]-n[1]; } }); for(Map.EntryInteger,Integerentry:map.entrySet){ int numentry.getKey() int countentry.getValue(); if(queue.size()k){ if(queue.peek()[1]count){ queue.poll(); } queue.offer(new int[]{num,count}); }else{ queue.offer(new int []{nums,count}); } } int retnew int[k]; for(int i0;ik;i){ ret[i]queue.poll()[0]; } return ret; } }借鉴了题解思想的自己写出来的解法题解中的一个思想感觉非常不错虽然最后跑出来没有我第一次单纯用堆排序跑出来的效率高但是也很不错了下面是思路首先用HashMap将所有的数字和数字的数量都收集起来再用一个数组all将其全部都变成可以直接索引的。然后分析题目我们可以构建一个数量为k的小根堆当数量不够k的时候就直接往小根堆数组里面加当数量够了之后直接开始构建小根堆。然后后面的数据就跟小根堆的第一个元素比较目前最小的值如果比小根堆里最小的值还小就直接将其替换掉然后让其下沉到他该在的位置保证小根堆里的第一个元素是最小的。遍历完all数组记录着nums数组的数字和个数的数组后再将从结果数组中提取出我们要的答案在本代码中我的是直接找到所有奇数索引的值。记录做这个思路时的一个关键报错在遇到像是这道题目需要用数组来记录一对数据为一组比如这道题目中需要记录数字本身还有数字对应的个数在遍历的时候一定要直接在外层for遍历的时候直接写ii2不要写i然后在循环体里面再i;这样很容易报错切记切记for(int i0;i2*size;i){ if(countk){ int numcount*2; r[num]all[i]; r[num1]all[i]; swap(r,0,num); swap(r,1,num1); beHeap(r,0,count1); count; }else{ if(all[i1]r[1]){ r[0]all[i]; r[1]all[i]; swap(r,0,2*k-2); swap(r,1,2*k-1); beHeap(r,0,k); } } } int c0; for(int i0;i2*k;i){ result[c]r[i]; } return result; }自己做的大根堆的解法首先用HashMap来将所有的数据的值和数量统计下来在用一个数组all将所有的值和数量放进去一个数字一个数量然后构建大根堆只不过这些东西都需要稍微调整一下。别的倒是都还好。刚刚看了一眼题解我发现题解也是将数字和数量都存储到同一个数组里面我感觉我的做法更好理解。然后我看到了一种优化的点大概是这样的我的方法是构建大根堆然后除了最初构建大根堆后面还需要不断地交换第一个和最后一个元素但是如果构建的是小根堆就不需要了小根堆的叶子节点一定是大于他的非叶子结点的所以如果K的值大于叶子节点的数量的时候叶子节点就可以直接抄到结果的数组中去。剩下的只需要在非叶子节点上再稍微拍个序就好了。我刚刚试了一下这个方法不行。是我理解错了题解中的思想。此思路是失败的思考前面我自己做的是正确的我想优化但优化错了class Solution { MapInteger,Integermapnew HashMap(); public int[] topKFrequent(int[] nums, int k) { int nnums.length; int []resultnew int [k]; for(int i0;in;i){ int valuemap.getOrDefault(nums[i],0)1; map.put(nums[i],value); } //size表示有多少对 int sizemap.size(); int [] allnew int [2*size]; SetIntegerkeysmap.keySet(); int index0; for(Integer key:keys){ Integer valuemap.get(key); all[index]key; all[index]value; } buildHeap(all); for(int i0;ik;i){ result[i]all[0]; swap(all,0,2*(size-1-i)); swap(all,1,2*(size-1-i)1); beHeap(all,0,size-i-1); } return result; } public void buildHeap(int []nums){ int nnums.length/2; for(int in/2-1;i0;i--){ beHeap(nums,i,n); } } //nums两个值看作是一个前面的是键后面的是值n表示有多少对 public void beHeap(int [] nums,int k,int n ){ int ik; while(in/2){ int li2*i1; int ri2*i2; int larili; if(rin-1nums[2*ri1]nums[2*li1])lariri; if(nums[2*lari1]nums[2*i1]){ swap(nums,2*lari1,2*i1); swap(nums,2*lari,2*i); }else{ break; } ilari; } } public void swap(int []a ,int i,int j){ int tmpa[i]; a[i]a[j]; a[j]tmp; } }295数据流的中位数解法题解中的解法如果num不比minQ中的最大值大放到minQ里面否则放到maxQ里面。如果放完以后maxQ的数量比minQ的数量多或者minQ的数量比maxQ的数量多2那么要调整两个数组的个数。class MedianFinder { PriorityQueueIntegerminQ; PriorityQueueIntegermaxQ; public MedianFinder() { minQnew PriorityQueue((a,b)-b-a);//大根堆 maxQnew PriorityQueue((a,b)-a-b);//小根堆 } public void addNum(int num) { if(minQ.isEmpty()||numminQ.peek()){ minQ.offer(num); if(maxQ.size()1minQ.size()){ maxQ.offer(minQ.poll()); } }else{ maxQ.offer(num); if(maxQ.size()minQ.size()){ minQ.offer(maxQ.poll()); } } } public double findMedian() { if(maxQ.size()minQ.size()){ return (maxQ.peek()minQ.peek())/2.0; }else{ return minQ.peek(); } } } /** * Your MedianFinder object will be instantiated and called as such: * MedianFinder obj new MedianFinder(); * obj.addNum(num); * double param_2 obj.findMedian(); */看了题解之后自己写的解法建立两个PriorityQueue数组他的原理是推排序记作minQ和maxQminQ中的元素全部都比maxQ中的小前者大根堆后者小根堆。如果两个一样长将num和minQ的最大值比较num小直接放到minQ中如果num大将maxQ中的最小值移动到minQ中再将其放入maxQ中。如果两个不一样长吗将num和maxQ的最小值比较num大直接放入到maxQ中去num小将minQ的最大值移动到maxQ中去再将num放入到minQ中去。在此之前要先保证两个队列中都是有元素的。class MedianFinder { PriorityQueueIntegerminQ; PriorityQueueIntegermaxQ; public MedianFinder() { minQnew PriorityQueue((a,b)-b-a);//大根堆 maxQnew PriorityQueue((a,b)-a-b);//小根堆 } public void addNum(int num) { int snminQ.size(); int bnmaxQ.size(); if(minQ.isEmpty()){ minQ.offer(num); return; } if(maxQ.isEmpty()){ if(numminQ.peek()){ maxQ.offer(num); }else{ maxQ.offer(minQ.poll()); minQ.offer(num); } return; } if(snbn){ if(nummaxQ.peek()){ minQ.offer(num); }else{ minQ.offer(maxQ.poll()); maxQ.offer(num); } }else{ if(numminQ.peek()){ maxQ.offer(num); }else { maxQ.offer(minQ.poll()); minQ.offer(num); } } } public double findMedian() { int sizemaxQ.size()minQ.size(); if(size%21){ return minQ.peek(); }else{ return (minQ.peek()maxQ.peek())/2.0; } } } /** * Your MedianFinder object will be instantiated and called as such: * MedianFinder obj new MedianFinder(); * obj.addNum(num); * double param_2 obj.findMedian(); */自己写的用列表的超出时间限制的解法添加的时候直接排序找中位数的时候直接找。class MedianFinder { ListIntegerlist; public MedianFinder() { listnew ArrayList(); } public void addNum(int num) { if(list.size()0){ list.add(num); }else{ int index0; if(list.get(list.size()-1)num)indexlist.size(); for(int i0;ilist.size();i){ if(numlist.get(i)){ indexi; break; } } list.add(index,num); } } public double findMedian() { if(list.size()%2!0){ return list.get(list.size()/2); }else{ return (list.get(list.size()/2)list.get(list.size()/2-1))/2.0; } } } /** * Your MedianFinder object will be instantiated and called as such: * MedianFinder obj new MedianFinder(); * obj.addNum(num); * double param_2 obj.findMedian(); */121买卖股票的最佳时机解法自己写的优化后的方法思想首先遍历所有情况的时间复杂度是O(n2)会超出系统的时间限制。我是在暴力法两层遍历所有的情况的基础上观察发现如果我们已经检查了一个元素那么这个元素后面的比这个元素大的元素我们就都不需要看了。比如7,1,5,3,6,4这组数据我们检查了1后面的就不用检查了。class Solution { public int maxProfit(int[] prices) { int nprices.length; int ret0; int minprices[0]; for(int i0;in;i){ int money0; if(i0||prices[i]min){ for(int ji;jn;j){ if(prices[j]-prices[i]money){ moneyprices[j]-prices[i]; } } minprices[i]; } if(moneyret)retmoney; } return ret; } }报错超出时间限制的解法直接将所有的情况都遍历一遍。超出时间限制了就是。class Solution { public int maxProfit(int[] prices) { int nprices.length; int ret0; for(int i0;in;i){ int money0; for(int ji;jn;j){ if(prices[j]-prices[i]money){ moneyprices[j]-prices[i]; } } if(moneyret)retmoney; } return ret; } }55跳跃游戏解法题解中的贪心算法遍历每个值计算其能到达的最远距离如果最远的点已经超过了n-1那么可以直接返回true;如果遍历的点的索引大于之前所有的点的最大可达范围那么说明不可达到。返回false不过在代码中是进不去那个循环里面的内容就只能空转到结束放回false;class Solution { boolean []flag; public boolean canJump(int[] nums) { int nnums.length; int max0; for(int i0;in;i){ if(imax){ if(inums[i]n-1)return true; maxMath.max(inums[i],max); } } return false; } }自己写的递归的成功的解法用递归的思想。每次都找最大的路径。如果最大的路径走不通就到最近的节点换一个看看能不能走通。同时记录下最近的所有数值都走了还是走不通的路径将其标记为true(表示已经确定这个点是不可行的)。在判断的时候可以通过这个标记来提升一下速度如果没有这个如果数组是递减的并且刚好无法通过那么将会做很多无用功导致抄出时间限制class Solution { boolean []flag; public boolean canJump(int[] nums) { int nnums.length; flagnew boolean[n]; if(n1)return true; for(int knums[0];k0;k--){ if(jump(nums,k))return true; } return false; } public boolean jump(int []nums,int i){ int nnums.length; if(in-1){ return true; } if(in-1||flag[i])return false; if(nums[i]0)return false; for(int knums[i];k0;k--){ if(jump(nums,ik)){ return true; } } flag[i]true; return false; } }自己写的递归超出时间限制的方法利用递归的思路每次都走最远的路走不通就在最近的路上减1直到最后可以走通为止。class Solution { public boolean canJump(int[] nums) { int nnums.length; if(n1)return true; for(int knums[0];k0;k--){ if(jump(nums,k))return true; } return false; } public boolean jump(int []nums,int i){ int nnums.length; if(in-1){ return true; } if(in-1)return false; if(nums[i]0)return false; for(int knums[i];k0;k--){ if(jump(nums,ik)){ return true; } } return false; } }45跳跃游戏2解法题解中的贪心算法每次都更新可以到达终点的点。更新一次次数加一题目中保证了可以到n-1所以我们每次只需要找到离终点最远的点也就是索引最小的点。直到索引为0的点也可以到达最后的点。返回次数。class Solution { public int jump(int[] nums) { int nnums.length; int count0; int poin-1; while(poi0){ for(int i0;in;i){ if(inums[i]poi){ poii; break; } } count; } return count; } }自己写的使用队列的解法倒着找。每次都将可以到达n-1位置的点加入到队列中去。第一次直接将n-1加入到队列中后面当inums[i]队列的头的时候将i加入到辅助队列因为边加边取就会无法区分是上一次的还是这一次的中当i打大于队列的头的时候将队列的头出队。这样就可以精确地找到次数了。class Solution { public int jump(int[] nums) { int nnums.length; if(n1)return 0; boolean []listnew boolean[n]; int count0; QueueIntegerqueuenew LinkedList(); QueueIntegerqnew LinkedList(); queue.offer(n-1); for(int j0;jn;j){ if(j!0){ queueq; qnew LinkedList(); } for(int i0;in;i){ int poi-1; if(!queue.isEmpty()){ poiqueue.peek(); }else{ break; } if(inums[i]poi){ list[i]true; q.offer(i); } if(ipoi)queue.poll(); } count; if(list[0])return count; } return count; } }763划分字母区间解法题解中的贪心算法先找到每个字符的最大索引遍历字符串定义start和endend为同一段的遍历过的所有字符的最大索引。如果遍历到的索引和end相同说明可以结束了。class Solution { public ListInteger partitionLabels(String s) { int ns.length(); ListIntegerresultnew ArrayList(); int start0,end0; int[]chnew int [26]; for(int i0;in ;i){ int pois.charAt(i)-a; ch[poi]i; } for(int i0;in;i){ endMath.max(end,ch[s.charAt(i)-a]); if(endi){ int lenend-start1; result.add(len); startend1; } } return result; } }自己写的利用数组来存储的解法在看了题解之后题解中存储每个字符的最大索引用的是数组我尝试将其改为map来存储我发现我改了之后运行所需的时间变多了。我猜测我之前的方法执行用时只超5%的人可能是因为我采用了map的原因改了之后变好了很多。class Solution { public ListInteger partitionLabels(String s) { int ns.length(); ListIntegerresultnew ArrayList(); int []chnew int[26]; for(int i0;in;i){ int pois.charAt(i)-a; ch[poi]i; } int poi0; while(poin){ int ppoi; int maxch[s.charAt(p)-a]; while(pnpmax){ int ppch[s.charAt(p)-a]; maxMath.max(pp,max); p; } poimax; result.add(poi); poi; } int pre0; ListIntegerrnew ArrayList(); int c0; for(int i0;iresult.size();i){ int countresult.get(i)-pre; if(c0)count; r.add(count); preresult.get(i); c; } return r; } }自己写的利用map的解法将整个字符串中每个字符的最大的索引存储到map中然后每次以第一个字符的最大索引为边界在找到在他前面的字符的最大索引如果比之前的大就将更大的更新为边界。结束后就得到了一组再去找下一组的直到最后。class Solution { public ListInteger partitionLabels(String s) { int ns.length(); ListIntegerresultnew ArrayList(); MapCharacter,Integermapnew HashMap(); for(int i0;in;i){ map.put(s.charAt(i),Math.max(i,map.getOrDefault(s.charAt(i),-1))); } int poi0; while(poin){ int ppoi; int maxmap.get(s.charAt(p)); while(pnpmax){ char cs.charAt(p); int ppmap.get(c); maxMath.max(pp,max); p; } poimax; result.add(poi); poi; } int pre0; ListIntegerrnew ArrayList(); int c0; for(int i0;iresult.size();i){ int countresult.get(i)-pre; if(c0)count; r.add(count); preresult.get(i); c; } return r; } }70爬楼梯解法题解中的动态规划解法n个数的数目可以表示位f(n)f(n-1)f(n-2)这么理解从最后一步来看最后一步的步长可以是1也可以是2如果是1那么前面的数量就是f(n-1)。如果是2那么前面的数量就是f(n-2)class Solution { public int climbStairs(int n) { int a0; int b0; int r1; for(int i0;in;i){ ab; br; rab; } return r; } }自己写的按照2的次数分类讨论的方法按照需要的2的数量为统计标准来计量比如n3分类为两种情况一2的数量为0 二2的数量为1然后根据高中的数列的知识可以知道当2的数量为i的时候可以想象成往n-i因为有i个2所以数量变成了n-i个位置上放i个2和其余位置上都用1来填这样就是C(i,n-i)class Solution { public int climbStairs(int n) { //count表示可以取多少个2 int countn/2; long sum0; //按照取2的个数来分类 for(int i0;icount1;i){ int nnn-i; long r1; for(int j0;ji;j){ rr*(nn-j)/(j1); } //单次的个数 sumr; } return (int)sum; } }关键报错这是我之前的一个版本当n比较大的时候会报错。是因为我在计算的过程中使用了阶乘这直接导致当n比较大的时候会无法被表示出来。class Solution { public int climbStairs(int n) { //count表示可以取多少个2 int countn/2; long sum0; //按照取2的个数来分类 for(int i0;icount1;i){ int nnn-i; long s1; long x1; for(int j0;ji;j){ ss*(nn-j); xx*(j1); } //单次的个数 long sss/x; sumss; } return (int)sum; } }118杨辉三角解法自己做的动态规划的方法记录上一行的数组的情况然后按照杨辉三角的那个样子相加的得到中间的值再在开头和结尾的部分加上1就可以了。刚刚看了题解跟我的思路差不多class Solution { public ListListInteger generate(int numRows) { ListListIntegerresultnew ArrayList(); ListIntegerprenew ArrayList(); pre.add(1); int nnumRows; for(int i1;in;i){ if(i1){ result.add(pre); continue; } ListIntegercurrnew ArrayList(); curr.add(1); for(int j0;ji-2;j){ curr.add(pre.get(j)pre.get(j1)); } curr.add(1); precurr; result.add(curr); } return result; } }198打家劫舍解法自己写的动态规划的解法观察发现f(k)max{f(k-2)nums[k],f(k-1)},其中f(k)的意思是前k项的最大金额动态规划的思路确实蛮好用的哎。看了一眼题解跟我的差不多class Solution { public int rob(int[] nums) { int nnums.length; if(n1)return nums[0]; int anums[0],bMath.max(nums[0],nums[1]); if(n2)return b; //f(k)max{f(k-2)nums[k],f(k-1)} for(int i2;in;i){ int bbb; //f(k-2)nums[k] if(anums[i]b){ banums[i]; }//b本来就是f(k-1)所以不用写 abb; } return b; } }278完全平方数解法题解中的动态规划动态规划的思路从1开始一直到最后只要记录了前面的每个值的最小值我只需要计算下面公式中的i-j*j中j取不同值时的最小值就可以了。class Solution { public int numSquares(int n) { ListIntegerlistnew ArrayList(); int []fnew int[n1]; f[1]1; int c1; for(int i2;in;i){ if(i(c1)*(c1)){ f[i]1; c; continue; } int mini-1; for(int j2;jc;j){ if(f[min]f[i-j*j]){ mini-j*j; } } f[i]f[min]1; } return f[n]; } }322零钱兑换解法题解中的动态规划的解法思路跟我的一样(在下面的解法中)但是代码更加简洁逻辑更加清晰。值得学习class Solution { public int coinChange(int[] coins, int amount) { int ncoins.length; int []fnew int [amount1]; Arrays.fill(f,amount1); f[0]0; for(int i0;iamount1;i){ for(int j0;jn;j){ if(icoins[j]){ f[i]Math.min(f[i],f[i-coins[j]]1); } } } return f[amount]amount1?-1:f[amount]; } }自己写的动态规划的解法首先对coins数组进行排序amount的个数记为f[amount],那么f[amount]min{f[i-coins[]}1从1到amount 的所有都记录上去就可以了。class Solution { public int coinChange(int[] coins, int amount) { //初始化coins Arrays.sort(coins); int ncoins.length; if(amount%coins[n-1]0){ return amount/coins[n-1]; } int []fnew int [amount1]; if(amountcoins[0])return -1; //初始化所有在coins数组中的值全部置为1 for(int i0;in;i){ if(coins[i]amount){ f[coins[i]]1; } } //初始化从1到coins[0]全部置为-1 for(int i1;icoins[0];i){ f[i]-1; } int c0;//coins的索引 for(int icoins[0]1;iamount1;i){ if(cn-1icoins[c1]){ while(cn-1coins[c1]i)c; continue; } int minInteger.MAX_VALUE; for(int j0;jc;j){ if(f[i-coins[j]]-1){ continue; } if(minf[i-coins[j]]){ minf[i-coins[j]]; } } if(minInteger.MAX_VALUE){ f[i]-1; }else{ f[i]min1; } } return f[amount]; } }139单词拆分解法题解中的动态规划的解法直接暴力找到所有的情况一个一个匹配题解中的解法中我没有想到的点是用哈希表来记录别的情况提高效率的一个小方法。class Solution { public boolean wordBreak(String s, ListString wordDict) { SetStringsetnew HashSet(wordDict); boolean []dpnew boolean[s.length()1]; dp[0]true; for(int i1;is.length()1;i){ for(int j0;ji;j){ if(dp[j]set.contains(s.substring(j,i))){ dp[i]true; } } } return dp[s.length()]; } }自己写的动态规划的解法动态规划的思路遍历字符串如果其中的某一段字符串比如假如是[i,ij)则说明f[ij]f[i]更新所有值最后我们只需要检查f[ns]就可以了。class Solution { public boolean wordBreak(String s, ListString wordDict) { int nwordDict.size(); int nss.length(); //每个字符串的长度 int []ninew int [n]; boolean []fnew boolean[ns1]; //获取wordDict里的最小长度和最大长度 int minlInteger.MAX_VALUE,maxl0; for(int i0;in;i){ int nss wordDict.get(i).length(); ni[i]nss; if(nssminl)minlnss; if(nssmaxl)maxlnss; } for(int i0;ins;i){ for(int j0;jn;j){ if(ini[j]nss.substring(i,ini[j]).equals(wordDict.get(j))){ if(f[ini[j]]true)continue; f[ini[j]]f[i]; if(i0)f[ini[j]]true; } } } return f[ns]; } }300最长递增子序列解法自己写的动态规划的解法将从0到最后的每个值的情况都记录下来然后只需要在前面的数量上加1就可以的到现在指向的数字的情况。动态规划的思路。看了一眼题解跟我的思路差不多。class Solution { public int lengthOfLIS(int[] nums) { int nnums.length; int []dpnew int [n1]; for(int i1;in1;i){ dp[i]1; for(int j1;ji;j){ if(nums[i-1]nums[j-1]){ dp[i]dp[j]; } if(nums[i-1]nums[j-1]){ dp[i]Math.max(dp[i],dp[j]1); } } } int max0; for(int i0;in1;i){ if(dp[i]max)maxdp[i]; } return max; } }152乘积最大子数组解法自己写的动态规划的解法乘积可正可负如果前面的连续的数的乘积是负的同时nums[i]也是负的那么就有可能会有一个比较大的正数本解法中我创建了一个m[]数组来存储可能产生的负数将所有的负数都记录下来缺点是当nums[]数组的长度为1且是负的的时候无法排除这种情况我在第一个里面就给他单列出来了。然后思路就是动态规划的思路将所有的情况都列出来我们要找的情况就在可以在上一个情况中加上一些条件就找出来了。我看了一眼题解感觉跟我的思路很像。我不准备再看了。class Solution { public int maxProduct(int[] nums) { int nnums.length; if(n1)return nums[0]; int []dpnew int [n]; int []pnew int [n]; int []mnew int [n]; dp[0]nums[0]; int x0; while(xnnums[x]0){ if(xn-1){ return nums[0]; } x; } if(nums[x]0){ p[x]nums[x]; }else{ m[x]nums[x]; } int maxnums[0]; for(int ix1;in;i){ if(nums[i]0){ p[i]Math.max(nums[i],p[i-1]*nums[i]); m[i]Math.min(nums[i],m[i-1]*nums[i]); }else if(nums[i]0){ p[i]Math.max(nums[i],m[i-1]*nums[i]); m[i]Math.min(nums[i],p[i-1]*nums[i]); } } for(int i0;in;i){ maxMath.max(p[i],max); } return max; } //包头包尾 public int mul(int []nums,int i,int j){ int mul1; if(ji)return nums[i]; for(int ai;aj;a){ mulmul*nums[a]; } return mul; } }自己写的超出时间限制的解法定义两个数组分别记录第i个位置的前面的连续乘积的正数的最大值和负数的最小值这样如果nums[i]是正数就用正数最大值去乘如果是负数就用负数的最小值去乘。class Solution { public int maxProduct(int[] nums) { int nnums.length; int []dpnew int [n]; int []pnew int [n]; int []mnew int [n]; dp[0]nums[0]; int maxnums[0]; for(int i1;in;i){ //p[i]和m[i]中的i都是索引后一位 for(int j0;ji;j){ int mulmul(nums,j,i-1); if(mul0){ p[i]Math.max(p[i],mul); }else if(mul0){ m[i]Math.min(m[i],mul); } } if(nums[i]0){ dp[i]Math.max(nums[i],p[i]*nums[i]); }else if(nums[i]0){ dp[i]Math.max(nums[i],m[i]*nums[i]); } } for(int i0;in;i){ maxMath.max(dp[i],max); } return max; } //包头包尾 public int mul(int []nums,int i,int j){ int mul1; if(ji)return nums[i]; for(int ai;aj;a){ mulmul*nums[a]; } return mul; } }416分割等和子集解法题解中的动态规划的解法dp[i][j]数组记录前i个数字是否存在一种选取方案使得被选取的正整数的和等于 j。则有当jnums[i]时两种情况加上nums[i]后可以凑齐j不用加上nums[i]就可以凑齐j。这两种情况分别对应dp[i-1][target-nums[i]]和dp[i-1][j]。然后是初始化的问题。dp[0][nums[0]]true因为索引为0的元素就是nums[0]可以凑齐nums[0]所以他一定是true。还有所有的dp[i][0]true因为什么都不选就可以凑齐0.class Solution { public boolean canPartition(int[] nums) { Arrays.sort(nums); int nnums.length; int sum0,maxnums[0]; for(int i0;in;i){ sumnums[i]; if(maxnums[i])maxnums[i]; } int targetsum/2; if(sum%2!0||maxtarget)return false; boolean [][]dpnew boolean[n][target1]; dp[0][nums[0]]true; for(int i0;in;i)dp[i][0]true; for(int i1;in;i){ for(int j0;jtarget1;j){ if(jnums[i]){ dp[i][j]dp[i-1][j]||dp[i-1][j-nums[i]]; }else{ dp[i][j]dp[i-1][j]; } } } return dp[n-1][target]; } }32最长有效括号解法自己做的用栈的解法创建dp数组记录是字符串的每一位是否有括号可以匹配遍历字符串如果是‘’就将其索引入栈否则就出栈则出栈的是后括号对应的前括号将这两个索引都置为true。别的不用管因为创建dp数组的时候boolean数组默认是全部false然后遍历dp数组找到最长的true就解决了这个问题。class Solution { public int longestValidParentheses(String s) { int ns.length(); if(n0)return 0; DequeIntegerstacknew LinkedList(); boolean []dpnew boolean[n]; for(int i0;in;i){ char cs.charAt(i); if(c(){ stack.push(i); }else{ if(stack.isEmpty()){ dp[i]false; }else{ int prestack.pop(); dp[pre]true; dp[i]true; } } } int max0,count0; for(int i0;in;i){ if(dp[i]false){ maxMath.max(max,count); count0; }else{ count; } } maxMath.max(max,count); return max; } }62不同路径解法自己写的动态规划的解法创建一个二维数组记录从开始点到这个点的路径数量因为机器人只能往下或者往右显然所有的行号为0和列号为0的点的路径数量都是1。其余点的路径数量为上面的点和左边的点的和。这样就找到了所有点的路径数量再返回右下角的那个点的值就可以了。看了一眼题解跟我的思路差不多class Solution { public int uniquePaths(int m, int n) { int [][]snew int [m][n]; s[0][0]1; for(int i0;im;i){ s[i][0]1; } for(int i0;in;i){ s[0][i]1; } for(int i1;im;i){ for(int j1;jn;j){ s[i][j]s[i-1][j]; s[i][j]s[i][j-1]; } } return s[m-1][n-1]; } }64最小路径和解法自己写的动态规划的解法定义一个记录所有点的最小路径数字和的二维数组首先初始化第一行和第一列。中间的每个点都将上一个和左一个点的最小路径和加上本身取这两个值得最小值就得到了其最小路径数字和。更新全部列表列表得最后一个值就是答案。看了一眼题解跟我得思路跟像。class Solution { public int minPathSum(int[][] grid) { int mgrid.length; int ngrid[0].length; int [][]snew int [m][n]; s[0][0]grid[0][0]; for(int i1;in;i){ s[0][i]grid[0][i]s[0][i-1]; } for(int i1;im;i){ s[i][0]grid[i][0]s[i-1][0]; } for(int i1;im;i){ for(int j1;jn;j){ int ups[i-1][j]grid[i][j]; int lefts[i][j-1]grid[i][j]; s[i][j]Math.min(up,left); } } return s[m-1][n-1]; } }5最长回文子串解法题解中的动态规划的解法dp[i][j]表示从索引为i到索引为j的字符串是否是回文字符串是一个boolean数组如果遍历的字符串长度大于2也即是j-i2则dp[i][j]dp[i1][j-1](ch[i]ch[j])如果长度刚好等于2则dp[i][j]ch[i]ch[j]。最后返回最长的字符串就可以了。class Solution { public String longestPalindrome(String s) { int ns.length(); if(n1)return s; char []chs.toCharArray(); int max1; int begin0; boolean [][]dpnew boolean[n][n]; for(int i0;in;i){ dp[i][i]true; } for(int L2;Ln;L){ for(int i0;in;i){ int jiL-1; if(jn)break; if(L2){ dp[i][j](ch[i]ch[j]); }else{ dp[i][j]dp[i1][j-1](ch[i]ch[j]); } if(dp[i][j]maxL){ maxL; begini; } } } return s.substring(begin,beginmax); } }自己做的分类讨论的解法分两种情况一种是aa型的个数是最大的一种是aba型的个数是最大的。我们遍历字符串两次分别找到两种类型的最大的个数。遍历到其中某一个字符的时候记录下当时的那个值然后不断往前往后检查直到不一样看看这个长度是否比最大长度大大的话就更新result。就是这个思路详情见代码。class Solution { public String longestPalindrome(String s) { int ns.length(); if(n1)return s; int max0,count 0; String result; //aa型 int index0; for(int i0;in-1;i){ int preii, nextii1; char pres.charAt(prei),nexts.charAt(nexti); while(prenext){ count2; indexprei; if((prei0||nextin-1)prenext){ break; } prei--; nexti; pres.charAt(prei); nexts.charAt(nexti); } if(count max){ results.substring(index,indexcount); maxcount; } count0; } //aba型 index0; count0; for(int i1;is.length()-1;i){ int preii-1; int nextii1; char pres.charAt(i-1); char nexts.charAt(i1); if(prenext)count; while(prenext){ count2; indexprei; if((prei0||nextin-1)prenext){ break; } prei--; nexti; pres.charAt(prei); nexts.charAt(nexti); } if(maxcount){ results.substring(index,countindex); maxcount; } count 0; } if(max0n!0)return s.substring(0,1); return result; } }1143最长公共子序列解法题解中的动态规划的解法class Solution { public int longestCommonSubsequence(String text1, String text2) { int n1text1.length(); int n2text2.length(); if(n10||n20)return 0; int[][]dpnew int[n11][n21]; char[]s1text1.toCharArray(); char[]s2text2.toCharArray(); for(int i1;in11;i){ for(int j1;jn21;j){ if(dp[i-1][j]dp[i][j-1]s1[i-1]s2[j-1]){ dp[i][j]dp[i-1][j-1]1; }else{ dp[i][j]Math.max(dp[i-1][j],dp[i][j-1]); } } } return dp[n1][n2]; } }失败的方法class Solution { public int longestCommonSubsequence(String text1, String text2) { int n1text1.length(); int n2text2.length(); if(n10||n20)return 0; int[][]dpnew int[n1][n2]; char[]s1text1.toCharArray(); char[]s2text2.toCharArray(); for(int i0;in1;i){ if(s1[i]s2[0]||i!0dp[i-1][0]!0){ dp[i][0]1; } } for(int i0;in2;i){ if(s2[i]s1[0]||i!0dp[0][i-1]!0){ dp[0][i]1; } } for(int i1;in1;i){ for(int j1;jn2;j){ int mMath.max(dp[i-1][j],dp[i][j-1]); if(s1[i]s2[j]){ dp[i][j] m1; }else{ dp[i][j]m; } } } return dp[n1-1][n2-1]; } }72编辑距离解法题解中的动态规划的解法D[i][j-1] 为 A 的前 i 个字符和 B 的前 j - 1 个字符编辑距离的子问题。即对于 B 的第 j 个字符我们在 A 的末尾添加了一个相同的字符那么 D[i][j] 最小可以为 D[i][j-1] 1D[i-1][j] 为 A 的前 i - 1 个字符和 B 的前 j 个字符编辑距离的子问题。即对于 A 的第 i 个字符我们在 B 的末尾添加了一个相同的字符那么 D[i][j] 最小可以为 D[i-1][j] 1D[i-1][j-1] 为 A 前 i - 1 个字符和 B 的前 j - 1 个字符编辑距离的子问题。即对于 B 的第 j 个字符我们修改 A 的第 i 个字符使它们相同那么 D[i][j] 最小可以为 D[i-1][j-1] 1。特别地如果 A 的第 i 个字符和 B 的第 j 个字符原本就相同那么我们实际上不需要进行修改操作。在这种情况下D[i][j] 最小可以为 D[i-1][j-1]。那么我们可以写出如下的状态转移方程若 A 和 B 的最后一个字母相同D[i][j]min(D[i][j−1]1,D[i−1][j]1,D[i−1][j−1])若 A 和 B 的最后一个字母不相同D[i][j]1min(D[i][j−1],D[i−1][j],D[i−1][j−1]−1)class Solution { public int minDistance(String word1, String word2) { int n1word1.length(); int n2word2.length(); char[]c1word1.toCharArray(); char[]c2word2.toCharArray(); int [][]dpnew int[n11][n21]; if(n10||n20)return Math.max(n1,n2); boolean flagfalse; for(int i0;in21;i){ dp[0][i]i; } for(int i0;in11;i){ dp[i][0]i; } for(int i1;in1;i){ for(int j1;jn2;j){ int leftdp[i][j-1]1; int downdp[i-1][j]1; int lddp[i-1][j-1]; if(c1[i-1]!c2[j-1])ld; dp[i][j]Math.min(left,Math.min(down,ld)); } } return dp[n1][n2]; } }未完成代码class Solution { public int minDistance(String word1, String word2) { int n1word1.length(); int n2word2.length(); char[]c1word1.toCharArray(); char[]c2word2.toCharArray(); int [][]dpnew int[n1][n2]; if(n10||n20)return Math.max(n1,n2); boolean flagfalse; for(int i0;in2;i){ if(c2[i]c1[0]||flag){ dp[0][i]i; flagtrue; }else{ dp[0][i]i1; } } for(int i0;in1;i){ if(c1[i]c2[0]||flag){ dp[i][0]i; flagtrue; }else{ dp[i][0]i1; } } if(n11||n21)return dp[n1-1][n2-1]; for(int i1;in1;i){ for(int j1;jn2;j){ if(c1[i]c2[j]){ dp[i][j]1Math.min(dp[i-1][j],Math.min(dp[i][j-1],dp[i-1][j-1]-1)); }else{ dp[i][j]1Math.min(dp[i-1][j],Math.min(dp[i][j-1],dp[i-1][j-1])); } } } return dp[n1-1][n2-1]; } }136只出现一次的数字解法题解中的异或的解法一个数异或自身是0异或0是自身。并且异或满足结合律和交换律。数组中的所有数都异或掉就可以得到那个单个的数字。class Solution { public int singleNumber(int[] nums) { int r0; for(int num:nums){ rr^num; } return r; } }自己想的用set集合的解法只有一个是一个的其余全是两个的。创建一个set集合遍历元素如果不在集合里面就加入集合如果在集合里面就从集合里面删掉。就算最坏的情况也只会用到n/2的空间量。class Solution { public int singleNumber(int[] nums) { int nnums.length; if(n1)return nums[0]; SetIntegersnew HashSet(); for(int i0;in;i){ if(s.contains(nums[i])){ s.remove(nums[i]); }else{ s.add(nums[i]); } } return s.iterator().next(); } }169多数元素解法自己写的利用map集合的解法用一个Map记录所有数据的元素数量再进行统计。就得到最后的答案了。看了一眼题解跟我的思路差不多。class Solution { public int majorityElement(int[] nums) { int nnums.length; MapInteger,Integermapnew HashMap(); for(int i0;in;i){ map.put(nums[i],map.getOrDefault(nums[i],0)1); } int max0; int result0; for(Map.EntryInteger,Integerentry:map.entrySet()){ int keyentry.getKey(); int valueentry.getValue(); if(valuemax){ maxvalue; resultkey; } } return result; } }75颜色分类解法自己写的两轮交换法因为数字的种类只有三个012如果采用快排的方法或者别的感觉时间复杂度反而会比较高。我直接遍历两轮第一次直接将所有的2放到正确的位置上第二次直接将所有的1放到正确的位置上。看了一眼题解跟我的思路差不多class Solution { public void sortColors(int[] nums) { int nnums.length; int jn-1; int i0; while(ij){ while(j0nums[j]2)j--; if(nums[i]2){ swap(nums,i,j); j--; } i; while(j0nums[j]2)j--; } i0; while(ij){ while(j0nums[j]1)j--; if(nums[i]1){ swap(nums,i,j); j--; } i; while(j0nums[j]1)j--; } } public void swap(int []nums,int i,int j){ int tmpnums[i]; nums[i]nums[j]; nums[j]tmp; } }31下一个排列解法题解中的思路自己写的代码大体思路跟我的差不多区别的地方在于题解的实现算法的能力更好一些。比如找到index右边的最小的大数。可以直接从右边向左边遍历而我的做法是从左边向右边遍历这就导致做法非常的麻烦要判断的东西很多。class Solution { public void nextPermutation(int[] nums) { int nnums.length; int indexn-1; while(index0nums[index]nums[index-1]){ index--; } if(index0){ sort(nums,0); }else{ for(int jn-1;jindex;j--){ if(nums[j]nums[index-1]){ swap(nums,index-1,j); sort(nums,index); break; } } } } public void sort(int []nums,int i){ for(int ki;knums.length;k){ int mink; for(int sk;snums.length;s){ if(nums[min]nums[s])mins; } swap(nums,min,k); } } public void swap(int []nums,int i,int j){ int tmpnums[i]; nums[i]nums[j]; nums[j]tmp; } }自己的解法思路从右向左遍历数组找到第一个数字记作index这个数字比前一位数字要大然后判断如果index0也就是数组是递减的那么直接将整个数组从小到大排个序就好了。在index向右遍历找到index右边的比index-1大的最小的数字记作min判断如果右边没有比index更大的数字那么交换index和index-1将从index到最后的数字从小到大排序如果找到了min,交换index-1和min将index到最后的数字从小到大排序。算法流程描述寻找转折点​从右向左遍历数组定位第一个满足nums[index] nums[index-1]的位置index该位置标志着字典序增长的潜在转折点。处理完全降序情况​若index 0表明整个数组呈完全降序排列此时直接将数组整体按升序排序即可得到最小排列。寻找最小交换元素​在index右侧区间内寻找比nums[index-1]大的最小元素记为minValue或其位置minIndex。执行元素交换​情况一若右侧不存在比nums[index-1]更大的元素则交换nums[index]与nums[index-1]。情况二若找到minValue则交换nums[index-1]与minValue。优化剩余序列​将index至数组末尾的子序列按升序排序确保该区间构成最小字典序从而得到严格的下一个排列。这道题目中学到的在这道题目中我很迷糊在如何找到index右边的比index-1大的最小的数字上花了很长时间。最后我发现直接在主方法的外面单独创建一个方法来专门解决这个问题会好很多。class Solution { public void nextPermutation(int[] nums) { int nnums.length; int indexn-1; int poin-1; while(index0nums[index]nums[index-1]){ if(index!n-1)poi--; index--; } if(index0){ sort(nums,0,n-1); }else{ int minifindMin(nums,index1,nums[index-1]); if(mini-1){ swap(nums,index,index-1); sort(nums,index,n-1); }else{ swap(nums,mini,index-1); sort(nums,index,n-1); } } } public void sort(int []nums,int i,int j){ for(int ki;kj;k){ int mink; for(int sk;sj;s){ if(nums[min]nums[s])mins; } swap(nums,min,k); } } public void swap(int []nums,int i,int j){ int tmpnums[i]; nums[i]nums[j]; nums[j]tmp; } public int findMin(int []nums,int i,int num){ int minii; int nnums.length; if(in-1)return -1; for(int ki;knums.length;k){ if(nums[k]num){ minik; break; } } if(miniinums[mini]num){ return -1; }else if(miniinums[mini]num){ return n-1; }else{ return mini-1; } } }287寻找重复数解法题解中的快慢指针的解法参考之前链表那块有道题目找链表中的环的题目。快慢指针的方法快指针一次走两步慢指针一次走一步然后他们最后会相交。再让慢指针指向头节点再一直遍历到最后就找到重复的元素了。class Solution { public int findDuplicate(int[] nums) { int slow0,fast0; do{ slownums[slow]; fastnums[nums[fast]]; }while(fast!slow); slow0; while(fast!slow){ slownums[slow]; fastnums[fast]; } return slow; } }自己写的超出时间限制的暴力法没什么说的暴力破解class Solution { public int findDuplicate(int[] nums) { int nnums.length; for(int i0;in-1;i){ for(int ji1;jn;j){ if(nums[i]nums[j]){ return nums[i]; } } } return -1; } }自己写的用哈希表的解法空间复杂度不符合要求class Solution { public int findDuplicate(int[] nums) { SetIntegersetnew HashSet(); for(int i0;inums.length;i){ if(set.contains(nums[i])){ return nums[i]; }else{ set.add(nums[i]); } } return -1; } }
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

优秀的html5网站 2016wordpress dedecms哪个好

LangFlow节点系统揭秘:灵活组合组件实现复杂逻辑 在构建大语言模型(LLM)驱动的应用时,开发者常面临一个现实困境:即使只是搭建一个具备记忆、检索和提示工程能力的简单对话机器人,也需要编写大量胶水代码来…

张小明 2025/12/26 5:23:26 网站建设

网站正在建设中页面 英文个人网站创建平台

如何快速构建AI工作流:cube-studio可视化编排终极指南 【免费下载链接】cube-studio cube studio开源云原生一站式机器学习/深度学习AI平台,支持sso登录,多租户/多项目组,数据资产对接,notebook在线开发,拖…

张小明 2025/12/26 5:23:29 网站建设

济宁网站建设 中企动力临沂济宁城乡住房建设网站

文章目录系统截图项目技术简介可行性分析主要运用技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 :文章底部获取博主联系方式!系统截图 python_django基于web平台的社区医疗服务平台_nk5a3uy7 项目技术简介 Python版本&#…

张小明 2025/12/26 5:23:30 网站建设

保定市建设施工许可证查询网站网站建设时间规划

清华镜像站助力TensorFlow部署,CUDA安装不再难 在人工智能研发一线摸爬滚打过的开发者,恐怕都经历过这样的夜晚:凌晨两点,盯着终端里缓慢爬行的 pip install tensorflow 进度条,下载速度时断时续,动辄超时…

张小明 2025/12/28 14:53:45 网站建设

东莞做网站公司首选!要建设一个网站需要什么手续费

第一章:环境风险评估的范式转型与R语言机遇传统环境风险评估长期依赖静态模型和经验公式,难以应对复杂生态系统中的非线性动态与不确定性。随着大数据与开源计算生态的发展,评估范式正从“假设驱动”向“数据驱动”转型。R语言凭借其强大的统…

张小明 2025/12/26 5:23:31 网站建设

果洛州公司网站建设个人网站备案备注写什么

Simple Clock终极指南:从零开始掌握时间管理艺术 【免费下载链接】Simple-Clock Combination of a beautiful clock with widget, alarm, stopwatch & timer, no ads 项目地址: https://gitcode.com/gh_mirrors/si/Simple-Clock 在快节奏的现代生活中&am…

张小明 2025/12/26 5:23:32 网站建设