牛客剑指offer题目汇总

牛客剑指offer共81道题目汇总

题号 题目 知识点 难度 通过率
JZ3 数组中重复的数字 数组 简单 53.87%
JZ4 二维数组中的查找 数组 中等 26.30%
JZ11 旋转数组的最小数字 二分 简单 34.28%
JZ17 打印从1到最大的n位数 数组 简单 59.61%
JZ21 调整数组顺序使奇数位于偶数前面(一) 数组 中等 53.15%
JZ81 调整数组顺序使奇数位于偶数前面(二) 数组 排序 简单 59.82%
JZ29 顺时针打印矩阵 数组 简单 19.71%
JZ10 斐波那契数列 数组 递归 动态规划 快速幂 记忆化搜索 入门 35.75%
JZ39 数组中出现次数超过一半的数字 哈希 简单 32.19%
JZ45 把数组排成最小的数 数组 排序 贪心 中等 30.37%
JZ47 礼物的最大价值 数组 动态规划 中等 58.01%
JZ53 数字在升序数组中出现的次数 数组 二分 简单 33.04%
JZ49 丑数 基础数学 二分 中等 23.41%
JZ51 数组中的逆序对 数组 归并 中等 17.05%
JZ66 构建乘积数组 数组 简单 41.12%
JZ42 连续子数组的最大和 贪心 动态规划 简单 39.96%
JZ85 连续子数组的最大和(二) 数组 贪心 动态规划 双指针 中等 35.56%
JZ56 数组中只出现一次的两个数字 哈希 位运算 中等 57.31%
JZ57 和为S的两个数字 数组 双指针 中等 30.87%
JZ5 替换空格 字符串 简单 58.79%
JZ19 正则表达式匹配 字符串 递归 动态规划 较难 32.28%
JZ20 表示数值的字符串 字符串 较难 26.40%
JZ38 字符串的排列 字符串 递归 中等 23.72%
JZ48 最长不含重复字符的子字符串 字符串 哈希 双指针 中等 39.05%
JZ50 第一个只出现一次的字符 字符串 简单 31.64%
JZ58 左旋转字符串 字符串 中等 33.45%
JZ67 把字符串转换成整数(atoi) 字符串 中等 23.52%
JZ73 翻转单词序列 字符串 双指针 简单 21.40%
JZ74 和为S的连续正数序列 穷举 中等 29.59%
JZ75 字符流中第一个不重复的字符 字符串 中等 33.48%
JZ9 用两个栈实现队列 简单 40.70%
JZ12 矩阵中的路径 dfs 中等 38.92%
JZ13 机器人的运动范围 递归 较难 24.64%
JZ14 剪绳子 基础数学 中等 35.19%
JZ83 剪绳子(进阶版) 快速幂 基础数学 较难 23.32%
JZ15 二进制中1的个数 基础数学 简单 35.68%
JZ16 数值的整数次方 基础数学 中等 33.66%
JZ62 圆圈中最后剩下的数 基础数学 中等 32.82%
JZ63 买卖股票的最好时机(一) 贪心 动态规划 简单 51.72%
JZ64 求1+2+3+···+n 基础数学 中等 42.97%
JZ65 不用加减乘除做加法 基础数学 简单 45.62%
JZ43 从1到n整数中1出现的次数 基础数学 中等 35.89%
JZ6 从尾到头打印链表 链表 简单 28.67%
JZ18 删除链表的节点 链表 简单 60.16%
JZ76 删除链表中重复的结点 链表 中等 21.93%
JZ22 链表中倒数第K个结点 链表 简单 38.72%
JZ23 链表中环的入口结点 链表 哈希 双指针 中等 36.34%
JZ24 反转链表 链表 简单 38.23%
JZ25 合并两个排序的链表 链表 简单 31.33%
JZ35 复杂链表的复制 链表 较难 23.14%
JZ52 两个链表的第一个公共结点 链表 简单 37.65%
JZ26 树的子结构 中等 25.48%
JZ27 二叉树的镜像 简单 67.43%
JZ28 对称的二叉树 简单 33.25%
JZ30 包含min函数的栈 简单 34.57%
JZ31 栈的压入弹出序列 中等 31.42%
JZ7 重建二叉树 数组 树 dfs 中等 26.97%
JZ8 二叉树的下一个结点 中等 31.28%
JZ32 从上往下打印二叉树 队列 树 简单 29.63%
JZ33 二叉搜索树的后序遍历序列 栈 树 中等 25.33%
JZ82 判断二叉树中是否存在和为某一值的路径 树 dfs 简单 42.26%
JZ34 二叉树中从根到叶子节点和为某一值的所有路径 中等 28.48%
JZ84 二叉树中任意节点路径和为某一值的所有路径 中等 51.39%
JZ36 二叉搜索树与双向链表 分治 中等 30.54%
JZ37 序列化二叉树 队列 树 较难 24.49%
JZ40 最小的K个数 堆 排序 分治 中等 27.01%
JZ41 数据流中的中位数 堆 排序 中等 28.38%
JZ44 数字序列中某一位的数字 模拟 简单 32.38%
JZ46 把数字翻译成字符串 动态规划 中等 24.12%
JZ54 二叉搜索树的第k个节点 树 递归 dfs 中等 44.24%
JZ55 二叉树的深度 简单 49.88%
JZ59 滑动窗口的最大值 队列 堆 双指针 较难 27.15%
JZ61 扑克牌顺子 模拟 简单 27.89%
JZ69 跳台阶 递归 动态规划 记忆化搜索 简单 40.24%
JZ70 矩形覆盖 递归 动态规划 中等 36.58%
JZ71 跳台阶扩展问题 递归 动态规划 记忆化搜索 简单 42.44%
JZ77 按之字形顺序打印二叉树 栈 队列 树 中等 28.18%
JZ78 把二叉树打印成多行 树 广度优先搜索(BFS) 中等 33.65%
JZ79 判断是不是平衡二叉树 树 dfs 简单 38.78%
JZ68 二叉搜索树的最近公共祖先 树 递归 简单 64.18%
JZ86 在二叉树中找到两个节点的最近公共祖先 中等 47.85%

数组中重复的数字

public int duplicate (int[] numbers) {
    if (numbers == null || numbers.length == 0) {
        return -1;
    }
    HashSet<Integer> hashset = new  HashSet<>();
    for (int i : numbers) {
        if (hashset.contains(i)) {
            return i;
        } else {
            hashset.add(i);
        }
    }
    return -1;
}

打印从1到最大的n位数

public int[] printNumbers (int n) {
    int max = 9;
    for(int i=1; i<n; i++){
        max = max*10 + 9;
    }
    int[] result = new int[max];
    for(int i=0; i<max; i++){
        result[i] = i+1;
    }
    return result;
}

1.二维数组中的查找

public boolean Find(int target, int [][] array) {
    for(int i=0;i<array.length;i++){
        for(int j=0;j<array[0].length;j++){
            if(array[i][j] == target){
                return true;
            }
        }
    }
    return false;
}

解法2:从左下找

给定规则:每一行从左到右递增,每一列从上到下递增

public boolean Find(int target, int [][] array) {
    int rows = array.length;
    if (rows == 0) {
        return false;
    }
    int cols = array[0].length;
    if(cols == 0){
        return false;
    }
     // 左下
    int row = rows-1;
    int col = 0;
    while(row>=0 && col<cols){
        if(array[row][col] < target){
            col++;
        }else if(array[row][col] > target){
            row--;
        }else{
            return true;
        }
    }
    return false;
}

解法3:从右上找

public boolean Find(int target, int [][] array) {
    int rows = array.length;
    if(rows == 0){
        return false;
    }
    int cols = array[0].length;
    if(cols == 0){
        return false;
    }
    // 右上
    int row = 0;   //注意
    int col = cols-1;    //注意
    while(row<rows && col>=0){    //注意
        if(array[row][col] < target){
            row++;        //注意
        }else if(array[row][col] > target){
            col--;      //注意
        }else{
            return true;
        }
    }
    return false;
}

6.旋转数组的最小数字

 public int minNumberInRotateArray(int [] array) {
    // 如果数组无元素,那么返回0
    if (array.length <= 0)
        return 0;
    // 定义边界
    int left = 0;
    int right = array.length - 1;
    while (left <= right){
        // 计算左右区间最中间的索引
        int mid = left + ((right - left)>>1);
        // 如果中间的值小于右边的值,说明此时数组最小值在左半部,
        // 挪动右边界指针到中间索引,为了避免此时的中间索引值就是最小的值,所以mid不能够减1
        if (array[mid] < array[right]) {
            right = mid;
        } 
        else if (array[mid] > array[right]) {
            left = mid + 1;
        } else{
            // 如果中间值与右边界值相同,那么挪动右边界向左靠一位,这样就可以在下次循环时重新计算出中间索引值
            right--;
        } 
    }
    // 左边界永远小于或等于右边界,那么就直接返回左边界所对应的数组值
    return array[left];
}

13.调整数组顺序使奇数位于偶数前面(一)

public int[] reOrderArray (int[] array) {
    int[] arr=new int[array.length];
    int i=0;
    for(int a:array){
        if((a&1) == 1){    //奇数
            arr[i++] = a;
        }
    }
  
    for(int a:array){
        if((a&1) == 0){    //偶数
            arr[i++]=a;             
        }
    }
    return arr;
}

调整数组顺序使奇数位于偶数前面(二)

public int[] reOrderArrayTwo (int[] array) {
    int low = 0;
    int high = array.length - 1;
    while (low < high) {
        while (low < high && array[low] % 2 == 1) low++;
        while (low < high && array[high] % 2 == 0) high--;
        if (low < high) {
            int temp = array[low];
            array[low] = array[high];
            array[high] = temp;
        }
    }
    return array;
}

19.顺时针打印矩阵

public ArrayList<Integer> printMatrix(int [][] matrix) {
  ArrayList<Integer> list = new ArrayList<Integer>();
  if(matrix.length==0)	return list;
  
  int l=0, r=matrix[0].length-1;
  int top=0, down=matrix.length-1;
  while( l<=r && top<=down ){
      for(int i=l;i<=r;i++){
          list.add(matrix[top][i]);
      }
      for(int i=top+1;i<=down;i++){
          list.add(matrix[i][r]);
      }
      if(top<down){
          for(int i=r-1;i>=l;i--){
              list.add(matrix[down][i]);
          }
      }
      if(l<r){
          for(int i=down-1;i>=top+1;i--){
              list.add(matrix[i][l]);
          }
      }
      l++;r--;top++;down--;
  }
  return list;
}

28.数组中出现次数超过一半的数字

public int MoreThanHalfNum_Solution(int [] array) {
    int res = array[0];
    for(int i=1, idx = 1; i<array.length; i++){
        if(array[i] == res){
            idx++;
        }else{
            idx--;
            if(idx == 0){
                idx = 1;
                res = array[i];
            }
        }
    }
    return res;
}

30.连续子数组的最大和

public int FindGreatestSumOfSubArray(int[] array) {
    int sum = 0;
    int max = array[0];
    for (int i = 0; i < array.length; i++) {
        // 优化动态规划,确定sum的最大值
        sum = Math.max(sum + array[i], array[i]);
        // 每次比较,保存出现的最大值
        max = Math.max(max, sum);
    }
    return max;
}

连续子数组的最大和(二)

public int[] FindGreatestSumOfSubArray (int[] array) {
    //动态规划
    int sum = array[0],num = array[0];
    //当前遍历位置子串首尾位置
    int start_tmp = 0, end_tmp = 1;
    //最大和子串首尾位置
    int start = 0, end = 1;
    for(int i = 1; i < array.length; i++){
        //数组中包含i位置的连续串最大值(比较当前数组值与之前累加值大小)
        if(array[i] > num + array[i]){
            num = array[i];
            start_tmp = i;
            end_tmp = i + 1;
        }else{
            num = num + array[i];
            end_tmp++;
        }
        //记录并更新当前遍历数组的最大子串和
        if(num > sum || (num == sum) && (end_tmp - start_tmp) > (end - start)){
            sum = num;
            start = start_tmp;
            end = end_tmp;
        }
    }
    return Arrays.copyOfRange(array,start,end);
}

数组中只出现一次的两个数字

public int[] FindNumsAppearOnce (int[] array) {
    HashMap<Integer, Integer> map = new HashMap<>();
    int[] arr = new int[2];
    int j = 0;
    //  先遍历一遍,将每个元素,及对应的个数装入map中
    for (int i = 0; i < array.length; i++) {
        if (!map.containsKey(array[i])) {
            map.put(array[i], 1);
        } else {
            map.put(array[i], map.get(array[i]) + 1);
        }
    }
    // 遍历map。取出两个个数为1的元素
    for (int i : map.keySet()) {
        if (map.get(i) == 1) {
            arr[j++] = i;
        }
        if (j == 2) break;
    }
    // 对两个元素进行升序排列
    if (arr[0] > arr[1]) {
        int t = arr[0];
        arr[0] = arr[1];
        arr[1] = t;
    }
    return arr;
}

解法2:

public int[] FindNumsAppearOnce (int[] array) {
    int eor = 0;//任何数和0异或都等于它本身
    for (int i = 0; i < array.length; i++) {
        eor ^= array[i];//这样结束了就是两个不一样的数相异或
    }
    int eor2 = 0;//再搞一个eor
    int rightOne = eor & (~eor +
                          1);//固定写法,一个不为0的数最右边的1就这么求
    //自己与上自己取反加1的值就是最右边的一个1
    for (int j = 0; j < array.length; j++) {
        if ((array[j] & rightOne) ==
                0) { //如果某个数在这一位上面是1,相与结果才是1,注意优先级
            eor2 ^= array[j]; //这样异或下来eor2就是其中一个数
        }
    }
    int a = eor2;
    int b = eor ^ eor2;
    return a < b ? new int[] {a, b} : new int[] {b, a};
}

32.把数组排成最小的数

public String PrintMinNumber(int [] numbers) {
    if(numbers.length == 0){return "";}
    String[] str1 = new String[numbers.length];
    for(int i = 0; i < numbers.length; i++){
        str1[i] = numbers[i] + "";
    }
    //贪心算法:相邻两者的字典序最小
    Arrays.sort(str1, new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            return (o1 + o2).compareTo(o2 + o1);
        }
    });
    String res = new String();
    for (String tmp : str1) {
        res += tmp;
    }
    return res;
}

礼物的最大价值

public int maxValue (int[][] grid) {
    int m = grid.length;
    int n = grid[0].length;
    int[][] dp = new int[m + 1][n + 1];
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]) + grid[i - 1][j - 1];
        }
    }
    return dp[m][n];
}

数字在升序数组中出现的次数

public int GetNumberOfK(int [] array, int k) {
    if (array == null || array.length == 0) return 0;
    int left = 0, right = array.length - 1;
    int mid = 0, cnt = 0;
    while (left < right) {
        mid = left + (right - left) / 2;
        if (array[mid] > k)
            right = mid - 1;
        else if (array[mid] < k)
            left = mid + 1;
        else {
            break;
        }
    }
    for (int i = left; i <= right; i++)
        if (array[i] == k)
            cnt++;
    return cnt;
}

35.数组中的逆序对

public class Solution {
    public int MOD = 1000000007;
    public int count = 0;
    
    public int InversePairs(int [] array) {
        mergeSort(array, 0, array.length - 1);
        return count;
    }
    
    private void mergeSort(int[] array, int l, int r) {
        if (l < r) {
            int mid = (l + r) / 2;
            mergeSort(array, l, mid);
            mergeSort(array, mid + 1, r);
            merge(array, l, mid, mid + 1, r);
        }
    }
    
    private void merge(int[] array, int l1, int r1, int l2, int r2) {
        int i = l1;
        int j = l2;
        List<Integer> list = new ArrayList<>();
        while (i <= r1 && j <= r2) {
            if (array[i] > array[j]) {
                list.add(array[j++]);
                count += (r1 - i + 1);
                count = count % MOD;
            } else {
                list.add(array[i++]);
            }
        }
        while (i <= r1) {
            list.add(array[i++]);
        }
        while (j <= r2) {
            list.add(array[j++]);
        }
        for (int k = l1; k <= r2; k++) {
            array[k] = list.get(k - l1);
        }
    }
}

构建乘积数组

public int[] multiply(int[] A) {
    int[] B = new int[A.length];
    int temp = 0;
    for (int i = 0; i < A.length; i++) {
        temp = A[i];
        A[i] = 1;
        B[i] = 1;
        for (int j = 0; j < A.length; j++) {
            B[i] *= A[j];
        }
        A[i] = temp;
    }
    return B;
}

2.替换空格

public String replaceSpace (String s) {
    StringBuilder sb = new StringBuilder();
    for(int i = 0; i < s.length();i++){
        char temp = s.charAt(i);
        if(temp == ' '){
            sb.append("%20");
        }else{
            sb.append(temp);
        }
    }
    return sb.toString();
}

27.字符串的排列格

public class Solution {
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> list = new ArrayList<>();
        if (str == null || str.length() < 1) return list;

        recur(str, "", list);
        return list;
    }

    public void recur(String str, String cur, ArrayList<String> list) {
        if ( str.length() == 0 && !list.contains(cur) ) {
            list.add(cur);
        }
        for (int i = 0; i < str.length(); i++) {
            recur(str.substring(0, i) + str.substring(i + 1, str.length()),
                  cur + str.charAt(i), 
                  list);
        }
    }
}

最长不含重复字符的子字符串

public int lengthOfLongestSubstring (String s) {
    int start = -1, sub = 1;
    //哈希表存储字符和字符位置的对应关系
    HashMap<Character, Integer> map = new HashMap<Character, Integer>();
    //遍历字符串
    for (int i = 0; i < s.length(); i++) {
        if (map.containsKey(s.charAt(i))) {
            //更新最近的相同字符作为长度计算起点
            start = Math.max(start, map.get(s.charAt(i)));
        }
        map.put(s.charAt(i), i);
        //更新最长不同字符字串长度值
        sub = Math.max(sub, i - start);
    }
    return sub;
}

解法2:

public int lengthOfLongestSubstring (String s) {
    int max = 0;
    boolean[] sign = new boolean[256];
    int left = 0, right = 0;
    while (right < s.length()) {
        char ch = s.charAt(right);
        if (sign[ch]) {
            while (s.charAt(left) != ch) {
                sign[s.charAt(left++)] = false;
            }
            sign[s.charAt(left++)] = false;
        }
        sign[ch] = true;
        right++;
        max = Math.max(max, right - left);
    }
    return max;
}

34.第一个只出现一次的字符

public int FirstNotRepeatingChar(String str) {
    for (int i = 0; i < str.length(); i++) {
        if (str.indexOf(str.charAt(i)) != str.lastIndexOf(str.charAt(i))) continue;
        else return i;
    }
    return -1;
}

解法2:

public int FirstNotRepeatingChar(String str) {
    if (str.length() == 0 || str == null) return -1;

    HashMap<Character, Integer> map = new LinkedHashMap<>();
    for (int i = 0; i < str.length(); i++) {
        if (!map.keySet().contains(str.charAt(i))) {
            map.put(str.charAt(i), 1);
        } else {
            map.put(str.charAt(i), map.get(str.charAt(i)) + 1);
        }
    }

    for (int i = 0; i < str.length(); i++) {
        if (map.get(str.charAt(i)) == 1) {
            return i;
        }
    }
    return -1;
}

43.左旋转字符串

public String LeftRotateString(String str,int n) {
    if (str == null || str.length() == 0) return "";
    int m = n%str.length();
    return str.substring(m, str.length()) + str.substring(0, m);
}

把字符串转换成整数(atoi)

public int StrToInt (String s) {
    s = s.trim();
    if (s.equals("") || (s.charAt(0) < '0' || s.charAt(0) > '9') && (s.charAt(0) != '+' && s.charAt(0) != '-')) {
        return 0;
    }
    int index = 0;
    boolean over = false;
    int res = 0;
    if (s.charAt(0) == '+' || s.charAt(0) == '-') {
        index++;
    }
    while (index < s.length()) {
        if (s.charAt(index) > '9' || s.charAt(index) < '0') break;
        else {
            res = res * 10 + s.charAt(index++) - '0';
            if (res < 0) {
                over = true;
                break;
            }
        }
    }
    if (over) {
        return s.charAt(0) == '-' ? Integer.MIN_VALUE : Integer.MAX_VALUE;
    } else {
        return s.charAt(0) == '-' ? res * -1 : res;
    }
}

44.翻转单词序列

public String ReverseSentence(String str) {
    String[] slice = str.split(" ");
    StringBuilder sb = new StringBuilder();
    for(int i=slice.length-1; i>=0; i--) {
        sb.append(slice[i]);
        if(i!=0){
            sb.append(" ");
        }
    }
    return sb.toString();
}

52.正则表达式匹配

public boolean match (String ss, String pp) {
    // 往原字符头部插入空格,这样得到char数组是从 1 开始,而且可以使得 f[0][0] = true,可以将 true 这个结果滚动下去
    int n = ss.length(), m = pp.length();
    ss = " " + ss;
    pp = " " + pp;
    char[] s = ss.toCharArray();
    char[] p = pp.toCharArray();
    // f(i,j) 代表考虑 s 中的 1~i 字符和 p 中的 1~j 字符 是否匹配
    boolean[][] f = new boolean[n + 1][m + 1];
    f[0][0] = true;
    for (int i = 0; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            // 如果下一个字符是 '*',则代表当前字符不能被单独使用,跳过
            if (j + 1 <= m && p[j + 1] == '*') continue;

            // 对应了 p[j] 为普通字符和 '.' 的两种情况
            if (i - 1 >= 0 && p[j] != '*') {
                f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
            } else if (p[j] == '*') {
                // 对应了 p[j] 为 '*' 的情况
                f[i][j] = (j - 2 >= 0 && f[i][j - 2]) || (i - 1 >= 0 && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.'));
            }
        }
    }
    return f[n][m];
}

53.表示数值的字符串

public boolean isNumeric (String s) {
    if (s == null || s.length() == 0) return false;
    //去掉首位空格
    s = s.trim();
    boolean numFlag = false;
    boolean dotFlag = false;
    boolean eFlag = false;
    for (int i = 0; i < s.length(); i++) {
        //判定为数字,则标记numFlag
        if (Character.isDigit(s.charAt(i))) {
            numFlag = true;
            //判定为.  需要没出现过.并且没出现过e// 点在e前面
        } else if (s.charAt(i) == '.' && !dotFlag && !eFlag) {
            dotFlag = true;
            //判定为e,需要没出现过e,并且出过数字了
        } else if ((s.charAt(i) == 'e' || s.charAt(i) == 'E') && !eFlag && numFlag) {
            eFlag = true;
            numFlag = false;//为了避免123e这种请求,出现e之后就标志为false
            //判定为+-符号,只能出现在第一位或者紧接e后面
        } else if ((s.charAt(i) == '+' || s.charAt(i) == '-') && (i == 0 ||
                   s.charAt(i - 1) == 'e' || s.charAt(i - 1) == 'E')) {

            //其他情况,都是非法的
        } else {
            return false;
        }
    }
    return numFlag;
}

54.字符流中第一个不重复的字符串

public class Solution {
    private int[] cnt = new int[256];
    private Queue<Character> queue = new LinkedList<>();

    public void Insert(char ch) {
        cnt[ch]++;
        queue.add(ch);
        //关键:队列不为空,且队首字符个数大于1的全部出队
        while (!queue.isEmpty() && cnt[queue.peek()] > 1) 
            queue.poll();
    }
    
    public char FirstAppearingOnce() {
        return queue.isEmpty() ? '#' : queue.peek();
    }
}

3.从尾到头打印链表

迭代:

 public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
     ArrayList<Integer> list = new ArrayList<>();
     ListNode tmp = listNode;
     while(tmp!=null){
         list.add(0,tmp.val);
         tmp = tmp.next;
     }
     return list;
 }

递归:

public void reversePrint(ListNode head) {
    if (head == null)
        return;
    reversePrint(head.next);
    System.out.println(head.val);
}

删除链表中的节点

public ListNode deleteNode (ListNode head, int val) {
    ListNode dummy = new ListNode(-1);
    dummy.next = head;
    ListNode node = dummy;
    while(node.next!=null){
        if(node.next.val==val){
           node.next = node.next.next;
            break;
        }
        node = node.next;
    }
    return dummy.next;
}

14.链表中倒数第K个结点

双指针:

 public ListNode FindKthToTail(ListNode pHead, int k) {
    if (pHead == null)
        return pHead;
    ListNode first = pHead;
    ListNode second = pHead;
    //第一个指针先走k步
    while (k-- > 0) {
        if (first == null)
            return null;
        first = first.next;
    }
    //然后两个指针在同时前进
    while (first != null) {
        first = first.next;
        second = second.next;
    }
    return second;
}

使用栈:

 public ListNode FindKthToTail(ListNode pHead, int k) {
     Stack<ListNode> stack = new Stack<>();
     //链表节点压栈
     int count = 0;
     while (pHead != null) {
         stack.push(pHead);
         pHead = pHead.next;
         count++;
     }
     if (count < k || k == 0)
         return null;
     //在出栈串成新的链表
     ListNode firstNode = stack.pop();
     while (--k > 0) {
         ListNode temp = stack.pop();
         temp.next = firstNode;
         firstNode = temp;
     }
     return firstNode;
 }

15.反转链表

public ListNode ReverseList(ListNode head) {
    ListNode pre = null;
    ListNode next = head;
    while(head != null){
        next = head.next;
        head.next = pre;
        pre = head;
        head = next;
    }
    return pre;
}

递归:

public ListNode ReverseList(ListNode head) {
    if (head == null || head.next == null) return head;
    ListNode newHead = ReverseList(head.next);
    head.next.next = head;
    head.next = null;
    return newHead;
}

16.合并两个排序的链表

递归:

public ListNode Merge(ListNode list1,ListNode list2) {
    if(list1==null) return list2;
	if(list2==null) return list1;
    
    if(list2.val>list1.val){
        list1.next = Merge(list1.next,list2);
        return list1;
    } else {
        list2.next = Merge(list1,list2.next);
        return list2;
    }
}

迭代:

public ListNode Merge(ListNode list1,ListNode list2) {
    ListNode dummy = new ListNode(-1);
    ListNode p = dummy;
    // 注意下面的与不能是短路与,必须保证两个list都不为空
    while(list1 != null && list2 != null) {
        if(list1.val > list2.val) {
            p.next = list2;
            list2 = list2.next;
            p = p.next;
        } else if(list1.val <= list2.val) {
            p.next = list1;
            list1 = list1.next;
            p = p.next;
        }
    }
    // list1后面还有,就把剩下的全部拿走
    if(list1 != null) {
        p.next = list1;
    }
    if(list2 != null) {
        p.next = list2;
    }
    return dummy.next;
}

25.复杂链表的复制

public RandomListNode Clone(RandomListNode head) {
    if (head == null) return null;
    RandomListNode dummy = new RandomListNode(-1);
    dummy.next = head;
    while (head != null) {
        RandomListNode node = new RandomListNode(head.label);
        node.next = head.next;
        head.next = node;
        head = node.next;
    }
    head = dummy.next;
    while (head != null) {
        if (head.random != null) {
            head.next.random = head.random.next;
        }
        head = head.next.next;
    }
    head = dummy.next;
    RandomListNode p = head.next;
    while (head != null) {
        RandomListNode tmp = head.next;
        if (head.next != null) head.next = head.next.next;
        head = tmp;
    }
    return p;
}

36.两个链表的第一个公共结点

public ListNode FindFirstCommonNode(ListNode a, ListNode b) {
    ListNode ta = a, tb = b;
    while (ta != tb) {
        ta = ta == null ? b : ta.next;
        tb = tb == null ? a : tb.next;
    }
    return ta;
}

55.链表中环的入口结点

public ListNode EntryNodeOfLoop(ListNode pHead) {
    if (pHead == null || pHead.next == null) {
        return null;
    }

    ListNode fast = pHead;
    ListNode slow = pHead;

    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow) {
            ListNode p = pHead;
            while (p != slow) {
                p = p.next;
                slow = slow.next;
            }
            return p;
        }
    }
    return null;
}

56.删除链表中重复的结点

public ListNode deleteDuplication(ListNode pHead) {
    if (pHead == null || pHead.next == null) {
        return pHead;
    }
    // 自己构建辅助头结点
    ListNode head = new ListNode(Integer.MIN_VALUE);
    head.next = pHead;
    ListNode pre = head;
    ListNode cur = head.next;
    while (cur != null) {
        if (cur.next != null && cur.next.val == cur.val) {
            // 相同结点一直前进
            while (cur.next != null && cur.next.val == cur.val) {
                cur = cur.next;
            }
            // 退出循环时,cur 指向重复值,也需要删除,而 cur.next 指向第一个不重复的值
            // cur 继续前进
            cur = cur.next;
            // pre 连接新结点
            pre.next = cur;
        } else {
            pre = cur;
            cur = cur.next;
        }
    }
    return head.next;
}

4.重建二叉树

public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
    if (pre.length == 0 || in.length == 0) {
        return null;
    }
    TreeNode root = new TreeNode(pre[0]);
    // 在中序中找到前序的根
    for (int i = 0; i < in.length; i++) {
        if (in[i] == pre[0]) {
            // 左子树,注意 copyOfRange 函数,左闭右开
            root.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i + 1), Arrays.copyOfRange(in, 0, i));
            // 右子树,注意 copyOfRange 函数,左闭右开
            root.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i + 1, pre.length), Arrays.copyOfRange(in, i + 1, in.length));
            break;
        }
    }
    return root;
}

17.树的子结构

public class Solution {
     public boolean isSame(TreeNode root1,TreeNode root2){
        //如果root2为空,则为true(不需要考虑root1的状况)
        if(root2 == null) return true;
        if(root1 == null) return false;
        //判断首节点的值,然后root1与root2的左子树,然后root1与root2的右子树
        return root1.val == root2.val  &&  isSame(root1.left, root2.left)  &&  isSame(root1.right, root2.right);
    }
    
    //方法一:递归的方式(利用深度优先遍历的思想)
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        //判断root1和root2是否为null(空树不是任意一个树的子结构)
        if(root1 == null || root2 == null) return false;
        //如果首结点不相等,则依次比较左子树、右子树与root2的关系
        return isSame(root1, root2) || HasSubtree(root1.left, root2) || HasSubtree(root1.right,root2);
    }
}

非递归:

public class Solution {
    //判断结构相同必须需要的函数
    public boolean isSame(TreeNode root1,TreeNode root2){
        //如果root2为空,则为true(不需要考虑root1的状况)
        if(root2 == null) return true;
        if(root1 == null) return false;
        //判断首节点的值,然后root1与root2的左子树,然后root1与root2的右子树
        return root1.val == root2.val  &&  isSame(root1.left, root2.left)  &&  isSame(root1.right, root2.right);
    }
    //方法二:层次遍历的方式(利用广度优先遍历的思想)
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        //判断root1和root2是否为null(空树不是任意一个树的子结构)
        if(root1 == null || root2 == null){
            return false;
        }
        //队列(先进先出的特性)
        Queue<TreeNode> queue = new LinkedList<>();
        //把root1放入队列中
        queue.offer(root1);
        //判断队列是否为null
        while(!queue.isEmpty()){
            //取出队列中的结点
            TreeNode cur = queue.poll();
            //判断该结点是否和root相等
            if(isSame(cur, root2)) return true;
            else{
                //不相等则把该结点的不为空的左右子节点放入队列中
                if(cur.left != null) queue.offer(cur.left);
                if(cur.right != null) queue.offer(cur.right);
            }
        }
        return false;
    }
}

18.二叉树的镜像

BFS解决:

public TreeNode Mirror(TreeNode root) {
    //如果为空直接返回
    if (root == null)
        return null;
    //队列
    final Queue<TreeNode> queue = new LinkedList<>();
    //首先把根节点加入到队列中
    queue.add(root);
    while (!queue.isEmpty()) {
        //poll方法相当于移除队列头部的元素
        TreeNode node = queue.poll();
        //交换node节点的两个子节点
        TreeNode left = node.left;
        node.left = node.right;
        node.right = left;
        //如果当前节点的左子树不为空,就把左子树
        //节点加入到队列中
        if (node.left != null) {
            queue.add(node.left);
        }
        //如果当前节点的右子树不为空,就把右子树
        //节点加入到队列中
        if (node.right != null) {
            queue.add(node.right);
        }
    }
    return root;
}

DFS解决:

public TreeNode Mirror(TreeNode root) {//DFS
    //如果为空直接返回
    if (root == null)
        return null;
    //栈
    Stack<TreeNode> stack = new Stack<>();
    //根节点压栈
    stack.push(root);
    //如果栈不为空就继续循环
    while (!stack.empty()) {
        //出栈
        TreeNode node = stack.pop();
        //子节点交换
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
        //左子节点不为空入栈
        if (node.left != null)
            stack.push(node.left);
        //右子节点不为空入栈
        if (node.right != null)
            stack.push(node.right);
    }
    return root;
}

递归中序:

public TreeNode Mirror(TreeNode root) {
    if (root == null) return null;
    Mirror(root.left);
    //子节点交换
    TreeNode temp = root.left;
    root.left = root.right;
    root.right = temp;
    //上面交换过了,这里root.right要变成root.left
    Mirror(root.left);
    return root;
}

递归后续:

public TreeNode Mirror(TreeNode root) {
    if (root == null)
        return null;
    TreeNode left = Mirror(root.left);
    TreeNode right = Mirror(root.right);
    root.left = right;
    root.right = left;
    return root;
}

22.从上往下打印二叉树(空节点不打印,跳过)

public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
    ArrayList<Integer> list = new ArrayList<Integer>();
    if(root==null){//注意:空树返回一个默认构造的空List,而不是一个空指针null
        return list;
    }
    
 	//queue用来保存当前遍历到了哪个节点,一次性把一个节点的左右子都入队
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    TreeNode cur = root;
    queue.offer(cur);
    //只要队列中还有节点就说明还没遍历完,继续。
    //每次从队列出队,然后将这个节点左右子入队列(FIFO,故能完成广度/层级遍历),再将这个节点记录在list中即可。
    while(!queue.isEmpty()){
        cur = queue.poll();
        list.add(cur.val);
        if(cur.left!=null){
            queue.offer(cur.left);
        }
        if(cur.right!=null){
            queue.offer(cur.right);
        }
    }
    return list;
}

23.二叉搜索树的后序遍历序列

public boolean VerifySquenceOfBST(int[] sequence) {
    if (sequence == null || sequence.length == 0)
        return false;
    return verify(sequence, 0, sequence.length - 1);
}

private boolean verify(int[] sequence, int first, int last) {
    if (last - first <= 1)  return true;
    int rootVal = sequence[last];
    int cutIndex = first;
    
    while (cutIndex < last && sequence[cutIndex] <= rootVal)
        cutIndex++;
    
    for (int i = cutIndex; i < last; i++)
        if (sequence[i] < rootVal)
            return false;
    
    return verify(sequence, first, cutIndex - 1) && verify(sequence, cutIndex, last - 1);
}

24.判断二叉树中是否存在和为某一值的路径

  • 注明:从根节点到叶子节点的节点值之和等于 sum 的路径
public boolean hasPathSum(TreeNode root, int sum) {
    if (root == null) return false;
    //如果到叶子节点了,并且剩余值等于叶子节点的值,说明找到了这样的结果,直接返回true
    if (root.left == null && root.right == null && sum - root.val == 0)
        return true;
    //分别沿着左右子节点走下去,然后顺便把当前节点的值减掉,左右子节点只要有一个返回true,
    //说明存在这样的结果
    return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
}

二叉树中从根到叶子节点和为某一值的所有路径

public class Solution {
    private ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
    private LinkedList<Integer> path = new LinkedList<>();
    
    void dfs(TreeNode root, int number) {
        // 处理树为空
        if (root == null) return;
        // 路径更新
        path.add(root.val);
        // number更新
        number -= root.val;
        // 如果递归当前节点为叶子节点且该条路径的值已经达到了expectNumber,则更新ret
        if(root.left == null && root.right == null && number == 0) {
            ret.add(new ArrayList<>(path));
        }
        // 左右子树递归
        dfs(root.left, number);
        dfs(root.right, number);
        path.removeLast();
    }
    
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int expectNumber) {
        dfs(root, expectNumber);
        return ret;
    }
}

二叉树中任意节点路径和为某一值的所有路径

public class Solution {
    public int key = 0;
    public void dfs(TreeNode root, int sum) {
        if (null == root) return;
        sum -= root.val;
        // 注意这里为什么不return?
        // - 题目定义不需要从根节点开始,也不需要在叶子节点结束;
        // - 因此需要从根节点开始,一个节点一个节点的进行深度遍历;
        // - 也就是从根节点到叶子节点可能有多条可能的路径;
        // - 可能是无序的小于等于0的Val;
        // - 因此只要保证后继连续节点Value和为0,就又是一条新路径;
        if (sum == 0) key++;
        dfs(root.left, sum);
        dfs(root.right, sum);
    }
    public int FindPath (TreeNode root, int sum) {
        if (null == root) return key;
        // write code here
        // 题目定义不需要从根节点开始,也不需要在叶子节点结束;
        // 因此需要从根节点开始,一个节点一个节点的进行深度遍历;
        dfs(root, sum);
        FindPath(root.left, sum);
        FindPath(root.right, sum);
        return key;
    }
}

写法2:

public int FindPath (TreeNode root, int sum) {
    // write code here
    if(root==null)
        return 0;
    return help(root,sum) + FindPath(root.left,sum) + FindPath(root.right,sum);
}
  
private int help(TreeNode root, int sum){
    if(root==null){
        return 0;
    }
    int count=0;
    if(root.val==sum)
        count++;
    return count + help(root.left,sum-root.val) + help(root.right,sum-root.val);
}

写法3:

public class Solution {
    HashMap<Integer,Integer> map = new HashMap<>();
    
    public int FindPath (TreeNode root, int sum) {
        if(root == null) return 0;
        map.put(0,1);
        return dfs(root,0,sum);
    }
    int dfs(TreeNode root, int sum, int target){
        if(root == null) return 0;
        sum += root.val;
        int count = 0;
        if(map.containsKey(sum - target)){
            count += map.get(sum - target);
        }
        map.put(sum,map.getOrDefault(sum,0) + 1);
        count += dfs(root.left,sum,target);
        count += dfs(root.right,sum,target);
        return count;
    }
}

26.二叉搜索树与双向链表

迭代:

public TreeNode Convert(TreeNode root) {
    if(root==null)
        return null;
    Stack<TreeNode> stack = new Stack<TreeNode>();
    TreeNode p = root;
    TreeNode pre = null;// 用于保存中序遍历序列的上一节点
    boolean isFirst = true;
    while(p!=null||!stack.isEmpty()){
        while(p!=null){
            stack.push(p);
            p = p.left;
        }
        p = stack.pop();
        if(isFirst){
            root = p;// 将中序遍历序列中的第一个节点记为root
            pre = root;
            isFirst = false;
        }else{
            pre.right = p;
            p.left = pre;
            pre = p;
        }      
        p = p.right;
    }
    return root;
}

递归:

public TreeNode leftLast = null;
public TreeNode Convert(TreeNode root) {
    if (root == null)
        return null;
    if (root.left == null && root.right == null) {
        leftLast = root;// 最后的一个节点可能为最右侧的叶节点
        return root;
    }
    // 1.将左子树构造成双链表,并返回链表头节点
    TreeNode left = Convert(root.left);
    // 3.如果左子树链表不为空的话,将当前root追加到左子树链表
    if (left != null) {
        leftLast.right = root;
        root.left = leftLast;
    }
    leftLast = root;// 当根节点只含左子树时,则该根节点为最后一个节点
    // 4.将右子树构造成双链表,并返回链表头节点
    TreeNode right = Convert(root.right);
    // 5.如果右子树链表不为空的话,将该链表追加到root节点之后
    if (right != null) {
        right.left = root;
        root.right = right;
    }
    return left != null ? left : root;
}

写法2:

TreeNode head, pre;
public TreeNode Convert(TreeNode pRootOfTree) {
    if (pRootOfTree == null) return null;
    dfs(pRootOfTree);
    return head;
}
public void dfs(TreeNode root) {
    if (root.left != null)    dfs(root.left);
    if (pre != null)    pre.right = root;
    else    head = root;
    root.left = pre;
    pre = root;
    if (root.right != null)    dfs(root.right);
}

38.二叉树的深度

// DFS 深度遍历
public int TreeDepth(TreeNode root) {
    //空节点没有深度
    if(root == null) return 0;
    //返回子树深度+1
    return Math.max(TreeDepth(root.left), TreeDepth(root.right)) + 1;
}

39.判断是不是平衡二叉树

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root == null){
            return true;
        }
        if( (Math.abs(maxDepth(root.left) - maxDepth(root.right)) ) > 1 ) {
            return false;   
        }
        return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
    }
    // 最大深度
    private int maxDepth(TreeNode root) {
        if(root == null) {
            return 0;
        }
        return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
    }
}

BM38在二叉树中找到两个节点的最近公共祖先

public class Solution {
    public TreeNode commonAncestor (TreeNode root, int p, int q) {
        if (null == root) return null;
        if (root.val == p || root.val == q) return root;
        // 通过递归假设我们知道了运算结果 题目含义是不会出现重复节点
        TreeNode left = commonAncestor(root.left, p, q);
        TreeNode right = commonAncestor(root.right, p, q);
        if (left == null) return right;
        else if (right == null) return left;
        else return root;
    } 
    
    public int lowestCommonAncestor (TreeNode root, int p, int q) {
        return commonAncestor(root, p, q).val;
    }
}

迭代:

public int lowestCommonAncestor(TreeNode root, int o1, int o2) {
    //记录遍历到的每个节点的父节点。
    Map<Integer, Integer> map = new HashMap<>();
    Queue<TreeNode> queue = new LinkedList<>();
    map.put(root.val, Integer.MIN_VALUE);//根节点没有父节点,给他默认一个值
    queue.add(root);
    //直到两个节点都找到为止。
    while (!map.containsKey(o1) || !map.containsKey(o2)) {
        //队列是一边进一边出,这里poll方法是出队,
        TreeNode node = queue.poll();
        if (node.left != null) {
            //左子节点不为空,记录下他的父节点
            map.put(node.left.val, node.val);
            //左子节点不为空,把它加入到队列中
            queue.add(node.left);
        }
        //右节点同上
        if (node.right != null) {
            map.put(node.right.val, node.val);
            queue.add(node.right);
        }
    }
    Set<Integer> ancestors = new HashSet<>();
    //记录下o1和他的祖先节点,从o1节点开始一直到根节点。
    while (map.containsKey(o1)) {
        ancestors.add(o1);
        o1 = map.get(o1);
    }
    //查看o1和他的祖先节点是否包含o2节点,如果不包含再看是否包含o2的父节点……
    while (!ancestors.contains(o2))
        o2 = map.get(o2);
    return o2;
}

JZ68二叉搜索树的最近公共祖先

public class Solution {
     //求根节点到目标节点的路径
    public ArrayList<Integer> getPath(TreeNode root, int target) {
        ArrayList<Integer> path = new ArrayList<Integer>();
        TreeNode node = root;
        //节点值都不同,可以直接用值比较
        while(node.val != target){
            path.add(node.val);
            //小的在左子树
            if(target < node.val)
                node = node.left;
            //大的在右子树
            else
                node = node.right;
        }
        path.add(node.val);
        return path;
    }
    
    public int lowestCommonAncestor (TreeNode root, int p, int q) {
        //求根节点到两个节点的路径
        ArrayList<Integer> pList = getPath(root, p);
        ArrayList<Integer> qList = getPath(root, q);
        int res = 0;
        //比较两个路径,找到第一个不同的点
        for(int i = 0; i < pList.size() && i < qList.size(); i++){
            int x = pList.get(i);
            int y = qList.get(i);
            //最后一个相同的节点就是最近公共祖先
            if(x == y)
                res = pList.get(i);
            else
                break;
        }
        return res;
    }
}

利用二叉搜索树的性质递归:

public class Solution {
    public TreeNode commonAncestor (TreeNode root, int p, int q) {
        if (null == root) return null;
        if (root.val == p || root.val == q) return root;
        // 通过递归假设我们知道了运算结果 题目含义是不会出现重复节点
        if (p < root.val && q < root.val) return commonAncestor(root.left, p, q);
        else if (p > root.val && q > root.val) return commonAncestor(root.right, p, q);
        else return root;
    } 
    
    public int lowestCommonAncestor (TreeNode root, int p, int q) {
        return commonAncestor(root, p, q).val;
    }
}

57.二叉树的下一个结点

public TreeLinkNode GetNext(TreeLinkNode pNode) {
     // 1. 
     if (pNode.right != null) {
         TreeLinkNode pRight = pNode.right;
         while (pRight.left != null) {
             pRight = pRight.left;
         }
         return pRight;
     }
     // 2. 
     if (pNode.next != null && pNode.next.left == pNode) {
         return pNode.next;
     }
     // 3. 
     if (pNode.next != null) {
         TreeLinkNode pNext = pNode.next;
         while (pNext.next != null && pNext.next.right == pNext) {
             pNext = pNext.next;
         }
         return pNext.next;
     }
     return null;
}

58.对称的二叉树

public class Solution {
   public boolean isSymmetrical(TreeNode root) {
        return check(root, root);
    }
    boolean check(TreeNode a, TreeNode b) {
        if (a == null && b == null) return true;
        if (a == null || b == null) return false;
        if (a.val != b.val) return false;
        return check(a.left, b.right) && check(a.right, b.left);
    }
}

59.按之字形顺序打印二叉树

public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
    LinkedList<TreeNode> q = new LinkedList<>();
    ArrayList<ArrayList<Integer>> res = new ArrayList<>();
    boolean rev = true;
    q.add(pRoot);
    while(!q.isEmpty()){
        int size = q.size();
        ArrayList<Integer> list = new ArrayList<>();
        for(int i=0; i<size; i++){
            TreeNode node = q.poll();
            if(node == null){
                continue;
            }
            if(rev){
                list.add(node.val);
            }else{
                list.add(0, node.val);
            }
            q.offer(node.left);
            q.offer(node.right);
        }
        if(list.size()!=0){
            res.add(list);
        }
        rev = !rev;
    }
    return res;
}

60.把二叉树打印成多行

// 递归:
public class Solution {
    //层次遍历
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer> > res = new ArrayList<ArrayList<Integer> >();
        //树的层级从1开始递归计数
        traverse(pRoot, res, 1);
        return res;
    }

    private void traverse(TreeNode root, ArrayList<ArrayList<Integer> > res, int depth) {
        if (root == null) return ;

        //数组长度小于当前层数,新开一层
        if (res.size() < depth)
            res.add(new ArrayList<Integer>());
        //数组从0开始计数因此减1,在节点当前层的数组中插入节点
        res.get(depth - 1).add(root.val);
        //递归左右时节点深度记得加1
        traverse(root.left, res, depth + 1);
        traverse(root.right, res, depth + 1);
    }
}

迭代:

//层次遍历
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
    TreeNode head = pRoot;
    ArrayList<ArrayList<Integer> > res = new ArrayList<ArrayList<Integer> >();
    if(head == null)
        //如果是空,则直接返回空数组
        return res; 
    //队列存储,进行层次遍历
    Queue<TreeNode> temp = new LinkedList<TreeNode>(); 
    temp.offer(head);
    TreeNode p;
    while(!temp.isEmpty()){
        //记录二叉树的某一行
        ArrayList<Integer> row = new ArrayList<Integer>();
        int n = temp.size();
        //因先进入的是根节点,故每层节点多少,队列大小就是多少
        for(int i = 0; i < n; i++){
            p = temp.poll();
            row.add(p.val);
            //若是左右孩子存在,则存入左右孩子作为下一个层次
            if(p.left != null)
                temp.offer(p.left);
            if(p.right != null)
                temp.offer(p.right);
        }
        res.add(row);
    }
    return res;
}

61.序列化二叉树

public class Solution {
    int INF = 0x3f3f3f3f;
    TreeNode emptyNode = new TreeNode(INF);
    public String Serialize(TreeNode root) {
        if (root == null) return "";
 
        StringBuilder sb = new StringBuilder();
        // 使用队列进行层序遍历,起始先将 root 放入队列
        Deque<TreeNode> d = new ArrayDeque<>();
        d.addLast(root);
        while (!d.isEmpty()) {
            // 每次从队列中取出元素进行「拼接」,包括「正常节点」和「叶子节点对应的首位空节点」
            TreeNode poll = d.pollFirst();
            sb.append(poll.val + "_");
            // 如果取出的节点不为「占位节点」,则继续往下拓展,同时防止「占位节点」不继续往下拓展
            if (!poll.equals(emptyNode)) {
                d.addLast(poll.left != null ? poll.left : emptyNode);
                d.addLast(poll.right != null ? poll.right : emptyNode);
            }
        }
        return sb.toString();
    }
 
    public TreeNode Deserialize(String data) {
        if (data.equals("")) return null;
 
        // 根据分隔符进行分割
        String[] ss = data.split("_");
        int n = ss.length;
        // 怎么序列化就怎么反序列化
        // 使用队列进行层序遍历,起始先将 root 构建出来,并放入队列
        TreeNode root = new TreeNode(Integer.parseInt(ss[0]));
        Deque<TreeNode> d = new ArrayDeque<>();
        d.addLast(root);
        for (int i = 1; i < n - 1; i += 2) {
            TreeNode poll = d.pollFirst();
            // 每次从中取出左右节点对应 val
            int a = Integer.parseInt(ss[i]), b = Integer.parseInt(ss[i + 1]);
            // 如果左节点对应的值不是 INF,则构建「真实节点」
            if (a != INF) {
                poll.left = new TreeNode(a);
                d.addLast(poll.left);
            }
            // 如果右节点对应的值不是 INF,则构建「真实节点」
            if (b != INF) {
                poll.right = new TreeNode(b);
                d.addLast(poll.right);
            }
        }
        return root;
    }
}

递归:

public class Solution {
    public String Serialize(TreeNode root) {
        if(root == null) return "#";    // 对所有的空节点也要存储占位符号"#"
        // 递归前序遍历返回字符串
        return root.val + "," + Serialize(root.left) + "," + Serialize(root.right);    
    }
    
    public TreeNode Deserialize(String str) {
       String[] s = str.split(",");            // 切分字符串
       Queue<String> q = new LinkedList<String>();
       for(int i = 0; i != s.length; i++) 
           q.offer(s[i]);        // 将分割后的字符串数组顺序入队
       return de(q);             // 按照新的递归函数进行返回
    }
    
    public TreeNode de(Queue<String> queue) {
        String s = queue.poll();                                // 递归函数每次从队首读出一个元素,因此读出的顺序也是前序
        if(s.equals("#")) return null;                          // 对递归推出条件之一进行处理
        TreeNode head = new TreeNode(Integer.valueOf(s));       // 前序首先建立一个节点
        head.left = de(queue);                                  // 然后递归左右子节点
        head.right = de(queue);
        return head;
    }
}

62.二叉搜索树的第K个节点

public class Solution {
    int count = 0;    //标记遍历的节点数
    public int KthNode (TreeNode proot, int k) {
        if(proot == null)  return -1;
        
        int l = KthNode(proot.left, k);
        if (l != -1) return l;
        
        ++count;
        
        if(count == k) return proot.val;
        int r = KthNode(proot.right, k);
        
        return r;
    }
}

65.矩阵中的路径

public class Solution {

    public boolean hasPath (char[][] matrix, String word) {
        char[] words = word.toCharArray();
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                //从[i,j]这个坐标开始查找
                if (dfs(matrix, words, i, j, 0))
                    return true;
            }
        }
        return false;
    }

    boolean dfs(char[][] matrix, char[] word, int i, int j, int index) {
        if (i >= matrix.length || i < 0 || j >= matrix[0].length || j < 0 || matrix[i][j] != word[index])
            return false;
        if (index == word.length - 1) return true;
        char tmp = matrix[i][j];
        matrix[i][j] = '.';
        //走递归,沿着当前坐标的上下左右4个方向查找
        boolean res = dfs(matrix, word, i + 1, j, index + 1)
                      || dfs(matrix, word, i - 1, j, index + 1)
                      || dfs(matrix, word, i, j + 1, index + 1)
                      || dfs(matrix, word, i, j - 1, index + 1);
        //递归之后再把当前的坐标复原
        matrix[i][j] = tmp;
        return res;
    }
}

66.机器人的运动范围

public int movingCount(int threshold, int rows, int cols) {
    //临时变量visited记录格子是否被访问过
    boolean[][] visited = new boolean[rows][cols];
    return dfs(0, 0, rows, cols, threshold, visited);
}
 
public int dfs(int i, int j, int rows, int cols, int threshold, boolean[][] visited) {
    //i >= rows || j >= cols是边界条件的判断,threshold < sum(i, j)判断当前格子坐标是否
    // 满足条件,visited[i][j]判断这个格子是否被访问过
    if (i >= rows || j >= cols || threshold < sum(i, j) || visited[i][j])
        return 0;
    //标注这个格子被访问过
    visited[i][j] = true;
    //沿着当前格子的右边和下边继续访问
    return 1 + dfs(i + 1, j, rows, cols, threshold, visited) +
            dfs(i, j + 1, rows, cols, threshold, visited);
}
 
//计算两个坐标数字的和
private int sum(int i, int j) {
    int sum = 0;
    //计算坐标i所有数字的和
    while (i != 0) {
        sum += i % 10;
        i /= 10;
    }
    //计算坐标j所有数字的和
    while (j != 0) {
        sum += j % 10;
        j /= 10;
    }
    return sum;
}

剪绳子

public int cutRope (int target) {
    int max = Integer.MIN_VALUE;
    int sum = 1;
    for(int i=2; i<=target; i++){
         int num = target;
        for(int j=i; j>0; j--){
            if(target < j){
                continue;
            }
            sum = sum * (num/j);
            num = num - num/j;
        }
         
        max = Math.max(max, sum);
        sum =1;
    }
    return max;
}

剪绳子(进阶版)

public class Solution {
   public long cutRope (long number) {
        if (number <= 3) return number - 1;
        long a = number / 3;
        long b = number % 3;
        long c = 998244353L;
        if (b == 0) {
            return pow3WithMod(a, c) % c;
        } else if (b == 1) {
            return pow3WithMod(a-1, c) * 4 % c;
        } else {
            return pow3WithMod(a, c) * 2 % c;
        }
    }
     
    public long pow3WithMod(long n, long mod) {
        if (n == 0) return 1;
        if (n == 1) return 3;
        long part = pow3WithMod(n/2, mod);
        if (n % 2 == 0) return part * part % mod;
        else return 3 * part * part % mod;
    }
}

5.用两个栈实现队列

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
   //入栈操作
    public void push(int node) {
        stack1.push(node);
    }
 
    //出栈操作
     public int peek() {
        if (stack2.isEmpty())
            while (!stack1.isEmpty())
                stack2.push(stack1.pop());
        return stack2.isEmpty() ? -1 : stack2.peek();
    }
 
    public int pop() {
        int res = peek();
        stack2.pop();
        return res;
    }
 
    public boolean empty() {
        return stack1.empty() && stack2.empty();
    }
}

20.包含min函数的栈

public class Solution {
    Stack<Integer> stackmin = new Stack<>();
    Stack<Integer> stack = new Stack<>();
    public void push(int node) {
        stack.push(node);
        if (stackmin.empty()) {
            stackmin.push(node);
        } else {
            if (stackmin.peek() < node)
                stackmin.push(stackmin.peek());
            else
                stackmin.push(node);
        }
    }

    public void pop() {
        stack.pop();
        stackmin.pop();
    }

    public int top() {
        return stack.peek();
    }

    public int min() {
        return stackmin.peek();
    }
}

21.栈的压入弹出序列

public boolean IsPopOrder(int [] pushA, int [] popA) {
    if (pushA.length == 0 || popA.length == 0) return false;

    Stack<Integer> stack = new Stack<>();
    int popIndex = 0;

    for (int i = 0; i < pushA.length; i++) {
        stack.push(pushA[i]);
        while (!stack.isEmpty() && stack.peek() == popA[popIndex]) {
            stack.pop();
            popIndex++;
        }
    }
    return stack.isEmpty();
}

7.斐波那契数列

// 递归
public int Fibonacci(int n) {
    if(n<=1){
        return n;
    }
    return Fibonacci(n-1) + Fibonacci(n-2);
}

// 迭代
public int Fibonacci(int n) {
   if(n == 0) return 0;
   if(n == 1) return 1;
   
   int a = 1;
   int b = 0;
   for(int i=2;i<=n;i++){
       a = a + b;
       b = a - b;
   }
   return a;
}

8.跳台阶

// 会超时
public static int JumpFloor(int n) {
    if (n <= 1) return 1;
    if (n < 3) return n;
    return JumpFloor(n - 1) + JumpFloor(n - 2);
}

尾递归方式:

public int jumpFloor(int n) {
    return Fibonacci(n, 1, 1);
}

public int Fibonacci(int n, int a, int b) {
    if (n <= 1) return b;
    return Fibonacci(n - 1, b, a + b);
}

迭代:

public int jumpFloor(int n) {
    if (n <= 2) return n;
    int first = 1, second = 2, sum = 0;
    while (n-- > 2) {
        sum = first + second;
        first = second;
        second = sum;
    }
    return sum;
}

9.跳台阶扩展问题

//f(n)=2^(n−1)
public int jumpFloorII(int target) {
    return 1<<(target-1);
}

10.矩形覆盖

public int rectCover(int target) {
    if (target <= 2){
        return target;
    }
    int pre1 = 2; // n 最后使用一块,剩下 n-1 块的写法
    int pre2 = 1; // n 最后使用两块,剩下 n-2 块的写法
    for (int i = 3; i <= target; i++){
        int cur = pre1 + pre2;
        pre2 = pre1;
        pre1 = cur;
    }
    return pre1; //相对于 n+1 块来说,第 n 种的方法
}

递归:

public int rectCover(int target) {
    if (target <= 2)  return target;
    return rectCover(target - 1) + rectCover(target - 2);
}

12.数值的整数次方

public class Solution {
    public double Power(double base, int exponent) {
        return exponent > 0 ? quickPow(base, exponent) : quickPow(1/base, -exponent);
    }

    public double quickPow(double base, int exp) {
        if(exp == 0) {
            return 1;
        }
        if(exp == 1) {
            return base;
        }
        return Power(base, exp/2)*Power(base, exp - exp/2);
    }
}

47.求1+2+3+···+n数字之和

public int Sum_Solution(int n) {
     return (1+n)*n/2;
}

48.不用加减乘除做加法

迭代解法:

public int Add(int a, int b) {
    while (b != 0) {
        int temp = a ^ b;
        b = (a & b) << 1;
        a = temp;
    }
    return a;
}

递归:

public int Add(int a, int b) {
    if (a == 0 || b == 0)
        return a ^ b;
    return Add(a ^ b, (a & b) << 1);
}

11.二进制中1的个数

public int NumberOf1(int n) {
    int res = 0;
    while(n != 0){
        if((n & 1) == 1) res++;
        //无符号右移,正数负数最高位都补0
        n >>>= 1;
    }
    return res;
}

29.最小的K个数

public class Solution {
	// 使用快速排序取前k个
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if(input.length < k){
           return list;
        }
        quickSort(input,0,input.length - 1);
        for(int i = 0;i < k;i++){
            list.add(input[i]);
        }
        return list;
    }
    
    public static void quickSort(int[] array,int start,int tail) {
		if(start >= tail) {
			return;
		}
		//将第一个元素作为比较元素,从第二个开始到最后一个执行快速排序算法
		int begin = start;
		int end = tail;
		int key = array[start];
		while(begin < end) {
			while(array[end] >= key && begin < end) {
				end = end - 1;
			}
			while(array[begin] <= key && begin < end) {
				begin = begin + 1;
			}
			if(end > begin) {
				int temp = array[begin];
				array[begin] = array[end];
				array[end] = temp;
			}
		}
		array[start] = array[begin];
		array[begin] = key;
		quickSort(array,start,begin - 1);
		quickSort(array,begin + 1,tail);
	}
}

解法2:优先队列(大根堆)

public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
      PriorityQueue<Integer> queue=new PriorityQueue<Integer>((a, b)->{
          return a.intValue()-b.intValue();
      });
      for(int a:input)
          queue.offer(a);
      ArrayList<Integer> list = new ArrayList<>();
      for(int i=0;i<k;i++)
          list.add(queue.poll());
      return list;
  }

实现大根堆:

public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> list = new ArrayList<>();
        if (input == null || input.length == 0 || k > input.length || k == 0)
            return list;
        int[] arr = new int[k + 1];//数组下标0的位置作为哨兵,不存储数据
        //初始化数组
        for (int i = 1; i < k + 1; i++)
            arr[i] = input[i - 1];
        buildMaxHeap(arr, k + 1);//构造大根堆
        for (int i = k; i < input.length; i++) {
            if (input[i] < arr[1]) {
                arr[1] = input[i];
                adjustDown(arr, 1, k + 1);//将改变了根节点的二叉树继续调整为大根堆
            }
        }
        for (int i = 1; i < arr.length; i++) {
            list.add(arr[i]);
        }
        return list;
    }
    
    public void buildMaxHeap(int[] arr, int length) {
        if (arr == null || arr.length == 0 || arr.length == 1)
            return;
        for (int i = (length - 1) / 2; i > 0; i--) {
            adjustDown(arr, i, arr.length);
        }
    }
   
    public void adjustDown(int[] arr, int k, int length) {
        arr[0] = arr[k];//哨兵
        for (int i = 2 * k; i <= length; i *= 2) {
            if (i < length - 1 && arr[i] < arr[i + 1])
                i++;//取k较大的子结点的下标
            
            if (i > length - 1 || arr[0] >= arr[i])
                break;
            else {
                arr[k] = arr[i];
                k = i; //向下筛选
            }
        }
        arr[k] = arr[0];
    }
}

31.从1到n整数中1出现的次数

public int NumberOf1Between1AndN_Solution(int n) {
    // 公式
    // cur = 1  count = high * i + (low  + 1);
    // cur = 0 count = high * i;
    // cur = 其他数 count = (high + 1) * i;
    // 一位一位的算
    int count = 0;
    for (int i = 1; i <= n; i *= 10) {
        int high = n/(i*10);
        int low = n%i;
        int cur = (n/i) % 10;
        if (cur == 0) {
            count += high*i;
        } else if (cur == 1) {
            count += high*i + (low + 1);
        } else {
            count += (high + 1) * i;
        }
    }
    return count;
 }

解法2:

public int NumberOf1Between1AndN_Solution(int n) {
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        String str = String.valueOf(i);
        for (int j = 0; j < str.length(); j++) {
            if (str.charAt(j) == '1') cnt++;
        }
    }
    return cnt;
}

33.丑数

public int GetUglyNumber_Solution(int n) {
    if (n <= 0) return 0;
    int p2 = 0, p3 = 0, p5 = 0; //初始化三个指向三个潜在成为最小丑数的位置
    int[] result = new int[n];
    result[0] = 1;
    for (int i = 1; i < n; i++) {     //每一次乘以2、3、5的最小值当做本次的丑数
        result[i] = Math.min(result[p2] * 2, Math.min(result[p3] * 3, result[p5] * 5));
        if (result[i] == result[p2] * 2) p2++; //如果本次的丑数是乘2得到的,则下次就用紧接着的丑数result[p2++]乘2生成丑数
        if (result[i] == result[p3] * 3) p3++; //否则,下次乘2生成丑数,还用该丑数生成result[p2]
        if (result[i] == result[p5] * 5) p5++; //
    }
    return result[n - 1];
}

41.和为S的连续正数序列

public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
   ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
   //双指针
   int start = 1; 
   int end = 2; 
   int curSum = 3; //当前的和
   while ( start < end ){
       if( curSum < sum ){
           end++;
           curSum += end;
       }else if( curSum > sum ){
           curSum -= start;
           start++;
       }else{
           ArrayList<Integer> list = new ArrayList<Integer>();
           for( int i = start; i <= end; i++ ){
               list.add(i);
           }
           result.add(list);
           //这里要移动start否则while旋死 curSum的值也要继续处理 
           //否则继续while的时候就算错了
           curSum -= start;
           start++;
           //下面移动end 也可以不用管 交给下一次while的时候判断 
           //但是这里移动了到最后可以少进一次while
           end++;
           curSum +=end;
       }
   }
   return result;
}

42.和为S的两个数字

public ArrayList<Integer> FindNumbersWithSum(int[] array,int sum) {
    int l = 0;
    int r = array.length-1;
    ArrayList<Integer> arr = new ArrayList<Integer>();
    while (l < r){
        if (array[l] + array[r] == sum){
            arr.add(array[l]);
            arr.add(array[r]);
            return arr;
        }else if (array[l] + array[r] > sum){
            r--;
        }else{
            l++;
        }
    }
    return arr;
}

45.扑克牌顺子

public boolean IsContinuous(int [] numbers) {
    Set<Integer> set = new HashSet<>();
    for (int i = 0; i < numbers.length; i++) {
        if (numbers[i] == 0) continue;
        if (set.contains(numbers[i])) {
            return false;
        }
        set.add(numbers[i]);
    }
    ArrayList<Integer> list = new ArrayList<>(set);
    if (list.get(list.size() - 1) - list.get(0) < 5) return true;
    return false;
}

实现2:

public boolean IsContinuous(int [] numbers) {
    int low = Integer.MAX_VALUE;
    int high = Integer.MIN_VALUE;
    int[] cnt = new int[14];
    
    for(int num : numbers){
        cnt[num]++;
        if((num != 0 && cnt[num] > 1) || (num == 0 && cnt[num] > 4)) return false;
        if(num == 0) continue;
        low = Math.min(num,low);
        high = Math.max(num,high);
    }
    return high - low < 5 ? true : false;
}

46.圆圈中最后剩下的数

public int LastRemaining_Solution(int n, int m) {
    if(n <= 0 || m <= 0) return -1;
    int ans = 0;
    for(int i = 2; i <= n; i++){
        ans = (ans + m) % i;
    }
    return ans;
}

买卖股票的最好时机(一)

public int maxProfit (int[] prices) {
    int min = prices[0];
    int max = 0;
    for(int i=0;i<prices.length;i++){
        min=Math.min(prices[i], min);
        max=Math.max(max, prices[i]-min);
    }
    return max;
}

63.数据流中的中位数

public class Solution {
    //小顶堆,元素数值都比大顶堆大
    private PriorityQueue<Integer> max = new PriorityQueue<>();
    //大顶堆,元素数值较小 
    private PriorityQueue<Integer> min = new PriorityQueue<>((a, b) -> b.compareTo(a)); 
    //维护两个堆,取两个堆顶部即与中位数相关
    public void Insert(Integer num) {
        //先加入较小部分
        min.offer(num);
        //将较小部分的最大值取出,送入到较大部分
        max.offer(min.poll());  
        //平衡两个堆的数量
        if(min.size() < max.size())  
            min.offer(max.poll());
    }

    public Double GetMedian() {
        //奇数个
        if(min.size() > max.size())  return (double)min.peek();
        else return (double)(min.peek() + max.peek()) / 2; 
    }
}

解法2:

public class Solution {
    
    private static List<Integer> list = new ArrayList<>();

    public void Insert(Integer num) {
        if (num == null)  return;

        if (list.size() == 0) {
            list.add(num);
            return;
        }
        int left = 0;
        int right = list.size() - 1;
        int mid = 0;
        while (left <= right) {
            mid = (left + right) >> 1;
            if (list.get(mid) > num) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        list.add(left, num);
    }

    public Double GetMedian() {
        int size = list.size();
        if ((size & 1) == 1) {
            return (double) list.get(size >> 1);
        }
        return (list.get(size >> 1) + list.get((size - 1) >> 1)) / 2.0;
    }
}

64.滑动窗口的最大值

public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size) {
        ArrayList<Integer> res = new ArrayList<>();
        int len = num.length;
        //左闭右开
        int preMaxIndex = -1;
        for(int p=0,q = size; q <= len; p++,q++){
            int maxIndex = preMaxIndex;
            if(preMaxIndex >= p && preMaxIndex< q){
                // 前置窗口最大值还在当前窗口内,只需比较前置最大值和窗口末尾值
                if(num[q-1] > num[preMaxIndex]){
                    maxIndex = q-1;
                }
            }else{
                maxIndex = getMax(num,p,q);
            }
            res.add(num[maxIndex]);
            preMaxIndex = maxIndex;
        }
        return res;
    }
    /**
    * 返回最大值的索引
    */
    public int getMax(int[] num, int p, int q){
        //初始化为第一个
        int maxIndex = p;
        for(int i = p+1; i< q; i++){
            if(num[i]>num[maxIndex]){
                maxIndex = i;
            }
        }
        return maxIndex;
    }
}

解法2:

public ArrayList<Integer> maxInWindows(int[] num, int size) {
    Deque<Integer> queue = new ArrayDeque<>();
    int left = 0;
    int right = 0;
    ArrayList<Integer> res = new ArrayList<>();
    while (right < num.length) {
        if (right - left < size) {
            int val = num[right];
            while (!queue.isEmpty() && val > queue.peekLast()) {
                queue.pollLast();
            }
            queue.offerLast(val);
            right++;
        } else {
            // len >= size
            int leftVal = num[left];
            int minVal = queue.peekFirst();
            res.add(minVal);
            if (leftVal == minVal) {
                queue.pollFirst();
            }
            left++;
        }
    }
    res.add(queue.peekFirst());
    return res;
}

JZ44数字序列中某一位的数字

  public int findNthDigit (int n) {
        // 0
        // 1 ~ 9      | digit = 1 start = 1 * 1       count = 1 * 9 * 1
        // 10 ~ 99    | digit = 2 start = 1 * 10      count = 10 * 9 * 2
        // 100 ~ 999  | digit = 3 start = 1 * 10 * 10 count = 100 * 9 * 3
        if (n <= 0) return 0;
        long start = 1, digit = 1, count = 9;
        while (n > count) {
            n -= count; // 减去当前位数的总长度
            start *= 10;
            digit += 1;
            count = start * 9 * digit;
        }
        // 找到当前位数的区间了
        String num = (start + (n - 1) / digit) + ""; // 减去第0号元素0
        int idx = (int)((n - 1) % digit);
        return Integer.parseInt(num.charAt(idx) + "");
    }

JZ46把数字翻译成字符串

public int solve (String nums) {
    //用来保留前面两个字符位置对应的可能译码结果数
    int first = 1, second = 1;
    for(int i = 0;i < nums.length();i++){
        //遇到0不能译码,second清零
        if(nums.charAt(i) == '0'){
            second = 0;
        }
        //更新前两个字符对应的值
        //符合条件则可以译1个或者2个数字
        if(i >= 1 && Integer.parseInt(nums.substring(i-1,i+1)) <= 26){
            second = first + second;
            first = second - first;
        }
        //只能译1个数字
        else{
            first = second;
        }
    }
    return second;
}

你可能感兴趣的:(Java,数据结构与算法,java,leetcode,算法,刷题)