主要思路:题目说不采用额外的数组,这里我们就引入一个辅助变量的思路来解决这个问题,用for循环中的一个变量i,和另外一个计数的变量count,变量i先向前遍历,当前的数和下一个数不等时,count后移,相等时,只有变量i后移,并且我们当count后移时,把nums[count]=nums[i+1];这是关键语句,剩下的看代码!不明白需要多多思考一下。count相当于新数组的下标,只是数组还是这个数组。
相似题目:27. 移除元素
package Easy; public class TwentySix { public static void main(String[] args) { int[] test={1,1,2,3,3,3,5,5,6,7,8,8,8,9}; TwentySixSolution a=new TwentySixSolution(); System.out.println(a.removeDuplicates(test)); } } class TwentySixSolution { public int removeDuplicates(int[] nums) { int count=0; for (int i = 0; i < nums.length-1; i++) { if (nums[i]!=nums[i+1]){ count++; nums[count]=nums[i+1]; } } for (int i = 0; i < count+1; i++) {//这里只是按照题目意思输出前count个数,看看是否重复 System.out.print(nums[i]+" "); } System.out.println(); return count+1; } }
主要思路:动态规划,主要要想到以每一次排列的最后一个元素为起始的开始进行选择,例如a,b,c,d,e,我们以b为最后一个元素,则它的排序有a,b和b,以d为结束元素,则有a,b,c,d和b,c,d和c,d和d。然后我们可以采用动态规划,当第一个元素a的时候,代码很好理解,当为b时,此时temp就是a+b和b中那个较大的数,然后当为c时,有a+b+c,b+c,c,若a+b<b,则代码内只比较b+c和c,反之会比较a+b+c和c,这就省略一步比较。这就是代码的精妙之处。并且还有个max把过程中最大的数给记录下来了。然后按照这个思路推到下计算到d是怎么样的就很清楚了。虽然这个很难想到,但只要多练习还是可以的 ,主要还是练的少了,要加强训练
package Easy; public class FiftyThree { public static void main(String[] args) { FiftyThreeSolution a=new FiftyThreeSolution(); int[] test={-2,1,-3,4,-1,2,1,-5,4}; System.out.println(a.maxSubArray(test)); } } class FiftyThreeSolution { public int maxSubArray(int[] nums) { int temp = 0, max = nums[0]; for (int x : nums) { temp = Math.max(temp + x, x); max = Math.max(max, temp); } return max; } }
主要思路:从后往前遍历,利用左右指针。先对右边判断,从第一个不是空格字符的开始,左指针向前遍历,知道碰到空格字符停止,输出right-left的值,就是结果。
package Easy; public class FiftyEight { public static void main(String[] args) { FiftyEightSolution a=new FiftyEightSolution(); System.out.println(a.lengthOfLastWord("Hello World")); } } class FiftyEightSolution { public int lengthOfLastWord(String s) { int right=s.length()-1; while (right>=0&&s.charAt(right)==' ') right--; if (right<0) return 0; int left=right; while (left>=0&&s.charAt(left)!=' ') left--; return right-left; } }
主要思路:动态规划
走一个台阶只要一种,走两个有两种方法,走三个可以看成走到第一个走两个台阶上来和走到第二个走一个让台阶上来,然后再加上从走到第二个台阶有几种,和第一个台阶有几种方法。以此类推走到n个台阶,可以看出是从第n-1和n-2个走上来的,逐个累加即可以求得。
package Easy; public class Seventy { public static void main(String[] args) { SeventySolution a=new SeventySolution(); System.out.println(a.climbStairs(30)); } } class SeventySolution { public int climbStairs(int n) { int dp[]=new int[n+2];//我这里用了一个数组其实完全没必要,用三个数即可 dp[1]=1; dp[2]=2; for (int i = 1; i < n+1; i++) { if (i>2){ dp[i]=dp[i-1]+dp[i-2]; } } return dp[n]; // 官方: // int p = 0, q = 0, r = 1; // for (int i = 1; i <= n; ++i) { // p = q; // q = r; // r = p + q; // } // return r; } }
主要思路:先对数组进行排序,然后两两比较,当不同时,输出比较的第一个数。比较到最后只剩一个数,则输出最后一个数。
官方: 异或的方法
package Easy; public class OneHundredThirtySix { public static void main(String[] args) { OneHundredThirtySixSolution a = new OneHundredThirtySixSolution(); int[] nums = {4, 1, 2, 1, 2}; System.out.println(a.singleNumber(nums)); } } class OneHundredThirtySixSolution { public int singleNumber(int[] nums) { int count = 0; for (int i = 0; i < nums.length; i++) { for (int j = i + 1; j < nums.length; j++) { if (nums[i] > nums[j]) { int a = nums[i]; nums[i] = nums[j]; nums[j] = a; } } } for (int i = 0; i < nums.length - 1; i = i + 2) { if (nums[i] != nums[i + 1]) { count = nums[i]; return count; } } count = nums[nums.length - 1]; return count; } // 异或: // int single = 0; // for (int num : nums) { // single ^= num; // } // return single; }
主要思路:新建一个链表,然后用头插法加入到新链表里面,最后返回该链表即可。
官方还有个 递归解法。
class TwoHundredSixSolution { public ListNode reverseList(ListNode head) { if (head==null){ return head; } ListNode count=new ListNode(0,null); ListNode temp=head; ListNode temp1=temp.next; while (temp1!=null){ temp.next=count.next; count.next=temp; temp=temp1; temp1=temp1.next; } temp.next=count.next; count.next=temp; return count.next; //这里是借助辅助变量,画个图既可以理解 // ListNode prev = null; // ListNode curr = head; // while (curr != null) { // ListNode next = curr.next; // curr.next = prev; // prev = curr; // curr = next; // } // return prev; } }
主要思路:主要就是换位,找到中间数,依次把中间数左右两边字符交换,这里有个奇数偶数的问题,奇数不用改变,偶数则把中间数与它左边的先换就OK了。
package Easy; public class ThreeHundredFortyFour { public static void main(String[] args) { ThreeHundredFortyFourSolution a=new ThreeHundredFortyFourSolution(); char[] test={'h','e','l','l','o'}; a.reverseString(test); for (int i = 0; i < test.length; i++) { System.out.println(test[i]); } } } class ThreeHundredFortyFourSolution { public void reverseString(char[] s) { char temp; if(s.length%2==0){ for (int i = 0; i < s.length/2; i++) { temp=s[s.length/2+i]; s[s.length/2+i]=s[s.length/2-i-1]; s[s.length/2-i-1]=temp; } }else { for (int i = 0; i < s.length/2; i++) { temp=s[s.length/2+i+1]; s[s.length/2+i+1]=s[s.length/2-i-1]; s[s.length/2-i-1]=temp; } } } }
主要思路:遍历。不过要解决一些问题,例如元素重复,数组长度不等。这里用的是Collection集合类中的Set来解决的,也用了Set的方法,不过主要思路还是遍历,然后去除掉重复的数字,以及确定数组的长度。
package Easy; import java.util.HashSet; import java.util.Set; public class ThreeHundredFortyNine { public static void main(String[] args) { ThreeHundredFortyNineSolution a=new ThreeHundredFortyNineSolution(); int[] nums1={4,9,5}; int[] nums2={9,4,9,8,4}; int[] test=a.intersection(nums1,nums2); for (int i = 0; i < test.length; i++) { System.out.println(test[i]); } } } class ThreeHundredFortyNineSolution { public int[] intersection(int[] nums1, int[] nums2) { Set<Integer> set1 = new HashSet<Integer>(); Set<Integer> set2 = new HashSet<Integer>(); for (int num : nums1) { set1.add(num); } for (int num : nums2) { set2.add(num); } return Intersection(set1, set2); } public int[] Intersection(Set<Integer> set1, Set<Integer> set2) { if (set1.size() > set2.size()) { return Intersection(set2, set1); } Set<Integer> intersectionSet = new HashSet<Integer>(); for (int num : set1) { if (set2.contains(num)) { intersectionSet.add(num); } } int[] intersection = new int[intersectionSet.size()]; int temp = 0; for (int num : intersectionSet) { intersection[temp] = num; temp++; } return intersection; } }
主要思路:二分查找,当数值小于2时不需要查找,所以定义左边的为2,右边的为num除2,因为大于2的平方数,必然大于等于这个数的两倍,所以把右边界定义为num除2,然后找到中间数,算出其平方并与num相比较,若是大于则把右边的数换位中间数减一,反之,把左边书换位中间数减一,直至找到或者没找到。
package Easy; public class ThreeHundredSixtysSeven { public static void main(String[] args) { ThreeHundredSixtysSevenSolution a = new ThreeHundredSixtysSevenSolution(); System.out.println(a.isPerfectSquare(2000105819)); } } class ThreeHundredSixtysSevenSolution { public boolean isPerfectSquare(int num) { if (num < 2) { return true; } long left = 2, right = num / 2, n, temp; while (left <= right) { n = left + (right - left) / 2; temp = n * n; if (temp == num) { return true; } if (temp > num) { right = n - 1; } else { left = n + 1; } } return false; } }
主要思路:求其每个字符的ASCII码的和,相减的即为多出的字符。
package Easy; public class ThreeHundredEightyNine { public static void main(String[] args) { ThreeHundredEightyNineSolution a=new ThreeHundredEightyNineSolution(); System.out.println(a.findTheDifference("abcd","abcde")); } } class ThreeHundredEightyNineSolution { public char findTheDifference(String s, String t) { int count1 = 0, count2 = 0; for (int i = 0; i < s.length(); ++i) { count1 += s.charAt(i); } for (int i = 0; i < t.length(); ++i) { count2 += t.charAt(i); } return (char) (count2 - count1); } }
主要思路:递归。查找每一个员工的直系员工,用一个变量记录重要度之和。
class SixHundredNinetySolution { int count = 0; public int getImportance(List<Employee> employees, int id) { for (Employee e : employees) { if (e.id == id) { count=count+e.importance; for (int a:e.subordinates) { getImportance(employees,a); } } } return count; } } class Employee { public int id; public int importance; public List<Integer> subordinates; }
主要思路:用双指针来遍历字符串,右指针先遍历,直至碰到与遍历过的字符有相同的时候停止,这时候左指针开始遍历,直至当前字符串内没有重复的字符时停止。也可以说删除字符,删除第一个字符,第二个…直至左指针到右指针这区间内没有相同字符结束,此时右指针接着遍历。在这个过程中,用一个数来记录其中最长的字符串数——也被称为滑动窗口思想。即把左右指针内的取间看作为一个窗口,此窗口内没有相同字符
package Medium; import java.util.HashSet; import java.util.Set; public class Three { public static void main(String[] args) { ThreeSolution a=new ThreeSolution(); String test="abcabcbb"; System.out.println(a.lengthOfLongestSubstring(test)); } } class ThreeSolution { public int lengthOfLongestSubstring(String s) { Set<Character> n=new HashSet<>();//主要要理解HashSet的用法 int count=0,temp=-1;//count用来计算最长的无重复字串,temp用来充当右指针。 for (int i = 0; i < s.length(); i++) {//i充当左指针 if (i!=0){//主要用来删除字符,当while循环结束时就代表当前的s.charAt(temp+1)与前面的有重复字符 n.remove(s.charAt(i-1));//从第一个开始删除,直至没有重复的为止 } while (temp+1<s.length()&&!n.contains(s.charAt(temp+1))){//判断是否存在重复的字符 n.add(s.charAt(temp+1)); temp++; } count=Math.max(count,temp+1-i);//把两个值大的赋值给count,相当于当后面有无重复字符串的长度大于之前的那个时,更新count的值 } return count; } }
主要思路:采用双指针,从数组左右两边开始遍历,每次移动值小的那一边的指针
package Medium; public class Eleven { public static void main(String[] args) { ElevenSolution a=new ElevenSolution(); int[] nums={1,1}; System.out.println(a.maxArea(nums)); } } class ElevenSolution { public int maxArea(int[] height) { int left = 0, right = height.length - 1; int ans = 0; while (left < right) { int area = Math.min(height[left], height[right]) * (right - left); ans = Math.max(ans, area); if (height[left] <= height[right]) { ++left; } else { --right; } } return ans; } }
主要思路:普通的枚举需要三重循环,并且之后还要去重,时间空间复杂度很高,我这里用的是,双指针+排序。用双指针去除重复的项,我们保持按照三个数从小到大的顺序,这样就不会有重复的项。16题与此题类似,就不再重复。
class FifteenSolution { public List<List<Integer>> threeSum(int[] nums){ //对数组进行排序 Arrays.sort(nums); List<List<Integer>> ans = new ArrayList<List<Integer>>(); //枚举第一个数也就是a; for (int first = 0; first < nums.length; first++) { //相同的数就跳过,要枚举不同的数 if (nums[first]!=nums[first-1]&&first>0){ continue; } //第三个数对应的是数组最右边的数 int third=nums.length-1; //和为零,所有剩下两个数和为第一个数的负数即可 int count=-nums[first]; //枚举第二个数 for (int second = first+1; second < nums.length; second++) { //相同的数就跳过,要枚举不同的数 if (second>first+1&&nums[second]==nums[second-1]) continue; //保持第二个数在第三个数的左边 while (second<third&&nums[second]+nums[third]>count){ third--; } // 如果指针重合,随着 b 后续的增加,就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环 if (third==second){ break; } if (nums[second]+nums[third]==count){ List<Integer> temp=new ArrayList(); temp.add(nums[first]); temp.add(nums[second]); temp.add(nums[third]); ans.add(temp); } } } return ans; } }
主要思路:与三树之和思路相同,只是多了一层循环,以及不同的判断条件,这些我都在代码里面注释了。这题目与三数之和也类似,但判断条件变了,所以放在这里供参考。
class EighteenSolution { public List<List<Integer>> fourSum(int[] nums, int target) { List<List<Integer>> list=new ArrayList<>(); //数组长度小于四,直接返回 if (nums==null||nums.length<3){ return list; } Arrays.sort(nums); for (int i = 0; i < nums.length-3; i++) { if (i>0&&nums[i]==nums[i-1]){ continue; } //因为按照从小到大排序,前四个和大于target,则不存在和为target的组合 if (nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target){ break; } //这里是与最后三个相加,如果小于target则不用枚举其他的组合,调整i的值即可 if (nums[i]+nums[nums.length-3]+nums[nums.length-2]+nums[nums.length-1]<target){ continue; } for (int j = i+1; j < nums.length-2; j++) { if (j>i+1&&nums[j]==nums[j-1]){ continue; } //同理 if (nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) { break; } //同理 if (nums[i] + nums[j] + nums[nums.length - 2] + nums[nums.length - 1] < target) { continue; } //这里用双指针代替双重循环,减少时间复杂度,与三数之和同理 int left = j + 1, right = nums.length - 1; while (left < right) { int sum = nums[i] + nums[j] + nums[left] + nums[right]; if (sum == target) { list.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right])); while (left < right && nums[left] == nums[left + 1]) { left++; } left++; while (left < right && nums[right] == nums[right - 1]) { right--; } right--; } else if (sum < target) { left++; } else { right--; } } } } return list; } }
主要思路:先遍历链表,算出长度,倒数第n个节点,即为顺数第链表长度-n+1个节点,删除此节点即可。
class NineteenSolution { public ListNode removeNthFromEnd(ListNode head, int n) { int count=0; ListNode temp=head; while (temp!=null){ count++; temp=temp.next; } ListNode a=new ListNode(0,head); temp=a; for (int i = 1; i < count-n+1; i++) { temp=temp.next; } temp.next=temp.next.next; return a.next; } }
主要思路:双指针,从数组两端开始遍历。
package Easy; public class ThirtyFour { public static void main(String[] args) { int[] nums={5,7,7,8,8,10}; int target=8; ThirtyFourSolution a=new ThirtyFourSolution(); int[] temp=a.searchRange(nums,target); for (int i = 0; i < temp.length; i++) { System.out.println(temp[i]); } } } class ThirtyFourSolution { public int[] searchRange(int[] nums, int target) { //双指针 int[] temp={-1,-1}; int left=0,right=nums.length-1; while (left<=right){ if (nums[left]==target&&nums[right]==target){ temp[0]=left; temp[1]=right; break; }else if (nums[left]!=target){ left++; }else if (nums[right]!=target){ right--; } } return temp; } }
主要思路:把left和right内的链表单独拿出来逆转,然后再接到原来的链表上去即可。这里逆转链表可以参看206题。因为头节点可能发生改变,所以用一个新链表进行操作。结合图与代码。
class NintyTwoSolution { public ListNode reverseBetween(ListNode head, int left, int right) { ListNode Temp=new ListNode(0,head); ListNode left1=Temp; for (int i = 0; i < left-1; i++) { left1=left1.next; } ListNode right2=left1; for (int i = 0; i < right-left+1; i++) { right2=right2.next; } ListNode left2=left1.next; ListNode right1=right2.next; left1.next=null; right2.next=null; reverseList(left2); left1.next=right2; left2.next=right1; return Temp.next; } public ListNode reverseList(ListNode head) { ListNode prev = null; ListNode curr = head; while (curr != null) { ListNode next = curr.next; curr.next = prev; prev = curr; curr = next; } return prev; } }
主要思路:排序暴力遍历,先排序,我们只要比较第一个和第三个是否相等,然后向后移动三位,因为有三个数相等,当不相等时,输出前面的,然后照这样遍历,如果那个数字最大,最后还会剩一位就输出最后一位即可。当数组只有一位,输出即可
package Medium; import java.util.Arrays; public class OneHundredThirtySeven { public static void main(String[] args) { int[] nums={0,1,0,1,0,1,99}; OneHundredThirtySevenSolution a=new OneHundredThirtySevenSolution(); System.out.println(a.singleNumber(nums)); } } class OneHundredThirtySevenSolution { public int singleNumber(int[] nums) { if (nums.length<1){ return nums[0]; } Arrays.sort(nums); for (int i = 0; i < nums.length-2; i=i+3) { if (nums[i]!=nums[i+2]){ return nums[i]; } } return nums[nums.length-1]; } }
主要思路:遍历,并且利用内置函数Math.sqrt(),这个作用是求平方根,从0开始枚举。然后减去i的平方,求根号是否为整数,是则返回true。
package Medium; public class SixHundredThirtyThree { public static void main(String[] args) { SixHundredThirtyThreeSolution a=new SixHundredThirtyThreeSolution(); System.out.println(a.judgeSquareSum(7)); } } class SixHundredThirtyThreeSolution { public boolean judgeSquareSum(int c) { for (int i = 0; i*i < c; i++) { double b=Math.sqrt(c-i*i); if (b==(int)b) return true; } return false; } }
主要思路:二分法,左边界就是一个数组中最大的那个,因为不是数组最大的那个,就运输不了最大的那个包裹了,右边界就是数组总全部的元素之和,因为最少一天就全部运输完,然后就采用二分法来查找最佳运输质量。
package Medium; import java.util.Arrays; public class OneThousandEleven { public static void main(String[] args) { OneThousandElevenSolution a=new OneThousandElevenSolution(); int[] test={1,2,3,4,5,6,7,8,9,10}; System.out.println(a.shipWithinDays(test,5)); } } class OneThousandElevenSolution { public int shipWithinDays(int[] weights, int D) { int left= Arrays.stream(weights).max().getAsInt(); int right=Arrays.stream(weights).sum(); while (left<right){ int mid=(left+right)/2; int need=0,count=0; for (int i = 0; i < weights.length; i++) { if (count+weights[i]>mid){ need++; count=0; } count+=weights[i]; } if (need<D){ right=mid; }else { left=mid+1; } } return left; } }
主要思路:一开始用的暴力求解,提交之后发现超出时间限制,就用一个数组记录arr数组的前n项异或的结果,然后用异或的运算可以得到:
然后就for循环求出对应查询的异或值。
class ThousandThreeHundredTenSolution { public int[] xorQueries(int[] arr, int[][] queries) { int[] temp=new int[arr.length+1]; for (int i = 0; i < temp.length-1; i++) { temp[i+1]=temp[i]^arr[i]; } int[] count=new int[queries.length]; for (int i = 0; i < queries.length; i++) { count[i]=temp[queries[i][0]]^temp[queries[i][1]+1]; } return count; //暴力求解,超出了时间限制。 // int[] count=new int[queries.length]; // for (int i = 0; i < queries.length; i++) { // while(queries[i][0]<=queries[i][1]){ // count[i]=count[i]^arr[queries[i][0]]; // queries[i][0]++; // } // } // return count; } }
主要思路:一开始我也没想到这个方法,只想到暴力,看了题解的前一部分,我就知道怎么做了,还是太菜了,题解如下:
相信看到这个基本都会了,主要是异或的运算,异或还是不够了解。
class ThousandFourHundredFortyTwoSolution { public int countTriplets(int[] arr) { int n=arr.length; int[] count=new int[n+1]; int temp=0; for (int i = 0; i < n; i++) { count[i+1]=count[i]^arr[i]; } for (int i = 0; i < n; i++) { for (int j = i+1; j < n; j++) { for (int k = j; k <n ; k++) { if (count[i]==count[k+1]){ temp++; } } } } return temp; } }
主要思路:先合并两个数组,然后取中间的数即可,偶数个取中间两个数的平均数。
package Hard; public class Four { public static void main(String[] args) { FourSolution a=new FourSolution(); int[] nums={1,2}; int[] nums1={3,4}; System.out.println(a.findMedianSortedArrays(nums,nums1)); } } class FourSolution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { float num; int[] count=new int[nums1.length+nums2.length]; for (int i = 0; i < nums1.length; i++) { count[i]=nums1[i]; } for (int i = nums1.length; i < nums1.length+nums2.length; i++) { count[i]=nums2[i-nums1.length]; } for (int i = 0; i < count.length; i++) { for (int j = i+1; j < count.length; j++) { int a=0; if (count[i]>count[j]){ a=count[i]; count[i]=count[j]; count[j]=a; } } } int temp=count.length/2; if (count.length%2==0){ num=(float) (count[temp-1]+count[temp])/2;//因为整数相除结果会去掉小数,所以在前面加一个(float)转换为浮点型。 }else { num=count[temp]; } return num; } }
主要思路:逐个合并。创建一个辅助节点,把辅助节点和数组第一个链表合并,然后逐一与数组下一个链表合并即可。这个个人感觉并不是Hard题