左神算法课程
递归复杂度估计 通用式子
这种不能使用:因为子问题规模不一样
对于归并排序:
T(N) = 2T(N/2)+O(N);后面是合并的时候复杂度
二分相关
二分法 刘维维https://www.bilibili.com/video/BV147411i7zu?p=2&spm_id_from=pageDriver
35题:
寻找旋转排序数组中的等于target的下标 33 81题,和下面类似
核心:相比于没有重复数字的情况,有重复数字的时候,方法是收缩右边界。
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = (left + right) / 2;
if (nums[mid] == target) {
return mid;
}
if (nums[mid] < nums[right]) {
if (nums[mid] < target && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
} else {
if (nums[left] <= target && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
}
}
return nums[left] == target ? left : -1;
}
}
81题题解:
class Solution {
public boolean search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = (left + right) / 2;
if (nums[mid] == target) {
return true;
}
if (nums[mid] < nums[right]) {
if (nums[mid] < target && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
} else if (nums[mid] > nums[right]) {
if (nums[left] <= target && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else{
if (nums[right] == target) {
return true;
} else {
right--;
}
}
}
return nums[left] == target;
}
}
寻找旋转排序数组中的最小值 153 154
只能是nums[mid]和nums[right]比,可以想一下,如果是跟左边比,有以下三种情况:
1、mid在左边,mid > left,最小值在右边,
2、mid在右边,mid < left,最小值在左边
3、数组没有旋转,mid在中间,mid > left,最小值在左边,有歧义。
所以只能跟右边比,至于是大于小于无所谓。
153题题解
class Solution {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
while(left < right){
int mid = left + ((right - left) >> 1);
if(nums[mid] < nums[right]){
right = mid;
}else{
left = mid + 1;
}
}
return nums[left];
}
}
class Solution {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
while(left < right){
int mid = left + ((right - left) >> 1);
if(nums[mid] > nums[right]){
left = mid + 1;
}else{
right = mid;
}
}
return nums[left];
}
}
回溯相关
全排列,组合总和,子集,
组合总和II 40题 39 题
39题多叉树解法
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
//这里排序是为了剪枝,就是dfs里面的<0,break,如果没有排序,不剪枝也是可以的。
Arrays.sort(candidates);
List<Integer> path = new ArrayList<>();
int len = candidates.length;
dfs(candidates, path, 0, len, target);
return res;
}
public void dfs(int[] nums, List<Integer> path, int begin, int len, int target){
if(target == 0){
res.add(new ArrayList<>(path));
return;
}
for(int i = begin; i < len; i++){
if(target - nums[i] < 0){
break;
}
path.add(nums[i]);
dfs(nums, path, i, len, target - nums[i]);
path.remove(path.size() - 1);
}
}
}
39题 二叉树解法
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
List<Integer> path = new ArrayList<>();
int len = candidates.length;
dfs(candidates, path, 0, len, target);
return res;
}
public void dfs(int[] nums, List<Integer> path, int begin, int len, int target){
if(begin == len){
return;
}
if(target == 0){
res.add(new ArrayList<>(path));
return;
}
dfs(nums, path, begin + 1, len, target);
if(target - nums[begin] >= 0){
path.add(nums[begin]);
dfs(nums, path, begin, len, target - nums[begin]);
path.remove(path.size() - 1);
}
}
}
40题组合之和II 很容易错
仔细看下下面两个代码的区别,这两个都是多叉树的解法,但是有一个没有用used。
多叉树 没有used
//只能是i > begin ,否则会错,因为没有used
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
List<Integer> path = new ArrayList<>();
int len = candidates.length;
dfs(candidates, path, 0, len, target);
return res;
}
public void dfs(int[] nums, List<Integer> path, int begin, int len, int target){
if(target == 0){
res.add(new ArrayList<>(path));
return;
}
for(int i = begin; i < len; i++){
if(i > begin && nums[i - 1] == nums[i]){
continue;
}
if(target - nums[i] < 0){
break;
}
path.add(nums[i]);
dfs(nums, path, i + 1, len, target - nums[i]);
path.remove(path.size() - 1);
}
}
}
多叉树用了used,所以可以是i >0
//多叉树解法 选和不选
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
List<Integer> path = new ArrayList<>();
int len = candidates.length;
boolean[] used = new boolean[len];
dfs(candidates, path, 0, len, target, used);
return res;
}
public void dfs(int[] nums, List<Integer> path, int begin, int len, int target, boolean[] used){
if(target == 0){
res.add(new ArrayList<>(path));
return;
}
for(int i = begin; i < len; i++){
if(used[i]){
continue;
}
if(i > 0 && nums[i - 1] == nums[i] && !used[i - 1]){
continue;
}
if(target - nums[i] < 0){
break;
}
path.add(nums[i]);
used[i] = true;
dfs(nums, path, i + 1, len, target - nums[i], used);
used[i] = false;
path.remove(path.size() - 1);
}
}
}
二叉树剪枝 没有解出来
子集78题不重复的两种做法
多叉树思路
//多叉树做法
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
List<Integer> path = new ArrayList<>();
Arrays.sort(nums);
dfs(nums, path, 0);
return res;
}
public void dfs(int[] nums, List<Integer> path, int begin){
res.add(new ArrayList<>(path));
for(int i = begin; i < nums.length; i++){
path.add(nums[i]);
dfs(nums, path, i + 1);
path.remove(path.size() - 1);
}
}
}
二叉树做法
//二叉树做法
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
List<Integer> path = new ArrayList<>();
int len = nums.length;
dfs(nums, 0,len, path);
return res;
}
//二叉树做法,选和不选
private void dfs(int[] nums, int index, int len, List<Integer> path) {
if(len == index){
res.add(new ArrayList<>(path));
return;
}
dfs(nums, index + 1, len, path);
path.add(nums[index]);
dfs(nums, index + 1, len, path);
path.remove(path.size() - 1);
}
}
子集90重复
多叉树做法 不带Used
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<Integer> path = new ArrayList<>();
int len = nums.length;
Arrays.sort(nums);
dfs(nums, path, len, 0);
return res;
}
public void dfs(int[] nums, List<Integer> path, int len, int begin){
res.add(new ArrayList<>(path));
for(int i = begin; i < len; i++){
if(i > begin && nums[i] == nums[i - 1]){
continue;
}
path.add(nums[i]);
dfs(nums, path, len, i + 1);
path.remove(path.size() - 1);
}
}
}
多叉树做法带used, i > 0或者i > begin 都可以
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<Integer> path = new ArrayList<>();
int len = nums.length;
Arrays.sort(nums);
boolean[] used = new boolean[len];
dfs(nums, path, len, 0, used);
return res;
}
public void dfs(int[] nums, List<Integer> path, int len, int begin, boolean[] used){
res.add(new ArrayList<>(path));
for(int i = begin; i < len; i++){
if(i > begin && nums[i] == nums[i - 1] && !used[i - 1]){
continue;
}
path.add(nums[i]);
used[i] = true;
dfs(nums, path, len, i + 1, used);
used[i] = false;
path.remove(path.size() - 1);
}
}
}
二叉树解法 暂无
TOPk问题
剑指 Offer 40. 最小的k个数
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
quickSort(arr, 0, arr.length - 1, k);
return Arrays.copyOf(arr, k);
}
public int[] quickSort(int[] arr, int l, int r, int k) {
if(l < r){
int left = l;
int right = r;
int pivot = arr[left];
while(left < right){
while(left < right && arr[right] >= pivot){
right--;
}
if(left < right){
arr[left] = arr[right];
}
while(left < right && arr[left] <= pivot){
left++;
}
if(left < right){
arr[right] = arr[left];
}
}
arr[left] = pivot;
if(left > k){
return quickSort(arr, l, left - 1, k);
}
if(left < k){
return quickSort(arr, left + 1, r, k);
}
//quickSort(arr, l, left - 1);
//quickSort(arr, left + 1, r);
}
return Arrays.copyOf(arr, k);
}
}
栈实现队列和队列实现栈
答案来自评论区
232题 栈实现队列
class MyQueue {
Stack<Integer> stack1;
Stack<Integer> stack2;
public MyQueue() {
this.stack1 = new Stack<>();//输入
this.stack2 = new Stack<>();//输出
}
public void push(int x) {
stack1.push(x);
}
public int pop() {
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
public int peek() {
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.peek();
}
public boolean empty() {
return stack1.isEmpty() && stack2.isEmpty();
}
}
225题 队列实现栈
class MyStack {
private Queue<Integer> queue1;
private Queue<Integer> queue2;
/** Initialize your data structure here. */
public MyStack() {
this.queue1 = new LinkedList<>();//输入队列
this.queue2 = new LinkedList<>();//输出队列
}
public void push(int x) {
queue1.offer(x);
while(!queue2.isEmpty()){
queue1.offer(queue2.poll());
}
Queue<Integer> temp = queue2;
queue2 = queue1;
queue1 = temp;
}
public int pop() {
return queue2.poll();
}
/** Get the top element. */
public int top() {
return queue2.peek();
}
/** Returns whether the stack is empty. */
public boolean empty() {
return queue2.isEmpty() && queue1.isEmpty();
}
}
动态规划
爬楼梯和硬币问题区别(很有用)(零钱兑换问题、爬楼梯问题、组合之和4)
70爬楼梯题和518零钱兑换II
518零钱兑换
在定义二维的情况下,下面两种方法等价:for循环 变化,不会影响状态方程的定义:
下面是二维状态:
class Solution {
public int change(int amount, int[] coins) {
int len = coins.length;
int[][] dp = new int[len + 1][amount + 1];
dp[0][0] = 1;
for(int j = 0; j < amount + 1; j++){
for(int i = 1; i < len + 1; i++){
int coin = coins[i - 1];
if(coin > j){
dp[i][j] = dp[i - 1][j];
}else {
dp[i][j] = dp[i - 1][j] + dp[i][j - coin];
}
}
}
return dp[len][amount];
}
}
class Solution {
public int change(int amount, int[] coins) {
int len = coins.length;
int[][] dp = new int[len + 1][amount + 1];
dp[0][0] = 1;
for(int i = 1; i < len + 1; i++){
for(int j = 0; j < amount + 1; j++){
int coin = coins[i - 1];
if(coin > j){
dp[i][j] = dp[i - 1][j];
}else {
dp[i][j] = dp[i - 1][j] + dp[i][j - coin];
}
}
}
return dp[len][amount];
}
}
降维方法 看官网解释:
此时的子问题是,对于硬币从 0 到 k,我们必须使用第k个硬币的时候,当前金额的组合数。
因 此状态数组 DP[i] 表示的是对于第k个硬币能凑的组合数 状态转移方程如下
DP[[i] = DP[i] + DP[i-k];
class Solution {
public int change(int amount, int[] coins) {
int len = coins.length;
int[] dp = new int[amount + 1];
dp[0] = 1;
for(int coin : coins){
for(int i = 1; i < amount + 1; i++){
if(coin > i){
continue;
}
dp[i] += dp[i - coin];
}
}
return dp[amount];
}
}
此时for循环就不可以再交换,交换后的意义就从:必选硬币coin,能凑成i的方案数目。变为:对于金额i,硬币的组合方案;
爬楼梯当中:好像不能使用类似硬币兑换的那种二维。
除了常规的那种方法外,这种也可以,但是for循环缓过来就不行了,这里是i个楼梯的时候的方案,题意让求得是排列数,如果for循环反过来,求得就是组合数目。
可以这么想,第一个for先选择楼梯数目,后面是步数,就是固定一个楼梯数 比如是3,步数为1,需要加上dp[2], 步数为2,需要加上dp[1],这种其实就是一个组合问题
。
class Solution {
public int climbStairs(int n) {
int[] dp = new int[n + 1];
dp[0] = 1;
//dp[1] = 1;
int[] steps = {1, 2};
for(int i = 1; i < n + 1; i++){
for(int j = 0; j < 2; j++){
int step = steps[j];
if(i < step){
continue;
}
dp[i] = dp[i] + dp[i - step];
}
}
return dp[n];
}
}
错误解法:
可以这么想,第一个for先选择步数,1步、2步,这种先固定步数,只能是1,2,这种组合问题,因为已经排好序。
class Solution {
public int climbStairs(int n) {
int[] dp = new int[n + 1];
dp[0] = 1;
int[] steps = {1, 2};
for(int j = 0; j < 2; j++){
int step = steps[j];
for(int i = 1; i < n + 1; i++){
if(i < step){
continue;
}
dp[i] += dp[i - step];
}
}
return dp[n];
}
}
总结
爬楼梯当中求得是排列数目,硬币求得是组合数目。
01背包问题
分割等和子集,一和零,目标和里面,在循环的时候,j有时候从0开始,有时候从1开始。
动态规划的式子
最长公共系列
300. 最长递增子序列(LIS问题)
https://leetcode.cn/leetbook/read/learning-algorithms-with-leetcode/9w3wiv/
dp[i] 表示:以 nums[i] 结尾的上升子序列的长度。
class Solution {
public int lengthOfLIS(int[] nums) {
int len = nums.length;
int[] dp = new int[len];
Arrays.fill(dp, 1);
int res = 1;
for (int i = 1; i < len; i++) {
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i]) {
dp[i] = Math.max(dp[j] + 1, dp[i]);
}
}
res = Math.max(res, dp[i]);
}
return res;
}
}
53. 最大子数组和
public class Solution {
public int maxSubArray(int[] nums) {
int len = nums.length;
if (len == 0) {
return 0;
}
int[] dp = new int[len];
dp[0] = nums[0];
// dp[i]:表示以 nums[i] 结尾的连续子数组的最大和(这样的定义满足无后效性)
for (int i = 1; i < len; i++) {
// 分类讨论的标准:选或者不选前面的连续子数组的和
dp[i] = Math.max(nums[i], dp[i - 1] + nums[i]);
}
// 遍历 dp 数组,找出最大值,即连续子数组的最大和
int res = dp[0];
for (int i = 1; i < len; i++) {
res = Math.max(res, dp[i]);
}
return res;
}
}
作者:liweiwei1419
链接:https://leetcode.cn/leetbook/read/learning-algorithms-with-leetcode/9w6p0t/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public int maxSubArray(int[] nums) {
if (nums == null || nums.length < 1) {
return 0;
}
int len = nums.length;
int[] dp = new int[len];
dp[0] = nums[0];
int res = dp[0];
for (int i = 1; i < len; i++) {
if (dp[i - 1] < 0) {
dp[i] = nums[i];
} else {
dp[i] = nums[i] + dp[i - 1];
}
res = Math.max(res, dp[i]);
}
return res;
}
}
674. 最长连续递增序列(注意和300题目区别)
300题不用连续,所以需要两个for循环
class Solution {
public int findLengthOfLCIS(int[] nums) {
int len = nums.length;
int[] dp = new int[len];
int res = 1;
Arrays.fill(dp, 1);
for (int i = 1; i < len; i++) {
if (nums[i] > nums[i - 1]) {
dp[i] = dp[i - 1] + 1;
}
res = Math.max(dp[i], res);
}
return res;
}
}
718. 最长重复子数组
1143. 最长公共子序列
https://leetcode-cn.com/problems/longest-common-subsequence
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int len1 = text1.length();
int len2 = text2.length();
int[][] dp = new int[len1 + 1][len2 + 1];
for (int i = 1; i < len1 + 1; i++) {
for (int j = 1; j < len2 + 1; 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][j - 1], dp[i - 1][j]);
}
}
}
return dp[len1][len2];
}
}
72. 编辑距离
class Solution {
public int minDistance(String word1, String word2) {
int len1 = word1.length();
int len2 = word2.length();
int[][] dp = new int[len1 + 1][len2 + 1];
for (int i = 0; i < len1 + 1; i++) {
dp[i][0] = i;
}
for (int i = 0; i < len2 + 1; i++) {
dp[0][i] = i;
}
for (int i = 1; i < len1 + 1; i++) {
for (int j = 1; j < len2 + 1; j++) {
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i][j - 1], dp[i - 1][j])) + 1;
}
}
}
return dp[len1][len2];
}
}
583. 两个字符串的删除操作(和编辑距离很像)
区别:
72题编辑距离,操作替换算是一个步骤,这里不相同的时候每个字符串需要执行一次删除操作。
思路一:动态规划
class Solution {
public int minDistance(String word1, String word2) {
int len1 = word1.length();
int len2 = word2.length();
int[][] dp = new int[len1 + 1][len2 + 1];
for(int j = 1; j < len2 + 1; j++){
dp[0][j] = j;
}
for(int i = 1; i < len1 + 1; i++){
dp[i][0] = i;
}
for(int i = 1; i < len1 + 1; i++){
for(int j = 1; j < len2 + 1; j++){
if(word1.charAt(i - 1) == word2.charAt(j - 1)){
dp[i][j] = dp[i - 1][j - 1];
}else{
dp[i][j] = Math.min(dp[i - 1][j - 1] + 1, Math.min(dp[i][j - 1], dp[i - 1][j])) + 1;
}
}
}
return dp[len1][len2];
}
}
思路二:转化为最长公共子序列问题(1143),最后把两个字符串长度之和减去最长公共子序列的结果:
class Solution {
public int minDistance(String word1, String word2) {
return word1.length() + word2.length() - 2 * longestCommonSubsequence(word1, word2);
}
public int longestCommonSubsequence(String text1, String text2) {
int len1 = text1.length();
int len2 = text2.length();
int[][] dp = new int[len1 + 1][len2 + 1];
for (int i = 1; i < len1 + 1; i++) {
for (int j = 1; j < len2 + 1; 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][j - 1], dp[i - 1][j]);
}
}
}
return dp[len1][len2];
}
}
5. 最长回文子串(回文串相关,还有647题,516题)
https://leetcode.cn/leetbook/read/learning-algorithms-with-leetcode/9ddzn2/
思路一:中间扩散法
class Solution {
public String longestPalindrome(String s) {
char[] arr = s.toCharArray();
int res = 1;
int startIndex = 0;
for (int i = 0; i < arr.length; i++) {
int[] odd = findLength(arr, i, i);
int[] even = findLength(arr, i, i + 1);
if (odd[1] > even[1] && odd[1] > res) {
res = odd[1];
startIndex = odd[0];
} else if (even[1] > odd[1] && even[1] > res) {
res = even[1];
startIndex = even[0];
}
}
return s.substring(startIndex, startIndex + res);
}
public int[] findLength(char[] arr, int left, int right) {
if (left > right) {
return new int[] {0, 0};
}
int len = arr.length;
int l = left;
int r = right;
while (l >= 0 && r < len) {
if (arr[l] == arr[r]) {
l--;
r++;
} else {
break;
}
}
return new int[]{l + 1, r - l - 1};
}
}
思路二:动态规划,按到倒序遍历,参考516题目
class Solution {
public String longestPalindrome(String s) {
int ans = 0;
int len = s.length();
boolean[][] dp = new boolean[len][len];
for (int i = 0; i < len; i++) {
dp[i][i] = true;
}
int startIndex = 0;
int res = 1;
for (int i = len - 2; i >= 0; i--) {
for (int j = i + 1; j < len; j++) {
if (s.charAt(j) == s.charAt(i) && (j - i < 3 || dp[i + 1][j - 1])) {
dp[i][j] = true;
}
if (dp[i][j] && (j - i + 1) > res) {
res = j - i + 1;
startIndex = i;
}
}
}
return s.substring(startIndex, startIndex + res);
}
}
516. 最长回文子序列
核心思想:对于i,从下往上,是反着遍历,对于j的方向,还是正序
class Solution {
public int longestPalindromeSubseq(String s) {
int len = s.length();
if (len < 2) {
return len;
}
char[] array = s.toCharArray();
int res = 0;
int[][] dp = new int[len][len];
for (int i = 0; i < len; i++) {
dp[i][i] = 1;
}
for (int i = len - 2; i >= 0; i--) {
for (int j = i + 1; j < len; j++) {
if (array[i] == array[j]) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
}
}
}
return dp[0][len - 1];
}
}
647. 回文子串的数量
class Solution {
public int countSubstrings(String s) {
int ans = 0;
int len = s.length();
boolean[][] dp = new boolean[len][len];
for (int i = 0; i < len; i++) {
ans++;
dp[i][i] = true;
}
for (int i = len - 2; i >= 0; i--) {
for (int j = i + 1; j < len; j++) {
if (s.charAt(j) == s.charAt(i) && (j - i < 3 || dp[i + 1][j - 1])) {
ans++;
dp[i][j] = true;
}
}
}
return ans;
}
}
二叉树
三种基本遍历
中序
中序:
class Solution {
List<Integer> res = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
inorder(root);
return res;
}
public void inorder(TreeNode root){
if(root == null){
return;
}
inorder(root.left);
res.add(root.val);
inorder(root.right);
}
}
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null){
return res;
}
ArrayDeque<TreeNode> stack = new ArrayDeque<>();
while(!stack.isEmpty() || root != null){
while(root != null){
stack.offerFirst(root);
root = root.left;
}
root = stack.pollFirst();
res.add(root.val);
root = root.right;
}
return res;
}
前序
class Solution {
List<Integer> res = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
preorder(root);
return res;
}
public void preorder(TreeNode root){
if(root == null){
return;
}
res.add(root.val);
preorder(root.left);
preorder(root.right);
}
}
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
ArrayDeque<TreeNode> stack = new ArrayDeque<>();
if(root == null){
return res;
}
stack.offerFirst(root);
while(!stack.isEmpty()){
TreeNode node = stack.pollFirst();
res.add(node.val);
if(node.right != null){
stack.offerFirst(node.right);
}
if(node.left != null){
stack.offerFirst(node.left);
}
}
return res;
}
}
后序
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
ArrayDeque<TreeNode> stack = new ArrayDeque<>();
if(root == null){
return res;
}
stack.offerFirst(root);
while(!stack.isEmpty()){
TreeNode node = stack.pollFirst();
res.add(0, node.val);
if(node.left != null){
stack.offerFirst(node.left);
}
if(node.right != null){
stack.offerFirst(node.right);
}
}
return res;
}
}
class Solution {
List<Integer> res = new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root) {
postorder(root);
return res;
}
public void postorder(TreeNode root){
if(root == null){
return;
}
postorder(root.left);
postorder(root.right);
res.add(root.val);
}
}
递归
二叉树其他系列
111. 二叉树的最小深度 跟层序遍历很类似
class Solution {
public int minDepth(TreeNode root) {
if(root == null){
return 0;
}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.add(root);
int depth = 0;
while(!queue.isEmpty()){
depth++;
int size = queue.size();
for(int i = 0; i < size; i++){
TreeNode node = queue.poll();
if(node.left == null && node.right == null){
return depth;
}
if(node.left != null){
queue.add(node.left);
}
if(node.right != null){
queue.add(node.right);
}
}
}
return depth;
}
}
java
```java
class Solution {
public int minDepth(TreeNode root) {
if(root == null){
return 0;
}
if(root.left == null){
return minDepth(root.right) + 1;
}
if(root.right == null){
return minDepth(root.left) + 1;
}
return Math.min(minDepth(root.left), minDepth(root.right)) + 1;
}
}
226. 翻转二叉树
/**
* 递归方式遍历反转
*/
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
invertTree(root.left);
invertTree(root.right);
return root;
}
/**
* 层序遍历方式反转
*/
public TreeNode invertTreeByQueue(TreeNode root) {
if (root == null) {
return null;
}
Queue<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
return root;
}
/**
* 深度优先遍历的方式反转
*/
private TreeNode invertTreeByStack(TreeNode root) {
if (root == null) {
return null;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
int size = stack.size();
for (int i = 0; i < size; i++) {
TreeNode cur = stack.pop();
TreeNode temp = cur.left;
cur.left = cur.right;
cur.right = temp;
if (cur.right != null) {
stack.push(cur.right);
}
if (cur.left != null) {
stack.push(cur.left);
}
}
}
return root;
}
最近公共祖先 235 236
235
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root.val < q.val && root.val < p.val){
return lowestCommonAncestor(root.right, p, q);
}
if(root.val > q.val && root.val > p.val){
return lowestCommonAncestor(root.left, p, q);
}
return root;
}
}
迭代法
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
while(true){
if(root.val < q.val && root.val < p.val){
root = root.right;
}else if(root.val > q.val && root.val > p.val){
root = root.left;
}else{
break;
}
}
return root;
}
}
236
递归法
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || root == p || root == q){
return root;
}
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left != null && right != null){
return root;
}
if(left == null){
return right;
}
return left;
}
}
数组问题
找数组中重复数字
287,剑指offer03
03:
class Solution {
public int findRepeatNumber(int[] nums) {
for(int i = 0; i < nums.length; i++){
if(nums[i] == i){
continue;
}else{
if(nums[nums[i]] == nums[i]){
return nums[i];
}else{
swap(nums, i, nums[i]);
i--;0//加上这个好像更块,不加这个也可以
}
}
}
return -1;
}
public void swap(int[] nums, int i, int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
下面这个版本刚上面那个带i–的是等价的
class Solution {
public int findRepeatNumber(int[] nums) {
if (nums.length == 0) {
return -1;
}
for (int i = 0; i < nums.length; ++i) {
while (nums[i] != i) {
//发现重复元素
if (nums[i] == nums[nums[i]]) {
return nums[i];
}
//置换,将指针下的元素换到属于他的索引处
int temp = nums[i];
nums[i] = nums[temp];
nums[temp] = temp;
}
}
return -1;
}
}
链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/solution/xue-sheng-wu-de-nu-peng-you-du-neng-kan-kdauw/