数位 DP - OI Wiki
按照大纲把下面的题目刷完,并把代码烂熟于心,就几乎可以应对 90% 的面试算法考题了。
本篇内容包括如下模块:
-
高频算法题系列:链表
-
【🔥】【有真题】高频算法题系列:字符串
-
【🔥】【有真题】高频算法题系列:数组问题
-
高频算法题系列:二叉树
-
【🔥】高频算法题系列:排序算法
-
【🔥】高频算法题系列:二分查找
-
【🔥】高频算法题系列:动态规划
-
高频算法题系列:BFS
-
【🔥】高频算法题系列:栈
-
【🔥】高频算法题系列:DFS
-
【🔥】高频算法题系列:回溯算法
其中标🔥的部分代表非常高频的考题,其中不乏笔者遇到的原题。其中对于每一类,首先会列出包含的考题,然后针对每一道考题会给出难度、考察知识点、是否是面试真题,在每道题详细介绍时,还会给出每道题的 LeetCode 链接,帮助读者理解题意,以及能够进行实际的测验,还可以观看其他人的答案,更好的帮助准备。
高频算法题系列:链表
笔者遇到的高频链表题主要包含这几道:
-
通过快慢指针寻找链表中点 【简单】
-
通过链表的后续遍历判断回文链表问题 【简单】
-
链表的反向输出 【简单】
-
合并 K 个升序链表 【困难】
-
K个一组翻转链表 【困难】
-
环形链表 【简单】
-
排序链表 【中等】
-
相交链表 【简单】
寻找链表中点
题解
通过快慢指针寻找链表中点
/**
*
*/
function findCenter(head) {
let slower = head, faster = head;
while (faster && faster.next != null) {
slower = slower.next;
faster = faster.next.next;
}
// 如果 faster 不等于 null,说明是奇数个,slower 再移动一格
if (faster != null) {
slower = slower.next;
}
return slower;
}
前序遍历判断回文链表
👉 【LeetCode 直通车】:234 回文链表(简单)[1]
题解1
利用链表的后续遍历,使用函数调用栈作为后序遍历栈,来判断是否回文
class Solution {
//方法2,倒叙遍历链表和正顺遍历链表
ListNode left;
boolean isPalindrome=true;
public boolean isPalindrome(ListNode head) {
this.left=head;
isPalindromeDFS(head);
return this.isPalindrome;
}
public void isPalindromeDFS(ListNode right){
if(right==null){
return;
}
isPalindromeDFS(right.next);
if(right.val!=this.left.val){
this.isPalindrome=false;
return;
}
if(this.left!=null){
this.left=this.left.next;
}
}
// public boolean isPalindrome(ListNode head) {
// ListNode fast=head,slow=head;
// while(fast!=null&&fast.next!=null){
// slow=slow.next;
// fast=fast.next.next;
// }
// ListNode lastNodes=null;
// if(fast==null){
// lastNodes=reverseNode2(slow);
// }else{
// lastNodes=reverseNode2(slow.next);
// }
// while(lastNodes!=null){
// if(head.val!=lastNodes.val){
// return false;
// }
// head=head.next;
// lastNodes=lastNodes.next;
// }
// return true;
// }
// public static ListNode reverseNode2(ListNode head){
// if(head==null){
// return null;
// }
// if(head.next==null){
// return head;
// }
// ListNode next=head.next;
// ListNode node=reverseNode2(next);
// next.next=head;
// head.next=null;
// return node;
// }
}
反转链表
👉 【LeetCode 直通车】:206 反转链表(简单)[2]
题解
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function(head) {
if (head == null || head.next == null) return head;
let last = reverseList(head.next);
head.next.next = head;
head.next = null;
return last;
};
方法2
public ListNode reverseList(ListNode head) {
if(head==null){
return null;
}
ListNode pre=null,cur=head,next=head.next;
while(cur!=null){
next=cur.next;
cur.next=pre;
pre=cur;
cur=next;
}
return pre;
}
合并K个升序链表
👉 【LeetCode 直通车】:23 合并K个升序链表(困难)[3]
题解
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<ListNode> pr=new PriorityQueue<ListNode>(new Comparator<ListNode>(){
public int compare(ListNode o1,ListNode o2){
return o1.val-o2.val;
}
});
ListNode head=new ListNode();
ListNode tail =head;
if(lists==null||lists.length==0){
return head.next;
}
for(ListNode temp:lists){
if(temp!=null){
pr.add(temp);
}
}
while(!pr.isEmpty()){
ListNode tempNode=pr.poll();
tail.next=tempNode;
tail =tail.next;
if(tempNode.next!=null)
pr.offer(tempNode.next);
}
return head.next;
}
class Solution {
public ListNode nextNewNode=null;
public ListNode reverseBetween(ListNode head, int left, int right) {
if(head==null){
return null;
}
if(right==0){
return head;
}
if(left==1){
return reverseNodesK(head,right);
}else{
head.next=reverseBetween(head.next,left-1,right-1);
return head;
}
}
ListNode reverseNodesK(ListNode head,int k){
if(head==null||head.next==null){
return head;
}
if(k==1){
this.nextNewNode=head.next;
return head;
}
ListNode next=head.next;
ListNode resultNode=reverseNodesK(next,k-1);
next.next=head;
head.next=nextNewNode;
return resultNode;
}
}
K 个一组翻转链表
👉 【LeetCode 直通车】:25 K 个一组翻转链表(困难)[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 reverseKGroup(ListNode head, int k) {
if(head==null||k==0){
return head;
}
ListNode tail =head;
for(int i=0;i<k;i++){
if(tail==null){
return head;
}
tail =tail.next;
}
ListNode newHead=reverseNodeAB(head,tail);
head.next=reverseKGroup(tail,k);
return newHead;
}
public ListNode reverseNodeAB(ListNode a,ListNode b){
if(a.next==b){
return a;
}
ListNode next=a.next;
ListNode newHead=reverseNodeAB(a.next,b);
next.next=a;
a.next=null;
return newHead;
}
// public ListNode reverseNodeAB(ListNode a,ListNode b){
// ListNode pre=null,next=null,cur=a;
// while(cur!=b){
// next=cur.next;
// cur.next=pre;
// pre=cur;
// cur=next;
// }
// return pre;
// }
}
环形链表
👉 【LeetCode 直通车】:141 环形链表(简单)[5]
题解
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function(head) {
if (head == null || head.next == null) return false;
let slower = head, faster = head;
while (faster != null && faster.next != null) {
slower = slower.next;
faster = faster.next.next;
if (slower === faster) return true;
}
return false;
};
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode faster=head,slower=head;
while(faster!=null&&faster.next!=null){
faster=faster.next.next;
slower=slower.next;
if(faster==slower){
slower=head;
while(slower!=faster){
slower=slower.next;
faster=faster.next;
}
return faster;
}
}
return null;
}
}
排序链表
👉 【LeetCode 直通车】:148 排序链表(中等)[6]
题解
class Solution {
public ListNode sortList(ListNode head) {
if(head==null||head.next==null){
return head;
}
ListNode middleNode=findMiddle(head);
ListNode nextList=middleNode.next;
middleNode.next=null;
ListNode sortedList1=sortList(head);
ListNode sortedList2=sortList(nextList);
return mergeSortedList(sortedList1,sortedList2);
}
public ListNode findMiddle(ListNode head){
ListNode faster=head,slower=head;
if(faster!=null&&faster.next!=null&&faster.next.next==null){
return faster;
}
while(faster!=null&&faster.next!=null){
faster=faster.next.next;
slower=slower.next;
}
return slower;
}
public ListNode mergeSortedList(ListNode a,ListNode b){
ListNode head=new ListNode(0);
ListNode tail=head;
while(a!=null&&b!=null){
if(a.val>b.val){
tail.next=b;
tail=tail.next;
b=b.next;
}else{
tail.next=a;
tail=tail.next;
a=a.next;
}
}
if(a==null){
tail.next=b;
}else{
tail.next=a;
}
return head.next;
}
}
相交链表
👉 【LeetCode 直通车】:160 相交链表(简单)[7]
题解
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode tempA=headA,tempB=headB;
int i=0;
while(tempA!=tempB){
tempA=tempA.next;
tempB=tempB.next;
if(tempA==null){
tempA=headB;
i++;
}
if(tempB==null){
tempB=headA;
i++;
}
if(i>=3){
return null;
}
}
return tempA;
}
}
【🔥】高频算法题系列:字符串
主要有以下几类高频考题:
-
最长回文子串 【中等】【双指针】【面试真题】
-
最长公共前缀 【简单】【双指针】
-
无重复字符的最长子串【中等】【双指针】
-
最小覆盖子串 【困难】【滑动窗口】【面试真题】
【面试真题】最长回文子串【双指针】
👉 【LeetCode 直通车】:5 最长回文子串(中等)[8]
题解
class Solution {
public String longestPalindrome(String s) {
int maxLength=0;
String longestPalindromeStr="";
if(s==null||s.length()==0){
return null;
}
if(s.length()==1){
return s;
}
int dp[][]=new int[s.length()][s.length()];
for(int i=0;i<s.length();i++){
dp[i][i]=1;
for(int j=0;j<i;j++){
if(i-j==1){
dp[j][i]=s.charAt(i)==s.charAt(j)?2:0;
}else{
if(dp[j+1][i-1]>0&&s.charAt(i)==s.charAt(j)){
dp[j][i]=dp[j+1][i-1]+2;
}
}
if(dp[j][i]>maxLength){
maxLength=dp[j][i];
longestPalindromeStr=s.substring(j,i+1);
}
}
}
if(maxLength==0){
return s.substring(0,1);
}
return longestPalindromeStr;
}
// public int longestPalindrome=0;
// public String longestPalindromeStr;
// public String longestPalindrome(String s) {
// if(s==null||s.length()==0){
// return null;
// }
// for(int i=0;i<s.length();i++){
// longestPalindromeDFS(s,i,i);
// longestPalindromeDFS(s,i,i+1);
// }
// return longestPalindromeStr;
// }
// public void longestPalindromeDFS(String s ,int start1,int start2){
// while(s!=null&&start1>=0&&start2<s.length()&&s.charAt(start1)==s.charAt(start2)){
// start1--;
// start2++;
// }
// if(start2-start1>longestPalindrome){
// longestPalindrome=start2-start1;
// longestPalindromeStr=s.substring(start1+1,start2);
// }
// }
}
class Solution {
public int longestPalindromeSubseq(String s) {
int m=s.length();
int [][]dp=new int[m][m];
for(int i=0;i<m;i++){
dp[i][i]=1;
for(int j=i-1;j>=0;j--){
if(s.charAt(i)==s.charAt(j)){
dp[j][i]=dp[j+1][i-1]+2;
}else{
dp[j][i]=Math.max(dp[j][i-1],dp[j+1][i]);
}
}
}
return dp[0][m-1];
}
}
最长公共前缀【双指针】
👉 【LeetCode 直通车】:14 最长公共前缀(简单)[9]
题解
class Solution {
public String longestCommonPrefix(String[] strs) {
String longestCommonPrefix=strs[0];
for(int i=1;i<strs.length;i++){
longestCommonPrefix=twoCommonPrefix(longestCommonPrefix,strs[i]);
if(longestCommonPrefix==null){
break;
}
}
return longestCommonPrefix;
}
public String twoCommonPrefix(String s1,String s2){
int index=0;
while(index<s1.length()&&index<s2.length()&&s1.charAt(index)==s2.charAt(index)){
index++;
}
return s1.substring(0,index);
}
}
无重复字符的最长子串【双指针】
283. 移动零(双指针)
👉 【LeetCode 直通车】:3 无重复字符的最长子串(中等)[10]
题解
class Solution {
public int lengthOfLongestSubstring(String s) {
int left=0,right=0;
int maxLength=0;
if(s==null||"".equals(s)){
return 0;
}
if(s.length()==1){
return 1;
}
Set<Character> set=new HashSet<Character>();
for( ;right<s.length();right++){
Character curChar=s.charAt(right);
while(set.contains(curChar)){
set.remove(s.charAt(left));
left++;
}
set.add(curChar);
maxLength=Math.max(maxLength,right-left+1);
}
return maxLength;
}
}
【面试真题】 最小覆盖子串【滑动窗口】
👉 【LeetCode 直通车】:76 最小覆盖子串(困难)[11]
题解
class Solution {
public String minWindow(String s, String t) {
if(s==null||t==null||s.length()<t.length()){
return "";
}
Map<Character,Integer> mapT=new HashMap<Character,Integer>();
Map<Character,Integer> mapS=new HashMap<Character,Integer>();
for(int i=0;i<t.length();i++){
mapT.put(t.charAt(i),mapT.getOrDefault(t.charAt(i),0)+1);
}
int left=0,fitNum=0,right=0,minLength=Integer.MAX_VALUE;
String minStr="";
for(;right<s.length();right++){
Character curChar=s.charAt(right);
if(mapT.containsKey(curChar)){
mapS.put(curChar,mapS.getOrDefault(curChar,0)+1);
if(mapT.get(curChar).equals(mapS.get(curChar))){
fitNum++;
}
while(fitNum==mapT.keySet().size()){
if(right-left<minLength){
minLength=right-left;
minStr=s.substring(left,right+1);
}
char leftChar=s.charAt(left);
left++;
if(mapT.containsKey(leftChar)){
if(mapS.get(leftChar).equals(mapT.get(leftChar))){
fitNum--;
}
mapS.put(leftChar,mapS.get(leftChar)-1);
}
}
}
}
return minStr;
}
}
【🔥】高频算法题系列:数组问题
主要有几类高频考题:
-
俄罗斯套娃信封问题【困难】【排序+最长上升子序列】【面试真题】
-
最长连续递增序列 【简单】【双指针】
-
最长连续序列【困难】【哈希表】
-
盛最多水的容器【困难】【面试真题】
-
寻找两个正序数组的中位数【困难】【双指针】
-
删除有序数组中的重复项【简单】【快慢指针】
-
和为K的子数组【中等】【哈希表】
-
nSum 问题【系列】【简单】【哈希表】
-
接雨水【困难】【暴力+备忘录优化】【面试真题】
-
跳跃游戏【系列】【中等】【贪心算法】
【面试真题】俄罗斯套娃信封问题【排序+最长上升子序列】
👉 【LeetCode 直通车】:354 俄罗斯套娃信封问题(困难)[12]
题解
class Solution {
public int maxEnvelopes(int[][] envelopes) {
Arrays.sort(envelopes,new Comparator<int[]>(){
public int compare(int []o1,int[]o2){
if(o1[0]!=o2[0]){
return o1[0]-o2[0];
}else{
return o2[1]-o1[1];
}
}
});
int []dp=new int [envelopes.length];
int result=0;
for(int i=0;i<envelopes.length;i++){
dp[i]=1;
for(int j=0;j<i;j++){
if(envelopes[i][1]>envelopes[j][1]){
dp[i]=Math.max(dp[i],dp[j]+1);
}
}
result=Math.max(result,dp[i]);
}
return result;
}
}
最长连续递增序列【快慢指针】
👉 【LeetCode 直通车】:674 最长连续递增序列(简单)[13]
题解
class Solution {
public int findLengthOfLCIS(int[] nums) {
if(nums==null||nums.length==0){
return 0;
}
int tempResult=1;
int result=1;
for(int i=0;i<nums.length-1;i++){
if(nums[i+1]>nums[i]){
tempResult++;
}else{
tempResult=1;
}
result=Math.max(result,tempResult);
}
return result;
}
}
最长连续序列 【哈希表】
👉 【LeetCode 直通车】:128 最长连续序列(困难)[14]
题解
class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> set=new HashSet<Integer>();
for(int num:nums){
set.add(num);
}
int result=0;
for(int i=0;i<nums.length;i++){
Integer curNum=nums[i];
if(!set.contains(curNum-1)){
int temp=1;
while(set.contains(curNum+1)){
temp++;
curNum+=1;
}
result=Math.max(result,temp);
}
}
return result;
}
}
【面试真题】盛最多水的容器【哈希表】
👉 【LeetCode 直通车】:11 盛最多水的容器(中等)[15]
题解
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let n = height.length;
let left = 0, right = n - 1;
let maxOpacity = 0;
while (left < right) {
let res = Math.min(height[left], height[right]) * (right - left);
maxOpacity = Math.max(maxOpacity, res);
if (height[left] < height[right]) left++
else right--;
}
return maxOpacity;
};
寻找两个正序数组的中位数【双指针】
👉 【LeetCode 直通车】:4 寻找两个正序数组的中位数(困难)[16]
题解
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m=nums1.length,n=num2.length;
if(m>n){
int []temp=nums1;
nums1=nums2;
nums2=temp;
}
m=nums1.length,n=num2.length;
int totalLfetCount=n-(n-m+1)/2;
int nums1LeftCount=0;
int nums2LeftCount=0;
int left=0;right=m-1;
while(left<=right){
int middle=right-(right-left)/2;
int nums2Right=totalLfetCount-middle-1;
if(nums1[nums1LeftCount-1]>nums2[nums2LeftCount]){
right=nums1left-1;
}else{
left=nums1left+1;
}
}
}
}
删除有序数组中的重复项【快慢指针】
👉 【LeetCode 直通车】:26 删除有序数组中的重复项(简单)[17]
题解
class Solution {
public int removeDuplicates(int[] nums) {
int slower=0,faster=0;
while(faster<nums.length){
if(nums[slower]!=nums[faster]){
slower++;
nums[slower]=nums[faster];
}
faster++;
}
return slower+1;
}
}
👉 【LeetCode 直通车】:695 岛屿的最大面积(中等)[18]
题解
class Solution {
public int maxAreaOfIsland=0;
public int tempMaxAreaOfIsland=0;
public int maxAreaOfIsland(int[][] grid) {
int m=grid.length;
int n=grid[0].length;
boolean [][]used=new boolean[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(!used[i][j]&&grid[i][j]==1){
this.tempMaxAreaOfIsland=0;
maxAreaOfIslandDFS(grid,i,j,used);
}
}
}
return maxAreaOfIsland;
}
public void maxAreaOfIslandDFS(int[][]grid,int i,int j,boolean [][]used){
if(i<0||i>grid.length-1||j<0||j>grid[0].length-1){
return;
}
if(used[i][j]||grid[i][j]==0){
return;
}
tempMaxAreaOfIsland++;
maxAreaOfIsland=Math.max(maxAreaOfIsland,tempMaxAreaOfIsland);
used[i][j]=true;
maxAreaOfIslandDFS(grid,i+1,j,used);
maxAreaOfIslandDFS(grid,i-1,j,used);
maxAreaOfIslandDFS(grid,i,j-1,used);
maxAreaOfIslandDFS(grid,i,j+1,used);
}
}
和为K的子数组【哈希表】
👉 【LeetCode 直通车】:560 和为K的子数组(中等)[19]
题解
//所有以nums[i]为结尾的和为k的连续子数组, j到i的连续子数组和为k,则0到j的和为(0到i的和减去k)
class Solution {
public int subarraySum(int[] nums, int k) {
if(nums==null||nums.length==0){
return 0;
}
Map<Integer,Integer> mapResult=new HashMap<Integer,Integer>();
int temp=0;
int result=0;
mapResult.put(0,1);
for(int i=0;i<nums.length;i++){
temp+=nums[i];
if(mapResult.containsKey(temp-k)){
result+=mapResult.get(temp-k);
}
mapResult.put(temp,mapResult.getOrDefault(temp,0)+1);
}
return result;
}
}
nSum问题【哈希表】【系列】
-
👉 【LeetCode 直通车】:1 两数之和(简单)[20]
-
public int[] twoSum(int[] nums, int target) { Map<Integer,Integer> map=new HashMap<Integer,Integer>(); for(int i=0;i<nums.length;i++){ if(map.containsKey(target-nums[i])){ return new int[]{map.get(target-nums[i]),i}; } map.put(nums[i],i); } return new int[]{-1,-1}; }
-
👉 【LeetCode 直通车】:167 两数之和 II - 输入有序数组(简单)[21]
-
public int[] twoSum(int[] numbers, int target) { int left=0,right=numbers.length-1; while(left<right){ int sum=numbers[left]+numbers[right]; if(sum==target){ return new int[]{left+1,right+1}; }else if(sum>target){ right--; }else{ left++; } } return new int[]{-1,-1}; }
-
👉 【LeetCode 直通车】:15 三数之和(中等)[22]
-
👉 【LeetCode 直通车】:18 四数之和(中等)[23]
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
return nSumDFS(nums,4,0,target);
}
public List<List<Integer>> nSumDFS(int []nums,int n,int start,int target){
List<List<Integer>> result=new ArrayList<List<Integer>>();
if(n==2){
int end=nums.length-1;
while(start<end){
if(nums[start]+nums[end]<target){
start++;
while(nums[start]==nums[start-1]&&start<end){
start++;
}
}else if(nums[start]+nums[end]>target){
end--;
while(nums[end]==nums[end+1]&&start<end){
end--;
}
}else{
List<Integer> twoSum=new ArrayList<Integer>();
twoSum.add(nums[start]);
twoSum.add(nums[end]);
result.add(twoSum);
start++;
end--;
while(start>0&&nums[start]==nums[start-1]&&start<end){
start++;
}
while(end<nums.length-1&&nums[end]==nums[end+1]&&start<end){
end--;
}
}
}
}else{
for(int i=start ;i<nums.length;i++){
int curNum=nums[i];
if(i>start&&nums[i]==nums[i-1]){
continue;
}
List<List<Integer>> tempResult=nSumDFS(nums,n-1,i+1,target-curNum);
if(tempResult!=null&&tempResult.size()>0){
for(List<Integer> tempList:tempResult){
tempList.add(curNum);
result.add(tempList);
}
}
}
}
return result;
}
}
接雨水【暴力+备忘录优化】
👉 【LeetCode 直通车】:42 接雨水(困难)[24]
题解
// 1.首先我们需要搞清楚,下标为i的雨水量是由什么决定的.
// 是由i左右两边最大值中较小的那个减去height[i]决定的.例 [0,1,0,2,1,0,1,3,2,1,2,1]中,下标为2的位置 值为0,而它的用水量是由左边的最大值1,右边最大值3 中较小的那个 也就是1减去0得到的。
// 2.本题解的双指针先找到当前维护的左、右最大值中较小的那个,例 当前 i 处左边的最大值如果比右边的小,那么就可以不用考虑 i 处右边最大值的影响了,因为 i 处 右边真正的最大值绝对比左边的最大值要大,在不断遍历时,更新max_l和max_r以及返回值即可。例 [0,1,0,2,1,0,1,3,2,1,2,1]中i=2时,值为0,此时max_l一定为1,当前max_r如果为2,即便max_r不是真正的i右边的最大值,也可忽略右边最大值的影响,因为右边真正的最大值一定比左边真正的最大值大。
public int trap(int[] height) {
if(height==null||height.length==0){
return 0;
}
int result=0,left=0,right=height.length-1,leftMax=0,rightMax=0;
while(left<right){
int heightLeft=height[left];
int heightRight=height[right];
leftMax=Math.max(leftMax, heightLeft);
rightMax=Math.max(rightMax, heightRight);
if(leftMax<rightMax){
left++;
result+=(leftMax-heightLeft);
}else{
right--;
result+=(rightMax-heightRight);
}
}
return result;
}
stack的方式
public int trap(int[] height) {
if(height==null||height.length==0){
return 0;
}
Stack<Integer> stack=new Stack<Integer>();
int result=0;
for(int i=0;i<height.length;i++){
if(stack.isEmpty()){
stack.push(i);
}else{
while(!stack.isEmpty()&&height[i]>height[stack.peek()]){
int heightLow=height[stack.pop()];
if(stack.isEmpty()){
break;
}
int deep=Math.min(height[stack.peek()],height[i])-heightLow;
result+=(deep*(i-stack.peek()-1)); //此处必须是stack中最后一个值,不能上上一个弹出的,例如 0的时候,7和2之间的宽度,2的时候7和4之间的宽度 [4,2,0,7]
}
stack.push(i);
}
}
return result;
}
跳跃游戏【贪心算法】【系列】
-
👉 【LeetCode 直通车】:55 跳跃游戏(中等)[25]
-
class Solution { // 这样以来,我们依次遍历数组中的每一个位置,并实时维护 最远可以到达的位置。对于当前遍历到的位置 xx,如果它在 最远可以到达的位置 的范围内,那么我们就可以从起点通过若干次跳跃到达该位置,因此我们可以用 x + \textit{nums}[x]x+nums[x] 更新 最远可以到达的位置。 public boolean canJump(int[] nums) { if(nums==null||nums.length==0){ return true; } int rightMost=nums[0]; for(int i=1;i<nums.length;i++){ if(i<=rightMost){ rightMost=Math.max(rightMost,i+nums[i]); } } return rightMost>=nums.length-1; } //暴力破解法 // public boolean canJump; // public boolean canJump(int[] nums) { // if(nums==null||nums.length==0){ // return false; // } // canJumpDFS(nums,0); // return canJump; // } // public void canJumpDFS(int[] nums,int start){ // if(canJump==true){ // return; // } // if(start>=nums.length-1){ // canJump=true; // return; // } // for(int i=start+1;i<=start+nums[start];i++){ // canJumpDFS(nums, i); // } // } }
-
👉 【LeetCode 直通车】:45 跳跃游戏 II(中等)[26]
受限于篇幅,这里只给出第一道题的代码模板,也是一面常考真题。
题解
class Solution {
public int jump(int[] nums) {
if(nums==null||nums.length<=1){
return 0;
}
int end=nums[0],farthest=nums[0],step=1;
for(int i=0;i<nums.length-1;i++){
if(i<=farthest){
farthest=Math.max(farthest,i+nums[i]);
}
if(i==end){
step++;
end=farthest;
}
}
return step;
}
}
高频算法题系列:二叉树
主要有以下几类高频考题:
-
二叉树的最近公共祖先【简单】【二叉树】
-
二叉搜索树中的搜索【简单】【二叉树】
-
删除二叉搜索树中的节点【中等】【二叉树】
-
完全二叉树的节点个数【中等】【二叉树】
-
二叉树的锯齿形层序遍历【中等】【二叉树】
二叉树的最近公共祖先【二叉树】
👉 【LeetCode 直通车】:236 二叉树的最近公共祖先(简单)[27]
题解
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
TreeNode result;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
lowestCommonAncestorDFS(root,p,q);
return result;
}
public boolean lowestCommonAncestorDFS(TreeNode root ,TreeNode p,TreeNode q){
if(root==null){
return false;
}
boolean inCurren=false;
if(root.val==p.val||root.val==q.val){
inCurren=true;
}
boolean inLeft=lowestCommonAncestorDFS(root.left,p,q);
boolean inRight=lowestCommonAncestorDFS(root.right,p,q);
if(inCurren&&(inLeft||inRight)){
result=root;
}
if(inLeft&&inRight){
result=root;
}
return inLeft||inRight||inCurren;
}
// public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// if(root==null){
// return null;
// }
// TreeNode left=lowestCommonAncestor(root.left,p,q);
// TreeNode right=lowestCommonAncestor(root.right,p,q);
// if((root.val==p.val||root.val==q.val)&&(left!=null||right!=null)){
// return root;
// }
// if(left!=null&&right!=null){
// return root;
// }
// if(left!=null){
// return left;
// }
// if(right!=null){
// return right;
// }
// if(root.val==p.val||root.val==q.val){
// return root;
// }
// return null;
// }
}
二叉搜索树中的搜索【二叉树】
👉 【LeetCode 直通车】:700 二叉搜索树中的搜索(简单)[28]
题解
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if(root==null){
return null;
}
if(root.val==val){
return root;
}
if(val>root.val){
return searchBST(root.right,val);
}else{
return searchBST(root.left,val);
}
}
}
删除二叉搜索树中的节点【二叉树】
👉 【LeetCode 直通车】:450 删除二叉搜索树中的节点(中等)[29]
题解
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if(root==null){
return null;
}
if(key>root.val){
root.right=deleteNode(root.right,key);
}else if(key<root.val){
root.left=deleteNode(root.left,key);
}else{
if(root.left==null){
return root.right;
}
if(root.right==null){
return root.left;
}
TreeNode smallestRight=getSmallestRight(root.right);
int temp=smallestRight.val;
smallestRight.val=root.val;
root.val=temp;
root.right=deleteNode(root.right,key);
}
return root;
}
//获取右子树最小子树
public TreeNode getSmallestRight(TreeNode root){
if(root==null){
return null;
}
while(root.left!=null){
root=root.left;
}
return root;
}
}
完全二叉树的节点个数【二叉树】
👉 【LeetCode 直通车】:222 完全二叉树的节点个数(中等)[30]
题解
class Solution {
public int countNodes(TreeNode root) {
if(root==null){
return 0;
}
int leftHeight=0;
TreeNode leftTree=root;
while(leftTree.left!=null){
leftTree=leftTree.left;
leftHeight++;
}
int rightHeight=0;
TreeNode rightTree=root;
while(rightTree.right!=null){
rightTree=rightTree.right;
rightHeight++;
}
if(leftHeight==rightHeight){
return (int)Math.pow(2,leftHeight+1)-1;
}else{
return 1+ countNodes(root.left)+countNodes(root.right);
}
}
}
二叉树的锯齿形层序遍历【二叉树】
👉 【LeetCode 直通车】:103 二叉树的锯齿形层序遍历(中等)[31]
题解
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
Queue <TreeNode> queue=new LinkedList<TreeNode>();
List<List<Integer>> result=new ArrayList<List<Integer>>();
int index=1;
if(root!=null){
queue.offer(root);
}
while(!queue.isEmpty()){
int size=queue.size();
Deque<Integer> deque=new LinkedList<Integer>();
for(int i=0;i<size;i++){
TreeNode curNode=queue.poll();
if(curNode.left!=null){
queue.offer(curNode.left);
}
if(curNode.right!=null){
queue.offer(curNode.right);
}
if(index%2==1){
deque.offerLast(curNode.val);
}else{
deque.offerFirst(curNode.val);
}
}
result.add(new ArrayList(deque));
index++;
}
return result;
}
}
【🔥】高频算法题系列:排序算法
主要有以下几类高频考题:
-
用最少数量的箭引爆气球【中等】【排序】
-
合并区间【中等】【排序算法+区间问题】【面试真题】
用最少数量的箭引爆气球【排序算法】
👉 【LeetCode 直通车】:452 用最少数量的箭引爆气球(中等)[32]
题解
如果按左端升序排序,可能出现这种:[0, 9], [0, 6], [7, 8]
当前第一个区间和第二个重合,我让当前第一个区间继续寻求重合,它和第三个也重合。
你想着一箭三雕,但第二个和第三个其实并不重合。
被「包含」在当前区间的重合区间,不一定和别的重合区间重合
当前区间可能和很多区间重合,但无法保证这些区间内部都互相重合。
//尾部排序,每个开始和之前的结尾做对比,结尾是有序的,若采用头部排序,后面的头部和结尾比较,结尾无续,可能存在前面的尾部更长,后面两个尾部更短的情况
class Solution {
public int findMinArrowShots(int[][] points) {
if(points==null){
return 0;
}
Arrays.sort(points,new Comparator<int []>(){
public int compare(int []o1,int []o2){
return Integer.compare(o1[1], o2[1]);
}
});
int startIndex=points[0][1];
int result=1;
for(int i=1;i<points.length;i++){
if(points[i][0]>startIndex){
result++;
startIndex=points[i][1];
}
}
return result;
}
}
合并区间【排序算法+区间问题】
👉 【LeetCode 直通车】:56 合并区间(中等)[33]
题解
class Solution {
public int[][] merge(int[][] intervals) {
if(intervals==null){
return new int[][]{};
}
Arrays.sort(intervals,new Comparator<int[]>(){
public int compare(int[]a,int[]b){
return Integer.compare(a[0],b[0]);
}
});
List<int[]> result=new ArrayList<int[]>();
result.add(intervals[0]);
int maxRight=intervals[0][1];
for(int i=1;i<intervals.length;i++){
if(intervals[i][0]> result.get(result.size()-1)[1]){
result.add(intervals[i]);
}else{
result.get(result.size()-1)[1]=Math.max(intervals[i][1], result.get(result.size()-1)[1]);
}
}
return result.toArray(new int [result.size()][]);
}
}
高频算法题系列:二分查找
主要有以下几类高频考题:
-
寻找两个正序数组的中位数【困难】【二分查找】
-
判断子序列【简单】【二分查找】
-
在排序数组中查找元素的第一个和最后一个位置【中等】【二分查找】
寻找两个正序数组的中位数【二分查找】
👉 【LeetCode 直通车】:4 寻找两个正序数组的中位数(困难)[34]
题解
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number}
*/
var findMedianSortedArrays = function(nums1, nums2) {
let m = nums1.length, n = nums2.length;
let i = 0, j = 0;
let newArr = [];
while (i < m && j < n) {
if (nums1[i] < nums2[j]) {
newArr.push(nums1[i++]);
} else {
newArr.push(nums2[j++]);
}
}
newArr = newArr.concat(i < m ? nums1.slice(i) : nums2.slice(j));
const len = newArr.length;
console.log(newArr)
if (len % 2 === 0) {
return (newArr[len / 2] + newArr[len / 2 - 1]) / 2;
} else {
return newArr[Math.floor(len / 2)];
}
};
判断子序列【二分查找】
👉 【LeetCode 直通车】:392 判断子序列(简单)[35]
题解
class Solution {
public boolean isSubsequence(String s, String t) {
if(s==null||t==null){
return true;
}
int m=s.length(),n=t.length(),i=0,j=0;
if(m>n){
return false;
}
while(i<m&&j<n){
if(s.charAt(i)==t.charAt(j)){
i++;
}
j++;
}
return i==m;
}
}
💁 在排序数组中查找元素的第一个和最后一个位置【二分搜索】
👉 【LeetCode 直通车】:34 在排序数组中查找元素的第一个和最后一个位置(中等)[36]
题解
class Solution {
public int[] searchRange(int[] nums, int target) {
if(nums==null||nums.length==0){
return new int[]{-1,-1};
}
int left=binarySearch(nums,target,true);
int right=binarySearch(nums,target,false);
return new int[]{left,right};
}
public int binarySearch(int []nums,int target,boolean isFirst){
int left=0,right=nums.length-1;
while(left<=right){
int middle=right-(right-left)/2;
if(isFirst){
if(target<=nums[middle]){
right=middle-1;
}else{
left=middle+1;
}
}else{
if(target>=nums[middle]){
left=middle+1;
}else{
right=middle-1;
}
}
}
if(isFirst&&left<nums.length&&nums[left]==target){
return left;
}
if(!isFirst&&right>=0&&nums[right]==target){
return right;
}
return -1;
}
}
【🔥】高频算法题系列:动态规划
主要有以下几类高频考题:
-
最长递增子序列【中等】【动态规划】
-
零钱兑换【中等】【动态规划】【面试真题】
-
最长公共子序列 【中等】【动态规划】【面试真题】
-
编辑距离 【困难】【动态规划】
-
最长回文子序列【中等】【动态规划】【面试真题】
-
最大子序和【简单】【动态规划】【面试真题】
-
买卖股票的最佳时机系列【系列】【动态规划】【面试真题】
最长递增子序列【动态规划】
👉 【LeetCode 直通车】:300 最长递增子序列(中等)[37]
题解
class Solution {
// 考虑往]dp[0…i−1] 中最长的上升子序列后面再加一个 nums[i]。由于dp[j] 代表 nums[0…j] 中以 nums[j] 结尾的最长上升子序列,所以如果
public int lengthOfLIS(int[] nums) {
if(nums==null||nums.length==0){
return 0;
}
int []dp=new int [nums.length];
dp[0]=1;
int result=0;
for(int i=0;i<nums.length;i++){
dp[i]=1;
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){
dp[i]=Math.max(dp[i],dp[j]+1);
}
}
result=Math.max(result,dp[i]);
}
return result;
}
}
【面试真题】 零钱兑换【动态规划】
👉 【LeetCode 直通车】:322 零钱兑换(中等)[38]
题解
class Solution {
public int coinChange(int[] coins, int amount) {
int dp[] =new int [amount+1];
for(int i=1;i<=amount;i++){
dp[i]=Integer.MAX_VALUE-1;
}
dp[0]=0;
for(int coin:coins){
for(int j=coin;j<=amount;j++){
dp[j]=Math.min(dp[j],dp[j-coin]+1);
}
}
return dp[amount]<(Integer.MAX_VALUE-1)?dp[amount]:-1;
}
}
【面试真题】 最长公共子序列【动态规划】
👉 【LeetCode 直通车】:1143 最长公共子序列(中等)[39]
题解
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
if(text1==null||text2==null){
return 0;
}
int dp[][]=new int[text1.length()+1][text2.length()+1];
for(int i=1;i<=text1.length();i++){
for(int j=1;j<=text2.length();j++){
if(text1.charAt(i-1)==text2.charAt(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[text1.length()][text2.length()];
}
}
编辑距离【动态规划】
👉 【LeetCode 直通车】:72 编辑距离(困难)[40]
题解
class Solution {
public int minDistance(String word1, String word2) {
int dp[][]=new int[word1.length()+1][word2.length()+1];
for(int i=0;i<=word1.length();i++){
dp[i][0]=i;
}
for(int i=0;i<=word2.length();i++){
dp[0][i]=i;
}
for(int i=1;i<=word1.length();i++){
for(int j=1;j<=word2.length();j++){
if(word1.charAt(i-1)==word2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1];
}else{
dp[i][j]=Math.min(
Math.min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1]
)+1;
}
}
}
return dp[word1.length()][word2.length()];
}
}
【面试真题】最长回文子序列【动态规划】
👉 【LeetCode 直通车】:516 最长回文子序列(中等)[41]
题解
class Solution {
public int longestPalindromeSubseq(String s) {
if(s==null||s.length()==0){
return 0;
}
int [][]dp=new int[s.length()+1][s.length()+1];
for(int i=0;i<s.length();i++){
dp[i][i]=1;
}
for(int i=0;i<s.length();i++){
for(int j=i-1;j>=0;j--){
if(s.charAt(j)==s.charAt(i)){
dp[j][i]=dp[j+1][i-1]+2;
}else{
dp[j][i]=Math.max(dp[j+1][i],dp[j][i-1]);
}
}
}
return dp[0][s.length()-1];
}
}
【LeetCode 直通车】:5最长回文子串(中等)[41]
题解
class Solution {
public String longestPalindrome(String s) {
int maxLength=0;
String longestPalindromeStr="";
if(s==null||s.length()==0){
return null;
}
if(s.length()==1){
return s;
}
int dp[][]=new int[s.length()][s.length()];
for(int i=0;i<s.length();i++){
dp[i][i]=1;
for(int j=0;j<i;j++){
if(i-j==1){
dp[j][i]=s.charAt(i)==s.charAt(j)?2:0;
}else{
if(dp[j+1][i-1]>0&&s.charAt(i)==s.charAt(j)){
dp[j][i]=dp[j+1][i-1]+2;
}
}
if(dp[j][i]>maxLength){
maxLength=dp[j][i];
longestPalindromeStr=s.substring(j,i+1);
}
}
}
if(maxLength==0){
return s.substring(0,1);
}
return longestPalindromeStr;
}
// public int longestPalindrome=0;
// public String longestPalindromeStr;
// public String longestPalindrome(String s) {
// if(s==null||s.length()==0){
// return null;
// }
// for(int i=0;i<s.length();i++){
// longestPalindromeDFS(s,i,i);
// longestPalindromeDFS(s,i,i+1);
// }
// return longestPalindromeStr;
// }
// public void longestPalindromeDFS(String s ,int start1,int start2){
// while(s!=null&&start1>=0&&start2<s.length()&&s.charAt(start1)==s.charAt(start2)){
// start1--;
// start2++;
// }
// if(start2-start1>longestPalindrome){
// longestPalindrome=start2-start1;
// longestPalindromeStr=s.substring(start1+1,start2);
// }
// }
}
【面试真题】💁 最大子序和【动态规划】
👉 【LeetCode 直通车】:53 最大子序和(简单)[42]
题解
class Solution {
public int maxSubArray(int[] nums) {
int dp[] =new int[nums.length]; //状态不能定义为前i个连续子数组,状态转移时 i和i-1未必连续,定位为以i为结尾的连续子数组
if(nums==null||nums.length==0){
return 0;
}
int maxSubArray=nums[0];
dp[0]=nums[0];
for(int i=1;i<nums.length;i++){
dp[i]=Math.max(nums[i],dp[i-1]+nums[i]);
maxSubArray=Math.max(maxSubArray,dp[i]);
}
return maxSubArray;
}
// public int maxSubArray(int[] nums) {
// if(nums==null||nums.length==0){
// return 0;
// }
// int tempSum=0,result=Integer.MIN_VALUE;
// for(int num:nums){
// tempSum+=num;
// if(tempSum>0){ //和大于0,就对后边新的数组有增益,可继续加
// result=Math.max(result,tempSum);
// }else{
// result=Math.max(result,tempSum);
// tempSum=0;
// }
// }
// return result;
// }
}
【面试真题】💁 买卖股票的最佳时机【动态规划】
-
👉 【LeetCode 直通车】:121 买卖股票的最佳时机(简单)[43]【面试真题】
-
class Solution { public int maxProfit(int[] prices) { int minValue=Integer.MAX_VALUE,result=0; if(prices==null||prices.length==1){ return 0; } for(int price :prices){ minValue=Math.min(minValue,price); result=Math.max(result,price-minValue); } return result; } }
-
👉 【LeetCode 直通车】:122 买卖股票的最佳时机 II(简单)[44]
-
class Solution { // //贪心算法,画个走势上升下降图,每个上升阶段最高点和最低点差值,相当于中间各个点差值相加 // public int maxProfit(int[] prices) { // if(prices==null||prices.length<2){ // return 0; // } // int result=0; // for(int i=1;i<prices.length;i++){ // if(prices[i]>prices[i-1]){ // result+=(prices[i]-prices[i-1]); // } // } // return result; // } public int maxProfit(int[] prices) { if(prices==null||prices.length<2){ return 0; } int [][]dp=new int[prices.length][2]; //当前持有股票和未持有股票两种情况 dp[0][0]=0; dp[0][1]=-prices[0]; for(int i=1;i<prices.length;i++){ dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i]); //当前未持有的最大收益,上一步就未持有,上一步持有,到当前股票价格时卖掉 dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]-prices[i]); //当前持有最大收益为 上一步持有或者上一步未持有,现在买入 } return Math.max(dp[prices.length-1][0], dp[prices.length-1][1]); } }
-
👉 【LeetCode 直通车】:123 买卖股票的最佳时机 III(困难)[45]
-
👉 【LeetCode 直通车】:188 买卖股票的最佳时机IV(困难)[46]
-
👉 【LeetCode 直通车】:309 买卖股票的最佳时机含冷冻期(中等)[47]
-
class Solution { public int maxProfit(int[] prices) { if(prices==null||prices.length<1){ return 0; } int n=prices.length; int dp [][]=new int [n][3]; // [i][0]表示今天过后处于冻结未持有,[i][1]表持今天过后持有 ,[i][2] 表示处于非冻结可以买入期 dp [0][1]=-prices[0]; for(int i=1;i<prices.length;i++){ //今天过后处于锁定期的话,昨天过后必然处于买入期,并且今天卖出 dp[i][0]=dp[i-1][1]+prices[i]; //今天过后处于持有期的话,昨天持有,或者昨天可买入期并且今天买入 dp[i][1]=Math.max(dp[i-1][1],dp[i-1][2]-prices[i]); //今天过后处于可买期的话,昨天过后可能处于锁定期或者昨天过后本身就是可买期 dp[i][2]=Math.max(dp[i-1][2],dp[i-1][0]); } return Math.max(Math.max(dp[prices.length-1][0],dp[prices.length-1][1]),dp[prices.length-1][2]); } }
-
👉 【LeetCode 直通车】:714 买卖股票的最佳时机含手续费(中等)[48]
受限于篇幅,这里只给出第一道题的代码模板,也是一面常考真题,笔者在面试字节跳动时就遇到过。
题解
/**
* @param {number[]} prices
* @return {number}
*/
var maxProfit = function(prices) {
let dp = [];
for (let i = -1; i < prices.length; i++) {
dp[i] = []
for (let j = 0; j <= 1; j++) {
dp[i][j] = [];
dp[i][j][0] = 0;
dp[i][j][1] = 0;
if (i === -1) {
dp[i][j][1] = -Infinity;
}
if (j === 0) {
dp[i][j][1] = -Infinity;
}
if (j === -1) {
dp[i][j][1] = -Infinity;
}
}
}
for (let i = 0; i < prices.length; i++) {
for (let j = 1; j <= 1; j++) {
dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
}
}
return dp[prices.length - 1][1][0];
};
class Solution {
public int minSwap(int[] nums1, int[] nums2) {
int n = nums1.length;
int a = 0, b = 1;
for (int i = 1; i < n; i++) {
int at = a, bt = b;
a = b = n;
if (nums1[i] > nums1[i - 1] && nums2[i] > nums2[i - 1]) {
a = Math.min(a, at);
b = Math.min(b, bt + 1);
}
if (nums1[i] > nums2[i - 1] && nums2[i] > nums1[i - 1]) {
a = Math.min(a, bt);
b = Math.min(b, at + 1);
}
}
return Math.min(a, b);
}
}
高频算法题系列:BFS
主要有以下几类高频考题:
-
打开转盘锁【中等】【BFS】
-
二叉树的最小深度【简单】【BFS】
打开转盘锁【BFS】
👉 【LeetCode 直通车】:752 打开转盘锁(中等)[49]
题解
class Solution {
public int openLock(String[] deadends, String target) {
Set<String> lockSet=new HashSet<String>();
Set<String> visited=new HashSet<String>();
for(String curLock:deadends){
lockSet.add(curLock);
}
Deque<String> queue=new LinkedList<String>();
queue.offer("0000");
// visited.add("0000");
int minLength=0;
while(!queue.isEmpty()){
int length=queue.size();
for(int j=0;j<length;j++){
String curstring=queue.poll();
if(lockSet.contains(curstring)||visited.contains(curstring)){
continue;
}
if(target.equals(curstring)){
return minLength;
}
visited.add(curstring);
for(int i=0;i<4;i++){
String plusString=plusOne(curstring,i);
queue.offer(plusString);
String minusString=minusOne(curstring,i);
queue.offer(minusString);
}
}
minLength++;
}
return -1;
}
public String plusOne(String s,int index){
char[] charArray=s.toCharArray();
if(charArray[index]=='9'){
charArray[index]='0';
}else{
charArray[index]+=1;
}
return new String(charArray);
}
public String minusOne(String s,int index){
char[] charArray=s.toCharArray();
if(charArray[index]=='0'){
charArray[index]='9';
}else{
charArray[index]-=1;
}
return new String(charArray);
}
}
二叉树的最小深度【BFS】
👉 【LeetCode 直通车】:111 二叉树的最小深度(简单)[50]
题解
class Solution {
public int minDepth(TreeNode root) {
if(root==null){
return 0;
}
Deque<TreeNode> queue=new LinkedList<TreeNode>();
queue.offer(root);
int minDeep=1;
while(!queue.isEmpty()){
int length=queue.size();
for(int i=0;i<length;i++){
TreeNode curNode=queue.poll();
if(curNode.left==null&&curNode.right==null){
return minDeep;
}
if(curNode.left!=null){
queue.offer(curNode.left);
}
if(curNode.right!=null){
queue.offer(curNode.right);
}
}
minDeep++;
}
return minDeep;
}
}
【🔥】高频算法题系列:栈
主要有以下几类高频考题:
-
最小栈【简单】【栈】
-
有效的括号【中等】【栈】【面试真题】
-
简化路径【中等】【栈】
-
下一个更大元素 【系列】【栈】
227. 基本计算器 II(点击看题)
最小栈【栈】
👉 【LeetCode 直通车】:155 最小栈(简单)[51]
题解
class MinStack {
Stack <Integer> stack;
Stack <Integer> minStack;
/** initialize your data structure here. */
public MinStack() {
stack=new Stack<Integer>();
minStack=new Stack<Integer>();
}
public void push(int val) {
stack.push(val);
minStack.push(minStack.isEmpty()?val:Math.min(minStack.peek(),val));
}
public void pop() {
stack.pop();
minStack.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.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();
*/
【系列】下一个更大元素 【栈】
-
👉 【LeetCode 直通车】:496 下一个更大元素 I(简单)[52]
-
👉 【LeetCode 直通车】:503 下一个更大元素 II(中等)[53]
受限于篇幅,这里只给出第一道题的代码模板
题解
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
Map<Integer,Integer> map=new HashMap<Integer,Integer>();
Stack<Integer> stack=new Stack<Integer>();
int []result=new int[nums1.length];
for(int i=nums2.length-1;i>=0;i--){
while(!stack.isEmpty()&&nums2[i]>stack.peek()){
stack.pop();
}
int iResult=stack.isEmpty()?-1:stack.peek();
stack.push(nums2[i]);
map.put(nums2[i],iResult);
}
for(int i=0;i<nums1.length;i++){
result[i]=map.get(nums1[i]);
}
return result;
}
}
下一个更大的元素2 罗列两遍数组
class Solution {
public int[] nextGreaterElements(int[] nums) {
Stack<Integer> stack=new Stack<Integer>();
int n=nums.length;
int [] result=new int[n];
for(int i=2*n-1;i>=0;i--){
int index=i%n;
while(!stack.isEmpty()&&stack.peek()<=nums[index]){
stack.pop();
}
int curValue=stack.isEmpty()?-1:stack.peek();
stack.push(nums[index]);
result[index]=curValue;
}
return result;
}
}
【面试真题】有效的括号【栈】
👉 【LeetCode 直通车】:20 有效的括号(中等)[54]
题解
class Solution {
public static boolean isValid(String s) {
if(s==null||s.length()==0){
return true;
}
while(s.contains("()")||s.contains("{}")||s.contains("[]")){
s=s.replace("()", "");
s=s.replace("{}", "");
s=s.replace("[]", "");
}
return s.length()>0?false:true;
}
// public static boolean isValid(String s) {
// if(s==null||s.length()==0){
// return true;
// }
// Stack<Character> stack=new Stack<Character>();
// for(int i=0;i<s.length();i++){
// char curChar=s.charAt(i);
// if(curChar=='{'||curChar=='['||curChar=='('){
// stack.push(curChar);
// }else{
// if(stack.isEmpty()){
// return false;
// }
// Character leftChar=stack.pop();
// if(leftChar!=null&&leftChar=='{'&&curChar=='}'){
// continue;
// }else if(leftChar!=null&&leftChar=='['&&curChar==']'){
// continue;
// }else if(leftChar!=null&&leftChar=='('&&curChar==')'){
// continue;
// }else{
// return false;
// }
// }
// }
// return stack.size()>0?false:true;
// }
}
简化路径【栈】
👉 【LeetCode 直通车】:71 简化路径(中等)[55]
题解
class Solution {
public String simplifyPath(String path) {
if(path==null||"".equals(path)){
return "/";
}
String [] pathArray=path.split("/");
Stack<String> stack=new Stack<String>();
for(int i=0;i<pathArray.length;i++){
String curString =pathArray[i];
if(".".equals(curString)||"".equals(curString)){
continue;
}else if("..".equals(curString)&&!stack.isEmpty()){
stack.pop();
} else if("..".equals(curString)&&stack.isEmpty()){
// return "/";
}else{
stack.push(curString);
}
}
StringBuilder sb=new StringBuilder();
for(int i=0;i<stack.size();i++){
sb.append("/");
sb.append(stack.get(i));
}
return sb.length()==0?"/":sb.toString();
}
}
【🔥】高频算法题系列:DFS
主要有以下几类高频考题:
-
岛屿的最大面积【中等】【DFS】
-
相同的树【简单】【DFS】
岛屿的最大面积【DFS】
👉 【LeetCode 直通车】:695 岛屿的最大面积(中等)[56]
题解
class Solution {
public int maxAreaOfIsland=0;
public int tempMaxAreaOfIsland=0;
public int maxAreaOfIsland(int[][] grid) {
int m=grid.length;
int n=grid[0].length;
boolean [][]used=new boolean[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(!used[i][j]&&grid[i][j]==1){
this.tempMaxAreaOfIsland=0;
maxAreaOfIslandDFS(grid,i,j,used);
}
}
}
return maxAreaOfIsland;
}
public void maxAreaOfIslandDFS(int[][]grid,int i,int j,boolean [][]used){
if(i<0||i>grid.length-1||j<0||j>grid[0].length-1){
return;
}
if(used[i][j]||grid[i][j]==0){
return;
}
tempMaxAreaOfIsland++;
maxAreaOfIsland=Math.max(maxAreaOfIsland,tempMaxAreaOfIsland);
used[i][j]=true;
maxAreaOfIslandDFS(grid,i+1,j,used);
maxAreaOfIslandDFS(grid,i-1,j,used);
maxAreaOfIslandDFS(grid,i,j-1,used);
maxAreaOfIslandDFS(grid,i,j+1,used);
}
}
相同的树【DFS】
👉 【LeetCode 直通车】:100 相同的树(简单)[57]
题解
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if((p!=null&&q!=null)&&(p.val==q.val)){
return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);
}else if(p==null&&q==null){
return true;
}else{
return false;
}
}
}
【🔥】高频算法题系列:回溯算法
主要有以下几类高频考题:
-
N皇后【困难】【回溯算法】【面试真题】
-
全排列【中等】【回溯算法】
-
括号生成【中等】【回溯算法】
-
复原 IP 地址【中等】【回溯算法】
-
子集 【简单】【回溯算法】
【面试真题】N皇后【回溯算法】
👉 【LeetCode 直通车】:51 N皇后(困难)[58]
题解
class Solution {
List<List<String>> result=new ArrayList<List<String>>();
public List<List<String>> solveNQueens(int n) {
char [][] queenChar=new char[n][n];
for(int i=0;i<n;i++){
Arrays.fill(queenChar[i],'.');
}
solveNQueensDFS(queenChar,0);
return result;
}
public void solveNQueensDFS(char[][]queenChar,int colIndex){
if(colIndex>queenChar.length){
return; //理论上不可能>n,有所有rowIndex都不满足的,就没有深度遍历了
}
if(colIndex==queenChar.length){
result.add(arrayToList(queenChar));
}
//在本行的所有列都暴力遍历一遍
for(int i=0;i<queenChar.length;i++){
if(isLegal(queenChar,colIndex,i)){
queenChar[colIndex][i]='Q';
solveNQueensDFS(queenChar,colIndex+1);
queenChar[colIndex][i]='.';
}else{
continue;
}
}
}
public boolean isLegal(char[][]queenChar,int colIndex,int rowIndex){
//上方
for(int i=colIndex-1;i>=0;i--){
if(queenChar[i][rowIndex]=='Q'){
return false;
}
}
//左上角
for(int i=colIndex-1,j=rowIndex-1;i>=0&&j>=0;i--,j--){
if(queenChar[i][j]=='Q'){
return false;
}
}
//右上角
for(int i=colIndex-1,j=rowIndex+1;i>=0&&j<queenChar.length;i--,j++){
if(queenChar[i][j]=='Q'){
return false;
}
}
return true;
}
public List<String> arrayToList(char [][]queenChar){
List<String> result=new ArrayList<String>();
for(char [] tempChar:queenChar){
String s=new String(tempChar);
result.add(s);
}
return result;
}
}
全排列【回溯算法】
👉 【LeetCode 直通车】:46 全排列(中等)[59]
题解
class Solution {
List<List<Integer>> result=new ArrayList<List<Integer>>();
public List<List<Integer>> permute(int[] nums) {
if(nums==null||nums.length==0){
return new ArrayList();
}
permuteDFS(nums,new ArrayList<Integer>(),new boolean [nums.length]);
return result;
}
public void permuteDFS(int []nums,List<Integer> tempList,boolean[]used){
if(tempList.size()==nums.length){
result.add(new ArrayList(tempList));
return;
}
for(int i=0;i<nums.length;i++){
if(used[i]){
continue;
}
tempList.add(nums[i]);
used[i]=true;
permuteDFS(nums,tempList,used);
used[i]=false;
tempList.remove(tempList.size()-1);
}
}
}
括号生成【回溯算法】
👉 【LeetCode 直通车】:22 括号生成(中等)[60]
题解
class Solution {
List<String> list = new ArrayList<>();
public List<String> generateParenthesis(int n) {
if(n==0){
return new ArrayList();
}
generateParenthesisDFS(n,0,0,"");
return list;
}
public void generateParenthesisDFS(int n,int l,int right,String tempStr){
if(l==right&&l==n){
list.add(tempStr);
}
if(l<n){
generateParenthesisDFS(n,l+1,right,tempStr+"(");
}
if(right<l){
generateParenthesisDFS(n,l,right+1,tempStr+")");
}
}
}
复原 IP 地址【回溯算法】
👉 【LeetCode 直通车】:93 复原 IP 地址(中等)[61]
题解
class Solution {
List<String> result=new ArrayList<String>();
public List<String> restoreIpAddresses(String s) {
if(s==null||s.length()==0){
return result;
}
restoreIpAddressesDFS(0,s,4,"");
return result;
}
public void restoreIpAddressesDFS(int startIndex,String s,int n,String tempS){
if(startIndex==s.length()&&n==0){
result.add(tempS);
}
if(startIndex==s.length()){
return;
}
if(n==0){
return;
}
char curChar=s.charAt(startIndex);
if(curChar=='0'){
if(!"".equals(tempS)){
tempS+=".";
}
tempS+=curChar;
restoreIpAddressesDFS(startIndex+1,s,n-1,tempS);
}else{
if(!"".equals(tempS)){
tempS+=".";
}
for(int i=1;i<4;i++){
if(startIndex+i>s.length()){
return;
}
String curStr=s.substring(startIndex,startIndex+i);
if(Integer.valueOf(curStr)>255){
return;
}
restoreIpAddressesDFS(startIndex+i,s,n-1,tempS+curStr);
}
}
}
}
子集【回溯算法】
👉 【LeetCode 直通车】:78 子集(中等)[62]
题解
class Solution {
List<List<Integer>> result=new ArrayList<List<Integer>>();
public List<List<Integer>> subsets(int[] nums) {
if(nums==null||nums.length==0){
return new ArrayList<List<Integer>>();
}
subsetsDFS(nums,0,new ArrayList<Integer>());
return result;
}
public void subsetsDFS(int []nums,int start,List<Integer> tempList){
result.add(new ArrayList(tempList));
for(int i=start;i<nums.length;i++){
int curNum=nums[i];
tempList.add(curNum);
subsetsDFS(nums,i+1,tempList);
tempList.remove(tempList.size()-1);
}
}
}
文末福利
推荐一个非常有帮助的刷算法题的网址,labuladong 的算法小抄[63],通过套路,认准高频题目,直通大厂;这本小炒目前已经出版成书,对应的 Github 仓库[64]也有 86.2K Star,而且作者还在频繁更新,非常值得学习!
❤️谢谢
往期精文
-
字节跳动最爱考的前端面试题:JavaScript 基础[65] 2696 👍
-
字节跳动最爱考的前端面试题:CSS 基础[66] 687 👍
-
字节跳动最爱考的前端面试题:计算机网络基础[67] 761 👍
欢迎关注公众号:图雀社区。 如果你想从零开始以实战的方式学习一门技术,亦或是想动手做一个比较完整的项目以准备面试,相信 「图雀社区」 的内容都能够帮助到你,成为初入前端的你成长路上的指南针。
原创不易
喜欢的话原创不易,给点鼓励吧 ❤️ 别忘了 分享、点赞、在看 三连哦~。
参考资料
[1]
【LeetCode 直通车】:234 回文链表(简单): https://leetcode-cn.com/problems/palindrome-linked-list/
[2]
【LeetCode 直通车】:206 反转链表(简单): https://leetcode-cn.com/problems/reverse-linked-list/
[3]
【LeetCode 直通车】:23 合并K个升序链表(困难): https://leetcode-cn.com/problems/merge-k-sorted-lists/
[4]
【LeetCode 直通车】:25 K 个一组翻转链表(困难): https://leetcode-cn.com/problems/reverse-nodes-in-k-group/
[5]
【LeetCode 直通车】:141 环形链表(简单): https://leetcode-cn.com/problems/linked-list-cycle/
[6]
【LeetCode 直通车】:148 排序链表(中等): https://leetcode-cn.com/problems/sort-list/
[7]
【LeetCode 直通车】:160 相交链表(简单): https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
[8]
【LeetCode 直通车】:5 最长回文子串(中等): https://leetcode-cn.com/problems/longest-palindromic-substring/
[9]
【LeetCode 直通车】:14 最长公共前缀(简单): https://leetcode-cn.com/problems/longest-common-prefix/
[10]
【LeetCode 直通车】:3 无重复字符的最长子串(中等): https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
[11]
【LeetCode 直通车】:76 最小覆盖子串(困难): https://leetcode-cn.com/problems/minimum-window-substring/
[12]
【LeetCode 直通车】:354 俄罗斯套娃信封问题(困难): https://leetcode-cn.com/problems/russian-doll-envelopes/
[13]
【LeetCode 直通车】:674 最长连续递增序列(简单): https://leetcode-cn.com/problems/longest-continuous-increasing-subsequence/
[14]
【LeetCode 直通车】:128 最长连续序列(困难): https://leetcode-cn.com/problems/longest-consecutive-sequence/
[15]
【LeetCode 直通车】:11 盛最多水的容器(中等): https://leetcode-cn.com/problems/container-with-most-water/
[16]
【LeetCode 直通车】:4 寻找两个正序数组的中位数(困难): https://leetcode-cn.com/problems/median-of-two-sorted-arrays/
[17]
【LeetCode 直通车】:26 删除有序数组中的重复项(简单): https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/
[18]
【LeetCode 直通车】:695 岛屿的最大面积(中等): https://leetcode-cn.com/problems/max-area-of-island/
[19]
【LeetCode 直通车】:560 和为K的子数组(中等): https://leetcode-cn.com/problems/subarray-sum-equals-k/
[20]
【LeetCode 直通车】:1 两数之和(简单): https://leetcode-cn.com/problems/two-sum/
[21]
【LeetCode 直通车】:167 两数之和 II - 输入有序数组(简单): https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/
[22]
【LeetCode 直通车】:15 三数之和(中等): https://leetcode-cn.com/problems/3sum/
[23]
【LeetCode 直通车】:18 四数之和(中等): https://leetcode-cn.com/problems/4sum/
[24]
【LeetCode 直通车】:42 接雨水(困难): https://leetcode-cn.com/problems/trapping-rain-water/
[25]
【LeetCode 直通车】:55 跳跃游戏(中等): https://leetcode-cn.com/problems/jump-game/
[26]
【LeetCode 直通车】:45 跳跃游戏 II(中等): https://leetcode-cn.com/problems/jump-game-ii/
[27]
【LeetCode 直通车】:236 二叉树的最近公共祖先(简单): https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/
[28]
【LeetCode 直通车】:700 二叉搜索树中的搜索(简单): https://leetcode-cn.com/problems/search-in-a-binary-search-tree/
[29]
【LeetCode 直通车】:450 删除二叉搜索树中的节点(中等): https://leetcode-cn.com/problems/delete-node-in-a-bst/
[30]
【LeetCode 直通车】:222 完全二叉树的节点个数(中等): https://leetcode-cn.com/problems/count-complete-tree-nodes/
[31]
【LeetCode 直通车】:103 二叉树的锯齿形层序遍历(中等): https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/
[32]
【LeetCode 直通车】:452 用最少数量的箭引爆气球(中等): https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/
[33]
【LeetCode 直通车】:56 合并区间(中等): https://leetcode-cn.com/problems/merge-intervals/
[34]
【LeetCode 直通车】:4 寻找两个正序数组的中位数(困难): https://leetcode-cn.com/problems/median-of-two-sorted-arrays/
[35]
【LeetCode 直通车】:392 判断子序列(简单): https://leetcode-cn.com/problems/is-subsequence/
[36]
【LeetCode 直通车】:34 在排序数组中查找元素的第一个和最后一个位置(中等): https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/
[37]
【LeetCode 直通车】:300 最长递增子序列(中等): https://leetcode-cn.com/problems/longest-increasing-subsequence/
[38]
【LeetCode 直通车】:322 零钱兑换(中等): https://leetcode-cn.com/problems/coin-change/
[39]
【LeetCode 直通车】:1143 最长公共子序列(中等): https://leetcode-cn.com/problems/longest-common-subsequence/
[40]
【LeetCode 直通车】:72 编辑距离(困难): https://leetcode-cn.com/problems/edit-distance/
[41]
【LeetCode 直通车】:516 最长回文子序列(中等): https://leetcode-cn.com/problems/longest-palindromic-subsequence/
[42]
【LeetCode 直通车】:53 最大子序和(简单): https://leetcode-cn.com/problems/maximum-subarray/
[43]
【LeetCode 直通车】:121 买卖股票的最佳时机(简单): https://leetcode-cn.com/problems/container-with-most-water/
[44]
【LeetCode 直通车】:122 买卖股票的最佳时机 II(简单): https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
[45]
【LeetCode 直通车】:123 买卖股票的最佳时机 III(困难): https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/
[46]
【LeetCode 直通车】:188 买卖股票的最佳时机IV(困难): https://leetcode-cn.com/problems/container-with-most-water/
[47]
【LeetCode 直通车】:309 买卖股票的最佳时机含冷冻期(中等): https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/
[48]
【LeetCode 直通车】:714 买卖股票的最佳时机含手续费(中等): https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/
[49]
【LeetCode 直通车】:752 打开转盘锁(中等): https://leetcode-cn.com/problems/open-the-lock/
[50]
【LeetCode 直通车】:111 二叉树的最小深度(简单): https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/
[51]
【LeetCode 直通车】:155 最小栈(简单): https://leetcode-cn.com/problems/min-stack/submissions/
[52]
【LeetCode 直通车】:496 下一个更大元素 I(简单): https://leetcode-cn.com/problems/next-greater-element-i/
[53]
【LeetCode 直通车】:503 下一个更大元素 II(中等): https://leetcode-cn.com/problems/next-greater-element-ii/
[54]
【LeetCode 直通车】:20 有效的括号(中等): https://leetcode-cn.com/problems/valid-parentheses/
[55]
【LeetCode 直通车】:71 简化路径(中等): https://leetcode-cn.com/problems/simplify-path/
[56]
【LeetCode 直通车】:695 岛屿的最大面积(中等): https://leetcode-cn.com/problems/max-area-of-island/
[57]
【LeetCode 直通车】:100 相同的树(简单): https://leetcode-cn.com/problems/same-tree/
[58]
【LeetCode 直通车】:51 N皇后(困难): https://leetcode-cn.com/problems/n-queens/
[59]
【LeetCode 直通车】:46 全排列(中等): https://leetcode-cn.com/problems/permutations/
[60]
【LeetCode 直通车】:22 括号生成(中等): https://leetcode-cn.com/problems/generate-parentheses/
[61]
【LeetCode 直通车】:93 复原 IP 地址(中等): https://leetcode-cn.com/problems/restore-ip-addresses/
[62]
【LeetCode 直通车】:78 子集(中等): https://leetcode-cn.com/problems/subsets/
[63]
labuladong 的算法小抄: https://www.yuque.com/tuture/interview/labuladong:https
[64]
Github 仓库: https://github.com/labuladong/fucking-algorithm
[65]
字节跳动最爱考的前端面试题:JavaScript 基础: https://juejin.cn/post/6934500357091360781
[66]
字节跳动最爱考的前端面试题:CSS 基础: https://juejin.cn/post/6936913689115099143
[67]
字节跳动最爱考的前端面试题:计算机网络基础: https://juejin.cn/post/6939691851746279437
前端技术优选
为你精选前端领域优质技术博文,欢迎关注。
61篇原创内容
公众号