文章目录

  • 动态规划
    • 01背包
    • 背包问题
      • [416. 分割等和子集](https://leetcode-cn.com/problems/partition-equal-subset-sum/)
      • [1049. 最后一块石头的重量 II](https://leetcode-cn.com/problems/last-stone-weight-ii/)
      • [494. 目标和](https://leetcode-cn.com/problems/target-sum/)
      • 这里用的回溯暴搜[39. 组合总和](https://leetcode-cn.com/problems/combination-sum/)
    • [322. 零钱兑换](https://leetcode-cn.com/problems/coin-change/)
    • [53. 最大子序和](https://leetcode-cn.com/problems/maximum-subarray/)
    • [300. 最长递增子序列](https://leetcode-cn.com/problems/longest-increasing-subsequence/)
    • [1143. 最长公共子序列](https://leetcode-cn.com/problems/longest-common-subsequence/)
    • [718. 最长重复子数组](https://leetcode-cn.com/problems/maximum-length-of-repeated-subarray/)
  • 回溯
    • [51. N 皇后](https://leetcode-cn.com/problems/n-queens/)
    • [47. 全排列 II](https://leetcode-cn.com/problems/permutations-ii/)
    • [869. 重新排序得到 2 的幂](https://leetcode-cn.com/problems/reordered-power-of-2/)
  • 分治
    • [53. 最大子序和](https://leetcode-cn.com/problems/maximum-subarray/)
  • 链表(一定要多画图)
    • [203. 移除链表元素](https://leetcode-cn.com/problems/remove-linked-list-elements/)
    • [2. 两数相加](https://leetcode-cn.com/problems/add-two-numbers/)
    • [160. 相交链表](https://leetcode-cn.com/problems/intersection-of-two-linked-lists/)
    • **JZ52** **两个链表的第一个公共结点**
    • [86. 分隔链表](https://leetcode-cn.com/problems/partition-list/)
    • [234. 回文链表](https://leetcode-cn.com/problems/palindrome-linked-list/)
    • [138. 复制带随机指针的链表](https://leetcode-cn.com/problems/copy-list-with-random-pointer/)
    • **JZ25** **复杂链表的复制**
    • **JZ15** **反转链表**
    • **JZ3** **从尾到头打印链表**
    • **JZ16** **合并两个排序的链表**
    • **JZ14** **链表中倒数最后k个结点**
    • **JZ23** **链表中环的入口结点**
    • **JZ76** **删除链表中重复的结点**
    • [155. 最小栈](https://leetcode-cn.com/problems/min-stack/)
    • [654. 最大二叉树](https://leetcode-cn.com/problems/maximum-binary-tree/)
    • 654变种题(掌握)
    • [739. 每日温度](https://leetcode-cn.com/problems/daily-temperatures/)
    • [42. 接雨水](https://leetcode-cn.com/problems/trapping-rain-water/)
  • 队列(包括双端队列)
    • [239. 滑动窗口最大值](https://leetcode-cn.com/problems/sliding-window-maximum/)
  • 字符串
    • [面试题 01.09. 字符串轮转](https://leetcode-cn.com/problems/string-rotation-lcci/)
    • [572. 另一棵树的子树(二叉树的序列化)](https://leetcode-cn.com/problems/subtree-of-another-tree/)
    • [242. 有效的字母异位词](https://leetcode-cn.com/problems/valid-anagram/)
    • [151. 翻转字符串里的单词](https://leetcode-cn.com/problems/reverse-words-in-a-string/)
    • [58. 最后一个单词的长度](https://leetcode-cn.com/problems/length-of-last-word/)
    • [3. 无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/)
    • [1048. 最长字符串链](https://leetcode-cn.com/problems/longest-string-chain/)
    • [32. 最长有效括号](https://leetcode-cn.com/problems/longest-valid-parentheses/)
    • [79. 单词搜索](https://leetcode-cn.com/problems/word-search/)
    • [124. 二叉树中的最大路径和](https://leetcode-cn.com/problems/binary-tree-maximum-path-sum/)
  • 排序
    • [88. 合并两个有序数组](https://leetcode-cn.com/problems/merge-sorted-array/)
    • [75. 颜色分类](https://leetcode-cn.com/problems/sort-colors/)
    • [面试题 16.16. 部分排序](https://leetcode-cn.com/problems/sub-sort-lcci/)
    • [164. 最大间距-排序](https://leetcode-cn.com/problems/maximum-gap/)
    • [977. 有序数组的平方](https://leetcode-cn.com/problems/squares-of-a-sorted-array/)
  • 其他类型
    • **NC79** **丑数**
    • **NC93** **设计LRU缓存结构**
  • 十大排序算法
    • 1、冒泡排序
    • 2、选择排序
    • 3、堆排序
      • TopK问题(堆排序)
    • 4、插入排序
    • 5、归并排序
    • 6、快速排序
    • 7、希尔排序(对插入排序的优化)
    • 8、计数排序
    • 9、基数排序(个、十、百位)(基于计数排序)
    • 10、桶排序(自定义规则)
  • 二叉树遍历
  • KMP算法(字符串匹配)
  • B+树(MySQL)

动态规划

01背包

背包问题

416. 分割等和子集

给你一个 只包含正整数非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。
class Solution {public boolean canPartition(int[] nums) {int mid = 0;for(int i=0;i<nums.length;i++){mid = mid + nums[i];}if(mid % 2 == 1) return false;mid = mid / 2;int[] dp = new int[10001]; for(int i=0;i<nums.length;i++){  //0-1背包问题for(int j=mid;j>=nums[i];j--){dp[j] = Math.max(dp[j],dp[j-nums[i]]+nums[i]);}}if(dp[mid] == mid){return true;}return false;}
}

1049. 最后一块石头的重量 II

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。

输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。
class Solution {public int lastStoneWeightII(int[] stones) {int mid = 0,sum = 0;for(int i=0;i<stones.length;i++){sum+=stones[i];}mid = sum / 2;int[] dp = new int[15001];for(int i=0;i<stones.length;i++){for(int j = mid;j>=stones[i];j--){dp[j] = Math.max(dp[j],dp[j-stones[i]]+stones[i]);}}return sum-dp[mid]-dp[mid];}
}

494. 目标和

给你一个整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :

例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3
class Solution {public int findTargetSumWays(int[] nums, int target) {int sum = 0;for(int i=0;i<nums.length;i++){sum+=nums[i];}if(sum < target || sum < Math.abs(target)) return 0;if((sum+target)%2==1) return 0;int bagSize = (sum+target)/2;int[] dp = new int[bagSize+1];dp[0] = 1;for(int i=0;i<nums.length;i++){for(int j=bagSize;j>=nums[i];j--){dp[j] += dp[j-nums[i]];}}return dp[bagSize];}
}

这里用的回溯暴搜39. 组合总和

给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。

candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。

对于给定的输入,保证和为 target 的唯一组合数少于 150 个。

输入: candidates = [2,3,6,7], target = 7
输出: [[7],[2,2,3]]
class Solution {List<List<Integer>> list = new ArrayList<>();Deque<Integer> path = new ArrayDeque<>();public List<List<Integer>> combinationSum(int[] candidates, int target) {Arrays.sort(candidates);backtracking(candidates,target,0,0);return list;}private  void backtracking(int[] candidates,int target,int sum, int startIndex){if(sum > target){return;}if(sum == target){list.add(new ArrayList<>(path));return;}for(int i=startIndex;i<candidates.length;i++){if((sum+candidates[i]) > target){ //剪枝break;}sum+=candidates[i];path.addLast(candidates[i]);backtracking(candidates,target,sum,i);sum-=candidates[i];path.removeLast();}}
}
//参考:https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw

322. 零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
class Solution {public int coinChange(int[] coins, int amount) {//dp(41) = 凑到41分需要的最少硬币个数//dp(41-25) = 凑到41-25分需要的最少硬币个数//dp(41) = dp(41-25)+1if(amount < 1 || coins == null || coins.length == 0) return 0;int[] dp = new int[amount+1];for(int i=1;i<=amount;i++){int min = Integer.MAX_VALUE;for(int coin:coins){if(i<coin || dp[i-coin] < 0) continue;  //当dp[i-coin]已经是-1时则不要继续向下min = Math.min(min,dp[i-coin]);}if(min == Integer.MAX_VALUE){dp[i] = -1;}else{dp[i] = min+1;}}return dp[amount];}
}

53. 最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
class Solution {public int maxSubArray(int[] nums) {//dp[i] = 以nums[i]结尾的最大连续子序列和if(nums == null || nums.length == 0) return 0;int[] dp = new int[nums.length];dp[0] = nums[0];int max = dp[0];for(int i=1;i<nums.length;i++){if(dp[i-1] <= 0){dp[i] = nums[i];}else{dp[i] = dp[i-1] + nums[i];}max = Math.max(dp[i],max);}return max;}
}//法2
class Solution {public int maxSubArray(int[] nums) {//dp[i] = 以nums[i]结尾的最大连续子序列和if(nums == null || nums.length == 0) return 0;int dp = nums[0]; //滚动数组原理,dp[i]只依赖于dp[i-1]int max = dp;for(int i=1;i<nums.length;i++){if(dp <= 0){dp = nums[i];}else{dp = dp + nums[i];}max = Math.max(dp,max);}return max;}
}

300. 最长递增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
class Solution {public int lengthOfLIS(int[] nums) {//dp[i] = 以nums[i]结尾的最长上升子序列的长度,初始值为1if(nums == null || nums.length == 0) return 0;int[] dp = new int[nums.length];dp[0] = 1;int maxLength = 1;for(int i=1;i<nums.length;i++){dp[i] = 1; //如果一个数比前面的数都大,则它的最长上升子序列的长度为1for(int j=0;j<i;j++){if(nums[i] > nums[j]){dp[i] = Math.max(dp[i],dp[j]+1);}}maxLength = Math.max(maxLength,dp[i]);}return maxLength;}
}

1143. 最长公共子序列

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace" ,它的长度为 3 。
class Solution {public int longestCommonSubsequence(String text1, String text2) {//dp[i][j] = text1在i位置,text2在j位置两个字符串的最长子序列长度if(text1 == null || text2 == null) return 0;char[] char1 = text1.toCharArray();char[] char2 = text2.toCharArray();int[][] dp = new int[char1.length+1][char2.length+1];for(int i=1;i<=char1.length;i++){for(int j=1;j<=char2.length;j++){if(char1[i-1] == char2[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[char1.length][char2.length];}
}//法2---类似滚动数组
class Solution {public int longestCommonSubsequence(String text1, String text2) {if(text1 == null || text2 == null) return 0;char[] char1 = text1.toCharArray();char[] char2 = text2.toCharArray();char[] rowsNums = char1, colsNums = char2;if (char1.length < char2.length) {colsNums = char1;rowsNums = char2;}int[] dp = new int[colsNums.length + 1];for (int i = 1; i <= rowsNums.length; i++) {int cur = 0;for (int j = 1; j <= colsNums.length; j++) {int leftTop = cur;cur = dp[j];if (rowsNums[i - 1] == colsNums[j - 1]) {dp[j] = leftTop + 1;} else {dp[j] = Math.max(dp[j], dp[j - 1]);}}}return dp[colsNums.length];}
}

718. 最长重复子数组

给两个整数数组 AB ,返回两个数组中公共的、长度最长的子数组的长度。

输入:
A: [1,2,3,2,1]
B: [3,2,1,4,7]
输出:3
解释:
长度最长的公共子数组是 [3, 2, 1] 。
class Solution {public int findLength(int[] nums1, int[] nums2) {if(nums1.length == 0 || nums2.length == 0) return 0;int[][] dp = new int[nums1.length+1][nums2.length+1];int max = 0;for(int i=1;i<=nums1.length;i++){for(int j=1;j<=nums2.length;j++){if(nums1[i-1] != nums2[j-1]){continue;}dp[i][j] = dp[i-1][j-1]+1;max = Math.max(max,dp[i][j]);}}return max;}
}//法2
class Solution {public int findLength(int[] nums1, int[] nums2) {if(nums1.length == 0 || nums2.length == 0) return 0;int[] rowArr,colArr;if(nums1.length < nums2.length){rowArr=nums2;colArr=nums1;}else{rowArr=nums1;colArr=nums2;}int[] dp = new int[colArr.length+1];int max = 0;for(int i=1;i<=rowArr.length;i++){int cur=0;for(int j=1;j<=colArr.length;j++){int leftTop = cur; //保存左上角的数cur = dp[j];if(nums1[i-1] != nums2[j-1]){dp[j] = 0;}else{dp[j] = leftTop+1;max = Math.max(max,dp[j]);}}}return max;}
}

47

5

72

回溯

51. N 皇后

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。
class Solution {private int[] cols; //数组索引是行号,数组元素是列号private List<List<String>> ways = new ArrayList<>(); //一共有多少种摆法public List<List<String>> solveNQueens(int n) {if(n < 1) return ways;cols = new int[n];place(0);return ways;}//从第row行开始摆放皇后public void place(int row){if(row == cols.length){//下面添加摆法List<String> temp=new ArrayList<>();for (int i = 0; i < cols.length; i++) {StringBuffer s = new StringBuffer();for (int col = 0; col < cols.length; col++) {if (cols[i] == col) {s.append('Q');} else {s.append('.');}}temp.add(s.toString());}// ways++;ways.add(temp);// show();return;}for(int col=0;col<cols.length;col++){if(isValid(row,col)){cols[row] = col; // 在第row行第col列摆放皇后place(row+1);}}}//判断第row行第col列是否可以摆放皇后public boolean isValid(int row,int col){for(int i=0;i<row;i++){// 第col列已经有皇后if(cols[i] == col){return false;}// 第i行的皇后跟第row行第col列格子处在同一斜线上if(row-i == Math.abs(col-cols[i])){return false;}}return true;}// void show() {//    for (int row = 0; row < cols.length; row++) {//       for (int col = 0; col < cols.length; col++) {//           if (cols[row] == col) {//                 System.out.print("1 ");//             } else {//              System.out.print("0 ");//             }//         }//         System.out.println();//     }//     System.out.println("------------------------------");// }
}

47. 全排列 II

给定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列。

输入:nums = [1,1,2]
输出:
[[1,1,2],[1,2,1],[2,1,1]]
class Solution {boolean[] vis;public List<List<Integer>> permuteUnique(int[] nums) {List<List<Integer>> ans = new ArrayList<List<Integer>>();List<Integer> perm = new ArrayList<Integer>();vis = new boolean[nums.length];Arrays.sort(nums);backtrack(nums, ans, 0, perm);return ans;}public void backtrack(int[] nums,List<List<Integer>> ans,int index,List<Integer> perm){if(index == nums.length){ans.add(new ArrayList<Integer>(perm)); //new ArrayListreturn;}for (int i = 0; i < nums.length; i++) {if(vis[i] || (i>0 && nums[i]==nums[i-1] && !vis[i-1])){continue;}perm.add(nums[i]);vis[i] = true;backtrack(nums,ans,index+1,perm);vis[i] = false;perm.remove(index);}}
}

869. 重新排序得到 2 的幂

给定正整数 N ,我们按任何顺序(包括原始顺序)将数字重新排序,注意其前导数字不能为零。

如果我们可以通过上述方式得到 2 的幂,返回 true;否则,返回 false。

输入:1
输出:true
输入:24
输出:false
class Solution {boolean[] vis;public boolean reorderedPowerOf2(int n) {char[] nums = Integer.toString(n).toCharArray();Arrays.sort(nums);vis = new boolean[nums.length];return backtrack(nums,0,0);}public boolean backtrack(char[] nums,int index,int num){if(index == nums.length){return isPowerOfTwo(num);}for(int i=0;i<nums.length;i++){if((num == 0 && nums[i]=='0') || vis[i] || (i>0 && nums[i]==nums[i-1] && !vis[i-1])){continue;}vis[i] = true;if(backtrack(nums,index+1,num*10+nums[i]-'0')){return true;}vis[i]=false;}return false;}public boolean isPowerOfTwo(int n){return (n & (n-1)) == 0;}
}

分治

53. 最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
class Solution {public int maxSubArray(int[] nums) {if(nums == null || nums.length == 0) return 0;return maxSub(nums,0,nums.length);}public int maxSub(int[] nums,int begin,int end){if(end - begin < 2) return nums[begin];int mid = (begin + end) >> 1;int leftMax = Integer.MIN_VALUE;int leftSum = 0;for(int i=mid-1;i>=begin;i--){ //从中间到前面,因为中心分割线那里可能产生左+右的最大值leftSum += nums[i];leftMax = Math.max(leftMax,leftSum);}int rightMax = Integer.MIN_VALUE;int rightSum = 0;for(int i=mid;i<end;i++){rightSum += nums[i];rightMax = Math.max(rightMax,rightSum);}return Math.max(leftMax+rightMax,Math.max(maxSub(nums,begin,mid),maxSub(nums,mid,end)));}
}
//更简单的做法--类似贪心算法
class Solution {public int maxSubArray(int[] nums) {int maxNum = nums[0];int addNum = 0;for(int i =0 ; i < nums.length; i ++){addNum += nums[i];maxNum = Math.max(maxNum,addNum);if(addNum < 0){addNum = 0;}}return maxNum;}
}

链表(一定要多画图)

虚拟头节点、快慢指针、多指针…

链表节点的插入、删除;反转一个链表;快慢指针求中心节点;计算链表的长度…

203. 移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
class Solution {public ListNode removeElements(ListNode head, int val) {if(head == null) return null;ListNode dummyHead = new ListNode(0);ListNode cur = dummyHead;while(head != null){ //遍历整个链表if(head.val != val){cur.next = head;cur = head;}head = head.next;}cur.next = null;return dummyHead.next;}
}

2. 两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode res = new ListNode(0);ListNode cur = res;int temp = 0;while(l1 != null || l2 != null){int v1 = (l1==null) ? 0:l1.val;int v2 = (l2==null) ? 0:l2.val;int sum = v1 + v2 + temp;temp = sum / 10;ListNode node = new ListNode(sum%10);cur.next = node;cur = node;l1 = (l1==null)?null:l1.next;l2 = (l2==null)?null:l2.next;}if(temp != 0){ListNode head = new ListNode(temp);cur.next = head;}return res.next;}
}

160. 相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if(headA == null || headB == null) return null;ListNode curA = headA,curB = headB;while(curA != curB){curA = (curA!=null)?curA.next:headB;curB = (curB!=null)?curB.next:headA;}return curA;}
}//解法2:HashSet
public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {Set<ListNode> visited = new HashSet<ListNode>();ListNode temp = headA;while (temp != null) {visited.add(temp);temp = temp.next;}temp = headB;while (temp != null) {if (visited.contains(temp)) {return temp;}temp = temp.next;}return null;}
}

JZ52 两个链表的第一个公共结点

输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。

看下面的链表例子:
0-1-2-3-4-5-null
a-b-4-5-null
代码的ifelse语句,对于某个指针p1来说,其实就是让它跑了连接好的的链表,长度就变成一样了。
如果有公共结点,那么指针一起走到末尾的部分,也就一定会重叠。看看下面指针的路径吧。
p1: 0-1-2-3-4-5-null(此时遇到ifelse)-a-b-4-5-null
p2:a-b-4-5-null(此时遇到ifelse)0-1-2-3-4-5-null
因此,两个指针所要遍历的链表就长度一样了!
如果两个链表存在公共结点,那么p1就是该结点,如果不存在那么p1将会是null。

public class Solution {public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {if(pHead1 == null || pHead2 == null)return null;ListNode p1 = pHead1;ListNode p2 = pHead2;while(p1!=p2){p1 = p1.next;p2 = p2.next;if(p1!=p2){if(p1==null) p1=pHead2;if(p2 == null)p2 = pHead1;}}return p1;}
}

86. 分隔链表

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你应当 保留 两个分区中每个节点的初始相对位置。

输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]
class Solution {public ListNode partition(ListNode head, int x) {if(head == null) return head;ListNode leftHead = new ListNode(0);ListNode leftTail = leftHead;ListNode rightHead = new ListNode(0);ListNode rightTail = rightHead;while(head!=null){if(head.val < x){leftTail.next = head;leftTail = head;}else{rightTail.next = head;rightTail = head;}head = head.next;}leftTail.next = rightHead.next;rightTail.next = null;return leftHead.next;}
}

234. 回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false

输入:head = [1,2,2,1]
输出:true
class Solution {public boolean isPalindrome(ListNode head) {if(head == null || head.next == null) return true;if(head.next.next == null) return head.val == head.next.val;//找到中间的节点ListNode mid = middleNode(head);//翻转右半部分(中间节点的右边部分)ListNode rHead = reverseList(mid.next);ListNode lHead = head;//从lHead、rHead出发,判断是否为回文链表while(rHead != null){if(rHead.val != lHead.val) return false;rHead = rHead.next;lHead = lHead.next;}return true;}public ListNode middleNode(ListNode head){if(head == null || head.next == null) return head;ListNode fast = head;ListNode slow = head;while(fast.next!=null && fast.next.next!=null){fast = fast.next.next;slow = slow.next;}return slow;}public ListNode reverseList(ListNode head){if(head == null || head.next == null) return head;ListNode cur = head;ListNode newHead = null;while(cur!=null){ListNode temp = cur.next;cur.next = newHead;newHead = cur;cur = temp;}return newHead;}
}//解法2:更简单
class Solution {public boolean isPalindrome(ListNode head) {List<Integer> vals = new ArrayList<Integer>();// 将链表的值复制到数组中ListNode currentNode = head;while (currentNode != null) {vals.add(currentNode.val);currentNode = currentNode.next;}// 使用双指针判断是否回文int front = 0;int back = vals.size() - 1;while (front < back) {if (!vals.get(front).equals(vals.get(back))) {return false;}front++;back--;}return true;}
}

138. 复制带随机指针的链表

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
你的代码 只 接受原链表的头节点 head 作为传入参数。

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
class Solution {public Node copyRandomList(Node head) {if(head == null) return head;Map<Node,Node> map = new HashMap<>(); //将每个节点用HashMap存储起来Node p = head;while(p!=null){Node node = new Node(p.val);map.put(p,node);p = p.next;}p=head;while(p!=null){Node newNode = map.get(p);if(newNode.next == null){newNode.next = map.get(p.next);}if(newNode.random == null){newNode.random = map.get(p.random);}p = p.next;}return map.get(head);}
}

JZ25 复杂链表的复制

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。 下图是一个含有5个结点的复杂链表。图中实线箭头表示next指针,虚线箭头表示random指针。为简单起见,指向null的指针没有画出。

输入:
{1,2,3,4,5,3,5,#,2,#}
返回值:
{1,2,3,4,5,3,5,#,2,#}
/*
public class RandomListNode {int label;RandomListNode next = null;RandomListNode random = null;RandomListNode(int label) {this.label = label;}
}
*/
import java.util.Map;
import java.util.HashMap;
public class Solution {public RandomListNode Clone(RandomListNode pHead) {if(pHead == null) return null;Map<RandomListNode,RandomListNode> map = new HashMap<>();RandomListNode newHead = null;RandomListNode p = pHead;RandomListNode q = null;while(p!=null){if(newHead == null){newHead = new RandomListNode(pHead.label);q = newHead;map.put(pHead,newHead);}else{if(p.next != null && map.containsKey(p.next)){q.next = map.get(p.next);}else{if(p.next != null){RandomListNode temp = new RandomListNode(p.next.label);map.put(p.next,temp);q.next = temp;}}if(p.random != null && map.containsKey(p.random)){q.random = map.get(p.random);}else{if(p.random != null){RandomListNode temp = new RandomListNode(p.random.label);map.put(p.random,temp);q.random = temp;}}p = p.next;q = q.next;}}return newHead;}
}

JZ15 反转链表

/*
public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}*/
public class Solution {public ListNode ReverseList(ListNode head) {//         if(head == null || head.next == null) return head; //递归//         ListNode newHead = ReverseList(head.next);
//         head.next.next = head;
//         head.next = null;
//         return newHead;ListNode cur = head; //迭代ListNode pre = null;while(cur != null){ListNode temp = cur.next;cur.next = pre;pre = cur;cur = temp;}return pre;}
}

JZ3 从尾到头打印链表

import java.util.ArrayList;
import java.util.Collections;
public class Solution {public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {ArrayList<Integer> list = new ArrayList<>();ListNode cur = listNode;while(cur != null){list.add(cur.val);cur = cur.next;}Collections.reverse(list);return list;
//         ArrayList<Integer> list = new ArrayList<>();
//         ListNode tmp = listNode;
//         while(tmp!=null){//             list.add(0,tmp.val);
//             tmp = tmp.next;
//         }
//         return list;}
}

JZ16 合并两个排序的链表

输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。

数据范围: 0 \le n \le 10000≤n≤1000,-1000 \le 节点值 \le 1000−1000≤节点值≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

输入:
{1,3,5},{2,4,6}
返回值:
{1,2,3,4,5,6}
public class Solution {public ListNode Merge(ListNode list1,ListNode list2) {if(list1 == null){return list2;}if(list2 == null){return list1;}ListNode h = new ListNode(-1);ListNode res = h;while(list1 != null){if(list2 != null){if(list1.val <= list2.val){res.next = list1;list1 = list1.next;}else{res.next = list2;list2 = list2.next;}}else{res.next = list1;list1 = list1.next;}res = res.next;}if(list2 != null){ //链表,直接接在后面就好res.next = list2;}return h.next;
//         ListNode h = new ListNode(-1);
//         ListNode cur = h;
//         while(list1 != null && list2 !=null){//             if(list1.val<=list2.val){//                 cur.next = list1;
//                 list1 = list1.next;
//             }else{//                 cur.next = list2;
//                 list2 = list2.next;
//             }
//             cur = cur.next;
//         }
//         if(list1!=null) cur.next = list1;
//         if(list2!=null) cur.next = list2;
//         return h.next;}
}

JZ14 链表中倒数最后k个结点

输入一个长度为 n 的链表,设链表中的元素的值为 ai ,输出一个链表,该输出链表包含原链表中从倒数第 k 个结点至尾节点的全部节点。

如果该链表长度小于k,请返回一个长度为 0 的链表。

输入:
{1,2,3,4,5},3
返回值:
{3,4,5}
public class Solution {public ListNode FindKthToTail (ListNode pHead, int k) {ListNode first = pHead; //双指针,一开始没想到,真是绝绝子for(int i=0;i<k;i++){if(first == null) return null;first = first.next;}ListNode secend = pHead;while(first != null){first = first.next;secend = secend.next;}return secend; }
}
public class Solution {public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {if(pHead1 == null || pHead2 == null)return null;ListNode p1 = pHead1;ListNode p2 = pHead2;while(p1!=p2){p1 = p1.next;p2 = p2.next;if(p1!=p2){if(p1==null) p1=pHead2;if(p2 == null)p2 = pHead1;}}return p1;}
}

JZ23 链表中环的入口结点

  1. 这题我们可以采用双指针解法,一快一慢指针。快指针每次跑两个element,慢指针每次跑一个。如果存在一个圈,总有一天,快指针是能追上慢指针的。
  2. 如下图所示,我们先找到快慢指针相遇的点,p。我们再假设,环的入口在点q,从头节点到点q距离为A,q p两点间距离为B,p q两点间距离为C。
  3. 因为快指针是慢指针的两倍速,且他们在p点相遇,则我们可以得到等式 2(A+B) = A+B+C+B. (感谢评论区大佬们的改正,此处应为:*如果环前面的链表很长,而环短,那么快指针进入环以后可能转了好几圈(假设为n圈)才和慢指针相遇。但无论如何,慢指针在进入环的第一圈的时候就会和快的相遇。等式应更正为 2(A+B)= A+ nB + (n-1)C)*
  4. 由3的等式,我们可得,C = A。
  5. 这时,因为我们的slow指针已经在p,我们可以新建一个另外的指针,slow2,让他从头节点开始走,每次只走下一个,原slow指针继续保持原来的走法,和slow2同样,每次只走下一个。
  6. 我们期待着slow2和原slow指针的相遇,因为我们知道A=C,所以当他们相遇的点,一定是q了。
  7. 我们返回slow2或者slow任意一个节点即可,因为此刻他们指向的是同一个节点,即环的起始点,q。

public class Solution {public ListNode EntryNodeOfLoop(ListNode pHead) {if(pHead == null || pHead.next==null) return null;ListNode fast = pHead;ListNode slow = pHead;while(fast!= null && fast.next!=null){slow = slow.next;fast = fast.next.next;if(fast == slow){ListNode p = pHead;while(p!=slow){p = p.next;slow = slow.next;}return p;}}return null;}
}

JZ76 删除链表中重复的结点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表 1->2->3->3->4->4->5 处理后为 1->2->5

数据范围:链表长度满足 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存失败,源站可能有防盗链机制,建议将图片保存下来直接上传下上传(imnrAfGkmopJ-1669553577479)(https://www.nowcoder.com/equation?tex=1%20%5Cle%20n%20%5Cle%201000%20%5C)( https://www.nowcoder.com/equation?tex=1%20%5Cle%20n%20%5Cle%201000%20%5C)],链表中的值满足 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z7NU3ASm-1635769557480)(https://www.nowcoder.com/equation?tex=1%20%5Cle%20val%20%5Cle%201000%20%5C)]

进阶:空间复杂度 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q7IVFvmT-1635769557480)(https://www.nowcoder.com/equation?tex=O(n)]%5C) ,时间复杂度 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jzUiaNE3-1635769557481)(https://www.nowcoder.com/equation?tex=O(n)]%20%5C)

输入:
{1,2,3,3,4,4,5}
返回值:
{1,2,5}
//暴力解法
import java.util.*;
public class Solution {public ListNode deleteDuplication(ListNode pHead) {Map<Integer,ListNode> map = new HashMap<>();Set<Integer> set = new HashSet<>();ListNode p = pHead;while(p!=null){if(map.containsKey(p.val)){set.add(p.val);}map.put(p.val,p);p=p.next;}ListNode newHead = new ListNode(0);ListNode cur = newHead;p=pHead;Iterator<Integer> it = set.iterator();Integer[] arr = new Integer[set.size()];int i=0;while(it.hasNext()){arr[i] = it.next();
//             System.out.println(arr[i]);i++;}while(p!=null){for(int j=0;j<arr.length;j++){while(p!=null && p.val == arr[j]){p=p.next;}}if(p!=null){//                 System.out.println(p.val);cur.next = p;cur=cur.next;p=p.next;}}cur.next=null;return newHead.next==null?null:newHead.next;}
}
//双指针解法
import java.util.*;
public class Solution {public ListNode deleteDuplication(ListNode pHead) {ListNode dummyHead = new ListNode(0);ListNode cur = dummyHead;while(pHead != null){// 进入循环时,确保了 pHead 不会与上一节点相同if(pHead.next == null || pHead.next.val != pHead.val){cur.next = pHead;cur = pHead;}// 如果 pHead 与下一节点相同,跳过相同节点(到达「连续相同一段」的最后一位)while(pHead.next!=null && pHead.val==pHead.next.val){pHead = pHead.next;}pHead = pHead.next;}cur.next=null;return dummyHead.next;}
}

155. 最小栈

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top() —— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。

输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]输出:
[null,null,null,null,-3,null,0,-2]解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.
class MinStack {private Stack<Integer> stack;private Stack<Integer> minstack; //解法1:用双栈来存储public MinStack() {stack = new Stack<>();minstack = new Stack<>();}    public void push(int val) {stack.push(val);if(minstack.isEmpty()){minstack.push(val);}else{minstack.push(Math.min(val,minstack.peek()));}}    public void pop() {stack.pop();minstack.pop();}    public int top() {return stack.peek();}    public int getMin() {return minstack.peek();}
}//解法2:使用节点存储(效率高)
class MinStack {Node head;public MinStack() {head = new Node(0,Integer.MAX_VALUE,null);}public void push(int val) {head = new Node(val,Math.min(val,head.min),head);}public void pop() {head = head.next;}public int top() {return head.val;}public int getMin() {return head.min;}private static class Node{ //新建一个节点类public int val;public int min;public Node next;public Node(int val,int min,Node next){this.val = val;this.min = min;this.next = next;}}
}

654. 最大二叉树

给定一个不含重复元素的整数数组 nums 。一个以此数组直接递归构建的 最大二叉树 定义如下:

二叉树的根是数组 nums 中的最大元素。
左子树是通过数组中 最大值左边部分 递归构造出的最大二叉树。
右子树是通过数组中 最大值右边部分 递归构造出的最大二叉树。
返回有给定数组 nums 构建的 最大二叉树 。

输入:nums = [3,2,1,6,0,5]
输出:[6,3,5,null,2,0,null,null,1]
解释:递归调用如下所示:
- [3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5] 。- [3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1] 。- 空数组,无子节点。- [2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1] 。- 空数组,无子节点。- 只有一个元素,所以子节点是一个值为 1 的节点。- [0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 [] 。- 只有一个元素,所以子节点是一个值为 0 的节点。- 空数组,无子节点。
//二叉树很多问题都可以通过递归来解决
class Solution {public TreeNode constructMaximumBinaryTree(int[] nums) {if(nums.length == 1) return new TreeNode(nums[0],null,null);return findNode(nums,0,nums.length);}public TreeNode findNode(int[] nums,int li,int ri){if(li == ri) return null;int maxIndex = li;for(int i=li+1;i<ri;i++){if(nums[i] > nums[maxIndex]) maxIndex = i;}TreeNode head = new TreeNode(nums[maxIndex]);head.left = findNode(nums,li,maxIndex);head.right = findNode(nums,maxIndex+1,ri);return head;}
}

654变种题(掌握)

//题目变种:返回一个数组,数组中存放每个节点的父节点的索引值(掌握)===(用栈)
public int[] parentIndexes(int[] nums){//1、扫描一遍所有的元素//2、保持栈从栈底到栈顶是单调递减的Stack<Integer> stack = new Stack<>();int[] lis = new int[nums.length];int[] ris = new int[nums.length];//初始化for(int i=0;i<nums.length;i++){ris[i]=-1;lis[i]=-1;}for(int i=0;i<nums.length;i++){while(!stack.isEmpty() && nums[i] > nums[stack.peek()]){ris[stack.pop()] = i;}lis[i] = stack.isEmpty()?-1:stack.peek();stack.push(i);}//求父节点数组int[] pis = new int[nums.length];for(int i=0;i<nums.length;i++){if(lis[i]==-1 && ris[i] == -1){//i位置的是根节点pis[i] = -1;continue;}if(lis[i] = -1){pis[i] = ris[i];}else if(ris[i] = -1){pis[i] = lis[i];}else if(nums[lis[i]] < nums[ris[i]]){pis[i] = lis[i];}else{pis[i] = ris[i];}}return pis;
}

739. 每日温度

请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。

输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
//跟上面变种题解法相似
class Solution {public int[] dailyTemperatures(int[] temperatures) {if(temperatures.length == 1) return new int[] {0};int[] ris = new int[temperatures.length];Stack<Integer> stack = new Stack<>();for(int i=0;i<temperatures.length;i++){while(!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]){int t = stack.pop();ris[t] = i-t;}stack.push(i);}return ris;}
}

//倒推法:类似与动态规划的思想
class Solution {public int[] dailyTemperatures(int[] temperatures) {if(temperatures.length == 0) return null;int[] res = new int[temperatures.length];for(int i=temperatures.length-2;i>=0;i--){int j=i+1;while(true){if(temperatures[i] < temperatures[j]){res[i] = j-i;break;}else if(res[j] == 0){res[i] = 0;break;}//当temperatures[i] == temperatures[j]的时候j = j + res[j];}}return res;}
}

42. 接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
//参见官方:https://leetcode-cn.com/problems/trapping-rain-water/solution/jie-yu-shui-by-leetcode-solution-tuvc/
//解法1:单调递减栈(采用上方所写方法)
class Solution {public int trap(int[] height) {int n = height.length;if(n<2) return 0;int res = 0;Stack<Integer> stack = new Stack<>(); //单调递减栈for(int i=0;i<n;i++){while(!stack.isEmpty() && height[i] > height[stack.peek()]){int top = stack.pop();if(stack.isEmpty()) break;int left = stack.peek();int curwidth = i-left-1; //坑宽度int curheight = Math.min(height[left],height[i]) - height[top]; //坑高度res += curwidth * curheight;}stack.push(i);}return res;}
}//解法2:双指针(最好)
class Solution {public int trap(int[] height) {int n = height.length;if(n<2) return 0;int res = 0;int left = 0,right = n-1;int leftMax = 0,rightMax = 0;while(left < right){leftMax = Math.max(leftMax,height[left]);rightMax = Math.max(rightMax,height[right]);if(height[left] < height[right]){ //如果左边小于右边res+=leftMax-height[left];left++;}else{res+=rightMax-height[right];right--;}}return res;}
}//解法3:动态规划
class Solution {public int trap(int[] height) {int n = height.length;if (n == 0) {return 0;}int[] leftMax = new int[n];leftMax[0] = height[0];for (int i = 1; i < n; ++i) {leftMax[i] = Math.max(leftMax[i - 1], height[i]);}int[] rightMax = new int[n];rightMax[n - 1] = height[n - 1];for (int i = n - 2; i >= 0; --i) {rightMax[i] = Math.max(rightMax[i + 1], height[i]);}int ans = 0;for (int i = 0; i < n; ++i) {ans += Math.min(leftMax[i], rightMax[i]) - height[i];}return ans;}
}

队列(包括双端队列)

239. 滑动窗口最大值

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

返回滑动窗口中的最大值。

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       31 [3  -1  -3] 5  3  6  7       31  3 [-1  -3  5] 3  6  7       51  3  -1 [-3  5  3] 6  7       51  3  -1  -3 [5  3  6] 7       61  3  -1  -3  5 [3  6  7]      7
class Solution {public int[] maxSlidingWindow(int[] nums, int k) {if(k==1) return nums;int[] maxes = new int[nums.length-k+1];Deque<Integer> deque = new LinkedList<>();//构建双端队列for(int i=0;i<nums.length;i++){//只要nums[队尾] <= nums[i],就删除队尾while(!deque.isEmpty() && nums[deque.peekLast()] <= nums[i]){deque.pollLast();}//将i加到队尾deque.offerLast(i);//检查窗口的索引是否合法int w = i-k+1;if(w<0) continue;//检查队头的合法性if(deque.peekFirst() < w){//队头不合法(失效,不在滑动窗口索引范围内)deque.pollFirst();}//设置窗口的最大值maxes[w] = nums[deque.peekFirst()];}return maxes;}
}

//暴力解法
class Solution {public int[] maxSlidingWindow(int[] nums, int k) {if(k==1) return nums;int[] maxes = new int[nums.length-k+1];int maxIndex = 0;//先求出前k个元素的最大值索引for(int i=1;i<k;i++){if(nums[i] > nums[maxIndex]) maxIndex = i;}//li是滑动窗口的最左索引for(int li=0;li<maxes.length;li++){//ri是滑动窗口的最右索引int ri = li+k-1;if(maxIndex < li){ //最大值的索引不在滑动窗口的合理范围内maxIndex = li;for(int i=li+1;i<=ri;i++){if(nums[i] >= nums[maxIndex]) maxIndex = i;}}else if(nums[ri] >= nums[maxIndex]){ //最大值的索引在滑动窗口的合理范围内maxIndex = ri;}maxes[li]=nums[maxIndex];}return maxes;}
}

字符串

面试题 01.09. 字符串轮转

字符串轮转。给定两个字符串s1s2,请编写代码检查s2是否为s1旋转而成(比如,waterbottleerbottlewat旋转后的字符串)。

 输入:s1 = "waterbottle", s2 = "erbottlewat"输出:True
class Solution {public boolean isFlipedString(String s1, String s2) {if(s1 == null || s2 == null) return false;if(s1.length() != s2.length()) return false;return (s1+s1).contains(s2);}
}

572. 另一棵树的子树(二叉树的序列化)

给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。

二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。

输入:root = [3,4,5,1,2], subRoot = [4,1,2]
输出:true
class Solution {public boolean isSubtree(TreeNode root, TreeNode subRoot) {if(root == null || subRoot == null) return false;return postSerialize(root).contains(postSerialize(subRoot));}public String postSerialize(TreeNode node){ //序列化// if(node == null) return null;StringBuilder s = new StringBuilder();postSerializeRealize(node,s);return s.toString();}public void postSerializeRealize(TreeNode node,StringBuilder str){if(node.left == null){str.append("#!");}else{str.append(postSerialize(node.left));}if(node.right == null){str.append("#!");}else{str.append(postSerialize(node.right));}str.append(node.val).append("!");}
}

242. 有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

输入: s = "anagram", t = "nagaram"
输出: true
class Solution {public boolean isAnagram(String s, String t) {int n = s.length();if(n != t.length()) return false;int[] count = new int[26];char[] sarr = s.toCharArray();char[] tarr = t.toCharArray();for(int i=0;i<n;i++){count[sarr[i]-'a']++;}for(int i=0;i<n;i++){if(--count[tarr[i]-'a'] < 0) return false;}return true;}
}

151. 翻转字符串里的单词

给你一个字符串 s ,逐个翻转字符串中的所有 单词 。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。

说明:

输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
翻转后单词间应当仅用一个空格分隔。
翻转后的字符串中不应包含额外的空格。

输入:s = "the sky is blue"
输出:"blue is sky the"
class Solution {public String reverseWords(String s) {if(s.length() == 1) return s;char[] arr = s.toCharArray();//首先消除多余的空格int len=0; //字符串最终的有效长度int cur=0; //当前用来存放字符的位置boolean prespace = true; //记录前一个字符是否为空格字符for(int i=0;i<arr.length;i++){if(arr[i] != ' '){ //char[i]是非空格字符arr[cur++] = arr[i];prespace = false;}else if(prespace == false){ //char[i]是空格字符,char[i-1]是非空格字符arr[cur++] = ' ';prespace = true;}}len = prespace?(cur-1):cur;//对整个字符串进行逆序reverse(arr,0,len);//对每一个单词进行逆序int space = -1; //前一个空格字符的位置(有-1位置有个假想的哨兵,就是一个假想的空格字符)for(int i=0;i<len;i++){if(arr[i] != ' ') continue;reverse(arr,space+1,i);space = i;}//翻转最后一个单词reverse(arr,space+1,len);return new String(arr,0,len);}public void reverse(char[] arr,int li,int ri){ //左闭右开ri--;while(li < ri){char temp = arr[li];arr[li] = arr[ri];arr[ri] = temp;li++;ri--;}}
}

58. 最后一个单词的长度

给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中最后一个单词的长度。

单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。

输入:s = "   fly me   to   the moon  "
输出:4
class Solution {public int lengthOfLastWord(String s) {if(s.length() == 1) return 1;char[] arr = s.toCharArray();int si = arr.length;int ei = arr.length;boolean space = true;for(int i=arr.length-1;i>=0;i--){if(arr[i] == ' '){if(!space){break;}si--;ei--;}else{si--;space = false;}}return ei-si;}
}//解法2
class Solution {public int lengthOfLastWord(String s) {int len;int lastSpace = s.length() - 1;while(lastSpace >= 0 && s.charAt(lastSpace) == ' ') lastSpace--;int start = lastSpace;while(start >= 0 && s.charAt(start) != ' ')start--;len = lastSpace - start;return len;}
}

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

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oCh1J09X-1635946803818)(.\pics\image-20211103193012546.png)]

输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
class Solution {public int lengthOfLongestSubstring(String s) {if(s==null || s.length()==0) return 0;if(s.length()==1) return 1;char[] arr = s.toCharArray();//用来保存每一个字符上一次出现的位置// Map<Character,Integer> prevIndexes = new HashMap<>();// prevIndexes.put(arr[0],0);int[] prevIndexes = new int[128]; //改进:用数组来存储for(int i=0;i<prevIndexes.length;i++){prevIndexes[i]=-1;}prevIndexes[arr[0]] = 0;//以i-1位置字符结尾的最长不重复字符串的开始索引(最左索引)int li=0;int max=1;for(int i=1;i<arr.length;i++){//i位置字符上一次出现的位置// Integer pi = prevIndexes.get(arr[i]);// if(pi!=null && li<=pi){//     li = pi+1;// }int pi = prevIndexes[arr[i]];if(li<=pi){li = pi+1;}//存储这个字符出现的位置// prevIndexes.put(arr[i],i);prevIndexes[arr[i]] = i;//求出最长不重复子串的长度max = Math.max(max,i-li+1);}return max;}
}

1048. 最长字符串链

给出一个单词列表,其中每个单词都由小写英文字母组成。

如果我们可以在 word1 的任何地方添加一个字母使其变成 word2,那么我们认为 word1 是 word2 的前身。例如,“abc” 是 “abac” 的前身。

词链是单词 [word_1, word_2, …, word_k] 组成的序列,k >= 1,其中 word_1 是 word_2 的前身,word_2 是 word_3 的前身,依此类推。

从给定单词列表 words 中选择单词组成词链,返回词链的最长可能长度。

输入:["a","b","ba","bca","bda","bdca"]
输出:4
解释:最长单词链之一为 "a","ba","bda","bdca"。
//动态规划
class Solution {public int longestStrChain(String[] words) {Arrays.sort(words, Comparator.comparingInt(String::length));int n=words.length;if(n == 1) return 1;int[] dp = new int[n];int res=0;for(int i=0;i<n;i++){String a=words[i];for(int j=i+1;j<n;j++){if(isPredecessor(a,words[j])){dp[j] = Math.max(dp[j],dp[i]+1);res = Math.max(res,dp[j]);}}}return res+1;}/*** 判断a是否是b的前身 是返回true 如 "bda" 是"bdca"的前身*/private boolean isPredecessor(String a, String b) {int m=a.length(),n=b.length();if(m+1 != n) return false;int i=0,j=0;while(i<m && j<n){if(a.charAt(i) == b.charAt(j)) i++;j++;}return i==m;}
}

32. 最长有效括号

给你一个只包含 '('')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
//666,没想到的!!!
class Solution {public int longestValidParentheses(String s) {int n = s.length();if(n<2) return 0;char[] arr = s.toCharArray();Stack<Integer> stack = new Stack<>(); //放索引stack.push(-1);int max=0;for(int i=0;i<n;i++){if(arr[i] == '('){stack.push(i);}else{stack.pop();if(stack.isEmpty()){stack.push(i);}else{max = Math.max(max,i-stack.peek());}}}return max;}
}

1143(见上)

79. 单词搜索

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
//回溯
class Solution {public boolean exist(char[][] board, String word) {int h = board.length,w = board[0].length;boolean[][] visited = new boolean[h][w];for(int i=0;i<h;i++){for(int j=0;j<w;j++){boolean flag = check(board,visited,i,j,word,0);if(flag){return true;}}}return false;}public boolean check(char[][] board, boolean[][] visited, int i, int j, String s, int k){if(board[i][j] != s.charAt(k)){return false;}else if(k == s.length()-1){return true;}visited[i][j] = true;int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};boolean result = false;for(int[] dir:directions){int newi = i+dir[0],newj = j+dir[1];if(newi>=0 && newi < board.length && newj >= 0 && newj < board[0].length){if(!visited[newi][newj]){boolean flag = check(board,visited,newi,newj,s,k+1);if(flag){result = true;break;}}}}visited[i][j] = false;return result;}
}

739(见上)

124. 二叉树中的最大路径和

路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和 。

    a/ \b   c

1.b + a + c。
2.b + a + a 的父结点。
3.a + c + a 的父结点。
其中情况 1,表示如果不联络父结点的情况,或本身是根结点的情况。
这种情况是没法递归的,但是结果有可能是全局最大路径和。
情况 2 和 3,递归时计算 a+b 和 a+c,选择一个更优的方案返回,也就是上面说的递归后的最优解啦。

另外结点有可能是负值,最大和肯定就要想办法舍弃负值(max(0, x))(max(0,x))。
但是上面 3 种情况,无论哪种,a 作为联络点,都不能够舍弃。

输入:root = [1,2,3]
输出:6
解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6
class Solution {int ans = Integer.MIN_VALUE;public int maxPathSum(TreeNode root) {calSum(root);return ans;}public int calSum(TreeNode root){if (root == null) return 0;int left = Math.max(0, calSum(root.left));int right = Math.max(0, calSum(root.right));ans = Math.max(ans, left + right + root.val);return Math.max(left,right)+root.val;}
}

排序

88. 合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {int li=0,le=m,ri=0,re=n;int ai=0;int[] leftArr = new int[m];for(int i=li;i<le;i++){leftArr[i] = nums1[i];}for(int i=ri;i<re;i++){nums1[m+i] = nums2[i];}while(li < le){if(ri < re && nums2[ri] < leftArr[li]){nums1[ai++] = nums2[ri++];}else{nums1[ai++] = leftArr[li++];}}}
}//法2
class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {for (int i = 0; i != n; ++i) {nums1[m + i] = nums2[i];}Arrays.sort(nums1);}
}
//法3
class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {//类似于归并排序int i1 = m-1; //指向nums1(有数)的末尾int i2 = n-1; //指向nums2的末尾int cur = nums1.length-1; //指向nums1的末尾while(i2 >= 0){if(i1 >= 0 && nums1[i1] > nums2[i2]){nums1[cur--] = nums1[i1--];}else{ //nums1[i1] <= nums2[i2]nums1[cur--] = nums2[i2--];}}}
}

75. 颜色分类

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
class Solution {public void sortColors(int[] nums) {// Arrays.sort(nums);//采用三指针方法,头和尾分别放一指针,遍历用一指针//你能想出一个仅使用常数空间的一趟扫描算法吗?int first = 0;int last = nums.length-1;int cur = 0;while(cur <= last){if(nums[cur] == 0){ //遇到0,则跟前面first交换,因为前面已经遍历过了,要么是0要么是1,所以指针直接往下走swap(nums,cur++,first++);}else if(nums[cur] == 2){ //遇到2,则跟后面进行交换,同时还要继续比较该交换过来的值swap(nums,cur,last--);}else{ //遇到1则继续向下走cur++;}}}public void swap(int[] nums,int a,int b){int temp = nums[a];nums[a] = nums[b];nums[b] = temp;}
}

面试题 16.16. 部分排序

给定一个整数数组,编写一个函数,找出索引m和n,只要将索引区间[m,n]的元素排好序,整个数组就是有序的。注意:n-m尽量最小,也就是说,找出符合条件的最短序列。函数返回值为[m,n],若不存在这样的m和n(例如整个数组是有序的),请返回[-1,-1]。

输入: [1,2,4,7,10,11,7,12,6,7,16,18,19]
输出: [3,9]
class Solution {public int[] subSort(int[] array) {if(array.length == 0) return new int[] {-1,-1};//从左扫描到右寻找逆序对(逐渐变大)int max = array[0];//用来记录最右的那个逆序对的位置int r = -1;for(int i=1;i<=array.length-1;i++){if(array[i] >= max){max = array[i];}else{r = i;}}//说明没有逆序对,则直接returnif(r == -1) return new int[] {-1,-1};//从右扫描到左寻找逆序对(逐渐变小)int min = array[array.length-1];//用来记录最左的那个逆序对的位置int l = -1;for(int i=array.length-2;i>=0;i--){if(array[i] <= min){min = array[i];}else{l = i;}}return new int[] {l,r};}
}

164. 最大间距-排序

给定一个无序的数组,找出数组在排序之后,相邻元素之间最大的差值。

如果数组元素个数小于 2,则返回 0。

输入: [3,6,9,1]
输出: 3
解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。
class Solution {public int maximumGap(int[] nums) {if(nums.length < 2) return 0;Arrays.sort(nums);int max = nums[1] - nums[0];for(int i=2;i<nums.length;i++){int temp = nums[i] - nums[i-1];if(temp > max){max = temp;}}return max;}
}

977. 有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
class Solution {public int[] sortedSquares(int[] nums) {//还是使用双指针的思想,一个指向头,一个指向尾,并用一个新数组来存放比较后的平方值int head = 0;int tail = nums.length-1;int[] ans = new int[nums.length];for(int i=nums.length-1;i>=0;i--){if(nums[head]*nums[head] > nums[tail]*nums[tail]){ans[i] = nums[head]*nums[head];head++;}else{ans[i] = nums[tail]*nums[tail];tail--;}}return ans;}
}

其他类型

NC79 丑数

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第 n个丑数。

输入:
7
返回值:
8
import java.util.List;
import java.util.ArrayList;public class Solution {public int GetUglyNumber_Solution(int index) {if(index < 7) return index;int p2=0,p3=0,p5=0;List<Integer> list = new ArrayList<>();list.add(1);for(int i=0;i<index;i++){int uglyNumber = Math.min(list.get(p2) * 2,Math.min(list.get(p3) * 3,list.get(p5) * 5));list.add(uglyNumber);if(uglyNumber % 2 == 0) p2++;if(uglyNumber % 3 == 0) p3++;if(uglyNumber % 5 == 0) p5++;}return list.get(index-1);}
}

NC93 设计LRU缓存结构

设计LRU(最近最少使用)缓存结构,该结构在构造时确定大小,假设大小为 k ,并有如下两个功能

  1. set(key, value):将记录(key, value)插入该结构

  2. get(key):返回key对应的value值

提示:

1.某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的,然后都会刷新缓存。

2.当缓存的大小超过k时,移除最不经常使用的记录。

3.输入一个二维数组与k,二维数组每一维有2个或者3个数字,第1个数字为opt,第2,3个数字为key,value

若opt=1,接下来两个整数key, value,表示set(key, value)
若opt=2,接下来一个整数key,表示get(key),若key未出现过或已被移除,则返回-1
对于每个opt=2,输出一个答案

4.为了方便区分缓存里key与value,下面说明的缓存里key用""号包裹

要求:set和get操作复杂度均为 O(1)

输入:
[[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]],3
返回值:
[1,-1]
说明:
[1,1,1],第一个1表示opt=1,要set(1,1),即将(1,1)插入缓存,缓存是{"1"=1}
[1,2,2],第一个1表示opt=1,要set(2,2),即将(2,2)插入缓存,缓存是{"1"=1,"2"=2}
[1,3,2],第一个1表示opt=1,要set(3,2),即将(3,2)插入缓存,缓存是{"1"=1,"2"=2,"3"=2}
[2,1],第一个2表示opt=2,要get(1),返回是[1],因为get(1)操作,缓存更新,缓存是{"2"=2,"3"=2,"1"=1}
[1,4,4],第一个1表示opt=1,要set(4,4),即将(4,4)插入缓存,但是缓存已经达到最大容量3,移除最不经常使用的{"2"=2},插入{"4"=4},缓存是{"3"=2,"1"=1,"4"=4}
[2,2],第一个2表示opt=2,要get(2),查找不到,返回是[1,-1]
import java.util.*;public class Solution {/*** lru design* @param operators int整型二维数组 the ops* @param k int整型 the k* @return int整型一维数组*/private Map<Integer,Node> map = new HashMap<>();private Node head = new Node(-1,-1);private Node tail = new Node(-1,-1);private int k;public int[] LRU (int[][] operators, int k) {this.k = k;head.next = tail;tail.prev = head;int len = (int)Arrays.stream(operators).filter(x -> x[0] == 2).count();int[] res = new int[len];for(int i=0,j=0;i<operators.length;i++){if(operators[i][0] == 1){set(operators[i][1],operators[i][2]);}else{res[j++] = get(operators[i][1]);}}return res;}private void set(int key,int val){if(get(key) > -1){map.get(key).val = val;}else{if(map.size() == k){int rk = tail.prev.key;tail.prev.prev.next = tail;tail.prev = tail.prev.prev;map.remove(rk);}Node node = new Node(key,val);map.put(key,node);moveToHead(node);}}private int get(int key){if(map.containsKey(key)){Node node = map.get(key);node.prev.next = node.next;node.next.prev = node.prev;moveToHead(node);return node.val;}return -1;}private void moveToHead(Node node){node.next = head.next;head.next.prev = node;head.next = node;node.prev = head;}static class Node{int key,val;Node prev,next;public Node(int key,int val){this.key = key;this.val = val;}}
}

十大排序算法

1、冒泡排序

public void BuubleSort(Integer[] arr){for(int end=arr.length-1;end>0;end--){//boolean sorted = true; //优化:如果已经有序则breakint sortedIndex = 1; //优化2:记录下最后一次交换的index,说明后面序列已有序for(int begin=1;begin<=arr.length;begin++){if(arr[begin] < arr[begin-1]){int temp = arr[begin];arr[begin] = arr[begin-1];arr[begin-1] = temp;//sorted = false;sortedIndex = begin;}} //if(sorted) break;end = sortedIndex;}
}

2、选择排序

public void SelectSort(Integer[] arr){for(int end=arr.length-1;end>0;end++){int maxIndex = 0;for(int begin=1;begin<=end;begin++){if(arr[maxIndex] <= arr[begin]){maxIndex = begin;}}int temp = arr[maxIndex];arr[maxIndex] = arr[end];arr[end] = temp;}
}

3、堆排序

Integer[] arr;
int heapSize = arr.length;public void HeapSort(){    //原地建堆//heapSize>>1表示把heapSize右移1位,相当于heapSize/2for(int i=(heapSize >> 1)-1;i>=0;i--){shiftDown(i);}while(heapSize > 1){//交换堆顶元素和尾部元素swap(0,--heapSize);//对0位置进行shiftDown(恢复堆的性质)shiftDown(0);}
}public void shiftDown(int index){Integer element = arr[index];int half = heapSize >> 1;while(index < half){ // index必须是非叶子节点// 默认是左边跟父节点比int childIndex = (index << 1) + 1; //根据公式得来的(2i+1)Integer child = arr[childIndex];int rightIndex = childIndex + 1;//如果右子节点存在且右子节点大于左子节点if(rightIndex < heapSize && arr[rightIndex] > child){child = arr[childIndex = rightIndex];}//该节点与其最大子节点比较if(element > child){break; }//否则用子节点覆盖父节点arr[index] = child;index = childIndex;}arr[index] = element;
}

TopK问题(堆排序)

给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可)。

import java.util.ArrayList;public class Solution {public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {ArrayList<Integer> list = new ArrayList<Integer>();if(k==0) return list;int[] arr = new int[k];for(int i=0;i<k;i++){arr[i]=input[i];}for(int i=(k >> 1)-1;i>=0;i--){shiftDown(i,arr);}for(int i=k;i<input.length;i++){ //从第k个数开始和大顶堆的堆顶元素比较,如小于则替换,最后堆中的k个元素则是最小的if(input[i] < arr[0]){arr[0] = input[i];shiftDown(0,arr);}}for (int i = 0; i < arr.length; i++) {list.add(arr[i]);}return list;}public void shiftDown(int index,int[] arr){ //下滤(构建大顶堆)Integer element = arr[index];int half = arr.length >> 1;while(index < half){ // index必须是非叶子节点// 默认是左边跟父节点比int childIndex = (index << 1) + 1; //根据公式得来的(2i+1)Integer child = arr[childIndex];int rightIndex = childIndex + 1;//如果右子节点存在且右子节点大于左子节点if(rightIndex < arr.length && arr[rightIndex] > child){child = arr[childIndex = rightIndex];}//该节点与其最大子节点比较if(element > child){break; }//否则用子节点覆盖父节点arr[index] = child;index = childIndex;}arr[index] = element;}
}

4、插入排序

public void InsertSort(Integer[] arr){for(int begin = 1;begin < arr.length;begin++){int cur = begin;int v = arr[cur];while(cur > 0 && v < arr[cur-1]){arr[cur] = arr[cur-1];cur--;}arr[cur] = v;// while(cur > 0 && arr[cur] < arr[cur-1]){//int temp = arr[cur];//arr[cur] = arr[cur-1];//arr[cur-1] = temp;//cur--;//}}
}

5、归并排序

int[] leftarr;public void MergeSort(Integer[] arr){sort(0,arr.length,arr);
}
public void sort(int begin,int end,Integer[] arr){if(end-begin<2) return;int mid = (end+begin) >> 1;sort(begin,mid,arr);sort(mid+1,end,arr);merge(begin,end,arr);
}
public void merge(int begin,int mid,int end,Integer[] arr){int li = 0,le = mid;int ri = mid+1,re = end;int ai = begin;//将左边数组备份for(int i=li;i<le;i++){leftarr[i] = arr[begin+i];}while(li < le){if(ri < re && leftarr[li] > arr[ri]){arr[ai++] = arr[ri++];}else{arr[ai++] = leftarr[li++];}}
}

6、快速排序

public void QuickSort(Integer[] arr){sort1(0,arr.length,arr);sort2(0,arr.length-1,arr);
}
public void sort1(int begin,int end,Integer[] arr){if(end-begin < 2) return;// 确定轴点位置 O(n)int mid = privotIndex1(begin,end,arr);// 对子序列进行快速排序sort1(begin,mid,arr);sort1(mid+1,end,arr);
}
/*** 构造出 [begin, end) 范围的轴点元素* @return 轴点元素的最终位置*/
public int privotIndex1(int begin,int end,Integer[] arr){int privot = arr[begin];end--;while(begin < end){while(begin < end){if(arr[end] > privot){ // 右边元素 > 轴点元素end--;}else{arr[begin] = arr[end];begin++;break;}}while(begin < end){if(arr[begin] < privot){  // 左边元素 < 轴点元素begin++;}else{arr[end] = arr[begin];end--;break;}}}// 将轴点元素放入最终的位置array[begin] = pivot;// 返回轴点元素的位置return begin;
}//法2
public void sort2(int begin,int end,Integer[] arr){if(end-begin < 2) return;// 确定轴点位置 O(n)int mid = privotIndex2(begin,end,arr);// 对子序列进行快速排序sort1(begin,mid-1,arr);sort1(mid+1,end,arr);
}
public int privotIndex2(int begin,int end,Integer[] arr){int privot = begin,index = privot+1;for(int i=index;i<=end;i++){if(arr[privot] > arr[i]){int temp = arr[index];arr[index] = arr[i];arr[i] = temp;index++;}}int temp = arr[privot];arr[privot] = arr[index-1];arr[index-1] = temp;return index-1;
}

7、希尔排序(对插入排序的优化)

public void ShellSort(Integer[] arr){List<Integer> stepSequence = shellStepSequence(arr);for(Integer step:stepSequence){sort(arr,step);}
}
public void sort(Integer[] arr,int step){for(int col=0;col<step;col++){ //对第col列进行排序//col、col+step、col+2*step、col+3*stepfor(int begin=col+step;begin<arr.length;begin+=step){int cur = begin;while(cur>col && arr[cur] < arr[cur-step]){int temp = arr[cur-step];arr[cur-step] = arr[cur];arr[cur] = temp;cur-=step;}}}
}
public List<Integer> shellStepSequence(Integer[] arr){List<Integer> stepSequence = new ArrayList<>();int step = arr.length;while((step >>= 1) > 0){stepSequence.add(step);}return stepSequence;
}

8、计数排序

public void CountingSort(Integer[] arr){//找出最大值int max = arr[0];for(int i=0;i<arr.length;i++){if(arr[i] > max){max = arr[i];}}//开辟内存空间,存储每个整数出现的次数int[] counts = new int[max+1];//统计每个整数出现的次数for(int i=0;i<counts.length;i++){count[arr[i]]++;}//根据整数的出现次数,对整数进行排序int index = 0;for(int i=0;i<counts.length;i++){while(counts[i]- > 0){arr[index++] = i;}}
}//改进后实现=============================================
public void CountingSort(Integer[] arr){//找出最大值和最小值int max = arr[0];int min = arr[0];for(int i=0;i<arr.length;i++){if(arr[i] > max){max = arr[i];}if(arr[i] < min){min = arr[i];}}//开辟内存空间,存储次数int[] counts = new int[max-min+1];//统计每个整数出现的次数for(int i=0;i<arr.length;i++){count[arr[i]-min]++;}//累加次数for(int i=1;i<counts.length;i++){counts[i] += counts[i-1];}//从后往前遍历元素,将它放到有序数组中的合适位置int[] newArray = new int[arr.length];for(int i=arr.length-1;i>=0;i--){newArray[--counts[arr[i]-min]] = arr[i];}//将有序数组赋值到arrfor(int i=0;i<arr.length;i++){arr[i] = newArray[i];}
}

9、基数排序(个、十、百位)(基于计数排序)

public void RadixSort(Integer[] arr){//找出最大值int max = arr[0];for(int i=0;i<arr.length;i++){if(arr[i] > max){max = arr[i];}}//max=593//百位数:593 / 100 % 10 = 5//十位数:593 / 10 % 10 = 9//个位数:593 / 1 % 10 = 3for(int divider=1;i<=max;divider*=10){countingSort(arr,divider);}
}
public void countingSort(int divider){//开辟内存空间,存储次数int[] counts = new int[10];//统计每个整数出现的次数for(int i=0;i<arr.length;i++){count[arr[i] / divider % 10]++;}//累加次数for(int i=1;i<counts.length;i++){counts[i] += counts[i-1];}//从后往前遍历元素,将它放到有序数组中的合适位置int[] newArray = new int[arr.length];for(int i=arr.length-1;i>=0;i--){newArray[--counts[arr[i] / divider % 10] = arr[i];}//将有序数组赋值到arrfor(int i=0;i<arr.length;i++){arr[i] = newArray[i];}
}

10、桶排序(自定义规则)

二叉树遍历

package binarytree;import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;public class Test {//递归遍历的方式与非递归遍历的方式://一、递归的方式://1、先序遍历:private static void preOrder1(Node root){if(root == null) return;System.out.print(root.value);preOrder1(root.left);preOrder1(root.right);}//2、中序遍历:private static void midOrder1(Node root){if(root == null) return;midOrder1(root.left);System.out.print(root.value);midOrder1(root.right);}//3、后序遍历:private static void postOrder1(Node root){if(root == null) return;postOrder1(root.left);postOrder1(root.right);System.out.print(root.value);}//二、非递归的方式://1、先序遍历:private static void preOrder2(Node root){if(root == null) return;Stack<Node> stack = new Stack<>();//用栈来存储(先进后出)stack.push(root);while(!stack.isEmpty()){Node node = stack.pop();System.out.print(node.value);if(node.right != null) stack.push(node.right);if(node.left != null) stack.push(node.left);}}//2、中序遍历private static void midOrder2(Node root){if(root == null) return;Stack<Node> stack = new Stack<>();Node cur = root;while(!stack.isEmpty() || cur != null){while(cur != null){stack.push(cur);cur = cur.left;}Node node = stack.pop();System.out.print(node.value);if(node.right != null) cur= node.right;}}//3、后序遍历:private static void postOrder2(Node root){if(root == null) return;Stack<Node> stack = new Stack<>();Stack<Node> stack2 = new Stack<>();stack.push(root);while(!stack.isEmpty()){Node node = stack.pop();stack2.push(node);if(node.left != null) stack.push(node.left);if(node.right != null) stack.push(node.right);}while(!stack2.isEmpty()){System.out.print(stack2.pop().value);}}//三、层序遍历private static void bfs(Node root){if(root == null) return;Queue<Node> queue = new LinkedList<>();//用队列来存储(先进先出)queue.add(root);while(!queue.isEmpty()){Node node = queue.poll();System.out.print(node.value);if(node.left != null) queue.add(node.left);if(node.right != null) queue.add(node.right);}}private static List<List<String>> bfs2(Node root){List<List<String>> res = new ArrayList<>();//存储节点(List里面套List)Queue<Node> queue = new LinkedList<>();//用队列来存储(先进先出)queue.add(root);List<String> list;while(!queue.isEmpty()){int size = queue.size();list = new ArrayList<>();while(size-- > 0){Node node = queue.poll();list.add(node.value);if(node.left != null) queue.add(node.left);if(node.right != null) queue.add(node.right);}res.add(list);}return res;}public static void main(String[] args) {Node nodeA = new Node("A");Node nodeB = new Node("B");Node nodeC = new Node("C");Node nodeD = new Node("D");Node nodeE = new Node("E");Node nodeF = new Node("F");Node nodeG = new Node("G");//构建二叉树nodeA.left = nodeB;nodeA.right = nodeC;nodeB.left = nodeD;nodeB.right = nodeE;nodeC.right = nodeF;nodeE.left = nodeG;preOrder1(nodeA);System.out.println();midOrder1(nodeA);System.out.println();postOrder1(nodeA);System.out.println();preOrder2(nodeA);System.out.println();midOrder2(nodeA);System.out.println();postOrder2(nodeA);System.out.println();bfs(nodeA);bfs2(nodeA);}public static class Node{ //节点public String value;public Node left;public Node right;public Node(String value){this.value = value;}}}

KMP算法(字符串匹配)

B+树(MySQL)


更多请见:https://gitee.com/monkeyhlj/algorithm

LeetCode刷题记录(2)相关推荐

  1. LeetCode刷题记录15——21. Merge Two Sorted Lists(easy)

    LeetCode刷题记录15--21. Merge Two Sorted Lists(easy) 目录 LeetCode刷题记录15--21. Merge Two Sorted Lists(easy) ...

  2. LeetCode刷题记录14——257. Binary Tree Paths(easy)

    LeetCode刷题记录14--257. Binary Tree Paths(easy) 目录 前言 题目 语言 思路 源码 后记 前言 数据结构感觉理论简单,实践起来很困难. 题目 给定一个二叉树, ...

  3. LeetCode刷题记录13——705. Design HashSet(easy)

    LeetCode刷题记录13--705. Design HashSet(easy) 目录 LeetCode刷题记录13--705. Design HashSet(easy) 前言 题目 语言 思路 源 ...

  4. LeetCode刷题记录12——232. Implement Queue using Stacks(easy)

    LeetCode刷题记录12--232. Implement Queue using Stacks(easy) 目录 LeetCode刷题记录12--232. Implement Queue usin ...

  5. LeetCode刷题记录11——290. Word Pattern(easy)

    LeetCode刷题记录11--290. Word Pattern(easy) 目录 LeetCode刷题记录11--290. Word Pattern(easy) 题目 语言 思路 源码 后记 题目 ...

  6. LeetCode刷题记录10——434. Number of Segments in a String(easy)

    LeetCode刷题记录10--434. Number of Segments in a String(easy) 目录 LeetCode刷题记录9--434. Number of Segments ...

  7. LeetCode刷题记录9——58. Length of Last Word(easy)

    LeetCode刷题记录9--58. Length of Last Word(easy) 目录 LeetCode刷题记录9--58. Length of Last Word(easy) 题目 语言 思 ...

  8. LeetCode刷题记录8——605. Can Place Flowers(easy)

    LeetCode刷题记录8--605. Can Place Flowers(easy) 目录 LeetCode刷题记录8--605. Can Place Flowers(easy) 题目 语言 思路 ...

  9. LeetCode刷题记录7——824. Goat Latin(easy)

    LeetCode刷题记录7--824. Goat Latin(easy) 目录 LeetCode刷题记录7--824. Goat Latin(easy) 题目 语言 思路 后记 题目 题目需要将一个输 ...

  10. LeetCode刷题记录6——696. Count Binary Substrings(easy)

    LeetCode刷题记录6--696. Count Binary Substrings(easy) 目录 LeetCode刷题记录6--696. Count Binary Substrings(eas ...

最新文章

  1. 继承ViewGroup类
  2. Redis:redis入门
  3. 告别加班:一个让你不再重复工作的技能
  4. java中如何将非整数保留到小数点后指定的位数
  5. 用jackson转json_用Jackson编写大JSON文件
  6. 测试页打印失败.是否参阅打印疑难解答以获得帮助_使用DeepState对API进行模糊测试(上)...
  7. python调用rocketmq的api_rocketmq-python
  8. Multiavatar 多元文化头像生成器
  9. 绝地求生服务器维护6.28,绝地求生6月28号更新完怎么进不去 绝地求生更新维护到几点6.28...
  10. 【线性代数及其应用】04 -线性变换
  11. Linux 设备驱动 ==== 字符驱动
  12. 全球及中国直流调速器行业市场调研及未来发展趋势预测报告2022-2027年
  13. Windows安装jupyter教程
  14. python微博自动评论转发_「Python」 - 自动删除微博评论
  15. CodeForces 596B Wilbur and Array 贪心
  16. 夏天CPU温度过高原因及解决办法
  17. 2006年最值得期待的大片
  18. 每日一道leetcode(python)844. 比较含退格的字符串
  19. 保姆级教程,一文理顺优惠券设计
  20. HTML怎么制作镂空文字遮罩,用纯 CSS 实现镂空效果

热门文章

  1. Linux网络编程 之 无连接和有连接通信(二)
  2. I/O数据有几种传送方式?各有什么特点?【最精炼】
  3. SpringMVC容器和Spring容器
  4. Web前端开发笔记——第二章 HTML语言 第九节 框架标签、脚本标签
  5. python asyncio回调函数_python回调函数用法实例分析
  6. 快准牌电脑发软件_电脑如何下载软件
  7. Shell Here Document 免交互命令和Expect
  8. Linux系统安全及应用(账号控制、su、sudo、开关机安全控制、终端登录安全控制等)
  9. Linux wifi连接桌面,【已解决】Arch linux 安装之后在deepin桌面环境下使用networkmanager连接wifi 出现间歇性重连的情况...
  10. pip 设置超时时间_pip install 超时