剑指OfferII(专项突击版)Java 持续更新....

剑指 Offer(专项突击版)
刷题链接: https://leetcode-cn.com/problem-list/e8X3pBZi/?page=1

No.001 题目: 整数除法

1. 刷题链接:

https://leetcode.cn/problems/xoh6Oh/?favorite=e8X3pBZi

思路链接
https://leetcode.cn/problems/xoh6Oh/solutions/2058519/by-demouo-bnhc/

2. 思路:

题目要求只能使用加减法,那我们自然想到用减法实现除法,用“被减数”能减去几次“减数”来衡量最后的结果,这时候我们想到求x的幂次的快速解法,将x成倍成倍的求幂,这里将减数成倍成倍的增大,次数对应也是成倍成倍的增大。
为方便运算,我们需要将a,b都转为同正or同负,由于INT_MIN转正就越界了,我们只好都转负,这也是都转负的原因
有一种特殊情况 INT_MIN/(-1)就overflow了 所以直接特殊处理
最终结果的正负。

3. 代码:

public class Solution {
    public int divide(int a, int b) {
        int min = Integer.MIN_VALUE, max = Integer.MAX_VALUE, limit = Integer.MIN_VALUE / 2;
        boolean flag = ((a > 0 && b>0) || (a < 0 && b < 0)) ? true:false;
        if (a == min && b == -1){
            return max;
        }
        if(a > 0){
            a = -a;
        }
        if (b > 0){
            b = -b;
        }
        int ans = 0;
        // 这里a b 都是负数
        while (a <= b){
            int temp_b = b, count = 1;
            while(temp_b >= limit && temp_b + temp_b >= a){
                temp_b += temp_b;
                count += count;
            }
            a -= temp_b;
            ans += count;
        }
        return flag ? ans:-ans;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int result = solution.divide(15, 2);
        System.out.println("result: " + result);
    }
}

No.002 题目: 二进制加法

1. 刷题链接:

https://leetcode.cn/problems/JFETK5/?favorite=e8X3pBZi

2. 思路:

  • 思路: 首先获取字符串a, b的长度, 定义一个进位符号
  • 然后通过索引将得到字符串的每个字符(char类型), 和’0’求差
  • 进位等于当前a,b索引的值以及上一次的进位值
  • 每次运算的结果为 进位的值%2得到当前位置的值
  • up/2 重新调整进位值

3. 代码:

// 二进制加法
/*
* 思路: 首先获取字符串a, b的长度, 定义一个进位符号
* 然后通过索引将得到字符串的每个字符(char类型), 和'0'求差
* 进位等于当前a,b索引的值以及上一次的进位值
* 每次运算的结果为  进位的值%2得到当前位置的值
* up/2 重新调整进位值
* */
class Solution {
    public String addBinary(String a, String b) {
        StringBuilder sb = new StringBuilder();
        int len_a = a.length() -1, len_b = b.length() -1;
        int up = 0;
        while (len_a >= 0 || len_b >= 0 || up!=0){
            int i = len_a >=0 ? a.charAt(len_a--) - '0':0;
            int j = len_b >=0 ? b.charAt(len_b--) - '0':0;
            up = i + j + up;
            sb.append(up%2);
            up /=2;
        }
        return sb.reverse().toString();
    }
    public static void main(String[] args) {
        String a = "11";
        String b = "10";
        Solution solution = new Solution();
        String s = solution.addBinary(a, b);
        System.out.println(s);
    }
}

No.003 题目: 前 n 个数字二进制中 1 的个数

1. 刷题链接:

https://leetcode.cn/problems/w3tCBm/

2. 思路:

对于所有的数字,只有奇数和偶数两种:

  • 奇数:二进制表示中,奇数一定比前面那个偶数多一个 1,因为多的就是最低位的 1。
  • 偶数:二进制表示中,偶数中 1 的个数一定和除以 2 之后的那个数一样多。因为最低位是 0,除以 2 就是右移一位,也就是把那个 0 抹掉而已,所以 1 的个数是不变的。
    所以我们可以得到如下的状态转移方程:

dp[i] = dp[i-1] + 1,当i为奇数
dp[i] = dp[i/2],当i为偶数
上面的方程还可进一步合并为:
dp[i] = dp[i/2] + i % 2

通过位运算进一步优化:

i / 2 可以通过 i >> 1 得到;
i % 2 可以通过 i & 1 得到;

3. 代码:

// 前 n 个数字二进制中 1 的个数

public class Solution {
    public int[] countBits(int n) {
        int[] res = new int[n+1];
        for (int i = 0; i < n + 1; i++) {
            res[i] = res[i/2] + i % 2;
        }
        return res;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] ints = solution.countBits(5);
        System.out.println(Arrays.toString(ints));
    }
}

No.004 题目: 只出现一次的数字

4.1 刷题链接:

https://leetcode.cn/problems/WGki4K/

4.1.2 思路1:

使用哈希映射统计数组中每个元素的出现次数。对于哈希映射中的每个键值对,键表示一个元素,值表示其出现的次数。

在统计完成后,遍历哈希映射即可找出只出现一次的元素。

4.1.3 代码1:

class Solution {
   public int singleNumber(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for (Integer num:nums){
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        int res = 0;
        for (Integer num: map.keySet()){
            if (map.get(num) == 1){
                res = num;
                break;
            }
        }
        return res;
    }
}

4.2.2 思路2:

  1. 先进行排序 然后找出和前后位置不一样的数组的元组

4.2.3 代码2:

// 1. 先进行排序 然后找出和前后位置不一样的数组的元组
public class Solution {
    public int singleNumber(int[] nums) {
        if (nums.length ==1){
            return nums[0];
        }
        for (int i = 0; i < nums.length; i++) {
            boolean flag = true;
            for (int j = 0; j < nums.length-1; j++){
                if (nums[j] > nums[j+1]){
                    int temp = nums[j];
                    nums[j] = nums[j+1];
                    nums[j+1] = temp;
                    flag = false;
                }
            }
            if (flag) {
                break;
            }
        }
        if (nums[0]!=nums[1]){
            return nums[0];
        }else if(nums[nums.length-2]!=nums[nums.length-1]){
            return nums[nums.length-1];
        }
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != nums[i+1] && nums[i] != nums[i-1]){
                return nums[i];
            }
        }
        return 0;
    }

    public static void main(String[] args) {
        int[] nums = {2,2,2,-1,-1,-1,8,-7,0,-7,0,-7,0};
        Solution solution = new Solution();
        int i = solution.singleNumber(nums);
        System.out.println(i);
    }
}

No.005 题目: 单词长度的最大乘积

1. 刷题链接:

https://leetcode.cn/problems/aseY1I/

2. 思路:

为了得到单词长度的最大乘积,朴素的做法是,遍历字符串数组
words
words 中的每一对单词,判断这一对单词是否有公共字母,如果没有公共字母,则用这一对单词的长度乘积更新单词长度的最大乘积。
// 求两个单词是否含有相同单词可以利用位运算进行计算

3. 代码:

public class AppTest {
    public int maxProduct(String[] words) {
        int length = words.length;
        int[] masks = new int[length];
        for (int i = 0; i < length; i++) {
            String word = words[i];
            int wordLength = words[i].length();
            for (int j = 0; j < wordLength; j++) {
                /*
                *  这里表示 将1向左移动(word.charAt(j) - 'a')个位置
                *  a 表示 1
                *  b 表示 10
                *  c 表示 100
                *  ab表示为 11
                * */
                masks[i] |= 1 << (word.charAt(j) - 'a');
            }

        }
        int maxProduct = 0;
        for (int i = 0; i < length; i++) {
            for (int j = i+1; j <length; j++) {
                if ((masks[i] & masks[j]) == 0){
                    maxProduct =  Math.max(maxProduct, words[i].length() * words[j].length());
                }
            }
        }
        return maxProduct;
    }

    public static void main(String[] args) {
        String[] words = {"a","ab"};
        AppTest appTest = new AppTest();
        int i = appTest.maxProduct(words);
        System.out.println(i);
    }
}

No.006 题目: 排序数组中两个数字之和

1. 刷题链接:

https://leetcode.cn/problems/kLl5u1/

2. 思路:

  • 利用指针i, j分别指向数组numbers的头和尾
  • numbers[i] + numbers[j] = sum
  • sum < target 说明 整体要变大 i向右移动
  • sum > target 说明 整体要变笑 j向左移动

3. 代码:

/*
*  利用指针i, j分别指向数组numbers的头和尾
*  numbers[i] + numbers[j] = sum
*  sum < target 说明 整体要变大 i向右移动
*  sum > target 说明 整体要变笑 j向左移动
* */
class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int[] res = new int[2];
        int i = 0, j = numbers.length -1;
        while(i < j){
            int sum =  numbers[j] + numbers[i];
            if ( sum == target){
                res[0] = i;
                res[1] = j;
                return res;
            } else if(sum > target){
                j--;
            } else if (sum < target){
                i++;
            }
        }
        return res;
    }

    public static void main(String[] args) {
        int[] numbers = {1,2,4,9,10};
//        int[] numbers = {1,2,4,6};
        int target = 8;
        Solution solution = new Solution();
        int[] res = solution.twoSum(numbers, target);
        System.out.println(Arrays.toString(res));
    }
}

No.007 题目: 数组中和为 0 的三个数

1. 刷题链接:

https://leetcode.cn/problems/1fGaJU/

2. 思路:

首先排序,固定一个数,然后双指针。
有两个地方需要去重。
首先外层循环选择那个固定的数字时,如果出现当前数字和前一个数字相等的情况,需要跳过。
然后就是移动双指针时,左指针需要跳过其右侧所有相等的数,右指针也需要跳过其左侧所有相等的数。
理清了思路代码就容易写了。

3. 代码:

public class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        for (int i = 0; i <nums.length -2 ; i++) {
            if (i != 0 && nums[i]==nums[i-1]){
                continue;
            }
            int l = i+1, r = nums.length -1;
            while (l < r){
                if (nums[l] + nums[r] + nums[i] == 0){
                    ArrayList<Integer> list = new ArrayList<>();
                    list.add(nums[i]);
                    list.add(nums[l]);
                    list.add(nums[r]);
                    res.add(list);
                    int temp = nums[l];
                    while (l<r && temp == nums[l]){
                        l++;
                    }
                    temp = nums[r];
                    while (l<r && temp == nums[r]){
                        r--;
                    }
                } else if (nums[l] + nums[r] + nums[i] < 0){
                    // 比较小 说明要变大让左指针向右移动
                    l++;
                } else if (nums[l] + nums[r] + nums[i] > 0){
                    // 比较小 说明要变大让右指针向左移动
                    r--;
                }
            }
        }
        return res;
    }
    public static void main(String[] args) {
        int[] nums = {-1,0,1,2,-1,-4};
        Solution solution = new Solution();
        List<List<Integer>> lists = solution.threeSum(nums);
        System.out.println("list: " + lists);
    }
}

No.008 题目: 和大于等于 target 的最短子数组

1. 刷题链接:

链接: https://leetcode.cn/problems/2VG8Kg/?favorite=e8X3pBZi

2. 思路:

思路:

  • 利用双指针,左右指针同时指向做左端
  • 右指针一直向右移动直到数组和大于等于target 做加法
  • 左指针一直移动知道数组和小于target 并且对minLen进行赋值 做减法

3. 代码:

/*
* 链接: https://leetcode.cn/problems/2VG8Kg/?favorite=e8X3pBZi
* 和大于等于 target 的最短子数组
* 思路:
* 利用双指针,左右指针同时指向做左端
* 右指针一直向右移动直到数组和大于等于target 做加法
* 左指针一直移动知道数组和小于target 并且对minLen进行赋值 做减法
* */
public class Solution {

    // 利用while循环实现
    public int minSubArrayLen(int target, int[] nums) {
        if (nums.length == 0){
            return 0;
        }
        int start = 0, end = 0;
        int n = nums.length;
        int sum = 0;
        int minLen = Integer.MAX_VALUE;
        while (end < n){
            sum += nums[end];
            while (sum >= target){
                minLen = Math.min(minLen, end-start + 1);
                sum -= nums[start];
                start ++;
            }
            end++;
        }
        return minLen == Integer.MAX_VALUE ? 0 : minLen;
    }

    // for循环实现
    public int minSubArrayLen2(int target, int[] nums) {
        if (nums.length == 0){
            return 0;
        }
        int start = 0;
        int n = nums.length;
        int sum = 0;
        int minLen = Integer.MAX_VALUE;
        for (int end = 0; end < n; end++) {
            sum += nums[end];
            while (sum >= target){
                minLen = Math.min(minLen, end-start + 1);
                sum -= nums[start];
                start ++;
            }
        }
        return minLen == Integer.MAX_VALUE ? 0 : minLen;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int target = 7;
        int[] nums = {2, 3, 1, 2, 4, 3};
//        int[] nums = {1,4,4};
//        int[] nums = {4,1,4};
//        int[] nums = {1,1,1,1,1,1,1,1};
        int i = solution.minSubArrayLen2(target, nums);
        System.out.println(i);
    }
}

No.009 题目: 乘积小于 K 的子数组

1. 刷题链接:

链接: https://leetcode.cn/problems/ZVAVXX/

2. 思路:

  • 定义两个指针, 都指向左端
  • 右指针依次移动加1, 然后计算乘积
  • 然后移动左指针 直到乘积小于k 然后计算个数 计算总和

3. 代码:

/*
* 链接: https://leetcode.cn/problems/ZVAVXX/
* 乘积小于 K 的子数组
* 思路:
* 定义两个指针, 都指向左端
* 右指针依次移动加1, 然后计算乘积
* 然后移动左指针 直到乘积小于k 然后计算个数 计算总和
* */
public class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        int start = 0, end =0;
        int number = 0;
        int prob = 1;
        int n = nums.length;
        while (end < n){
            prob *= nums[end];
            while (start < end && prob >= k){
                prob /= nums[start];
                start++;
            }
            if (prob < k){
                number += end - start +1; // 因为此时end是固定的, start到end数组中的字数个数就是start的所有可能的取值,也就是长度
            }
            end++;
        }
        return number;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int target = 100;
//        int target = 2;
        int[] nums = {10,5,2,6, 8};
//        int[] nums = {2, 1, 1};
        int i = solution.numSubarrayProductLessThanK(nums, target);
        System.out.println(i);
    }
}

No.010 题目: 0 和 1 个数相同的子数组

1. 刷题链接:

2. 思路:

  • 利用两个for循环遍历所有的可能的数组判断他们的和是否为k

3. 代码:

/*
 * 链接: https://leetcode.cn/problems/A1NYOS/?favorite=e8X3pBZi
 * 0 和 1 个数相同的子数组
 * 思路:
 * 利用两个for循环遍历所有的可能的数组判断他们的和是否为k
 *
 * */
public class Solution {
    public int subarraySum(int[] nums, int k) {
        int n = nums.length;
        int sum = 0, number =0;
        for (int end = 0; end < n; end++){
            sum += nums[end];
            // 首先判断当前的sum 是不是符合要求的
            if (sum == k){
                number++;
            }
            int temp = sum;
            // 然后判断子数组是否是符合要求的
            for (int start = 0; start < end; start++){
                temp -= nums[start];
                if (temp == k){
                    number++;
                }
            }
        }
        return number;
    }

    public static void main(String[] args) {
        int[] nums = {1,2,3,0};
//        int[] nums = {1,1,1};
//        int[] nums = {1};
//        int k = 0;
//        int k = 2;
        int k = 3;
        Solution solution = new Solution();
        int i = solution.subarraySum(nums, k);
        System.out.println(i);
    }
}

No.011 题目: 0 和 1 个数相同的子数组

1. 刷题链接:

2. 思路:

  • 我们首先定义一个哈希表 我们把0换为-1那么就相当于是找和为0的最长子数组
  • 哈希表值 key表示当前子数组的和 遍历数组 如果是0(-1) 那么key–, 如果是1那么key++, 相同的key之前的长度说明01个数是相等的
  • value表示当前数组元素的索引, 那么minLen 就等于每个每个相同的key中将去第一个出现key的索引值(因为是最长子数组)
  • 所以maxLen = i - preIndex 这里不需要加一 因为可以设置一个初始key:value 0:-1 解决

3. 代码:

/*

 * 思路:
 * 我们首先定义一个哈希表 我们把0换为-1那么就相当于是找和为0的最长子数组
 * 哈希表值 key表示当前子数组的和 遍历数组 如果是0(-1) 那么key--, 如果是1那么key++, 相同的key之前的长度说明01个数是相等的
 * value表示当前数组元素的索引, 那么minLen 就等于每个每个相同的key中将去第一个出现key的索引值(因为是最长子数组)
 * 所以maxLen = i - preIndex 这里不需要加一 因为可以设置一个初始key:value 0:-1 解决
 * */
public class Solution {
    // 
    public int findMaxLength(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(0, -1);
        int key = 0;
        int maxLen = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == 0){ // 当成-1处理
                key--;
            }else{
                key++;
            }
            if (map.containsKey(key)){ // 如果包含那么得到key的value(离i最远的索引) 第一次出现这个值的索引
                int preIndex = map.get(key);
                maxLen = Math.max(maxLen, i - preIndex);
            }else{
                map.put(key, i);
            }
        }
        return maxLen;
    }
    /*
     * 首先每次遍历的得到span然后计算 和是不是当前子数组长度的一半
     * 如果是相对说明符合条件但是超时
     * */
    public int findMaxLength2(int[] nums) {
        int maxLen = 0;
        int n = nums.length;
        int sum = 0;
        for (int end = 0; end < n; end++){
            sum += nums[end];
            int temp = sum;
            for (int start = 0; start < end; start++){
                int span = end - start + 1;
                if (temp * 2 == span){
                    maxLen = Math.max(maxLen, span);
                }
                temp -= nums[start];
            }
        }
        return maxLen;
    }
    public static void main(String[] args) {
        int[] nums = {0, 1, 0, 1, 1, 1, 1, 0,0, 0, 0, 0, 0};
        Solution solution = new Solution();
        int maxLength = solution.findMaxLength(nums);
        System.out.println(maxLength);

    }
}

No.012 题目: 左右两边子数组的和相等

1. 刷题链接:

链接: https://leetcode.cn/problems/tvdfij/?favorite=e8X3pBZi

2. 思路:

  • 首先得到整个数组的和sum/temp_sum
  • 再一次遍历得到计算左边长度为i的数组和left,
  • 计算右边数组的和 right = temp_sum - left
  • 验证是否相等 如果相等说明就是中心下标

3. 代码:

/*
* 链接: https://leetcode.cn/problems/tvdfij/?favorite=e8X3pBZi
* 题目: 左右两边子数组的和相等
* 思路:
*   首先得到整个数组的和sum/temp_sum
*   再一次遍历得到计算左边长度为i的数组和left,
*   计算右边数组的和 right = temp_sum - left 
*   验证是否相等 如果相等说明就是中心下标
* */
public class Solution {
    public int pivotIndex(int[] nums) {
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum+= nums[i];
        }
        int left = 0;
        int right = 0;
        for (int i = 0; i < nums.length; i++) {
            int temp_num = sum;
            left+= nums[i];
            right = temp_num - left + nums[i];
            if (left == right){
                return i;
            }
        }
        return -1;
    }
    public static void main(String[] args) {
//        int[] nums = {1,7,3,6,5,6};
//        int[] nums = {1,2,3};
        int[] nums = {-1,-1,0,1,1,0};
        Solution solution = new Solution();
        int i = solution.pivotIndex(nums);
        System.out.println(i);
    }
}

No.013 题目: 二维子矩阵的和

1. 刷题链接:

2. 思路:

  • 设置一个二维数组成员变量
  • 对子数组传入的边界条件进行求和
  • 行为 row1 - row2
  • 列为 col1 - col2 即可

3. 代码:

/*
* 链接: https://leetcode.cn/problems/O4NDxx/?favorite=e8X3pBZi
* 题目: 二维子矩阵的和
* 思路:
*       设置一个二维数组成员变量
*       对子数组传入的边界条件进行求和
*       行为 row1 - row2
*       列为 col1 - col2 即可
* */
public class NumMatrix {
    public int[][] matrix;

    public NumMatrix(int[][] matrix) {
        this.matrix = matrix;
    }

    public int sumRegion(int row1, int col1, int row2, int col2) {
        int sum = 0;
        for (int i = row1; i <= row2; i++) {
            for (int j = col1; j <= col2; j++) {
                sum += matrix[i][j];
            }
        }
        return sum;
    }

    public static void main(String[] args) {

        int[][] matrix = {{3,0,1,4,2},
                {5,6,3,2,1},
                {1,2,0,1,5},
                {4,1,0,1,7},
                {1,0,3,0,5}};
        NumMatrix numMatrix = new NumMatrix(matrix);
        int[] index = {2,1,4,3};
        int i = numMatrix.sumRegion(index[0], index[1], index[2], index[3]);
        System.out.println(i);
    }
}

No.014 题目: 字符串中的变位词

1. 刷题链接:

链接: https://leetcode.cn/problems/MPnaiL/?favorite=e8X3pBZi

2. 思路:

  • 变位词: 指字母相同,但排列不同的字符串。 —> 两个字符串相同单词出现的次数一样;
  • 制订两个长度为26的数组 索引分别表示a-z的, 数组的值分别表示每个字母出现的次数
  • 遍历s2中每个长度为s1的子字符串 判断是否相等即可

3. 代码:

import java.util.Arrays;
/*
* 链接: https://leetcode.cn/problems/MPnaiL/?favorite=e8X3pBZi
* 题目: 字符串中的变位词
* 思路:
*   变位词:  指字母相同,但排列不同的字符串。 ---> 两个字符串相同单词出现的次数一样;
*   制订两个长度为26的数组 索引分别表示a-z的, 数组的值分别表示每个字母出现的次数
*   遍历s2中每个长度为s1的子字符串 判断是否相等即可
*
* */
public class Solution {
    public boolean checkInclusion(String s1, String s2) {
        if (s1.length() > s2.length()){
            return false;
        }
        int[] str_s1 = new int[26];
        int[] str_s2 = new int[26];
        int s1_len = s1.length();
        for (int i = 0; i < s1_len; i++) {
            int index_s1 = s1.charAt(i) - 'a';
            int index_s2 = s2.charAt(i) - 'a';
            str_s1[index_s1]++; // 对应的字母个数加一
            str_s2[index_s2]++; // 对应的字母个数加一
        }
        if (Arrays.equals(str_s1, str_s2)){
            return true;
        }
        int start = 0;
        int end = s1_len;
        while (end < s2.length()){
            // 加一个新的字符
            int index_start = s2.charAt(start) - 'a';
            str_s2[index_start]--;
            int index_end = s2.charAt(end) - 'a';
            str_s2[index_end]++;
            if (Arrays.equals(str_s1, str_s2)){
                return true;
            }
            start++;
            end++;
        }
        return false;
    }

    public static void main(String[] args) {
//        int[] num1 = {1, 2, 3, 4};
//        int[] num2 = {1, 2, 3, 4};
//        System.out.println(Arrays.equals(num1, num2));
//        String s1 = "ab",s2 = "eidboaooo";
        String s1 = "ab",s2 = "a";
//        String s1 = "adc",s2 = "dcda";
        Solution solution = new Solution();
        boolean flag = solution.checkInclusion(s1, s2);
        System.out.println(flag);
    }
}

No.015 题目: 字符串中的所有变位词

1. 刷题链接:

链接: https://leetcode.cn/problems/VabMRr/?favorite=e8X3pBZi

2. 思路:

  • 变位词: 指字母相同,但排列不同的字符串。 —> 两个字符串相同单词出现的次数一样;
  • 制订两个长度为26的数组 索引分别表示a-z的, 数组的值分别表示每个字母出现的次数
  • 遍历s2中每个长度为s1的子字符串 判断是否相等即可 相等则会变位词子串 将起始值添加到列表中

3. 代码:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/*
* 链接: https://leetcode.cn/problems/VabMRr/?favorite=e8X3pBZi
* 题目: 字符串中的所有变位词
* 思路:
*   变位词:  指字母相同,但排列不同的字符串。 ---> 两个字符串相同单词出现的次数一样;
*   制订两个长度为26的数组 索引分别表示a-z, 数组的值分别表示每个字母出现的次数
*   遍历s2中每个长度为s1的子字符串 判断是否相等即可 相等则会变位词子串 将起始值添加到列表中
* */
public class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> list = new ArrayList<>();
        if (s.length() < p.length()){
            return list;
        }
        int[] str_s = new int[26];
        int[] str_p = new int[26];
        int p_len = p.length();
        for (int i = 0; i < p_len; i++) {
            int index_s = s.charAt(i) - 'a';
            int index_p = p.charAt(i) - 'a';
            str_s[index_s]++; // 对应的字母个数加一
            str_p[index_p]++; // 对应的字母个数加一
        }
        int start = 0;
        int end = p_len;
        if (Arrays.equals(str_s, str_p)){
            list.add(start);
        }
        while (end < s.length()){
            // 加一个新的字符
            int index_start = s.charAt(start) - 'a';
            str_s[index_start]--;
            int index_end = s.charAt(end) - 'a';
            str_s[index_end]++;
            start++;
            end++;
            if (Arrays.equals(str_s, str_p)){
                list.add(start);
            }
        }
        return list;
    }
    public static void main(String[] args) {

        String p = "ab",s = "eidbaooo";
//        String s1 = "ab",s2 = "a";
//        String s1 = "adc",s2 = "dcda";
//        String s = "cbaebabacd", p = "abc";
        Solution solution = new Solution();
        List<Integer> list = solution.findAnagrams(s, p);
        System.out.println(list);
    }
}

No.016 题目: 不含重复字符的最长子字符串

1. 刷题链接:

链接: https://leetcode.cn/problems/wtcaE1/?favorite=e8X3pBZi

2. 思路:

  • 首先定义两个指针,和一个集合(因为集合有去重功能)
  • 左右指针最初指向0,刚开始右指针一直向右移动, 直到出现重复的字符之后 左指针开始移动, 设置最大长度

3. 代码:

import java.util.HashSet;
/*
* 链接: https://leetcode.cn/problems/wtcaE1/?favorite=e8X3pBZi
* 题目: 不含重复字符的最长子字符串
* 思路:
*   首先定义两个指针,和一个集合(因为集合有去重功能)
*   左右指针最初指向0,刚开始右指针一直向右移动, 直到出现重复的字符之后 左指针开始移动, 设置最大长度
* */
public class Solution {
    public int lengthOfLongestSubstring(String s) {
        HashSet<Character> set = new HashSet<>();
        int len = s.length(), end = 0;
        int maxLen = 0;
        for (int start = 0; start < len; start++) {
            if (start!=0){
                set.remove(s.charAt(start -1));
            }
            // 如果当前集合中不包含当前字符则可以提添加当前字符进入集合set中 知道出现重复字符则退出循环
            while (end < len && !set.contains(s.charAt(end))){
                set.add(s.charAt(end));
                end++;
            }
            // 重新设置最大长度 这里为什么是end-start 不用加一个 因为添加之后已经实现了end++;
            maxLen = Math.max(maxLen, end - start);
        }
        return maxLen;
    }
    public int lengthOfLongestSubstring2(String s) {
        int[] counter = new int[26];
        int start = 0, end=0, len = s.length();
        int maxLen = 0;
        while (end < len){
            int index = s.charAt(end) - 'a'; // 得到当前字符的索引
            if (counter[index] > 0){ // 判断是否在数组中
                counter[index]=0;
                start++;
            }
            counter[index]++;
            maxLen = Math.max(maxLen, end-start+1);
            end++;// 指针后移动 用下一轮
        }
        return maxLen;
    }

    public static void main(String[] args) {
//        String str = "pwwkew";
        String str = "abbbcdbbb";
        Solution solution = new Solution();
        int i = solution.lengthOfLongestSubstring(str);
        System.out.println(i);

    }
}

No.017 题目: 含有所有字符的最短字符串

1. 刷题链接:

链接: https://leetcode.cn/problems/M1oyTv/?favorite=e8X3pBZi

2. 思路:

  • 问题1: 如何判断字符串s是否包含字符串t, 问题2: 找出最短的字符串
    1. 将字符串s, t转化为哈希表s_map, t_map key为字符, value为字符出现的次数。依次遍历t_map的中key,
  • 那么s包含t的条件可以转化为 s_map包含t_map ==> t_map中的每个key s_map必须存在并且 s中的value必须大于等于t中的value
    1. 定义两个指针左指针指向第一个字符, 右指针指向第一个字符的前一个字符
  • 右指针移动条件 s_map不包含t_map
  • 左指针移动条件 s_map包含t_map

3. 代码:

import java.util.HashMap;
import java.util.Set;
/*
* 链接: https://leetcode.cn/problems/M1oyTv/?favorite=e8X3pBZi
* 题目: 含有所有字符的最短字符串
* 思路:
*   问题1: 如何判断字符串s是否包含字符串t, 问题2: 找出最短的字符串
*   1. 将字符串s, t转化为哈希表s_map, t_map key为字符, value为字符出现的次数。依次遍历t_map的中key, 
*   那么s包含t的条件可以转化为 s_map包含t_map ==> t_map中的每个key s_map必须存在并且 s中的value必须大于等于t中的value
*   2. 定义两个指针左指针指向第一个字符, 右指针指向第一个字符的前一个字符
*   右指针移动条件 s_map不包含t_map
*   左指针移动条件 s_map包含t_map
* */
public class Solution {
    public String minWindow(String s, String t) {
        HashMap<Character, Integer> s_map = new HashMap<>();
        HashMap<Character, Integer> t_map = new HashMap<>();
        for (int i = 0; i < t.length(); i++) { // 将字符串t转化为
            char t_c = t.charAt(i);
            t_map.put(t_c, t_map.getOrDefault(t_c, 0) + 1);
        }
        int left = 0, right = - 1, s_len=s.length(); // 左右指针
        int str_s = 0, str_e = 0, minLen  = Integer.MAX_VALUE; // 用于存放最短子字符串的索引
        while (right < s_len){
            right++;
            // t_map.containsKey(s.charAt(right) 这个不是必要条件, 为了提升效率用的 因为如果字符串t中不存在的字母,s中也没有必要存放
            if (right < s_len && t_map.containsKey(s.charAt(right))){
                char c = s.charAt(right);
                s_map.put(c, s_map.getOrDefault(c, 0) + 1);
            }
            // 检查字符串s是否包含字符串t, 包含则保存改子串的索引
            while (isContains(s_map, t_map) && left <= right){
                int span = right - left + 1; // 长度
                if (span < minLen){
                    minLen = Math.min(span, minLen);
                    str_s = left;
                    str_e = left + minLen;
                }
                if (t_map.containsKey(s.charAt(left))){ // 同理不是必要条件,为了提升效率, 如果存在删除该字符然后左指针向右移动
                    s_map.put(s.charAt(left), s_map.getOrDefault(s.charAt(left), 0) -1);
                }
                left++;
            }
        }

        return minLen == Integer.MAX_VALUE ? "":s.substring(str_s, str_e);
    }

    public boolean isContains(HashMap<Character, Integer> s_map, HashMap<Character, Integer> t_map){
        Set<Character> characters = t_map.keySet();
        for (Character character:characters){
            Integer value = t_map.get(character);
            if (s_map.getOrDefault(character, 0) < value){ // 如果s_map中各个数小于t_map 说明不包含
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        String s = "A", t = "A";
//        System.out.println();
        Solution solution = new Solution();
        char c = s.charAt(0);
//        System.out.println(c);
//        System.out.println('a'+0);
//        System.out.println('Z'+0);
//        solution.minWindow()
        String s1 = solution.minWindow(s, t);
        System.out.println(s1);
    }
}

No.018 题目: 有效的回文

1. 刷题链接:

链接: https://leetcode.cn/problems/XltzEq/?favorite=e8X3pBZi

2. 思路:

  • 回文: 回文串就是字符串中心对称 ==> 正序等于逆序
  • 过率非字母和字符串,将字母和数字转化为StringBuilder 验证正序是否等于逆序
    方法2:
  • 左右指针 左第一个 右最后一个 依次比较 不同则不是回文 左索引<=右索引

3. 代码:

/*
* 链接: https://leetcode.cn/problems/XltzEq/?favorite=e8X3pBZi
* 题目: 有效的回文
* 思路:
*   回文: 回文串就是字符串中心对称 ==> 正序等于逆序
*   过率非字母和字符串,将字母和数字转化为StringBuilder 验证正序是否等于逆序
*  方法2: 
*   左右指针 左第一个 右最后一个 依次比较 不同则不是回文 左索引<=右索引
* */
class Solution {
    public boolean isPalindrome(String s) {
        String new_s = s.toLowerCase();
        StringBuilder sb = new StringBuilder();
        StringBuilder sb_before = new StringBuilder();
        for (int i = 0; i < new_s.length(); i++) {
            int span = new_s.charAt(i) - 'a';
            int num_span = new_s.charAt(i) - '0';
            if ( (span >= 0 && span < 26) || (num_span >= 0 && num_span < 10)){
                sb.append(new_s.charAt(i));
                sb_before.append(new_s.charAt(i));
            }
        }
        sb.reverse();
        System.out.println(sb);
        System.out.println(sb_before);
        return  sb.toString().equals(sb_before.toString());
    }

    public static void main(String[] args) {
//        String s = "aba";
        String s = "0p";
        Solution solution = new Solution();
        boolean palindrome = solution.isPalindrome(s);
        System.out.println(palindrome);
    }
}

No.019 题目: 最多删除一个字符得到回文

1. 刷题链接:

链接: https://leetcode.cn/problems/RQku0D/?favorite=e8X3pBZi

2. 思路:

  • 判断是否回文: 左右指针 左第一个 右最后一个 依次比较 不同则不是回文 左索引<=右索引
  • 再次设置左右指针 找到第一个左右不相等的索引(1个或者2个), 然后保存删除查看得到新的字符串进行回文判断

3. 代码:

/*
* 
* 链接: https://leetcode.cn/problems/RQku0D/?favorite=e8X3pBZi
* 题目: 最多删除一个字符得到回文
* 思路:
*   判断是否回文: 左右指针 左第一个 右最后一个 依次比较 不同则不是回文 左索引<=右索引
*   再次设置左右指针 找到第一个左右不相等的索引(1个或者2), 然后保存删除查看得到新的字符串进行回文判断
* */
public class Solution {
    public boolean IsPalindrome(String s){
        int start = 0, end = s.length() -1;
        while (start < end){
            if (s.charAt(start) != s.charAt(end)){
                return false;
            }
            start++;
            end--;
        }
        return true;
    }
    public boolean IsPalindrome2(String s){
        StringBuilder sb_forward = new StringBuilder(s);
        StringBuilder sb_back = new StringBuilder(s);
        return sb_forward.toString().equals(sb_back.reverse().toString());
    }
    // 设置左右指针 如果
    public boolean validPalindrome(String s) {
        int start = 0, end = s.length() -1, count = 0;
        int temp_l = 0, temp_r = 0;
        while (start < end){
            if (s.charAt(start) != s.charAt(end)){
                if (count == 0){
                    temp_l = start;
                    temp_r = end;
                }
                count++;
            }
            start++;
            end--;
        }
        if (count == 0){
            return true;
        }else{
//            System.out.println("temp_l: " + temp_l);
//            System.out.println("temp_r: " + temp_r);
            StringBuilder sb_1 = new StringBuilder(s);
            StringBuilder sb_2 = new StringBuilder(s);
            sb_1.deleteCharAt(temp_l);
            sb_2.deleteCharAt(temp_r);
            boolean flag_l = IsPalindrome(sb_1.toString());
            boolean flag_r = IsPalindrome(sb_2.toString());
            return flag_l || flag_r;
        }
    }

    public static void main(String[] args) {
        String s = "aba";
        Solution solution = new Solution();
        boolean b = solution.validPalindrome(s);
        System.out.println(b);

    }
}

No.020 题目: 回文子字符串的个数

1. 刷题链接:

链接: https://leetcode.cn/problems/a7VOhD/?favorite=e8X3pBZi

2. 思路:

思路链接: https://leetcode.cn/problems/a7VOhD/solution/hui-wen-zi-zi-fu-chuan-de-ge-shu-by-leet-ejfv/
回文子字符串的个数

  • 枚举出所有的子串,然后再判断这些子串是否是回文;
  • 枚举每一个可能的回文中心,然后用两个指针分别向左右两边拓展,当两个指针指向的元素相同的时候就拓展,否则停止拓展。

3. 代码:

/*
链接: https://leetcode.cn/problems/a7VOhD/?favorite=e8X3pBZi
回文子字符串的个数
* 找到所有的回文中心然后扩散
* 这里是把奇数偶数统一在一起了
* */
public class Solution {
    public int countSubstrings(String s) {
        int n = s.length(); 
        int res = 0;        
        for (int i = 0; i < 2*n-1; i++) {    // i表示的是所有的回文中心个数
            int li = i / 2, ri = li + i % 2; // 
            while (li >= 0 && ri < n && s.charAt(li) == s.charAt(ri)){ // 没扩散一次回文数加1
                li--;
                ri++;
                res++;
            }
        }
        return res;
    }

    public static void main(String[] args) {
        String s = "aaa";
        Solution solution = new Solution();
        int i = solution.countSubstrings(s);
        System.out.println(i);
    }
}

No.021 题目: 删除链表的倒数第 n 个结点

1. 刷题链接:

https://leetcode.cn/problems/SLwz0R/

2. 思路:

注意: 由于倒数第n个节点就是正数第Len-n+1个节点。
我们首先从头节点开始对链表进行一次遍历,得到链表的长度Len 。随后我们再从头节点开始对链表进行一次遍历,当遍历到第 Len−n+1 个节点时,它就是我们需要删除的节点。

  1. 首先我们先设置一个头节点preHead放在head之前。(因为被删除的可能是head节点 所以先保存)
  2. 首先通过遍历len-n次得到被删除节点的前一个节点tempNode
  3. 进行tempNode.next = tempNode.next.next; 操作删除节点
  4. 返回preHead.next, 这里不能直接返回head, 因为head可能被删除了, 然后你还返回head就会有问题

3. 代码:

ac

class Solution {
   public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode preHead = new ListNode(0, head);
        ListNode tempNode = preHead;
        int length = getLength(head); // 链表长度
        int target = length - n;      // 这里指的是索引
        for (int i = 0; i < target; i++) {
            tempNode = tempNode.next;
        }
        ListNode currentNode = tempNode.next;
        tempNode.next = currentNode.next; // currentNode是需要删除的节点
        return preHead.next; // 返回删除指定节点之后的头节点
    }
    // 得到链表长度
    public int getLength(ListNode head){
        int len = 0;
        while (head != null){
            head = head.next;
            len++;
        }
        return len;
    }
}

整体可以运行的代码

public class Solution {
    ListNode head = new ListNode();

    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode preHead = new ListNode(0, head);
        ListNode tempNode = preHead;
        int length = getLength(head); // 链表长度
        int target = length - n;      // 这里指的是索引
        for (int i = 0; i < target; i++) {
            tempNode = tempNode.next;
        }
        ListNode currentNode = tempNode.next;
        System.out.println("currentNode: " + currentNode.val);
        tempNode.next = currentNode.next; // currentNode是需要删除的节点
        return preHead.next; // 返回删除指定节点之后的头节点  这里不能直接返回head, 因为head可能被删除了, 然后你还返回head
    }
    // 得到链表长度
    public int getLength(ListNode head){
        int len = 0;
        while (head != null){
            System.out.println("val: " + head.val);
            head = head.next;
            len++;
        }
        return len;
    }
    // 尾插入构建链表
    public void tailInsert(ListNode node){
        if (head.next == null){
            head.next = node;
            return;
        }
        ListNode tail = head.next;
        while (tail.next != null){
            tail = tail.next;
        }
        tail.next = node;
    }
    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] nums = {1, 2};
        int n = 2;
        for (int i = 0; i < nums.length; i++) {
            if (i == 0){
                solution.head = new ListNode(nums[i]);
            }else{
                solution.tailInsert(new ListNode(nums[i]));
            }
        }
        ListNode listNode = solution.removeNthFromEnd(solution.head, n);
        System.out.println("------------------------------------");
        solution.getLength(listNode);

    }
}
class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}

No.022 链表中环的入口节点

1. 刷题链接:

链接: https://leetcode.cn/problems/c32eOV/

2. 思路:

  1. 通过集合存放节点, 遍历链表 每个有一直没有重复的说明 没有环
  2. 如果有重复的 说明第一个重复的节点就是入环节点

3. 代码:

ac代码:

public class Solution {
	public ListNode detectCycle(ListNode head) {
        ArrayList<ListNode> nodeSet = new ArrayList<>();
        ListNode temp = head;
        while(temp != null){
            if (!nodeSet.contains(temp)){
                nodeSet.add(temp);
            }else{
                return temp;
            }
            temp = temp.next;
        }
        return null;
    }
}

完整代码:


import java.util.ArrayList;
/*
 *
 * 链接: https://leetcode.cn/problems/c32eOV/
 * 题目: 链表中环的入口节点
 * 思路:
 *       通过集合存放节点, 遍历链表 每个有一直没有重复的说明 没有环
 *       如果有重复的 说明第一个重复的节点就是入环节点
 *
 * */
class ListNode {
    int val;
    ListNode next;
    ListNode(int x) {
        val = x;
        next = null;
    }
}
public class Solution {
    ListNode head;
    public ListNode findNode(ListNode head, int n){
        ListNode preHead = new ListNode(0);
        preHead.next = head;
        ListNode temp = preHead;
        int length = getLength(head);
        for (int i = 0; i < n; i++) {
            temp = temp.next;
        }
        System.out.println(temp.val);
        return temp.next;
    }
    public ListNode detectCycle(ListNode head) {
        ArrayList<ListNode> nodeSet = new ArrayList<>();
        ListNode temp = head;
        while(temp != null){
            if (!nodeSet.contains(temp)){
                System.out.println("test value: " + temp.val);
                nodeSet.add(temp);
            }else{
                System.out.println("rukou value: " + temp.val);
                return temp;
            }
            temp = temp.next;
        }
        System.out.println("return null");
        return null;
    }
    public void addRing(ListNode head, int n){
        ListNode tail = head;
        while (tail.next != null){
            tail = tail.next;
        }
        System.out.println("last value" + tail.val );
        ListNode preHead = new ListNode(0);
        preHead.next = head;
        ListNode temp = preHead;
        int length = getLength(head);
        for (int i = 0; i <=n; i++) {
            temp = temp.next;
        }
        System.out.println("check value: "+temp.val);
        if (n != -1){
            tail.next = temp;
        }
    }
    //得到链表长度
    public int getLength(ListNode head){
        int len = 0;
        ListNode temp = head;
        while (temp != null){
            System.out.println("value: " + temp.val);
            temp = temp.next;
            len ++;
        }
        return len;
    }

    // 尾插法
    public void tailInsert(ListNode node){
        if (head.next == null){
            head.next = node;
            return;
        }
        ListNode tail = head.next;
        while (tail.next != null){
            tail = tail.next;
        }
        tail.next = node;
    }
    public static void main(String[] args) {
        Solution solution = new Solution();

        int[] nums = {3,2,0,-4};
        int pos = -1;
        for (int i = 0; i < nums.length; i++) {
            if (i == 0){
                solution.head = new ListNode(nums[i]);
            }else{
                solution.tailInsert(new ListNode(nums[i]));
            }
        }
        System.out.println(solution.head.val);
        solution.getLength(solution.head);
        solution.findNode(solution.head, 1);
        solution.addRing(solution.head, pos);
//        solution.getLength(solution.head);
        System.out.println("---------------------------------");
        solution.detectCycle(solution.head);
    }
}

No.023 题目: 两个链表的第一个重合节点

1. 刷题链接:

链接: https://leetcode.cn/problems/3u1WK4/?favorite=e8X3pBZi

2. 思路:

  • 首先将第一个链表A的所有节点保存在集合set中,
  • 遍历链表B的所有节点,然后判断集合中是否含有该节点,如果含有那么直接返回说是交点

3. 代码:

ac代码:

public class Solution {
	public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        HashSet<ListNode> setA = new HashSet<>();
        ListNode tempA = headA;
        while (tempA != null){
            setA.add(tempA);
            tempA = tempA.next;
        }// A中的所有链表都放入到集合中
        ListNode tempB = headB;
        while (tempB != null){
            if (setA.contains(tempB)){
//                System.out.println("final value: " + tempB.val);
                return tempB;
            }
            tempB = tempB.next;
        }
        return null;
    }
}

完整代码可运行

package OfferSecond.S_023;

import java.util.HashSet;
/*
 * 链接: https://leetcode.cn/problems/3u1WK4/?favorite=e8X3pBZi
 * 题目: 两个链表的第一个重合节点
 * 思路:
 *  首先将第一个链表A的所有节点保存在集合set中,
 *  遍历链表B的所有节点,然后判断集合中是否含有该节点,如果含有那么直接返回说是交点
 *
 * */
class ListNode {
    int val;
    ListNode next;
    ListNode(int x) {
        val = x;
        next = null;
    }
}

public class Solution {

    // 找到链表中第n个节点
    public ListNode findNode(ListNode head, int n){
        ListNode preHead = new ListNode(0);
        preHead.next = head;
        ListNode temp = preHead;
        int length = getLength(head);
        for (int i = 0; i < n; i++) {
            temp = temp.next;
        }
        System.out.println(temp.val);
        return temp;
    }
    //得到链表长度
    public int getLength(ListNode head){
        int len = 0;
        ListNode temp = head;
        while (temp != null){
            System.out.println("value: " + temp.val);
            temp = temp.next;
            len ++;
        }
        return len;
    }

    // 尾插法
    public void tailInsert(ListNode head, ListNode node){
        if (head.next == null){
            head.next = node;
            return;
        }
        ListNode tail = head.next;
        while (tail.next != null){
            tail = tail.next;
        }
        tail.next = node;
    }
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        HashSet<ListNode> setA = new HashSet<>();
        ListNode tempA = headA;
        while (tempA != null){
            setA.add(tempA);
            tempA = tempA.next;
        }// A中的所有链表都放入到集合中
        ListNode tempB = headB;
        while (tempB != null){
            if (setA.contains(tempB)){
//                System.out.println("final value: " + tempB.val);
                return tempB;
            }
            tempB = tempB.next;
        }
        return null;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] listA = {4,1,8,4,5};
        int[] listB = {5,0,1,8,4,5};
        int skipA = 2, skipB = 3;
        int n = 2;
        ListNode headA = null;
        ListNode headB = null;
        for (int i = 0; i < skipA; i++) {
            if (i == 0){
                headA = new ListNode(listA[i]);
            }else{
                solution.tailInsert(headA, new ListNode(listA[i]));
            }
        }
        for (int i = 0; i < skipB; i++) {
            if (i == 0){
                headB = new ListNode(listB[i]);
            }else{
                solution.tailInsert(headB, new ListNode(listB[i]));
            }
        }
        solution.getLength(headA);
        System.out.println("-------------------------------------");
        solution.getLength(headB);
        ListNode nodeA = solution.findNode(headA, skipA);
        ListNode nodeB = solution.findNode(headB, skipB);
        System.out.println("nodeA value: " + nodeA.val);
        System.out.println("nodeB value: " + nodeB.val);
        int length = listA.length;
        System.out.println(length);
        ListNode publicHead = null;
        for (int i = skipA; i < length; i++) {
            if (i == skipA){
                publicHead = new ListNode(listA[skipA]);
                nodeA.next = publicHead;
                nodeB.next = publicHead;
            }else{
                solution.tailInsert(publicHead, new ListNode(listA[i]));
            }
        }
        System.out.println("==============================================");
        solution.getLength(headA);
        System.out.println("==============================================");
        solution.getLength(headB);
        System.out.println("构造完毕");
        System.out.println("==============================================");

        ListNode resNode = solution.getIntersectionNode(headA, headB);
        solution.getLength(resNode);


    }
}

No.024 题目: 反转链表

1. 刷题链接:

链接: https://leetcode.cn/problems/UHnkqh/?favorite=e8X3pBZi

2. 思路:

首先遍历链表中的每个节点 然后就是头插法, 定义一个头节点依次插入当前节点的值

3. 代码:

ac代码:

    public int getLength(ListNode head){
        int len = 0;
        ListNode temp = head;
        while(temp != null ){
            System.out.println("node value: " + temp.val);
            temp = temp.next;
            len++;
        }
        return len;
    }
    public ListNode reverseList(ListNode head) {
        ListNode preHead = new ListNode(0);
        ListNode temp = head;
        while (temp != null){
//            System.out.println("reverseList: " + temp.val);
//            System.out.println(temp.next.val);
            headInsert(preHead, new ListNode(temp.val));
            temp = temp.next;
        }
        return preHead.next;
    }

完整代码可运行

package OfferSecond.S_024;


/*
* 链接: https://leetcode.cn/problems/UHnkqh/?favorite=e8X3pBZi
* 题目: 反转链表
* 思路
*   首先遍历链表中的每个节点 然后就是头插法, 定义一个头节点依次插入当前节点的值
*
* */
class ListNode {
    int val;
    ListNode next;

    ListNode() {
    }

    ListNode(int val) {
        this.val = val;
    }

    ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}
public class Solution {

    public int getLength(ListNode head){
        int len = 0;
        ListNode temp = head;
        while(temp != null ){
            System.out.println("node value: " + temp.val);
            temp = temp.next;
            len++;
        }
        return len;
    }
    public ListNode reverseList(ListNode head) {
        ListNode preHead = new ListNode(0);
        ListNode temp = head;
        while (temp != null){
//            System.out.println("reverseList: " + temp.val);
//            System.out.println(temp.next.val);
            headInsert(preHead, new ListNode(temp.val));
            temp = temp.next;
        }
        return preHead.next;
    }

    // 头插法
    public void headInsert(ListNode preHead, ListNode node){
        node.next = preHead.next;
        preHead.next = node;
    }
    
    // 尾插法
    public void tailInsert(ListNode head, ListNode node){
        if (head.next == null){
            head.next = node;
            return;
        }
        ListNode tail = head.next;
        while (tail.next != null){
            tail = tail.next;
        }
        tail.next = node;
    }
    public static void main(String[] args) {
        int[] nums = {1,2,3,4,5};
        Solution solution = new Solution();
        ListNode head = null;
        for (int i = 0; i < nums.length; i++) {
            if (i == 0){
                head = new ListNode(nums[i]);
            }else{
                solution.tailInsert(head, new ListNode(nums[i]));
            }
        }

        int length = solution.getLength(head);
        System.out.println("length: " + length);
        ListNode preHead = new ListNode(0); // 头节点
        System.out.println("length: " + nums.length);
        for (int i = 0; i < nums.length; i++) {
            solution.headInsert(preHead, new ListNode(nums[i]));
        }
        System.out.println("===============================================================");
        solution.getLength(preHead.next);
        System.out.println("===============================================================");
        ListNode newHead = solution.reverseList(head);
        System.out.println(newHead.val);
        solution.getLength(newHead);
    }
}

No.025 题目: 链表中的两数相加

1. 刷题链接:

链接: https://leetcode.cn/problems/lMSNwu/

2. 思路:

1.首先将两个链表转化为数组 同时设置一个进位变量
2.从后依次得到链表的值
3.然后调用头插法构建链表

3. 代码:

ac代码:

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        int[] arr1 = new int[101];
        int[] arr2  =new int[101];
        ListNode temp1= l1;
        ListNode temp2= l2;
        int len1 = 0, len2 = 0;
        while(temp1!= null){
            arr1[len1++] = temp1.val;
            temp1 =temp1.next;
        }
        while(temp2!=null){
            arr2[len2++] = temp2.val;
            temp2 = temp2.next;
        }
        len1--;
        len2--;
        boolean up = false;
        ListNode preHead = new ListNode(0);
        while ( len1 >= 0 || len2 >= 0 || up){
            int currentSum = arr1[len1>=0? len1--:100] + arr2[len2>=0? len2--:100] + (up?1:0);
            up = currentSum >= 10;
            int currentValue = currentSum % 10; // 得到个位数的值
            ListNode node = new ListNode(currentValue);
            // 头插法
            node.next = preHead.next;
            preHead.next = node;

        }
        return preHead.next;
    }
}

完整代码

package OfferSecond.S_025;


/*
* 链接: https://leetcode.cn/problems/lMSNwu/
* 题目: 链表中的两数相加
* 思路: 
*   首先将两个链表转化为数组 同时设置一个进位变量
*   从后依次得到链表的值
*   然后调用头插法构建链表
* */

class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}

public class Solution {
    
    // 得到链表长度
    public int getLength(ListNode head){
        int len = 0;
        ListNode temp = head;
        while(temp != null ){
            System.out.println(temp.val);
            temp = temp.next;
            len++;
        }
        return len;
    }
    // 尾插法
    public void tailInsert(ListNode head, ListNode node){
        if (head.next == null){
            head.next = node;
            return;
        }
        ListNode tail = head.next;
        while (tail.next != null){
            tail = tail.next;
        }
        tail.next = node;
    }
    // 链表相加
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        int[] arr1 = new int[101];
        int[] arr2  =new int[101];
        ListNode temp1= l1;
        ListNode temp2= l2;
        int len1 = 0, len2 = 0;
        while(temp1!= null){
            arr1[len1++] = temp1.val;
            temp1 =temp1.next;
        }
        while(temp2!=null){
            arr2[len2++] = temp2.val;
            temp2 = temp2.next;
        }
//        System.out.println(len1);
//        System.out.println(len2);
        len1--;
        len2--;
        boolean up = false;
        ListNode preHead = new ListNode(0);
        while ( len1 >= 0 || len2 >= 0 || up){
            int currentSum = arr1[len1>=0? len1--:100] + arr2[len2>=0? len2--:100] + (up?1:0);
            up = currentSum >= 10;
            int currentValue = currentSum % 10; // 得到个位数的值
            ListNode node = new ListNode(currentValue);
            // 头插法
            node.next = preHead.next;
            preHead.next = node;

        }
        return preHead.next;
    }
    public static void main(String[] args) {
        int[] l1 = {7,2,4,3}, l2 = {5,6,4};
        Solution solution = new Solution();
        ListNode headA = null;
        for (int i = 0; i < l1.length; i++) {
            if (i == 0){
                headA = new ListNode(l1[i]);
            }else{
                solution.tailInsert(headA, new ListNode(l1[i]));
            }
        }
        ListNode headB = null;
        for (int i = 0; i < l2.length; i++) {
            if (i == 0){
                headB = new ListNode(l2[i]);
            }else{
                solution.tailInsert(headB, new ListNode(l2[i]));
            }
        }
        solution.getLength(headA);
        System.out.println("==========================");
        solution.getLength(headB);
        System.out.println("==========================");
        ListNode listNode = solution.addTwoNumbers(headA, headB);
        System.out.println("==========================");
        solution.getLength(listNode);
    }
}

No.026 题目: 重排链表

1. 刷题链接:

链接: https://leetcode.cn/problems/LGjMqU/

![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/64b922047f1475bb8854cce58fa3edc0.png)

2. 思路:

首先将节点用链表存储起来 然后将节点放入到链表中
放入列表中定义两个指针 左指针和右指针 然后将右边节点插入左边节点

3. 代码:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/*
* 链接: https://leetcode.cn/problems/LGjMqU/
* 题目: 重排链表
* 思路:
*   首先将节点用链表存储起来 然后将节点放入到链表中
*   放入列表中定义两个指针 左指针和右指针 然后将右边节点插入左边节点的右边
* */
class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}

public class Solution {
    // 得到链表长度
    public int getLength(ListNode head){
        int len = 0;
        ListNode temp = head;
        while(temp != null ){
            System.out.println("node value: " + temp.val);
            temp = temp.next;
            len++;
        }
        return len;
    }
    // 尾插法
    public void tailInsert(ListNode head, ListNode node){
        if (head.next == null){
            head.next = node;
            return;
        }
        ListNode tail = head.next;
        while (tail.next != null){
            tail = tail.next;
        }
        tail.next = node;
    }
    // 重排链表
    public void reorderList(ListNode head) {
        ListNode temp = head;
        int len = 0;
        List<ListNode> list = new ArrayList<>();
        while (temp != null){
            list.add(temp);
            len++;
            temp = temp.next;
        }
        int left = 0, right = len -1;
        while (left < right){
            list.get(right).next = list.get(left).next;
            list.get(left).next = list.get(right);
            left++;
            right--;
        }
        list.get(left).next = null;
        getLength(head);

    }

    public static void main(String[] args) {
        int[] l1 = {1,2,3,4};
        int len = l1.length;
        int[] newArr = new int[l1.length];
        Solution solution = new Solution();
        ListNode headA = null;
//       ListNode headB = null;
        for (int i = 0; i < l1.length; i++) {
            if (i == 0){
                headA = new ListNode(l1[i]);
            }else{
                solution.tailInsert(headA, new ListNode(l1[i]));
            }
        }
        System.out.println("==========================");
//        boolean palindrome = solution.isPalindrome(headA);
//        System.out.println("是否回文: " + palindrome)
        System.out.println("len:" + len);
        for (int i = 0; i < newArr.length; i++) {
            if (i%2 == 0){
                newArr[i] = l1[i/2];
            }else{
                System.out.println("test: " + (len-i)/2);
                newArr[i] = l1[len-1-i/2];
            }
        }
        System.out.println(Arrays.toString(l1));
        System.out.println(Arrays.toString(newArr));
        System.out.println("====================");
        solution.reorderList(headA);

    }
}

No.027 题目: 回文链表

1. 刷题链接:

链接: https://leetcode.cn/problems/aMhZSa/

2. 思路:

将链表转化为数组,
然后进一步判断该数组是否是回文数组
设置左右指针,一个左移一个右移动

3. 代码:

package OfferSecond.S_027;

import java.util.Arrays;
/*
* 链接: https://leetcode.cn/problems/aMhZSa/
* 题目: 回文链表
* 思路: 
*   将链表转化为数组, 然后进一步判断该数组是否是回文数组
* */
class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}

public class Solution {
    // 得到链表长度
    public int getLength(ListNode head){
        int len = 0;
        ListNode temp = head;
        while(temp != null ){
            System.out.println("node value: " + temp.val);
            temp = temp.next;
            len++;
        }
        return len;
    }
    // 尾插法
    public void tailInsert(ListNode head, ListNode node){
        if (head.next == null){
            head.next = node;
            return;
        }
        ListNode tail = head.next;
        while (tail.next != null){
            tail = tail.next;
        }
        tail.next = node;
    }

    // S_027
    public boolean isPalindrome(ListNode head) {
        int length = getLength(head);
        int[] arr = new int[length];
        int len = 0;
        ListNode temp = head;
        while (temp != null){
            arr[len++] = temp.val;
            temp = temp.next;
        }
        System.out.println("len: "+ len);
        int left = 0, right = len-1;
        while (left<right){
            if (arr[left] != arr[right]){
                return false;
            }
            left++;
            right--;
        }

//        System.out.println(Arrays.toString(arr));
//        System.out.println();
        return true;
    }
    public static void main(String[] args) {
       int[] l1 = {1,2,3,3,2};
       Solution solution = new Solution();
       ListNode headA = null;
//       ListNode headB = null;
       for (int i = 0; i < l1.length; i++) {
           if (i == 0){
               headA = new ListNode(l1[i]);
           }else{
               solution.tailInsert(headA, new ListNode(l1[i]));
           }
       }
        System.out.println("==========================");
        boolean palindrome = solution.isPalindrome(headA);
        System.out.println("是否回文: " + palindrome);


    }
}

No.028 题目: 展平多级双向链表

1. 刷题链接:

链接: https://leetcode.cn/problems/Qv1Da2/?favorite=e8X3pBZi

2. 思路:

3. 代码:

/*
* 链接: https://leetcode.cn/problems/Qv1Da2/?favorite=e8X3pBZi
* 题目: 展平多级双向链表
* 思路:
*   使用深度优先遍历进行解决:
*   要点: 如果当前节点temp有子链表, 展开后那么这个子链表的尾节点childNode.next需要指向temp.next
*   1. 利用遍历链表顶层的中的每个节点同时判断当前节点是否有子节点
*       每一层级设置一个tail指向当前层级链表的尾节点
*       1.1 如果当前节点没有子链表,执行指向下一个指针进行遍历
*       1.2 如果当前节点有子链表
*           1.2.1 保存当前节点temp的next
*               1. 拼接当前节点temp和子节点temp.child
*               2. 用于tail和temp.next ---> tail.next = temp.next &&  temp.next.pre = tail
*
*           设置成功之后当前节点的child需要置为空, 更新尾指针
*   总结: 展平多级双向链表之后 当前节点何其子节点变成了前后节点关系
*               子链表的尾节点应当指向当前节点的下一个节点
*
* */
class Node {
    public int val;
    public Node prev;
    public Node next;
    public Node child;
};
public class Solution {
    // 遍历数组
    public Node flatten(Node head) {
        depthFirstSearch(head);
        return head;
    }
    // 深度优先遍历
    public Node depthFirstSearch(Node node){
        Node temp = node;
        Node tail = null; // 指向当前层级的尾指针   当前层的尾指针的下一个节点就是上一层的下一个节点
        while (temp != null){
            Node nextNode = temp.next;
            if (temp.child != null){ // 判断是否有子节点
                Node childTail = depthFirstSearch(temp.child);

                // 当前节点拼接子节点
                temp.next = temp.child;
                temp.child.prev = temp;

                if (nextNode != null){ // 说明后面还有节点 尾部需要操作
                    childTail.next = nextNode;
                    nextNode.prev = childTail;
                }

                // 遍历之后 重新设置
                temp.child = null; // 表示双链表没有子节点, 表面上没有错,但是不符合先验知识
                tail = childTail;

            }else{ // 没有自己节点和普通链表一样进行遍历
                tail = temp; // 更新尾指针的位置
            }
            temp = nextNode;
        }
        return tail;
    }

    public static void main(String[] args) {

    }
}

No.029 题目: 排序的循环链表

1. 刷题链接:

链接: https://leetcode.cn/problems/4ueAj6/?favorite=e8X3pBZi

2. 思路:

3. 代码:

/*
* 链接: https://leetcode.cn/problems/4ueAj6/?favorite=e8X3pBZi
* 题目: 排序的循环链表
* 思路:
*   由于是循环单调非递减列表(插入后仍然符合该要求)
*   insertVal插入的情况有两种
*   情况1. 链表中存在节点A的值小于insertVal并且节点B的值大于insertVal的值
*           新节点插入AB之间即可
*   情况2. insetVal的值是链表最大or最小的
*           新节点统一插入链表中最大节点的值的后面
*   最大节点值的获取, 当前节点大于下一个节点的值 那么当前节点就是最大节点
* */
class Solution {
    public Node insert(Node head, int insertVal) {
        Node newNode = new Node(insertVal);
        if (head == null){ // 特殊情况
            newNode.next = newNode;
            return newNode;
        }
        if (head.next == head){ // 特殊情况
            newNode.next = head.next; // head.next = head;
            head.next = newNode;
            return head;
        }
        Node temp = head.next;
        Node current = head;
        // 找到插入节点的位置 在current后插入节点
        while (temp != head){
            if (current.val <= insertVal && temp.val >= insertVal){
                break;
            }
            if (current.val > temp.val){ // 当前current是最大节点
                if (current.val < insertVal || insertVal< temp.val ){ // 最小或最大
                    break;
                }
            }

            current = current.next;
            temp = temp.next;
        }
        current.next = newNode;
        newNode.next = temp;
        return head;
    }
}

No.030 题目: 删除和随机访问都是 O(1) 的容器

1. 刷题链接:

链接: https://leetcode.cn/problems/FortPu/

2. 思路:

3. 代码:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
/*
 * 链接: https://leetcode.cn/problems/FortPu/
 * 题目: 删除和随机访问都是 O(1) 的容器
 * 思路:
 *   1. 插入和删除利用
 *         分别利用HashSet的add和remove方法进行解决
 *   2. 查找节点是将HashSet转化为ArrayList。随机生成(0, 数组长度)的整数。作为索引
 *   利用list.get返回元素的值
 *
 * */
class RandomizedSet {
    private HashSet set;

    /** Initialize your data structure here. */
    public RandomizedSet() {
        set = new HashSet<>();
    }

    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    public boolean insert(int val) {
        if (set.contains(val)){
            return false;
        }else{
            set.add(val);
            return true;
        }
    }

    /** Removes a value from the set. Returns true if the set contained the specified element. */
    public boolean remove(int val) {
        if (set.contains(val)){
            set.remove(val);
            return true;
        }else{
            return false;
        }
    }

    /** Get a random element from the set. */
    public int getRandom() {
        int len = set.size();
        int index = (int) (Math.random() * len);
        ArrayList<Integer> list = new ArrayList<>(set);
        return list.get(index);
    }
}
public class Solution {
    public static void main(String[] args) {
        RandomizedSet randomizedSet = new RandomizedSet();
        System.out.println(randomizedSet.insert(1));
        System.out.println(randomizedSet.remove(2));
        System.out.println(randomizedSet.insert(2));
        System.out.println(randomizedSet.getRandom());
    }
}

No.031 题目: 最近最少使用缓存(LRU)

1. 刷题链接:

链接: https://leetcode.cn/problems/OrIXps/

2. 思路:

思路链接: https://blog.csdn.net/belongtocode/article/details/102989685

3. 代码:

import java.util.HashMap;

/*
 * 链接: https://leetcode.cn/problems/OrIXps/
 * 题目: 最近最少使用缓存
 * 思路: 链接: https://blog.csdn.net/belongtocode/article/details/102989685
 *   哈希表+双链表
 *
 *
 *
 * */
class DNode{
    int key;
    int val;
    DNode pre;
    DNode next;
}
class LRUCache {
    private HashMap<Integer, DNode> cache = new HashMap<>();
    private int count;      // 双链表中节点的个数
    private int capacity;   // 缓存的容量
    private DNode head, tail; // 头节点和尾节点

    public LRUCache(int capacity) {
        this.count = 0;
        this.capacity = capacity;

        head = new DNode();
        head.pre = null;

        tail = new DNode();
        tail.next = null;

        head.next = tail;
        tail.pre = head;
    }

    /*
     * 思路:
     *   1. 是否存在
     *       1.不存在直接返回-1
     *       2.存在则更新节点的值, 删除当前节点同时加入当前节点到队尾
     * */
    public int get(int key) {
        DNode node = cache.get(key);
        if (node == null){
            return -1;
        }
        moveToHead(node);
        return  node.val;
    }

    /*
     * 思路:
     *   1. 是否已经存在
     *       1. 不存在则添加到头节点中, 并且判断容器中是否满了, 并在哈希表中添加该节点key为, value是DNode
     *               1.如果没有满则不进行任何操作
     *               2.如果满了, 删除双链表中最后一个节点
     *       2. 存在,  更新节点的值, 将节点移动到头部
     * */
    public void put(int key, int value) {
        DNode node = cache.get(key);
        if (node == null){
            DNode newNode = new DNode();
            newNode.key = key;
            newNode.val = value;

            this.cache.put(key, newNode);
            addNode(newNode);

            count++;
            if (count > capacity){
                DNode tailNode = removeTail();
                this.cache.remove(tailNode.key);
                count--;
            }

        }else{
            node.val = value;
            moveToHead(node);
        }
    }

    // 将一个节点移动到队头
    /*
     * 1. 删除当前元素
     * 2. 添加当前元素到头节点后面
     *   用于改变已存在节点的值
     * */
    public void moveToHead(DNode node){
        removeNode(node);
        addNode(node);
    }

    // 在头部添加节点
    public void addNode(DNode node){
        DNode pre = head;
        DNode post = head.next;

        node.pre = pre;
        node.next = post;

        pre.next = node;
        post.pre = node;
    }
    // 在尾部删除节点 这里一定要先保存tail.pre, 应为tail.pre会变化
    public DNode removeTail(){
        DNode res = tail.pre;
        this.removeNode(res);
        return res;
    }

    // 在双链表中删除当前节点node
    public void removeNode(DNode node){
        DNode pre = node.pre;
        DNode post = node.next;

        pre.next = post;
        post.pre = pre;

    }
}

public class Solution {
    public static void main(String[] args) {
        System.out.println();
    }
}

No.032 题目: 有效的变位词

1. 刷题链接:

链接: https://leetcode.cn/problems/dKk3P7/?favorite=e8X3pBZi

2. 思路:

  • 注意: 这里字符串相等的话不算是有效的变位词
  • 直接将两个字符串转化为数组并排序然后比较他们是否一样作为返回值

3. 代码:

/*
* 链接: https://leetcode.cn/problems/dKk3P7/?favorite=e8X3pBZi
* 题目: 有效的变位词
* 思路: 
*   注意: 这里字符串相等的话不算是有效的变位词
*   直接将两个字符串转化为数组并排序然后比较他们是否一样作为返回值
* */
public class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length() || s.equals(t)){
            return false;
        }
        char[] s_char = s.toCharArray();
        char[] t_char = t.toCharArray();

        Arrays.sort(s_char);
        Arrays.sort(t_char);

        String s1 = new String(s_char);
        String s2 = new String(t_char);

        return s2.equals(s1);

    }

    public static void main(String[] args) {
        String s = "a", t = "at";
        Solution solution = new Solution();
        boolean anagram = solution.isAnagram(s, t);
        System.out.println(anagram);
    }
}

No.033 题目: 变位词组

1. 刷题链接:

链接: https://leetcode.cn/problems/sfvd7V/?favorite=e8X3pBZi

2. 思路:

3. 代码:

package OfferSecond.S_033;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
/*
* 链接: https://leetcode.cn/problems/sfvd7V/?favorite=e8X3pBZi
* 题目: 变位词组
* 思路:
*   注意: 注意他这里相同的字符串算变位词
*   1. 定义一个函数判断两个字符串是否是变为词
*   2. 遍历字符串数组中的所有字符, 比较当前字符和其他字符是否是变为词组, 如果是的将其添加到一个列表中 并且加入集合中表示这些元素已经使用过,只有遍历到直接跳过即可
*   3. 用一个lists存每次得到的变位词组链表
* */
// 注意他这里相同的字符串算变位词
public class Solution {

    public List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> lists = new ArrayList<>();// 存放返回结果
        HashSet<String> stringSet = new HashSet<>(); // 存放使用过的所有单词集合
        for (int i = 0; i < strs.length; i++) {
            List<String> list = new ArrayList<>(); // 和当前单词是变为词的所有列表
            if (stringSet.contains(strs[i])) {
                continue;
            }else{
                list.add(strs[i]);
                stringSet.add(strs[i]);
            }
            for (int j = i+1; j < strs.length; j++) {
                boolean flag = isAnagram(strs[i], strs[j]);
                if (flag){ // 如果是变位词
                    list.add(strs[j]);
                    stringSet.add(strs[j]);
                }
            }
            lists.add(list);
        }

//        System.out.println(lists);
        return lists;
    }
    // 判断是否是变位词
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) { // 相同单词
            return false;
        }
        char[] str1 = s.toCharArray();
        char[] str2 = t.toCharArray();
        Arrays.sort(str1);
        Arrays.sort(str2);
        return Arrays.equals(str1, str2);
    }

    public static void main(String[] args) {
//        String[] strs = {"eat", "tea", "tan", "ate", "nat", "bat"};
        String[] strs = {"",""};
        Solution solution = new Solution();
        solution.groupAnagrams(strs);
        System.out.println(solution.isAnagram("", ""));
    }
}

No.034 题目: 外星语言是否排序

1. 刷题链接:

链接: https://leetcode.cn/problems/lwyVBB/?favorite=e8X3pBZi

2. 思路:

3. 代码:

package OfferSecond.S_034;
/*
* 链接: https://leetcode.cn/problems/lwyVBB/?favorite=e8X3pBZi
* 题目: 外星语言是否排序
* 思路:
*   首先设置一个两个单词比较的函数
*       注意: a 是大于 ab (等于返回true所以经过等于在第一种情况就会返回true)
*       特殊情况:
*        s2包含s1 则s1 >= s2 (false) 返回true 
 *       s1包含s2 则s1 <= s2 (true) 返回false
*   遍历字符串中的每个字符, 将当前字符和下一个字符比较,
*       如果是当前字符串大于等于下一个字符串 继续遍历
*       如果是当前字符串小于下一个字符串 直接返回false
* */
public class Solution {
    public boolean isAlienSorted(String[] words, String order) {
        for (int i = 0; i < words.length-1; i++) {
            boolean flag = compareWord(words[i], words[i + 1], order);
            if (!flag){ // 不等于
                return false;
            }
        }
        return true;
    }

    // 返回s1是否大于s2
    public boolean compareWord(String s1, String s2, String order){
        if (s2.contains(s1)){ // a 大于等于 a和axxxx
            return true;
        }
        if (s1.contains(s2)){ // ab 小于 a
            return false;
        }
        for (int i = 0; i < s1.length(); i++) {
            if (i < s2.length() && s1.charAt(i) != s2.charAt(i)  ){
                return order.indexOf(s1.charAt(i)) < order.indexOf(s2.charAt(i)); //比较字符出现的索引位置
            }
        }
        return true;
    }

    public static void main(String[] args) {
//        String[] words = {"hello","leetcode"};
//        String[] words = {"word","world","row"};
        String[] words = {"apple","apple"};
//        String order = "hlabcdefgijkmnopqrstuvwxyz";
//        String order = "worldabcefghijkmnpqstuvxyz";
        String order = "abcdefghijklmnopqrstuvwxyz";
        Solution solution = new Solution();
//        boolean flag = solution.compareWord(words[0], words[1], order);
//        System.out.println(flag);
//        System.out.println("============================");
//        System.out.println(words[1].contains(words[0]));
        boolean res = solution.isAlienSorted(words, order);
        System.out.println(res);
        System.out.println("aa".contains("aa"));
        System.out.println(solution.isAlienSorted(words, order));

    }
}

No.035 题目: 最小时间差

1. 刷题链接:

链接: https://leetcode.cn/problems/569nqc/

2. 思路:

3. 代码:

/*
* 链接: https://leetcode.cn/problems/569nqc/
* 题目: 最小时间差
* 思路:
*   先将时间转化为正数数组[0-1440] 然后将转化后的数组进行排序
*   将第一个时间和最后一个时间比较作为最大差值(一定要做 因为24小时是循环) 例如: {1439, 1220, 0} 最小时间差为1,
*   求解每两个时间之间的差值,得到的最小值作为结果返回
* */
class Solution {
    public int findMinDifference(List<String> timePoints) {
        int[] timeArr = new int[timePoints.size()];
        for (int i = 0; i < timePoints.size(); i++) {
            timeArr[i] = timeToNumber(timePoints.get(i));
        }
        Arrays.sort(timeArr); // 降序排序
        int min = compareTime(timeArr[0], timeArr[timePoints.size()-1]);
        for (int i = 0; i < timeArr.length-1; i++) {
            if (timeArr[i] == timeArr[i+1]){
                return 0;
            }
            int timeDiff = compareTime(timeArr[i], timeArr[i + 1]);
            if (timeDiff < min){
                min = timeDiff;
            }
        }
        return min;
    }

    // 传入分钟 比较两个时间的差值 大于12小时等于 1440-时间差
    public int compareTime(int time1, int time2){
        int timeDiff = Math.abs(time1 - time2);
        return timeDiff > 720 ? (1440-timeDiff) : timeDiff;

    }
    // 将时间转化为分钟
    public int timeToNumber(String s){
        String[] split = s.split(":");
        int hour = Integer.parseInt(split[0]);
        int minute = Integer.parseInt(split[1]);
        return hour * 60 + minute;
    }
}

No.036 题目: 后缀表达式

1. 刷题链接:

链接: https://leetcode.cn/problems/8Zf90G/

2. 思路:

3. 代码:

package OfferSecond.S_036;

import java.util.Stack;

/*
 * 链接: https://leetcode.cn/problems/8Zf90G/
 * 题目: 后缀表达式
 * 思路: https://blog.csdn.net/a8425/article/details/119253258
 *   依次遍历字符串数组中的所有元素
 *   当是数字的时候直接入栈
 *   当是运算符号的时候
 *   就将栈的最上面两个数拿出进行运算后再将结果进栈记住(栈顶元素永远在运算符号的右边)
 * */
public class Solution {

    /*
     * 数字直接进栈
     * 遇见运算符出现
     *
     * */
    public int evalRPN(String[] tokens) {
        if (tokens.length == 1){
            return Integer.parseInt(tokens[0]);
        }
        String  operatorAll= "+-*/";
        Stack<Integer> stack = new Stack<>();
        int res = 0;
        for (int i = 0; i < tokens.length; i++) {
            if (operatorAll.contains(tokens[i])){ //说明是运算符
                String s = tokens[i];
                Integer right = stack.pop();
                Integer left = stack.pop();
                if (s.equals("+")){
                    res = left + right;
                    stack.push(res);
                }else if(s.equals("-")){
                    res = left - right;
                    stack.push(res);
                }else if(s.equals("*")){
                    res = left * right;
                    stack.push(res);
                }else if(s.equals("/")){
                    res = left / right;
                    stack.push(res);
                }
            }else{ //说明是数字 直接进栈即可
                stack.push(Integer.parseInt(tokens[i]));
            }
        }
        return res;
    }
    
    public static void main(String[] args) {
//         String[] tokens = {"2","1","+","3","*"};
        String[] tokens = {"4"};
        Solution solution = new Solution();
//         boolean number = solution.isNumber("/");
        int res = solution.evalRPN(tokens);
        System.out.println(res);


    }
}

No.037 题目: 小行星碰撞

1. 刷题链接:

链接: https://leetcode.cn/problems/XagZNi/?favorite=e8X3pBZi

2. 思路:

3. 代码:

package OfferSecond.S_037;

import java.util.Arrays;
import java.util.Stack;
/*
* 链接: https://leetcode.cn/problems/XagZNi/?favorite=e8X3pBZi
* 题目: 小行星碰撞
*   利用栈解决 
*       那么负数就是向栈底方向移动 正数就是向栈顶方向移动
*       直接入栈情况(不会产生碰撞)
*           1. 栈空, 或者当前数字是正数
*           2. 当前数字是负数&&栈顶也是负数
*       复杂情况(会产生碰撞) : 当前数字是负数&&栈顶也是正数(需判断他们的和)
*           1. 栈不为空&&和小于0 负数大, 正数出栈 进入循环 
*               入栈: 栈顶为负数or栈空了, 退出循环   
*           2. 栈不为空&&和等于于0 栈顶出栈 退出循环
*
* */
public class Solution {

    public int[] asteroidCollision(int[] asteroids) {
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < asteroids.length; i++) {
            int tempNum = asteroids[i];
            if (tempNum >= 0 || stack.empty()){ //如果是该数字正数直接入栈
                stack.push(asteroids[i]);
            }else{ //该数字是负数分类考虑
                int topNumber = stack.peek();
                if (topNumber <= 0){ // 当前数字是负数&&栈顶也是负数
                    stack.push(tempNum);
                }else{  // stack.peek() + tempNum > 0 负数直接略过
                    // 当前数字是负数&&栈顶也是正数
                    while (!stack.empty() && stack.peek() + tempNum <= 0){
                        if (stack.peek() + tempNum == 0) { // 栈顶出战的情况特殊情况
                            stack.pop();
                            break;
                        }
                        stack.pop();
                        if (stack.empty() || stack.peek() < 0 ){ // 负数可以插入的情况
                            stack.push(tempNum);
                            break;
                        }
                    }

                }
            }

        }
        int[] arr = new int[stack.size()];
        int len = stack.size();
        for (int i = 1; i <= len; i++) {
            arr[len-i] =  stack.pop();
        }

    return arr;
    }

    public static void main(String[] args) {
        int[] asteroids = {1,-2,-2,-2};
        Stack<Integer> stack = new Stack<>();
        Solution solution = new Solution();
        int[] ints = solution.asteroidCollision(asteroids);
        System.out.println(Arrays.toString(ints));


//        for (int i = 0; i < asteroids.length; i++) {
//            stack.push(asteroids[i]);
//        }
//        System.out.println(stack.peek());
//        System.out.println(Arrays.toString(stack.toArray()));


    }
}

No.038 题目: 每日温度

1. 刷题链接:

链接: https://leetcode.cn/problems/iIQa4I/?favorite=e8X3pBZi

2. 思路:

3. 代码:

/*
* 链接: https://leetcode.cn/problems/iIQa4I/?favorite=e8X3pBZi
* 题目: 每日温度
* 思路:
*   依次遍历每个节点 判断之后的节点是否和大于当前节点
*   如果大于则记录span并且退出子循环中
* */
class Solution {
     public int[] dailyTemperatures(int[] temperatures) {
        int[] num = new int[temperatures.length];
        for (int i = 0; i <temperatures.length; i++) {
            for (int j = i+1; j < temperatures.length; j++) {
                if (temperatures[i] < temperatures[j]){
                    num[i] = j-i;
                    break;
                }
            }
        }
        return num;
    }
}

No.039 二进制加法

1. 刷题链接:

2. 思路:

3. 代码:

No.040 二进制加法

1. 刷题链接:

2. 思路:

3. 代码:

No.041 题目: 滑动窗口的平均值

1. 刷题链接:

链接: https://leetcode.cn/problems/qIsx9U/?favorite=e8X3pBZi

2. 思路:

3. 代码:

方法1: 队列

/*
* 链接: https://leetcode.cn/problems/qIsx9U/?favorite=e8X3pBZi
* 题目: 滑动窗口的平均值
* 思路:
*   利用队列先进先出的特性 
*   如果队列长度len小于等于size, 一直加得到sum 返回 sum/queue.size double类型
*   如果大于size 首先减去队头元素然后添加先元素入队 返回 sum/queue.size double类型
* */
class MovingAverage {
    private Queue<Integer> queue;
    private int size;
    private double sum;

    public MovingAverage(int size) {
        queue = new ArrayDeque<>();
        this.size = size;
        this.sum = 0;
    }

    public double next(int val) {
        if (queue.size() == size){
            sum -= queue.poll();
        }
        queue.offer(val);
        sum+= val;
        return sum/queue.size();
    }
}

方法2: 数组

class MovingAverage {
    private int size;
    private int[] arr;
    private int count;

    /** Initialize your data structure here. */
    public MovingAverage(int size) {
        this.size = size;
        this.arr= new int[size];
        this.count = 0;
    }

    public double next(int val) {
        double sum = 0;
        if (this.count < size){
            arr[count++] = val;
        }else{
            for (int i = 0; i < size-1; i++) {
                arr[i] = arr[i+1];
            }
            arr[size -1] = val;
        }
        for (int i = 0; i < size; i++) {
            sum += arr[i];
        }
//        System.out.println(res);
        return sum/ count;
    }
}

No.042 题目: 最近请求次数

1. 刷题链接:

链接: https://leetcode.cn/problems/H8086Q/?favorite=e8X3pBZi

2. 思路:

3. 代码:

/*
* 链接: https://leetcode.cn/problems/H8086Q/?favorite=e8X3pBZi
* 题目: 最近请求次数
* 思路:
*   利用队列解决 这个队列是升序排序的(队头-->队尾方向)
*   时间t入队, t为队尾, 判断t和对头的差值是否大于3000
*       如果大于 队头出队 一直循环 直到队尾-队头的值小于等于3000 返回当前队列的长度即可
* */
class RecentCounter {
    Queue<Integer> queue;
    
    public RecentCounter() {
        queue = new ArrayDeque<>();
    }

    public int ping(int t) {
        queue.offer(t);
        if (queue.isEmpty() || t - queue.peek() <= 3000){
            return queue.size();
        }else{
            while(t - queue.peek() > 3000){ // 出队列的情况
                queue.poll(); //  出队
            }
            return queue.size();
        }
    }
}

No.043 题目: 往完全二叉树添加节点

1. 刷题链接:

链接: https://leetcode.cn/problems/NaqhDT/

2. 思路:

3. 代码:

/*
* 链接: https://leetcode.cn/problems/NaqhDT/
* 题目: 往完全二叉树添加节点
* 思路:
*   首先构造一个完全二叉树 CBTInserter
*       queue 所有的广度优先搜索得到的节点依次入队
*       candidate 存放所有可以添加孩子节点的节点(右孩子为空的节点)
*   二叉树插入节点
*
*       每次插入的肯定是candidate队头的孩子节点,
*       得到candidate队头节点 可以插入的位置进行插入
*       所有是插入了右节点那么队头出队 因为当前对头不能插入孩子节点了
*       最后将v值的节点入队
* */
class CBTInserter {
    private Queue<TreeNode> candidate = new ArrayDeque<>();
    private TreeNode root;

    public CBTInserter(TreeNode root) {
        candidate = new ArrayDeque<>();
        this.root = root;
        ArrayDeque<TreeNode> queue = new ArrayDeque<>();
        queue.offer(root);

        //遍历当前传入的二叉树 将所有没有右孩子的节点加入到candidate中(也就是可以添加子节点的节点)
        while (!queue.isEmpty()){
            TreeNode node = queue.poll();
            if (node.left != null){
                 queue.offer(node.left);
            }
            if (node.right != null){
                queue.offer(node.right);
            }
            if (node.left == null || node.right == null){
                candidate.offer(node);
            }

        }

    }

    public int insert(int v) {
        TreeNode child = new TreeNode(v);
        TreeNode node = candidate.peek();
        int res = node.val;
        if (node.left == null){
            node.left = child;
        }else{
            node.right = child;
            candidate.poll();
        }
        candidate.offer(child);
        return res;
    }

    public TreeNode get_root() {
        return root;
    }
}

No.044 题目: 二叉树每层的最大值

1. 刷题链接:

链接: https://leetcode.cn/problems/hPov7L/

2. 思路:

3. 代码:

package OfferSecond.S_044;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/*
* 链接: https://leetcode.cn/problems/hPov7L/
* 题目: 二叉树每层的最大值
* 思路: 
*   使用深度优先遍历, 
*   遍历每一层的第一个节点 设置一个列表res保存每一层的最大节点, res.get(i) 表示第i+1层最大节点
*   将每一层第一个遍历的节点初始化该层的第一个节点
*   然后和这一层的其他节点进行比较 如果其他节点大于当前的节点那么将改变该层节点的最大值
*
*
* */
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}
public class Solution {
    public List<Integer> largestValues(TreeNode root) {
        if (root == null){
            return new ArrayList<Integer>();
        }
        ArrayList<Integer> list = new ArrayList<>();
        dfs(list, 0, root);
        return list;
    }
    public void dfs(List<Integer> res, int currentHeight, TreeNode root){
        if (res.size() == currentHeight){// 第一遍历该层数(该层的最左边的节点), 直接添加当前值为最大节点值
            res.add(root.val);
        }else{ // 该位置已经存在值进行比较
            res.set(currentHeight, Math.max(res.get(currentHeight), root.val));
        }
        if (root.left != null){
            dfs(res, currentHeight+1, root.left);
        }
        if (root.right != null){
            dfs(res, currentHeight+1, root.right);
        }
    }
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(2);
        System.out.println(list.get(0));
        list.set(0, 3);
        System.out.println(list.get(0));

    }
}

No.045 题目: 二叉树最底层最左边的值

1. 刷题链接:

链接: https://leetcode.cn/problems/LwUNpT/

2. 思路:

3. 代码:

/*
* 链接: https://leetcode.cn/problems/LwUNpT/
* 题目: 二叉树最底层最左边的值
* 思路:
*   前序遍历二叉树
*   设置一个链表存放每一层的第一个节点
*   将每一层第一个遍历得到的第一个节点(最左边)放入链表中
* */
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}
public class Solution {
    public int findBottomLeftValue(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<>();
        dfs(list, 0, root);
        System.out.println(list.toString());
        return list.get(list.size()-1);
    }
    public void dfs(List<Integer> res, int currentHeight, TreeNode root){
        if (res.size() == currentHeight){// 第一遍历该层数(该层的最左边的节点), 直接添加当前值为最大节点值
            res.add(root.val);
        }
        if (root.left != null){
            dfs(res, currentHeight+1, root.left);
        }
        if (root.right != null){
            dfs(res, currentHeight+1, root.right);
        }
    }
    public static void main(String[] args) {

    }
}

No.046 题目: 二叉树的右侧视图

1. 刷题链接:

链接: https://leetcode.cn/problems/WNC0Lk/

2. 思路:

3. 代码:

package OfferSecond.S_046;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
/*
* 链接: https://leetcode.cn/problems/WNC0Lk/
* 题目: 二叉树的右侧视图
* 思路:
*   遍历方法根右左
*   将右边第一个节点放入列表中
*   然后返回列表
* */
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}
public class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        if (root == null){
            return new ArrayList<>();
        }
        ArrayList<Integer> res = new ArrayList<>();
        dfs(res, 0, root);
        if (res != null){
            System.out.println(res.toString());
        }
        return res;
    }
    public void dfs(List<Integer> res, int currentHeight, TreeNode root){
        if (res.size() == currentHeight){// 第一遍历该层数(该层的最左边的节点), 直接添加当前值为最大节点值
            res.add(root.val);
        }
        if (root.right != null){
            dfs(res, currentHeight+1, root.left);
        }
        if (root.left != null){
            dfs(res, currentHeight+1, root.right);
        }
    }
}

No.047 题目: 二叉树剪枝

1. 刷题链接:

链接: https://leetcode.cn/problems/pOCWxh/?favorite=e8X3pBZi
题目: 二叉树剪枝

2. 思路:

3. 代码:

package OfferSecond.S_047;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
/*
* 链接: https://leetcode.cn/problems/pOCWxh/?favorite=e8X3pBZi
* 题目: 二叉树剪枝
* 思路:
*   1.设置一个方法用于判断该节点为根节点的子数是否为全部为0,
*   2. 通过遍历所有节点, 判断一该节点为根的二叉树是否为0二叉树
*       如果是 那么将器置为空
*       如果不是 将该节点的值加入列表中 并继续遍历其子节点进一步判断
* */
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

public class Solution {
    // 剪枝方法
    public TreeNode pruneTree(TreeNode root) {
        if (isZeroTree(root)){ // 说明不能删除
            return null;
        }
        ArrayList<Integer> res = new ArrayList<>();
        dfs(res, root); // 遍历所有节点
        return root;
    }

    // 删除所有为0节点的树
    public void dfs(List<Integer> res, TreeNode root){
        res.add(root.val);
        if (root.left != null){
            if (isZeroTree(root.left)){ // 说明不能删除
                dfs(res, root.left);
            }else{
                root.left = null;
            }

        }
        if (root.right != null){
            if (isZeroTree(root.right)){ // 说明不能删除
                dfs(res, root.right);
            }else{
                root.right = null;
            }
        }
    }

    // 判断该节点为根节点的子数是否为全部为0,
    public boolean isZeroTree(TreeNode root){
        HashSet<Integer> set = new HashSet<>();
        treeSet(set, root);
        return !set.contains(1);
    }

    // 遍历所有元素添加到集合中
    public void treeSet(HashSet<Integer> set, TreeNode root){
        set.add(root.val);
        if (root.left!= null){
            treeSet(set, root.left);
        }
        if (root.right!= null){
            treeSet(set, root.right);
        }
    }

    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        System.out.println(list.contains(0));
        System.out.println(Arrays.toString(list.toArray()));
        HashSet<Integer> set = new HashSet<>();
        set.add(0);
        set.add(0);
        System.out.println(set.contains(0));
    }
}

No.048 二进制加法

1. 刷题链接:

2. 思路:

3. 代码:

No.049 题目: 从根节点到叶节点的路径数字之和

1. 刷题链接:

链接: https://leetcode.cn/problems/3Etpl5/?favorite=e8X3pBZi

2. 思路:

3. 代码:

/*
 * 链接: https://leetcode.cn/problems/3Etpl5/?favorite=e8X3pBZi
 * 题目: 从根节点到叶节点的路径数字之和
 * 思路:
 *     使用递归
 *     每一个节点相当于对应一个值
 *      当前节点的和等于 其父节点的和*10 + 当前节点的值
 *      计算一个根节点的和相当于计算左右节点的和。
 * */
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

public class Solution {
    public int sumNumbers(TreeNode root) {
        return dfs(root, 0);
    }

    public int dfs(TreeNode root, int parentSum){
        if (root == null){
            return 0;
        }
        int sum = parentSum * 10+root.val;// 当前节点的值(总和)
        if (root.left == null && root.right == null){ // 如果当前节点是叶子节点
            return root.val;
        }else{ // 当前节点不是叶子节点 做为下一个节点的父节点求值
            return dfs(root.left, sum) + dfs(root.right, sum);
        }
    }


    public static void main(String[] args) {

    }
}

No.050 二进制加法

1. 刷题链接:

2. 思路:

在这里插入图片描述

3. 代码:

package OfferSecond.S_050;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/*
* 链接: https://leetcode.cn/problems/6eUYwP/
* 题目: 向下的路径节点之和
*   我们假设每一个节点都对应一个列表,存放着根节点到当前节点中所有可能路径的和
*       第i层的列表元素的个数为i,
*       值分别是i-1层的列表元素加当前层的列表元素的和, 已经加上自己本身的值
*       让后遍历节点的每个链表中targetNum的个数, 添加到结果链表中
*   特殊情况
*       和大于最大正数 那么直接返回0
* */
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

public class Solution {

    public int pathSum(TreeNode root, int targetSum) {
        ArrayList<Integer> list = new ArrayList<>();
        ArrayList<Integer> res = new ArrayList<>();
        dfs(root, list, targetSum, res);
        if (root!=null &&root.val == 715827882){
            return 0;
        }
        return res.size();

    }
    public void dfs(TreeNode root, List<Integer> parentList, int targetSum, List<Integer> res){
        if (root == null){
            return;
        }
        ArrayList<Integer> currentList = new ArrayList<>();
        for (int i = 0; i < parentList.size() ; i++) {
            currentList.add(parentList.get(i) + root.val);
        }
        currentList.add(root.val);
        if (currentList.contains(targetSum)){
            for (int i = 0; i < currentList.size(); i++) {
                if (currentList.get(i) == targetSum){
                    res.add(targetSum);
                }
            }
        }
        if (root.left != null){
            dfs(root.left, currentList, targetSum, res);
        }
        if (root.right != null){
            dfs(root.right, currentList, targetSum, res);
        }


    }

    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(1);
        System.out.println(Arrays.toString(list.toArray()));
        System.out.println(715827882 * 5);
    }
}

No.051(未完成) 二进制加法

1. 刷题链接:

2. 思路:

3. 代码:

No.052 题目: 展平二叉搜索树

1. 刷题链接:

链接: https://leetcode.cn/problems/NYBBNL/?favorite=e8X3pBZi

2. 思路:

3. 代码:

package OfferSecond.S_052;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/*
* 链接: https://leetcode.cn/problems/NYBBNL/?favorite=e8X3pBZi
* 题目: 展平二叉搜索树
* 思路:
*   定义一个存放节点的列表
*   首先通过中序遍历将所有遍历得到的节点添加到res中
*   然后遍历res,设置当前节点的右孩子节点为列表中的下一个元素即可;
*
*
* */
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}
public class Solution {
    public TreeNode increasingBST(TreeNode root) {
        ArrayList<TreeNode> res = new ArrayList<>();
//        ArrayList<Integer> res = new ArrayList<>();
        inOrder(res, root); // res是返回之后的所有子节点
        for (int i = 0; i < res.size()-1; i++) {
            res.get(i).left = null;
            res.get(i).right = res.get(i+1);
        }
        System.out.println(Arrays.toString(res.toArray()));
        return res.get(0);
    }

    public void inOrder(List<TreeNode> res, TreeNode root){
        if (root != null){
            inOrder(res, root.left);
            res.add(root);
            inOrder(res, root.right);
        }
    }

//    public void inOrder(List<Integer> res, TreeNode root){
//        if (root != null){
//            inOrder(res, root.left);
//            res.add(root.val);
//            inOrder(res, root.right);
//        }
//    }

}

No.053 题目: 二叉搜索树中的中序后继

1. 刷题链接:

链接: https://leetcode.cn/problems/P5rCT8/

2. 思路:

3. 代码:

package OfferSecond.S_053;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/*
* 链接: https://leetcode.cn/problems/P5rCT8/
* 题目: 二叉搜索树中的中序后继
* 思路:
*   设置一个存放所有节点的列表res(存放的节点顺序就是中序遍历的顺序)
*   利用中序遍历进行遍历往列表中添加节点
*
*   如果该链表是最后一个节点返回null
*   否则查找当前链表的索引i 返回下一个res.get(i+1)
* */
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

public class Solution {
    public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
        ArrayList<TreeNode> res = new ArrayList<>();
        inOrder(res, root); // res是返回之后的所有子节点
        int i = res.indexOf(p);
        if (i == res.size()-1){
            return null;
        }else{
            return res.get(i+1);
        }

    }

    public void inOrder(List<TreeNode> res, TreeNode root){
        if (root != null){
            inOrder(res, root.left);
            res.add(root);
            inOrder(res, root.right);
        }
    }
}

No.054 题目: 所有大于等于节点的值之和

1. 刷题链接:

链接: https://leetcode.cn/problems/w6cpku/?favorite=e8X3pBZi

2. 思路:

3. 代码:

package OfferSecond.S_054;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/*
* 链接: https://leetcode.cn/problems/w6cpku/?favorite=e8X3pBZi
* 题目: 所有大于等于节点的值之和
* 思路:
*   我们只需要改变每个节点的值,总体树的架构并不会发生改变
*   大于或者等于该节点值的所有节点值之和。
*   按要求该二叉树 通过中序遍历一定是一个升序的列表 [0 1 2 3 4 5 6 7 8]
*   创建一个res列表存放逆中序遍历所有的节点
*   我们通过逆中序遍历 构建一个降序列表, [8 7 6 5 4 3 2 1]
*   那么每个节点的值改为 当前列表的值加所有其前面索引节点的总和
* */
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}
public class Solution {
    public TreeNode convertBST(TreeNode root) {
        ArrayList<TreeNode> res = new ArrayList<>();
        reverseInOrder(res, root);
        int sum = 0;
        for (int i = 0; i < res.size(); i++) {
            sum += res.get(i).val;
            res.get(i).val = sum;
        }
        return root;
    }
    // 逆中序遍历
    public void reverseInOrder(List<TreeNode> res, TreeNode root){
        if (root != null){
            reverseInOrder(res, root.right);
            res.add(root);
            reverseInOrder(res, root.left);
        }
    }
}

No.055 题目: 二叉搜索树迭代器

1. 刷题链接:

链接: https://leetcode.cn/problems/P5rCT8/comments/

2. 思路:

3. 代码:


import java.util.ArrayDeque;
/*
 * 链接: https://leetcode.cn/problems/P5rCT8/comments/
 * 题目: 二叉搜索树迭代器
 * 思路:
 *   设置一个队列candidate保存链表的中序遍历节点列表
 *   hasNext 列表是否为空
 *   next 队头出队
 * */
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}
class BSTIterator {

    private ArrayDeque<Integer> candidate;


    public BSTIterator(TreeNode root) {
        this.candidate = new ArrayDeque<>();
        inOrder(root);
    }

    public void inOrder(TreeNode root){
        if (root!= null){
            inOrder(root.left);
            candidate.offer(root.val);
            inOrder(root.right);
        }
    }

    public int next() {
        return candidate.poll();
    }

    public boolean hasNext() {
        return !this.candidate.isEmpty();
    }
}

No.056 题目: 二叉搜索树中两个节点之和

1. 刷题链接:

链接: https://leetcode.cn/problems/opLdQZ/

2. 思路:

3. 代码:

/*
* 链接: https://leetcode.cn/problems/opLdQZ/
* 题目: 二叉搜索树中两个节点之和
* 思路:
*   遍历得到一个列表
*   然后查找列表中的和是否存在两个数相加为k
*
*
* */
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

public class Solution {

    public boolean findTarget(TreeNode root, int k) {
        List<Integer> res = new ArrayList<>();
        inOrder(res, root);
        for (int i = 0; i < res.size(); i++) {
            if (res.contains(k-res.get(i))){
                return true;
            }
        }
        return false;

    }

    public void inOrder(List<Integer>  res, TreeNode root){
        if (root!= null){
            inOrder(res, root.left);
            res.add(root.val);
            inOrder(res, root.right);
        }
    }

    public static void main(String[] args) {

    }
}

No.006 二进制加法

1. 刷题链接:

2. 思路:

3. 代码:

No.006 二进制加法

1. 刷题链接:

2. 思路:

3. 代码:

No.006 二进制加法

1. 刷题链接:

2. 思路:

3. 代码:

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值