11 leetcode题目总结

左神算法课程

递归复杂度估计 通用式子

在这里插入图片描述
在这里插入图片描述

这种不能使用:因为子问题规模不一样

在这里插入图片描述

对于归并排序:

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个数

快速排序和堆
https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/wei-ruan-mian-shi-jiao-xun-x-by-jerry_nju/

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)

https://leetcode-cn.com/problems/coin-change-2/solution/ling-qian-dui-huan-iihe-pa-lou-ti-wen-ti-dao-di-yo/

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. 最长重复子数组

https://leetcode-cn.com/problems/maximum-length-of-repeated-subarray/solution/zhe-yao-jie-shi-ken-ding-jiu-dong-liao-by-hyj8/

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/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值