LeetCode 热题 HOT 100
1,两数之和
难度简单13695
给定一个整数数组
nums
和一个整数目标值target
,请你在该数组中找出 和为目标值target
的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
思路:
- 采用两层遍历法,代码比较简单,单复杂度高、
- 为了减少时间复杂度,因为题干中说明了,只有一种情况的答案,顾可以采用map<value,index>去存储nums数组,达到减少时间复杂度。
class Solution { public int[] twoSum(int[] nums, int target) { for(int i=0;i<nums.length-1;i++){ for(int j=i+1;j<nums.length;j++){ if(nums[i]+nums[j]==target) return new int[]{i,j}; } } return new int[]{0,0}; } }
class Solution { public int[] twoSum(int[] nums, int target) { HashMap<Integer,Integer> m=new HashMap<>(); for(int i=0;i<nums.length;i++){ m.put(nums[i],i); } for(int i=0;i<nums.length;i++){ int t=target-nums[i]; if(m.containsKey(t)&&m.get(t)!=i){// 相同的位置的值不能出现在答案中 return new int[]{i,m.get(t)}; } } return new int[]{0,0}; } }
2,两数相加(两链表相加)
难度中等7667
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
/** * 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 res=new ListNode(); ListNode tail=res; res.next=null; int jin=0; while(l1!=null&&l2!=null){ int t=l1.val+l2.val+jin; if(t>=10){ jin=1; t-=10; }else{ jin=0; } ListNode temp=new ListNode(t); temp.next=null; tail.next=temp; tail=temp; l1=l1.next; l2=l2.next; } while(l1!=null){ int t=l1.val+jin; if(t>=10){ jin=1; t-=10; }else{ jin=0; } ListNode temp=new ListNode(t); temp.next=null; tail.next=temp; tail=temp; l1=l1.next; } while(l2!=null){ int t=l2.val+jin; if(t>=10){ jin=1; t-=10; }else{ jin=0; } ListNode temp=new ListNode(t); temp.next=null; tail.next=temp; tail=temp; l2=l2.next; } if(jin==1){ ListNode temp=new ListNode(1); temp.next=null; tail.next=temp; tail=temp; } return res.next; } }
3,无重复字符的最长子串(for+indexOf)
难度中等7090
给定一个字符串
s
,请你找出其中不含有重复字符的 最长子串 的长度。class Solution { public int lengthOfLongestSubstring(String s) { int res=0; String temp=""; for(int i=0;i<s.length();i++){ int flag=temp.indexOf(s.charAt(i)); if(flag>=0){ temp=temp.substring(flag+1,temp.length()); } temp=temp+s.charAt(i); res=Integer.max(res,temp.length()); } return res; } }
4,寻找两个正序数组的中位数(大小根堆)
难度困难5124
给定两个大小分别为
m
和n
的正序(从小到大)数组nums1
和nums2
。请你找出并返回这两个正序数组的 中位数 。算法的时间复杂度应该为
O(log (m+n))
。class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { ArrayList<Integer> res=new ArrayList<>(); for(int i:nums1){ res.add(i); } for(int i:nums2){ res.add(i); } Collections.sort(res); int f=nums1.length+nums2.length; if(f%2==0){ return (double)(res.get(f/2-1)+res.get(f/2))/2; }else{ return res.get(f/2); } } }
5,最长回文子串(dp)
难度中等4842
给你一个字符串
s
,找到s
中最长的回文子串。示例 1:
输入:s = "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。class Solution { public String longestPalindrome(String s) { // 典型的dp问题 dp[i][j] 表示下标i-j的子字符串为回文串 // dp[i][j]=true or flase String res=""; res=res+s.charAt(0); boolean [][]dp=new boolean[s.length()][s.length()]; for(int i=0;i<s.length();i++){ dp[i][i]=true; } for(int len=2;len<=s.length();len++){ for(int i=0;i<s.length();i++){ int j=i+len-1; if(j>=s.length())break; if(s.charAt(i)==s.charAt(j)){ if(j-i<=2){ dp[i][j]=true; }else{ dp[i][j]=dp[i+1][j-1]; } } if(dp[i][j]==true&&j-i+1>res.length()){ res=s.substring(i,j+1); } } } return res; } }
11,盛最多水的容器(贪心)
难度中等3280
给定一个长度为
n
的整数数组height
。有n
条垂线,第i
条线的两个端点是(i, 0)
和(i, height[i])
。找出其中的两条线,使得它们与
x
轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。
说明:你不能倾斜容器。
class Solution { public int maxArea(int[] height) { int res=0; int left=0; int right=height.length-1; while(left<right){ res=Math.max(res,(right-left)*Math.min(height[left],height[right])); if(height[left]>height[right]){ right--; }else if(height[left]<height[right]){ left++; }else{ if(right-left<=2){ left++; }else{ if(height[left+1]>height[right-1]){ left++; }else { right--; } } } } return res; } }
15、三数之和(三层循环)
难度中等4464
给你一个包含
n
个整数的数组nums
,判断nums
中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为0
且不重复的三元组。注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]]思路:
- 三数之和,先排序,三层遍历,固定两个索引,从后向前遍历,去重步骤是开头两固定的索引(满足nums[i]!=nums[i-1])即可达到去重复的目的。
class Solution { public List<List<Integer>> threeSum(int[] nums) { int n = nums.length; Arrays.sort(nums); List<List<Integer>> ans = new ArrayList<List<Integer>>(); // 枚举 a for (int first = 0; first < n; ++first) { // 需要和上一次枚举的数不相同 if (first > 0 && nums[first] == nums[first - 1]) { continue; } // c 对应的指针初始指向数组的最右端 int third = n - 1; int target = -nums[first]; // 枚举 b for (int second = first + 1; second < n; ++second) { // 需要和上一次枚举的数不相同 if (second > first + 1 && nums[second] == nums[second - 1]) { continue; } // 需要保证 b 的指针在 c 的指针的左侧 while (second < third && nums[second] + nums[third] > target) { --third; } // 如果指针重合,随着 b 后续的增加 // 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环 if (second == third) { break; } if (nums[second] + nums[third] == target) { List<Integer> list = new ArrayList<Integer>(); list.add(nums[first]); list.add(nums[second]); list.add(nums[third]); ans.add(list); } } } return ans; } }
17、电话号码的字母组合(dfs)
难度中等1770
给定一个仅包含数字
2-9
的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:
输入:digits = "23" 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]class Solution { List<String> res=new ArrayList<>(); public List<String> letterCombinations(String digits) { if(digits.length()==0)return res; String[] map=new String[]{"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"}; dfs(0,digits,"",map); return res; } public void dfs(int index,String s,String cv,String []map){ if(index==s.length()){ res.add(new String(cv)); return; } for(int i=0;i<map[s.charAt(index)-'0'].length();i++){ dfs(index+1,s,cv+map[s.charAt(index)-'0'].charAt(i),map); } } }
19,删除链表的倒数第N个结点
难度中等1861
给你一个链表,删除链表的倒数第
n
个结点,并且返回链表的头结点。示例 1:
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]/** * 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) { ListNode res=new ListNode(-1); res.next=head; while(n!=0){ head=head.next; n--; } ListNode p1=res,p2=res.next; while(head!=null){ p1=p1.next; p2=p2.next; head=head.next; } p1.next=p2.next; return res.next; } }
20,有效的括号(栈)
难度简单3073
给定一个只包括
'('
,')'
,'{'
,'}'
,'['
,']'
的字符串s
,判断字符串是否有效。有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
class Solution { public boolean isValid(String s) { Stack<Character> stack=new Stack<>(); for(int i=0;i<s.length();i++){ if(s.charAt(i)=='('||s.charAt(i)=='{'||s.charAt(i)=='['){ stack.push(s.charAt(i)); }else{ if(stack.isEmpty())return false; switch(s.charAt(i)){ case ')': if(stack.peek()=='('){ stack.pop(); }else return false; break; case '}': if(stack.peek()=='{'){ stack.pop(); }else return false; break; case ']': if(stack.peek()=='['){ stack.pop(); }else return false; break; } } } return stack.isEmpty(); } }
21、合并两个有序链表
难度简单2264
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4]/** * 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 res=new ListNode(-1); res.next=null; ListNode tail=res; while(list1!=null&&list2!=null){ ListNode temp; if(list1.val>list2.val){ temp=list2; list2=list2.next; }else{ temp=list1; list1=list1.next; } temp.next=null; tail.next=temp; tail=temp; } if(list1!=null){ tail.next=list1; } if(list2!=null){ tail.next=list2; } return res.next; } }
22、括号生成(DFS)
难度中等2442
数字
n
代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。示例 1:
输入:n = 3 输出:["((()))","(()())","(())()","()(())","()()()"]思路:
- 深度遍历只需要管住(,然后对)的数量小于等于(的数量。
class Solution { List<String> res=new ArrayList<>(); public List<String> generateParenthesis(int n) { dfs(0,"",n); return res; } void dfs(int index,String cv,int n){ if(cv.length()==2*n){ res.add(new String(cv)); return; } if(index<n) dfs(index+1,cv+"(",n); if(2*index>cv.length()){ dfs(index,cv+")",n); } } }
23、合并k个升序链表
难度困难1806
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]] 输出:[1,1,2,3,4,4,5,6] 解释:链表数组如下: [ 1->4->5, 1->3->4, 2->6 ] 将它们合并到一个有序链表中得到。 1->1->2->3->4->4->5->6注意:
- throw new IllegalArgumentException("Comparison method violates its general contract!");
- google了一下:JDK7中的Collections.Sort方法实现中,如果两个值是相等的,那么compare方法需要返回0,否则可能会在排序时抛错,而JDK6是没有这个限制的。
/** * 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) { ListNode res=new ListNode(-1); ListNode tail=res; res.next=null; ArrayList<ListNode> arr=new ArrayList<>(); for(ListNode temp:lists){ ListNode t=temp; while(t!=null){ ListNode tt=t; t=t.next; tt.next=null; arr.add(tt); } } Collections.sort(arr,new Comparator<ListNode>(){ public int compare(ListNode o1,ListNode o2){ // if(o1.val>o2.val)return 1; // else if(o1.val<o2.val) return -1; // else return 0; return o1.val-o2.val; } }); for(int i=0;i<arr.size();i++){ ListNode temp=arr.get(i); tail.next=temp; tail=temp; } return res.next; } }
31,下一个排列
难度中等1586
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
- 例如,
arr = [1,2,3]
,以下这些都可以视作arr
的排列:[1,2,3]
、[1,3,2]
、[3,1,2]
、[2,3,1]
。整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
- 例如,
arr = [1,2,3]
的下一个排列是[1,3,2]
。- 类似地,
arr = [2,3,1]
的下一个排列是[3,1,2]
。- 而
arr = [3,2,1]
的下一个排列是[1,2,3]
,因为[3,2,1]
不存在一个字典序更大的排列。给你一个整数数组
nums
,找出nums
的下一个排列。必须 原地 修改,只允许使用额外常数空间。
思路:
- 从后向前遍历,索引i之后的数字只要满足一个大于nums[i]的就可以跳出,将i之后的最小大于nums[i]的数和nums[i]进行交换,在进行Arrays.sort(nums,i+1,nums.length);
class Solution { ArrayList<String> list=new ArrayList<>(); public void nextPermutation(int[] nums) { int i=nums.length-1; PriorityQueue<Integer> q=new PriorityQueue<>((x,y)->y-x); for(;i>=0;i--){ if(q.isEmpty()){ q.add(nums[i]); }else{ if(nums[i]<q.peek()){ break; }else{ q.add(nums[i]); } } } if(q.size()==nums.length){ Arrays.sort(nums,0,nums.length); return; } int last=-1; while(!q.isEmpty()&&q.peek()>nums[i]){ last=q.peek(); q.poll(); } int j=i+1; while(j<nums.length){ if(nums[j]==last){ break; } j++; } nums[i]^=nums[j]; nums[j]^=nums[i]; nums[i]^=nums[j]; Arrays.sort(nums,i+1,nums.length); } }
33,搜索旋转排序数组
难度中等1915
整数数组
nums
按升序排列,数组中的值 互不相同 。在传递给函数之前,
nums
在预先未知的某个下标k
(0 <= k < nums.length
)上进行了 旋转,使数组变为[nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]
(下标 从 0 开始 计数)。例如,[0,1,2,4,5,6,7]
在下标3
处经旋转后可能变为[4,5,6,7,0,1,2]
。给你 旋转后 的数组
nums
和一个整数target
,如果nums
中存在这个目标值target
,则返回它的下标,否则返回-1
。class Solution { public int search(int[] nums, int target) { // 左边从左到右是递增的 // 右边从右到左是递减的 int len=nums.length; int i=0; for(;i<len;i++) { if(nums[i]==target)return i; if(i>0&&nums[i]<nums[i-1]){ break; } } int j=len-1; while(i<=j){ int mid=(i+j)/2; if(target==nums[mid]){ return mid; }else if(nums[mid]>target){ j=mid-1; }else{ i=mid+1; } } return -1; } }
34,在排序数组中查找元素的第一个和最后一个位置
难度中等1533
给定一个按照升序排列的整数数组
nums
,和一个目标值target
。找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值
target
,返回[-1, -1]
。进阶:
- 你可以设计并实现时间复杂度为
O(log n)
的算法解决此问题吗?class Solution { public int[] searchRange(int[] nums, int target) { int index=Arrays.binarySearch(nums,target); if(index<0)return new int[]{-1,-1}; int l=index,r=index; while(l>=0&&nums[l]==target){ l--; } l++; while(r<nums.length&&nums[r]==target){ r++; } r--; return new int[]{l,r}; } }
39,组合总和(dfs)注意和139单词拆分的区别
难度中等1824
给你一个 无重复元素 的整数数组
candidates
和一个目标整数target
,找出candidates
中可以使数字和为目标数target
的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates
中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。对于给定的输入,保证和为
target
的不同组合数少于150
个。class Solution { List<List<Integer>> res=new ArrayList<>(); public List<List<Integer>> combinationSum(int[] candidates, int target) { dfs(candidates,target,0,new ArrayList<>()); return res; } void dfs(int[] candidates,int target,int index,ArrayList<Integer> tep){ if(index==candidates.length||target<0){ return; } if(target==0){ res.add(new ArrayList<>(tep)); return; } dfs(candidates,target,index+1,tep); if(target-candidates[index]>=0){ tep.add(candidates[index]); dfs(candidates,target-candidates[index],index,tep); tep.remove(new Integer(candidates[index])); } } }
42,接雨水
难度困难3227
给定
n
个非负整数表示每个宽度为1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]class Solution { public int trap(int[] height) { int left[]=new int[height.length]; int right[]=new int[height.length]; int min=-1; for(int i=0;i<height.length;i++){ if(height[i]>min){ min=height[i]; } left[i]=min; } min=-1; for(int i=height.length-1;i>=0;i--){ if(height[i]>min){ min=height[i]; } right[i]=min; } int res=0; for(int i=0;i<height.length;i++){ int cmin=Integer.min(left[i],right[i]); if(cmin>height[i]){ res+=(cmin-height[i]); } } return res; } }
46,全排列(DFS
难度中等1846
给定一个不含重复数字的数组
nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。class Solution { List<List<Integer>> res=new ArrayList<>(); public List<List<Integer>> permute(int[] nums) { dfs(0,nums,new boolean[nums.length],new ArrayList<Integer>()); return res; } void dfs(int index,int []nums,boolean []visited,ArrayList<Integer> temp){ if(index==nums.length){ res.add(new ArrayList<>(temp)); return; } for(int i=0;i<visited.length;i++){ if(!visited[i]){ temp.add(nums[i]); visited[i]=true; dfs(index+1,nums,visited,temp); temp.remove(new Integer(nums[i])); visited[i]=false; } } } }
48,旋转图像
难度中等1211
给定一个 n × n 的二维矩阵
matrix
表示一个图像。请你将图像顺时针旋转 90 度。你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
思路:
- 逆时针旋转90°等效于左上角和右下角对换+上交对换。
- System.arraycopy(),可以保证对原数组进行从新赋值。
class Solution { public void rotate(int[][] matrix) { int res[][]=new int[matrix.length][matrix[0].length]; int index=0; for(int i=0;i<matrix.length;i++){ int tep[]=new int[matrix.length]; int cv=0; for(int j=matrix.length-1;j>=0;j--){ tep[cv]=matrix[j][i]; cv++; } res[index]=Arrays.copyOf(tep,tep.length); index++; } // matrix=res; for(int i=0;i<matrix.length;i++) System.arraycopy(res[i],0,matrix[i],0,matrix.length); } }//
49,字母异或位词分组(DFS+Map<String,List<String>>)
难度中等1054
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。
思路:
- map<String,value>,String的比较方法被从写,是比较值。
class Solution { List<List<String>> res=new LinkedList<>(); public List<List<String>> groupAnagrams(String[] strs) { HashMap<String,List<String>> mymap=new HashMap<>(); int len=strs.length; for(int i=0;i<len;i++) { char[] chars = strs[i].toCharArray(); Arrays.sort(chars); String tep=new String(chars); List<String> strings = mymap.get(tep); if(strings==null) { strings=new LinkedList<>(); strings.add(strs[i]); mymap.put(tep,strings); } else{ strings.add(strs[i]); } } // 对mymap进行遍历 mymap.forEach((x,y)->{ res.add(new LinkedList<>(y)); }); return res; } }
53,最大子数组和(贪心)
难度简单4543
给你一个整数数组
nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。
class Solution { public int maxSubArray(int[] nums) { int res=Integer.MIN_VALUE; int cv=0; for(int i:nums){ if(cv+i<=i){ cv=i; }else{ cv+=i; } res=Integer.max(res,cv); } return res; } }
55,跳跃游戏(贪心)
难度中等1715
给定一个非负整数数组
nums
,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
思路:
- 从后向前跳跃,贪心思想。
class Solution { public boolean canJump(int[] nums) { int res=1; for(int i=nums.length-2;i>=0;i--){ if(nums[i]>=res){ res=1; }else{ res++; } } return res==1?true:false; } }
56,合并区间
难度中等1375
以数组
intervals
表示若干个区间的集合,其中单个区间为intervals[i] = [starti, endi]
。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。class Solution { public int[][] merge(int[][] intervals) { Arrays.sort(intervals, new Comparator<int[]>() { @Override public int compare(int[] o1, int[] o2) { if(o1[0]>o2[0]){ return 1; }else if(o1[0]==o2[0]){ if(o1[1]>o2[1]) { return 1; } else if(o1[1]==o2[1]) { return 0; }else{ return -1; } }else{ return -1; } } }); List<int[]> res=new ArrayList<>(); int index=0; int l=intervals[0][0]; int r=intervals[0][1]; index++; while(index<intervals.length){ // 判断是否相交 if(l<=intervals[index][0]&&intervals[index][0]<=r){ r=Integer.max(r,intervals[index][1]); }else{ res.add(new int[]{l,r}); l=intervals[index][0]; r=intervals[index][1]; } index++; } res.add(new int[]{l,r}); return res.toArray(new int [0][]); } }
62,不同路径(DP)
难度中等1326
一个机器人位于一个
m x n
网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
class Solution { public int uniquePaths(int m, int n) { int dp[][]=new int[m][n]; for(int i=0;i<m;i++)dp[i][0]=1; for(int i=0;i<n;i++)dp[0][i]=1; for(int i=1;i<m;i++){ for(int j=1;j<n;j++){ dp[i][j]=dp[i-1][j]+dp[i][j-1]; } } return dp[m-1][n-1]; } }
64,最小路径和(DP)
难度中等1179
给定一个包含非负整数的
m x n
网格grid
,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。说明:每次只能向下或者向右移动一步。
示例 1:
class Solution { public int minPathSum(int[][] grid) { int dp[][]=new int[grid.length][grid[0].length]; int csum=0; for(int i=0;i<grid.length;i++){ csum+=grid[i][0]; dp[i][0]=csum; } csum=0; for(int i=0;i<grid[0].length;i++){ csum+=grid[0][i]; dp[0][i]=csum; } for(int i=1;i<grid.length;i++){ for(int j=1;j<grid[0].length;j++){ dp[i][j]=Integer.min(dp[i-1][j],dp[i][j-1])+grid[i][j]; } } return dp[grid.length-1][grid[0].length-1]; } }
70,爬楼梯(DFS,迭代)
难度简单2264
假设你正在爬楼梯。需要
n
阶你才能到达楼顶。每次你可以爬
1
或2
个台阶。你有多少种不同的方法可以爬到楼顶呢?示例 1:
输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶。 1. 1 阶 + 1 阶 2. 2 阶class Solution { public int climbStairs(int n) { if(n==1)return 1; if(n==2)return 2; int l1=1,l2=2; int res=0; for(int i=3;i<=n;i++){ res=l1+l2; l1=l2; l2=res; } return res; } }
75,颜色分类
难度中等1193
给定一个包含红色、白色和蓝色、共
n
个元素的数组nums
,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。我们使用整数
0
、1
和2
分别表示红色、白色和蓝色。必须在不使用库的sort函数的情况下解决这个问题。
示例 1:
输入:nums = [2,0,2,1,1,0] 输出:[0,0,1,1,2,2]class Solution { public void sortColors(int[] nums) { int index0=0; int index2=0; int index1=0; for(int i=0;i<nums.length;i++){ if(nums[i]==0)index0++; else if(nums[i]==1)index1++; else index2++; } Arrays.fill(nums,0,index0,0); Arrays.fill(nums,index0,index0+index1,1); Arrays.fill(nums,index0+index1,index0+index1+index2,2); } }
78,子集(DFS)
难度中等1528
给你一个整数数组
nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
class Solution { List<List<Integer>> res=new ArrayList<>(); public List<List<Integer>> subsets(int[] nums) { dfs(0,nums,new ArrayList()); return res; } void dfs(int index,int []nums,ArrayList<Integer> cv){ if(index==nums.length){ res.add(new ArrayList<>(cv)); return; } // dfs(index+1,nums,cv); cv.add(nums[index]); dfs(index+1,nums,cv); cv.remove(new Integer(nums[index])); } }
79,单词搜索(DFS)
难度中等1224
给定一个
m x n
二维字符网格board
和一个字符串单词word
。如果word
存在于网格中,返回true
;否则,返回false
。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例 1:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED" 输出:trueclass Solution { private boolean res=false; public boolean exist(char[][] board, String word) { int m=board.length; int n=board[0].length; boolean [][]visited=new boolean[m][n]; for(int i=0;i<m;i++){ if(res)break; for(int j=0;j<n;j++){ if(res)break; if(board[i][j]==word.charAt(0)){ visited[i][j]=true; dfs(i,j,m,n,1,visited,board,word); visited[i][j]=false; } } } return res; } void dfs(int x,int y,int m,int n,int clen,boolean [][]visited,char[][] board,String word){ if(clen==word.length()){ res=true; return; } if(res)return; for(int i=0;i<4;i++){ switch(i){ case 0: if(x-1>=0&&!visited[x-1][y]&&board[x-1][y]==word.charAt(clen)){ visited[x-1][y]=true; dfs(x-1,y,m,n,clen+1,visited,board,word); visited[x-1][y]=false; } break; case 1: if(x+1<m&&!visited[x+1][y]&&board[x+1][y]==word.charAt(clen)){ visited[x+1][y]=true; dfs(x+1,y,m,n,clen+1,visited,board,word); visited[x+1][y]=false; } break; case 2: if(y-1>=0&&!visited[x][y-1]&&board[x][y-1]==word.charAt(clen)){ visited[x][y-1]=true; dfs(x,y-1,m,n,clen+1,visited,board,word); visited[x][y-1]=false; } break; case 3: if(y+1<n&&!visited[x][y+1]&&board[x][y+1]==word.charAt(clen)){ visited[x][y+1]=true; dfs(x,y+1,m,n,clen+1,visited,board,word); visited[x][y+1]=false; } break; } } } }
94,二叉树的中序遍历
难度简单1323
给定一个二叉树的根节点
root
,返回它的 中序 遍历。示例 1:
class Solution { private List<Integer> res=new ArrayList<>(); public List<Integer> inorderTraversal(TreeNode root) { inorder(root); return res; } void inorder(TreeNode root){ if(root==null){ return; } inorder(root.left); res.add(root.val); inorder(root.right); } }
96,不同的二叉搜索树(DP)
卡特兰数-动态规划
难度中等1624
给你一个整数
n
,求恰由n
个节点组成且节点值从1
到n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。解题思路:
假设 n 个节点存在二叉排序树的个数是 G (n),令 f(i) 为以 i 为根的二叉搜索树的个数,则
G(n) = f(1) + f(2) + f(3) + f(4) + ... + f(n)当 i 为根节点时,其左子树节点个数为 i-1 个,右子树节点为 n-i,则
f(i) = G(i-1)*G(n-i)综合两个公式可以得到 卡特兰数 公式
G(n) = G(0)*G(n-1)+G(1)*(n-2)+...+G(n-1)*G(0)G(n)=G(0)∗G(n−1)+G(1)∗(n−2)+...+G(n−1)∗G(0)class Solution { public int numTrees(int n) { int[] dp = new int[n+1]; dp[0] = 1;// 表示树长度为0 dp[1] = 1;// 表示树长度为1 // dp[i]表示长度为i的搜索二叉树的种数,dp[i]=dp[0]*dp[i]+dp[1]*dp[i-1]+dp[2]*dp[i-2].. for(int i = 2; i < n + 1; i++)// 树长 for(int j = 1; j < i + 1; j++) // 表示以j为根节点 dp[i] += dp[j-1] * dp[i-j]; return dp[n]; } }
98,验证二叉搜索树
难度中等1470
给你一个二叉树的根节点
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 { private long last=Integer.MAX_VALUE+1L; private boolean res=true; public boolean isValidBST(TreeNode root) { inorder(root); return res; } void inorder(TreeNode root){ if(root==null)return; if(!res)return; inorder(root.left); if(last==Integer.MAX_VALUE+1L){ last=root.val; }else{ if(root.val<=last){ res=false; }else{ last=root.val; } } inorder(root.right); } }
101,对称二叉树
难度简单1809
给你一个二叉树的根节点
root
, 检查它是否轴对称。class Solution { private boolean res=true; public boolean isSymmetric(TreeNode root) { // 根 左右 根 右左 dfs(root,root); return res; } void dfs(TreeNode root1,TreeNode root2){ if(root1==null&&root2==null)return; if(!res)return; if((root1==null||root2==null)||(root1.val!=root2.val)){ res=false; return; } dfs(root1.left,root2.right); dfs(root1.right,root2.left); } }
102,二叉树的层次遍历(队列)
难度中等1229
给你二叉树的根节点
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 { private List<List<Integer>> res=new ArrayList<>(); public List<List<Integer>> levelOrder(TreeNode root) { if(root==null)return res; LinkedList<TreeNode> q=new LinkedList<>(); q.addLast(root); int cv=0; int clen=1; int next=0; ArrayList<Integer> list=new ArrayList<>(); while(!q.isEmpty()){ TreeNode t= q.getFirst(); q.removeFirst(); cv++; list.add(t.val); if(t.left!=null){ next++; q.addLast(t.left); } if(t.right!=null){ next++; q.addLast(t.right); } if(cv==clen){ cv=0; clen=next; next=0; res.add(new ArrayList<>(list)); list.clear(); } } return res; } }
104,二叉树的最大深度
难度简单1152
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
class Solution { private int res=0; public int maxDepth(TreeNode root) { dfs(root,0); return res; } void dfs(TreeNode root,int cv){ if(root==null){ res=Integer.max(res,cv); return; } dfs(root.left,cv+1); dfs(root.right,cv+1); } }
105,从前序与中序遍历构造二叉树
难度中等1489
给定两个整数数组
preorder
和inorder
,其中preorder
是二叉树的先序遍历,inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。/** * 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 buildTree(int[] preorder, int[] inorder) { HashMap<Integer,Integer> map=new HashMap<>(); for(int i=0;i<inorder.length;i++){ map.put(inorder[i],i); } return creatTree(0,preorder.length-1,0,inorder.length-1,preorder,inorder,map); } TreeNode creatTree(int preorder_l,int preorder_r,int inorder_l,int inorder_r,int[] preorder,int inorder[], HashMap<Integer,Integer> map) { if(inorder_l>inorder_r)return null; //根 TreeNode root=new TreeNode(preorder[preorder_l]); // 获取中序遍历中根的下标 int root_index=map.get(preorder[preorder_l]); // 左子树的长度 int left_len=root_index-inorder_l; root.left=creatTree(preorder_l+1,preorder_l+left_len,inorder_l,root_index-1,preorder,inorder, map); root.right=creatTree(preorder_l+left_len+1,preorder_r,root_index+1,inorder_r,preorder,inorder, map); return root; } }
114,二叉树展开为链表
难度中等1104
给你二叉树的根结点
root
,请你将它展开为一个单链表:
- 展开后的单链表应该同样使用
TreeNode
,其中right
子指针指向链表中下一个结点,而左子指针始终为null
。- 展开后的单链表应该与二叉树 先序遍历 顺序相同。
class Solution { ArrayList<TreeNode> temp=new ArrayList<>(); public void flatten(TreeNode root) { if(root==null)return; preorder(root); TreeNode tail=temp.get(0); for(int i=1;i<temp.size();i++){ tail.left=null; tail.right=temp.get(i); tail=temp.get(i); } tail.left=null; tail.right=null; } void preorder(TreeNode root){ if(root==null)return; temp.add(root); preorder(root.left); preorder(root.right); } }
121,买卖股票的最佳时机(单调栈)
难度简单2208
给定一个数组
prices
,它的第i
个元素prices[i]
表示一支给定股票第i
天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回
0
。思路:
- 单调栈
class Solution { public int maxProfit(int[] prices) { Stack<Integer> s=new Stack<>(); int min=10001; int res=0; for(int i:prices){ if(s.isEmpty()){ s.push(i); min=i; }else{ while(!s.isEmpty()&&s.peek()>=i){ s.pop(); } s.push(i); // 更新min if(s.size()==1){ min=i; } } res=Integer.max(res,i-min); } return res; } }
128,最长连续序列
难度中等1163
给定一个未排序的整数数组
nums
,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。请你设计并实现时间复杂度为
O(n)
的算法解决此问题。// 考虑时间复杂度为o(n),不考虑空间复杂度 class Solution { public int longestConsecutive(int[] nums) { int len=nums.length; if(len==0)return 0; HashSet<Integer> myset=new HashSet<>(); for(int i=0;i<len;i++) myset.add(nums[i]); int res=1; for(Integer i:myset) { if(!myset.contains(i-1)) { int tep = i; int cv = 0; while (myset.contains(tep)) { tep++; cv++; } res = Integer.max(res, cv); } } return res; } }
class Solution { public int longestConsecutive(int[] nums) { Arrays.sort(nums); int len=0; int last=0; int res=0; for(int i:nums){ if(len==0){ len++; last=i; }else{ if(last+1==i){ len++; }else if(last==i){ }else{ len=1; } last=i; } res=Integer.max(res,len); } return res; } }
136,只出现一次的数字(二进制)
难度简单2315
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现
class Solution { public int singleNumber(int[] nums) { int res=0; for(int i:nums)res^=i; return res; } }
139,单词拆分(DP)
难度中等1485
给你一个字符串
s
和一个字符串列表wordDict
作为字典。请你判断是否可以利用字典中出现的单词拼接出s
。注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
提示:
1 <= s.length <= 300
1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 20
s 和 wordDict[i] 仅有小写英文字母组思想:
- 采用dfs思想,在配合String.startsWith()进行判断,结果部分用例超时。
- 采用dp的思想去解决问题,因为s.length()不算太长,dp[i]表示长度为i的子串(下标为0-i-1)可以被表示出来,dp[i]=dp[j]&&s.substring(j,i)是否为能表示出来。
class Solution { public boolean wordBreak(String s, List<String> wordDict) { Set<String> wordDictSet = new HashSet(wordDict); boolean[] dp = new boolean[s.length() + 1]; // dp[i]表示以0-i-1的字符串能被字典表示 dp[0] = true; for (int i = 1; i <= s.length(); i++) {// 长度 for (int j = 0; j < i; j++) { if (dp[j] && wordDictSet.contains(s.substring(j, i))) { dp[i] = true; break; } } } return dp[s.length()]; } }
141,环形链表
难度简单1414
给你一个链表的头节点
head
,判断链表中是否有环。如果链表中有某个节点,可以通过连续跟踪
next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。如果链表中存在环 ,则返回
true
。 否则,返回false
。public class Solution { public boolean hasCycle(ListNode head) { if(head==null)return false; ListNode p1=head; ListNode p2=head; while(p2!=null&&p1!=null){ p1=p1.next; if(p1==null)return false; p1=p1.next; p2=p2.next; if(p1==p2)return true; } return false; } }
142,环形链表2
难度中等1464
给定一个链表的头节点
head
,返回链表开始入环的第一个节点。 如果链表无环,则返回null
。如果链表中有某个节点,可以通过连续跟踪
next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果pos
是-1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。
思路:
- set记录节点。
- 判圈法:女生走一步,男生走两步,直到相遇,在男生从初始点和女生从相遇点一步一步走就能找到环的入口。
public class Solution { public ListNode detectCycle(ListNode head) { if(head==null)return null; ListNode p1=head,p2=head; // 相遇点,一个走一步,一个走两步 while(p1!=null&&p2!=null){ p1=p1.next; if(p1==null)return null; p1=p1.next; p2=p2.next; if(p1==p2){ break; } } // 一个为空代表木环 if(p1==null||p2==null)return null; // 从头结点和相遇点一步一步走会在此相遇 while(head!=p1){ head=head.next; p1=p1.next; } return head; } }
public class Solution { public ListNode detectCycle(ListNode head) { HashSet<ListNode> set=new HashSet<>(); while(head!=null){ if(!set.contains(head)){ set.add(head); }else return head; head=head.next; } return null; } }
146,LRU缓存(linkedHashMap)
难度中等2031
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现
LRUCache
类:
LRUCache(int capacity)
以 正整数 作为容量capacity
初始化 LRU 缓存int get(int key)
如果关键字key
存在于缓存中,则返回关键字的值,否则返回-1
。void put(int key, int value)
如果关键字key
已经存在,则变更其数据值value
;如果不存在,则向缓存中插入该组key-value
。如果插入操作导致关键字数量超过capacity
,则应该 逐出 最久未使用的关键字。函数
get
和put
必须以O(1)
的平均时间复杂度运行。思想:
LinkedHashMap中存在removeEldestEntry()方法就能自动开启LRU缓存功能,要写LinkedHashMap子类重写removeEldestEntry方法。
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)// 构造方法,accessOrder表示读取方式,false表示链表为插入顺序,true表示链表保持读取顺序。
class LRUCache extends LinkedHashMap<Integer, Integer>{ private int capacity; public LRUCache(int capacity) { super(capacity, 0.75F, true); this.capacity = capacity; } public int get(int key) { return super.getOrDefault(key, -1); } public void put(int key, int value) { super.put(key, value); } @Override protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) { return size() > capacity; } }
148,排序链表(排序)
难度中等1522
给你链表的头结点
head
,请将其按 升序 排列并返回 排序后的链表 。class Solution { public ListNode sortList(ListNode head) { if(head==null||head.next==null)return head; ArrayList<ListNode> c=new ArrayList<>(); while(head!=null){ c.add(head); head=head.next; } Collections.sort(c,new Comparator<ListNode>(){ public int compare(ListNode o1,ListNode o2){ return o1.val-o2.val; } }); ListNode res=new ListNode(-1),tail; res.next=null; tail=res; for(int i=0;i<c.size();i++){ ListNode temp= c.get(i); temp.next=null; tail.next=temp; tail=temp; } return res.next; } }
152,乘积最大子数组(DP)
难度中等1562
给你一个整数数组
nums
,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。测试用例的答案是一个 32-位 整数。
子数组 是数组的连续子序列。
class Solution { public int maxProduct(int[] nums) { int dpmin[]=new int[nums.length]; int dpmax[]=new int[nums.length]; dpmax[0]=nums[0]; dpmin[0]=nums[0]; int res=nums[0]; for(int i=1;i<nums.length;i++){ dpmax[i]=Integer.max(dpmax[i-1]*nums[i],dpmin[i-1]*nums[i]); dpmax[i]=Integer.max(dpmax[i],nums[i]); dpmin[i]=Integer.min(dpmax[i-1]*nums[i],dpmin[i-1]*nums[i]); dpmin[i]=Integer.min(dpmin[i],nums[i]); res=Integer.max(res,dpmax[i]); } return res; } }
155,最小栈(priorityQueue)
难度简单1233
设计一个支持
push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。实现
MinStack
类:
MinStack()
初始化堆栈对象。void push(int val)
将元素val推入堆栈。void pop()
删除堆栈顶部的元素。int top()
获取堆栈顶部的元素。int getMin()
获取堆栈中的最小元素。class MinStack { Stack<Integer> s=new Stack<>(); PriorityQueue<Integer> que=new PriorityQueue<>(); public MinStack() { } public void push(int val) { que.add(val); s.push(val); } public void pop() { que.remove(s.peek()); s.pop(); } public int top() { return s.peek(); } public int getMin() { return que.peek(); } } /** * 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(); */
160,相交链表
难度简单1611
给你两个单链表的头节点
headA
和headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回null
。图示两个链表在节点
c1
开始相交:public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { int len1=0,len2=0; ListNode h1=headA,h2=headB; while(h1!=null){len1++;h1=h1.next;} while(h2!=null){h2=h2.next;len2++;} if(len1<len2){ int flag=len2-len1; while(flag!=0){ headB=headB.next; flag--; } }else{ int flag=len1-len2; while(flag!=0){ headA=headA.next; flag--; } } while(headA!=null&&headB!=null){ if(headB==headA){ return headB; } headB=headB.next; headA=headA.next; } return null; } }
169,多数元素
难度简单1365
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于
⌊ n/2 ⌋
的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素。
class Solution { public int majorityElement(int[] nums) { int val=0; int len=0; for(int i:nums){ if(len==0){ len=1; val=i; }else{ if(val==i){ len++; }else{ len--; } } } return val; } }
198,打家劫舍(DP)
难度中等1983
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
class Solution { public int rob(int[] nums) { int n=nums.length; int [][]dp=new int[n][2]; dp[0][0]=0;//不偷 dp[0][1]=nums[0];//偷 for(int i=1;i<n;i++){ dp[i][0]=Integer.max(dp[i-1][1],dp[i-1][0]); dp[i][1]=Integer.max(dp[i-1][1],dp[i-1][0]+nums[i]); } return Integer.max(dp[n-1][0],dp[n-1][1]); } }
200,岛屿数量(DFS)
难度中等1611
给你一个由
'1'
(陆地)和'0'
(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
class Solution { private int res=0; public int numIslands(char[][] grid) { int m=grid.length, n=grid[0].length; boolean [][]visited=new boolean[m][n]; for(int i=0;i<m;i++){ for(int j=0;j<n;j++){ if(grid[i][j]=='1'&&!visited[i][j]){ res++; visited[i][j]=true; dfs(i,j,m,n,visited,grid); } } } return res; } void dfs(int x,int y,int m,int n,boolean [][]visited,char[][] grid){ for(int i=0;i<4;i++){ switch(i){ case 0: if(x-1>=0&&grid[x-1][y]=='1'&&!visited[x-1][y]){ visited[x-1][y]=true; dfs(x-1,y,m,n,visited,grid); } break; case 1: if(x+1<m&&grid[x+1][y]=='1'&&!visited[x+1][y]){ visited[x+1][y]=true; dfs(x+1,y,m,n,visited,grid); } break; case 2: if(y-1>=0&&grid[x][y-1]=='1'&&!visited[x][y-1]){ visited[x][y-1]=true; dfs(x,y-1,m,n,visited,grid); } break; case 3: if(y+1<n&&grid[x][y+1]=='1'&&!visited[x][y+1]){ visited[x][y+1]=true; dfs(x,y+1,m,n,visited,grid); } break; } } } }
206,反转链表
难度简单2379
给你单链表的头节点
head
,请你反转链表,并返回反转后的链表。class Solution { public ListNode reverseList(ListNode head) { ListNode res=new ListNode(-1); res.next=null; while(head!=null){ ListNode t=head; head=head.next; t.next=res.next; res.next=t; } return res.next; } }
207,课程表(拓扑排序)
难度中等1177
你这个学期必须选修
numCourses
门课程,记为0
到numCourses - 1
。在选修某些课程之前需要一些先修课程。 先修课程按数组
prerequisites
给出,其中prerequisites[i] = [ai, bi]
,表示如果要学习课程ai
则 必须 先学习课程bi
。
- 例如,先修课程对
[0, 1]
表示:想要学习课程0
,你需要先完成课程1
。请你判断是否可能完成所有课程的学习?如果可以,返回
true
;否则,返回false
。思想:
- 拓扑排序,任务有先后,求出一种没有冲突的任务执行方式,将入度为0的结点加入队列,每次出队为key,将其余结点的入度为key的进行删除并且当一节点入度为0时候进行入队,当出队数等于总结点数则表明可以进行拓扑排序。
class Solution { List<List<Integer>> edges; int[] indeg; public boolean canFinish(int numCourses, int[][] prerequisites) { edges = new ArrayList<List<Integer>>(); for (int i = 0; i < numCourses; ++i) { edges.add(new ArrayList<Integer>()); } indeg = new int[numCourses]; for (int[] info : prerequisites) { edges.get(info[1]).add(info[0]); ++indeg[info[0]]; } Queue<Integer> queue = new LinkedList<Integer>(); for (int i = 0; i < numCourses; ++i) { if (indeg[i] == 0) { queue.offer(i); } } int visited = 0; while (!queue.isEmpty()) { ++visited; int u = queue.poll(); for (int v: edges.get(u)) { --indeg[v]; if (indeg[v] == 0) { queue.offer(v); } } } return visited == numCourses; } }
208,实现trie前缀树
难度中等1115
Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie()
初始化前缀树对象。void insert(String word)
向前缀树中插入字符串word
。boolean search(String word)
如果字符串word
在前缀树中,返回true
(即,在检索之前已经插入);否则,返回false
。boolean startsWith(String prefix)
如果之前已经插入的字符串word
的前缀之一为prefix
,返回true
;否则,返回false
。class Trie { private HashSet<String> set=new HashSet<>(); public Trie() { } public void insert(String word) { set.add(word); } public boolean search(String word) { return set.contains(word); } public boolean startsWith(String prefix) { for(String s:set){ if(s.startsWith(prefix)){ return true; } } return false; } }
215,数组中的第k个最大元素
难度中等1546
给定整数数组
nums
和整数k
,请返回数组中第k
个最大的元素。请注意,你需要找的是数组排序后的第
k
个最大的元素,而不是第k
个不同的元素。class Solution { PriorityQueue<Integer> q=new PriorityQueue<>(); public int findKthLargest(int[] nums, int k) { for(int i:nums){ add(i,k); } return q.peek(); } void add(int val,int k){ q.add(val); if(q.size()>k){ q.poll(); } } }
221,最大正方形
难度中等1081
在一个由
'0'
和'1'
组成的二维矩阵内,找到只包含'1'
的最大正方形,并返回其面积。示例 1:
class Solution { public int maximalSquare(char[][] matrix) { int m=matrix.length,n=matrix[0].length; int [][]dp=new int[m][n]; int res=0; for(int i=0;i<m;i++){ dp[i][0]=matrix[i][0]-'0'; res=Integer.max(dp[i][0],res); } for(int i=0;i<n;i++) { dp[0][i]=matrix[0][i]-'0'; res=Integer.max(dp[0][i],res); } for(int i=1;i<m;i++){ for(int j=1;j<n;j++){ if(matrix[i][j]=='1'){ dp[i][j]=Integer.min(Integer.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1; res=Integer.max(dp[i][j],res); } } } return res*res; } }
226,翻转二叉树
难度简单1208
给你一棵二叉树的根节点
root
,翻转这棵二叉树,并返回其根节点。class Solution { public TreeNode invertTree(TreeNode root) { dfs(root); return root; } void dfs(TreeNode root){ if(root==null)return; TreeNode t=root.left; root.left=root.right; root.right=t; dfs(root.left); dfs(root.right); } }
234,回文链表
难度简单1309
给你一个单链表的头节点
head
,请你判断该链表是否为回文链表。如果是,返回true
;否则,返回false
。示例 1:
class Solution { public boolean isPalindrome(ListNode head) { StringBuffer a=new StringBuffer(); while(head!=null){ a.append(String.valueOf(head.val)); head=head.next; } if(a.length()==1)return true; String c=a.substring(0,a.length()/2); String b=a.reverse().substring(0,a.length()/2); if(b.equals(c))return true; return false; } }
236,二叉树的最近公共祖先
难度中等1633
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
class Solution { private TreeNode res=null; public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { dfs(root,p,q); return res; } boolean dfs(TreeNode root,TreeNode p,TreeNode q){ if(root==null)return false; boolean l=dfs(root.left,p,q); boolean r=dfs(root.right,p,q); if((l&&r)||((r||l)&&(root==p||root==q))) { res=root; return true; } return l||r||(root==p)||(root==q); } }
238,除自身以外组的乘积(两边记性标志-类似于接雨水)
难度中等1097
给你一个整数数组
nums
,返回 数组answer
,其中answer[i]
等于nums
中除nums[i]
之外其余各元素的乘积 。题目数据 保证 数组
nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。请不要使用除法,且在
O(n)
时间复杂度内完成此题。思路:
- 双向标记数组。
class Solution { public int[] productExceptSelf(int[] nums) { int []f=new int[nums.length]; int []l=new int[nums.length]; int cv=1; for(int i=0;i<nums.length;i++){ cv*=nums[i]; f[i]=cv; } cv=1; for(int i=nums.length-1;i>=0;i--){ cv*=nums[i]; l[i]=cv; } int res[]=new int[nums.length]; for(int i=0;i<nums.length;i++){ if(i==0){ res[i]=l[1]; }else if(i==nums.length-1){ res[i]=f[nums.length-2]; }else{ res[i]=l[i+1]*f[i-1]; } } return res; } }
240,搜索二维矩阵
难度中等962
编写一个高效的算法来搜索
m x n
矩阵matrix
中的一个目标值target
。该矩阵具有以下特性:
- 每行的元素从左到右升序排列。
- 每列的元素从上到下升序排列。
class Solution { public boolean searchMatrix(int[][] matrix, int target) { for(int i=0;i<matrix.length;i++){ if(Arrays.binarySearch(matrix[i],target)>=0){ return true; } } return false; } }
279,完全平方数(DP)
难度中等1289
给你一个整数
n
,返回 和为n
的完全平方数的最少数量 。完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,
1
、4
、9
和16
都是完全平方数,而3
和11
不是。思路:
- 局部性原路。
class Solution { public int numSquares(int n) { int []dp=new int[n+1]; // dp[i]表示数i需要表示为完全平方数之和的最小步数 Arrays.fill(dp,n+1); dp[0]=0; for(int i=1;i<=n;i++) { for(int j=1;j*j<=i;j++) { dp[i]=Math.min(dp[i],dp[i-j*j]+1); } } return dp[n]; } }
283,移动零
难度简单1501
给定一个数组
nums
,编写一个函数将所有0
移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。
class Solution { public void moveZeroes(int[] nums) { int zero=0; ArrayList<Integer> arr=new ArrayList<>(); for(int i:nums){ if(i==0){ zero++; }else{ arr.add(i); } } int index=0; for(;index<arr.size();index++){ nums[index]=arr.get(index); } for(int i=index;i<nums.length;i++){ nums[i]=0; } } }
287,寻找重复数
难度中等1641
给定一个包含
n + 1
个整数的数组nums
,其数字都在[1, n]
范围内(包括1
和n
),可知至少存在一个重复的整数。假设
nums
只有 一个重复的整数 ,返回 这个重复的数 。你设计的解决方案必须 不修改 数组
nums
且只用常量级O(1)
的额外空间。思路:
- 因为数堵在[1,n]之间,我可以对nums[nums[i]%n]加n,遍历完一遍只要nums[i]>2*n的证明至少有两个相同的数存在了。
class Solution { public int findDuplicate(int[] nums) { int n=nums.length-1; for(int i=0;i<n+1;i++){ nums[nums[i]%n]=nums[nums[i]%n]+n; } for(int i=0;i<n+1;i++){ if(nums[i]>2*n){ return i==0?n:i; } } return 1; } }
300,最长递增子序列(DP)
难度中等2341
给你一个整数数组
nums
,找到其中最长严格递增子序列的长度。子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,
[3,6,2,7]
是数组[0,3,1,6,2,2,7]
的子序列。class Solution { public int lengthOfLIS(int[] nums) { int n=nums.length; int []dp=new int[n]; Arrays.fill(dp,1); int res=1; for(int i=1;i<n;i++){ for(int j=i-1;j>=0;j--){ if(nums[i]>nums[j]){ dp[i]=Integer.max(dp[i],dp[j]+1); } } res=Integer.max(res,dp[i]); } return res; } }
301,删除无效的括号(DFS+剪枝)
难度困难689
给你一个由若干括号和字母组成的字符串
s
,删除最小数量的无效括号,使得输入的字符串有效。返回所有可能的结果。答案可以按 任意顺序 返回。
class Solution { HashSet<String> res=new HashSet<>(); int step_MIN=Integer.MAX_VALUE; public List<String> removeInvalidParentheses(String s) { dfs(0,0,0,0,"",s); return new ArrayList<>(res); } void dfs(int l,int r,int index,int step,String cv,String s){ if(index==s.length()) { if(l==r){ if(step<step_MIN){ step_MIN=step; res.clear(); res.add(new String(cv)); }else if(step==step_MIN){ res.add(new String(cv)); } } return; } char flag=s.charAt(index); if(flag=='('){ //可要可不要 dfs(l+1,r,index+1,step,cv+s.charAt(index),s); dfs(l,r,index+1,step+1,cv,s); }else if(flag==')'){ if(r+1<=l){ //可要可不要 dfs(l,r+1,index+1,step,cv+s.charAt(index),s); } dfs(l,r,index+1,step+1,cv,s); }else{ dfs(l,r,index+1,step,cv+s.charAt(index),s); } } }
309,最佳买卖股票时机含冷冻期(DP问题)
难度中等1130
给定一个整数数组
prices
,其中第prices[i]
表示第i
天的股票价格 。设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
- 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)
思路:
本题的难点在于处理冷冻期,冷冻期是表示当天卖出后一天则不可以买入,关注点不是下一天是否可以买入,而是关注什么是否卖出(必须表示出来)。
一天有两种状态,持股和不持股(不持股也有两种状态,当天卖出不持股,当天没有股票不持股)dp[i][0] :不持股(当天不卖出),dp[i][1]:持股, dp[i][2]:不持股(当天卖出)
转移方程:
dp[i][0]=max(dp[i-1][0],dp[i-1][2])
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-price[i])// 这里考虑到如果i-1卖出的话就不考虑,考虑了冷冻期
dp[i][2]=dp[i-1][1]+price[i]
class Solution { public int maxProfit(int[] prices) { int n=prices.length; if(n<=1) return 0; int [][] dp=new int[n][3]; dp[0][0]=0; dp[0][1]=-1*prices[0]; dp[0][2]=0; for(int i=1;i<n;i++){//从[1]...[n-1] dp[i][0]=Math.max(dp[i-1][0],dp[i-1][2]); dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]-prices[i]); dp[i][2]=dp[i-1][1]+prices[i]; } return Math.max(dp[n-1][0],dp[n-1][2]); } }
322,零钱兑换(DP-和单词拆分有着相同的思想)
难度中等1810
给你一个整数数组
coins
,表示不同面额的硬币;以及一个整数amount
,表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回
-1
。你可以认为每种硬币的数量是无限的。
class Solution { public int coinChange(int[] coins, int amount) { int []dp=new int[amount+1]; Arrays.sort(coins); Arrays.fill(dp,Integer.MAX_VALUE); dp[0]=0; for(int i=1;i<=amount;i++){ int cmin=Integer.MAX_VALUE; for(int j=0;j<coins.length;j++){ if(coins[j]>i){ break; } if(dp[i-coins[j]]!=Integer.MAX_VALUE){ cmin=Integer.min(cmin,dp[i-coins[j]]+1); } } dp[i]=cmin; } return dp[amount]==Integer.MAX_VALUE?-1:dp[amount]; } }
337,打家劫舍(DP+二叉树)
难度中等1204
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为
root
。除了
root
之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。给定二叉树的
root
。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。思路:
- 存在子问题结构,f表示该点选择了最大价值,g表示该点不选择后的最大价值。
class Solution { Map<TreeNode, Integer> f = new HashMap<TreeNode, Integer>(); Map<TreeNode, Integer> g = new HashMap<TreeNode, Integer>(); public int rob(TreeNode root) { dfs(root); return Math.max(f.getOrDefault(root, 0), g.getOrDefault(root, 0)); } // 后序遍历的思想+DP思想 public void dfs(TreeNode node) { if (node == null) { return; } dfs(node.left); dfs(node.right); f.put(node, node.val + g.getOrDefault(node.left, 0) + g.getOrDefault(node.right, 0)); g.put(node, Math.max(f.getOrDefault(node.left, 0), g.getOrDefault(node.left, 0)) + Math.max(f.getOrDefault(node.right, 0), g.getOrDefault(node.right, 0))); } }
338,比特位计数
难度简单939
给你一个整数
n
,对于0 <= i <= n
中的每个i
,计算其二进制表示中1
的个数 ,返回一个长度为n + 1
的数组ans
作为答案。class Solution { public int[] countBits(int n) { int res[]=new int[n+1]; for(int i=0;i<32;i++){ for(int j=1;j<=n;j++){ if(((j>>i)&1)==1){ res[j]++; } } } return res; } }
347,前k个高频元素
难度中等1077
给你一个整数数组
nums
和一个整数k
,请你返回其中出现频率前k
高的元素。你可以按 任意顺序 返回答案。class Solution { public int[] topKFrequent(int[] nums, int k) { HashMap<Integer,Integer> map=new HashMap<>(); for(int i:nums){ Integer t= map.get(i); if(t==null){ map.put(i,1); }else{ map.put(i,t+1); } } int [][] val=new int[map.size()][2]; int index=0; for(Map.Entry<Integer,Integer> entry:map.entrySet()){ val[index][0]=entry.getKey(); val[index][1]=entry.getValue(); index++; } Arrays.sort(val, new Comparator<int[]>() { @Override public int compare(int[] o1, int[] o2) { return o2[1]-o1[1]; } }); int []res=new int[k]; for(int i=0;i<k;i++){ res[i]=val[i][0]; } return res; } }
394,字符串解码(栈)
难度中等1074
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为:
k[encoded_string]
,表示其中方括号内部的encoded_string
正好重复k
次。注意k
保证为正整数。你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数
k
,例如不会出现像3a
或2[4]
的输入。class Solution { public String decodeString(String s) { Stack<Integer> n=new Stack<>(); Stack<Character> chars=new Stack<>(); String res=""; boolean last=false; for(int i=0;i<s.length();i++){ if(s.charAt(i)>='0'&&s.charAt(i)<='9'){ if(last){ int tt=n.peek(); n.pop(); n.push(tt*10+s.charAt(i)-'0'); }else{ n.push(s.charAt(i)-'0'); } last=true; }else if(s.charAt(i)==']'){ last=false; // 进行出栈 String t=""; while(chars.peek()!='['){ t=chars.peek()+t; chars.pop(); } chars.pop(); if(chars.isEmpty()){ // int flag=n.peek(); n.pop(); for(int j=0;j<flag;j++){ res=res+t; } }else{ int flag=n.peek(); n.pop(); String tep=""; for(int j=0;j<flag;j++){ tep=tep+t; } for(int j=0;j<tep.length();j++){ chars.push(tep.charAt(j)); } } }else{ last=false; chars.push(s.charAt(i)); } } String temp=""; while(!chars.isEmpty()){ temp=chars.peek()+temp; chars.pop(); } return res+temp; } }
437,路径总和三(先序遍历)
难度中等1276
给定一个二叉树的根节点
root
,和一个整数targetSum
,求该二叉树里节点值之和等于targetSum
的 路径 的数目。路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
class Solution { public int pathSum(TreeNode root, int targetSum) { if (root == null) { return 0; } int ret = rootSum(root, targetSum); ret += pathSum(root.left, targetSum); ret += pathSum(root.right, targetSum); return ret; } public int rootSum(TreeNode root, int targetSum) { int ret = 0; if (root == null) { return 0; } int val = root.val; if (val == targetSum) { ret++; } ret += rootSum(root.left, targetSum - val); ret += rootSum(root.right, targetSum - val); return ret; } }
438,找到字符串中所有字母异位词
难度中等820
给定两个字符串
s
和p
,找到s
中所有p
的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
class Solution { public List<Integer> findAnagrams(String s, String p) { int []flag=new int[26]; for(int i=0;i<p.length();i++){ flag[p.charAt(i)-'a']++; } int []t=new int[26]; int len=p.length(); List<Integer> res=new ArrayList<>(); for(int i=0;i<s.length();i++){ t[s.charAt(i)-'a']++; if(i>=len){ t[s.charAt(i-len)-'a']--; } if(Arrays.equals(t,flag)){ res.add(i-len+1); } } // abcd return res; } }
448,找到所有数组中消失的数字
难度简单937
给你一个含
n
个整数的数组nums
,其中nums[i]
在区间[1, n]
内。请你找出所有在[1, n]
范围内但没有出现在nums
中的数字,并以数组的形式返回结果。class Solution { public List<Integer> findDisappearedNumbers(int[] nums) { for(int i=0,len=nums.length;i<len;i++){ nums[nums[i]%len]=nums[nums[i]%len]+len; } List<Integer> res=new ArrayList<>(); for(int i=0,len=nums.length;i<len;i++){ if(nums[i]<=len){ if(i==0) res.add(len); else res.add(i); } } return res; } }
461,汉明距离
难度简单578
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
给你两个整数
x
和y
,计算并返回它们之间的汉明距离。class Solution { public int hammingDistance(int x, int y) { int res=0; for(int i=0;i<32;i++){ if(((x>>i)&1^(y>>i)&1)==1){ res++; } } return res; } }