剑指Offer题目及答案2020

前言:牛客网剑指offer:https://www.nowcoder.com/ta/coding-interviews
最新刷题,一共67道题目。所有方法均已通过全部案例,代码复制就能运行(都有主函数且有测试案例,题目分析都写在注释上)。但有些可能不是最优,所以会持续学习,持续更新。

文章目录

  • 1. 二维数组的查找
  • 2. 替换空格
  • 3. 从尾到头打印链表
  • 4. 重建二叉树
  • 5. 用两个栈实现队列
  • 6. 旋转数组的最小数字
  • 7. 斐波那契数列
  • 8. 跳台阶
  • 9. 变态跳台阶
  • 10. 矩阵覆盖
  • 11. 二进制中1的位数
  • 12. 数值的整数次方
  • 13. 调整数组顺序使奇数位于偶数前面
  • 14. 链表中倒数第k个结点
  • 15. 反转链表
  • 16. 合并两个排序的链表
  • 17. 树的子结构
  • 18. 二叉树的镜像
  • 19. 顺时针打印矩阵
  • 20. 包含min函数的栈
  • 21. 栈的压入、弹出序列
  • 22. 从上往下打印二叉树
  • 23. 二叉搜索树的后序遍历序列
  • 24. 二叉树中和为某一值的路径
  • 25. 复杂链表的复制
  • 26. 二叉搜索树与双向链表
  • 27. 字符串的排列
  • 28. 数组中出现次数超过一半的数字
  • 29. 最小的K个数
  • 30. 连续子数组的最大和
  • 31. 整数中1出现的次数(从1到n整数中1出现的次数)
  • 32. 把数组排成最小的数
  • 33. 丑数
  • 34. 第一个只出现一次的字符
  • 35. 数组中的逆序对
  • 36. 两个链表的第一个公共结点
  • 37. 数字在排序数组中出现的次数
  • 38. 二叉树的深度
  • 39. 平衡二叉树
  • 40. 数组中只出现一次的数字
  • 41. 和为S的连续正数序列
  • 42. 和为S的两个数字
  • 43. 左旋转字符串
  • 44. 翻转单词顺序列
  • 45. 扑克牌顺子
  • 46. 孩子们的游戏(圆圈中最后剩下的数)
  • 47. 求1+2+3+…+n
  • 48. 不用加减乘除做加法
  • 49. 把字符串转换成整数
  • 50. 数组中重复的数字
  • 51. 构建乘积数组
  • 52. 正则表达式匹配
  • 53. 表示数值的字符串
  • 54. 字符流中第一个不重复的字符
  • 55. 链表中环的入口结点
  • 56. 删除链表中重复的结点
  • 57. 二叉树的下一个结点
  • 58. 对称的二叉树
  • 59. 按之字形顺序打印二叉树
  • 60. 把二叉树打印成多行
  • 61. 序列化二叉树
  • 62. 二叉搜索树的第k个结点
  • 63. 数据流中的中位数
  • 64. 滑动窗口的最大值
  • 65. 矩阵中的路径
  • 66. 机器人的运动范围
  • 67.剪绳子

1. 二维数组的查找

  在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

/**
 * @author: Liu Wen
 * @create: 2020-03-01 16:48
 **/
public class Main1 {
    public static void main(String[] args){
        int target = 15;
        int [][] arr = {{11,12,13},{14,15,16},{18,20,25}};
//        for (int i = 0;i
//            for(int j = 0;j
//                System.out.print(arr[i][j]+"  ");
//            }
//            System.out.println();
//        }
        Boolean result = new Main1().Find(37,arr);
        System.out.println(result);
    }
    /*
     *               11    12    13
     *               14    15    16
     *               18    20    25
     */
    public boolean Find(int target, int [][] array) {
        Boolean result = false;
        if(array.length<1){
            return result;
        }
        int i = array.length-1;
        int j = 0;
        while (i>=0&&j<=array[0].length-1){
                if(array[i][j]<target){
                    j++;
                }else if(array[i][j]>target){
                    i--;
                }else if(array[i][j]==target){
                    result = true;
                    break;
                }
        }
        return result;
    }
}

2. 替换空格

  请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

import java.util.Scanner;
/**
 * @author: Liu Wen
 * @create: 2020-03-01 18:20
 **/
public class Main2 {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        String string = scanner.nextLine();
        StringBuffer stringBuffer = new StringBuffer(string);
        String result = replaceSpace(stringBuffer);
        System.out.println(result);
    }
    /**
     * @Description:  方法1:使用 String 自带函数 replaceAll()
      * @param str
     * @return java.lang.String
     */
    public static String replaceSpace(StringBuffer str) {
        String result = "";
        String s = str.toString();
        result = s.replaceAll(" ","%20");
        return result;
    }
  /**
   * @Description:    方法2. 遍历,遇见一个空格则加一个%20
    * @param str
   * @return java.lang.String
   */
    public static String replaceSpace1(StringBuffer str) {
        StringBuffer stringBuffer = new StringBuffer();
        if(str==null){
            return stringBuffer.toString();
        }
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if(c==' '){
                stringBuffer.append("%20");
            }else {
                stringBuffer.append(c);
            }
        }
        return stringBuffer.toString();
    }
    /**
     * @Description:     方法3:从后向前遍历
      * @param str
     * @return java.lang.String
     */
    public static String replaceSpace2(StringBuffer str) {
        //计算出新字符串的长度
        int space = 0;
        int oldLength = str.length();
        int newLength = 0;
        for (int i = 0; i < oldLength; i++) {
            if(str.charAt(i) == ' '){
                space++;
            }
        }
        System.out.println( space);
        newLength = oldLength + space*2;
        int newIndex = newLength-1;
        str.setLength(newLength);
        for (int i = oldLength-1; i >= 0 ; i--) {
            if(str.charAt(i)==' '){
                str.setCharAt(newIndex--,'0');
                str.setCharAt(newIndex--,'2');
                str.setCharAt(newIndex--,'%');
            }else {
                str.setCharAt(newIndex--,str.charAt(i));
            }
        }
        return str.toString();
    }
}

3. 从尾到头打印链表

  输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Stack;
/**
 * @description: 输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
 * @author: Liu Wen
 * @create: 2020-03-07 12:36
 **/
public class Main3 {
    public static void main(String[] args){
        ListNode node1 = new ListNode(1);
        ListNode node2 = new ListNode(2);
        ListNode node3 = new ListNode(3);
        ListNode node4 = new ListNode(4);
        ListNode node5 = new ListNode(5);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        ArrayList<Integer> arrayList = printListFromTailToHead1(node1);
        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()){
            System.out.print(iterator.next()+" ");
        }
    }
    /**
     * @Description:  方法1:使用栈结构(先进后出)
      * @param listNode
     * @return java.util.ArrayList
     * @date 20.3.7 13:29
     */
    public static ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> result = new ArrayList<>();
        Stack<Integer> stack = new Stack<>();          //使用栈结构
        while (listNode!=null){
            stack.push(listNode.val);
            listNode=listNode.next;
        }
        while (!stack.isEmpty()){
            result.add(stack.pop());
        }
        return result;
    }
    /**
     * @Description:     方法2:将链表反转(不推荐,破坏了原来的数据结构)
      * @param listNode
     * @return java.util.ArrayList
     * @date 20.3.7 13:37
     *
     */
    public static ArrayList<Integer> printListFromTailToHead1(ListNode listNode) {
        ArrayList<Integer> result = new ArrayList<>();
        ListNode newHead = null;
        ListNode tmp = null;
        ListNode cur = listNode;
        //1→2→3→4→5→null
        while (cur!=null){
            tmp = cur;
            cur = cur.next;
            tmp.next=newHead;
            newHead = tmp;
        }
        ListNode temp = newHead;
        while (temp!=null){
            result.add(temp.val);
            temp = temp.next;
        }
        return result;
    }
    //结点定义
    public static class ListNode {
        int val;
        ListNode next = null;
        ListNode(int val) {
            this.val = val;
        }
    }
}

4. 重建二叉树

  输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

import java.util.Arrays;
/**
 * @description: 重建二叉树
 * @author: Liu Wen
 * @create: 2020-03-07 19:36
 **/
public class Main4 {
    public static void main(String[] args){
        int[] pre = {1,2,4,7,3,5,6,8};
        int[] in = {4,7,2,1,5,3,8,6};
        TreeNode treeNode = reConstructBinaryTree(pre,in);
        preorderTraversal(treeNode);    //前序遍历查看结果

    }
    /**
     * @Description:  输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
     * 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
      * @param pre
     * @param in
     * @return com.liuwen.牛客网.TreeNode
     * @date 20.3.10 15:21
     */
    public static TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if (pre.length!=in.length||pre.length==0){
            return null;
        }
        TreeNode root = new TreeNode(pre[0]);      //找出根节点
        int i = 0;
        for (i = 0; i < pre.length; i++) {
            if(in[i]==pre[0]){
                break;
            }
        }
        /*
        int[] pre = {1,2,4,7,3,5,6,8};
        int[] in = {4,7,2,1,5,3,8,6};
                          i
         */ //分别找出新的左右子树的前序遍历和中序遍历
        int[] Leftnewpre = Arrays.copyOfRange(pre,1,i+1);
        int[] Leftnewin = Arrays.copyOfRange(in,0,i);
        int[] Rigntnewpre = Arrays.copyOfRange(pre,i+1,pre.length);
        int[] Rightnewin = Arrays.copyOfRange(in,i+1,in.length);
        root.left = reConstructBinaryTree(Leftnewpre,Leftnewin); //找出当前根节点的左子树的根结点
        root.right = reConstructBinaryTree(Rigntnewpre,Rightnewin);//找出当前根节点的右子树的根节点
        return root;
    }
    /**
     * @Description:  前序遍历  用于可视化检验结果
      * @param treeNode
     * @return void
     * @date 20.3.10 15:24
     */
    private static void preorderTraversal(TreeNode treeNode) {
        if(treeNode==null){
            return;
        }
        System.out.print(treeNode.val);
        preorderTraversal(treeNode.left);
        preorderTraversal(treeNode.right);
    }
}

5. 用两个栈实现队列

  用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

import java.util.Stack;
/**
 * @description: 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
 * @author: Liu Wen
 * @create: 2020-03-07 13:56
 **/
public class Main5 {
    public static void main(String[] args){
        push(1);
        push(2);
        push(3);
        System.out.print(pop()+" ");
        System.out.print(pop()+" ");
        push(4);
        System.out.print(pop()+" ");
        push(5);
        System.out.print(pop()+" ");
        System.out.print(pop()+" ");
    }
    // 分析→   栈:先进后出        队列:先进后出
    static Stack<Integer> stack1 = new Stack<Integer>();
    static Stack<Integer> stack2 = new Stack<Integer>();
    /**
     * @Description:      压栈操作     入stack1
      * @param node
     * @return void
     * @date 20.3.7 14:13
     */
    public static void push(int node) {
        stack1.push(node);
    }
    /**
     * @Description: 判断栈stack2是否为空,如果为空,则将栈stack1中所有元素pop,并push进栈stack2,
     *                 栈stack2出栈;如果不为空,栈stack2直接出栈。
     * @param
     * @return int
     * @date 20.3.7 14:13
     */
    public static int pop() {
        if(stack2.isEmpty()) {
            while (!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
            return stack2.pop();
    }
}

注意:在进行出栈的时候,要判断栈B是否为空?

6. 旋转数组的最小数字

  把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如:数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

import java.util.TreeSet;
/**
 * @description: 旋转数组的最小数字
 * @author: Liu Wen
 * @create: 2020-03-07 14:40
 **/
public class Main6 {
    public static void main(String[] args){
        int[] array = {4,5,6,1,2,3};
        int result = minNumberInRotateArray2(array);
        System.out.println(result);
    }
    /**
     * @Description:     方法1:找出最小的数的索引(选择排序)
      * @param array
     * @return int
     * @date 20.3.8 12:34
     */
    public static int minNumberInRotateArray(int[] array) {
        int minIndex = 0;
        for (int i = 0; i < array.length; i++) {
            if(array[minIndex]>=array[i]){
                minIndex = i;
            }
        }
        return array[minIndex];
    }
    /**
     * @Description:      方法2:利用TreeSet(TreeSet默认升序)  最简单但耗时耗内存
     * @param array
     * @return int
     * @date 20.3.7 15:11
     */
    public static int minNumberInRotateArray1(int[] array) {
        TreeSet treeSet = new TreeSet();
        for (int i = 0; i < array.length; i++) {
            treeSet.add(array[i]);
        }
        return Integer.valueOf(treeSet.first().toString());
    }
    /**
     * @Description:     方法3:二分查找      最优解,也是出这题的人希望看到的答案
      * @param array
     * @return int
     * @date 20.3.7 15:28
     */
    public static int minNumberInRotateArray2(int[] array) {
        int mid = 0;
        int L = 0;
        int R = array.length-1;
        while (L<R){
            mid = L+(R-L)/2;
            if(array[mid] > array[R]){            //情况1
                L = mid + 1 ;
            }else if(array[mid] == array[R]){     //情况2
                R--;
            }else{                                //情况3
                R = mid;
            }
        }
        return array[L];
    }
}

7. 斐波那契数列

  大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39

import java.util.Scanner;
/**
 * @description: 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
 * n<=39
 * @author: Liu Wen
 * @create: 2020-03-07 16:55
 **/
public class Main7 {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int result = Fibonacci(n);
        System.out.println(result);
    }
    /**
     * @Description:  递归解决
      * @param n
     * @return int
     * @date 20.3.7 16:58
     */
    public static int Fibonacci(int n) {
        if(n == 0){
            return 0;
        }
        if(n == 1){
            return 1;
        }
        return Fibonacci(n-1)+Fibonacci(n-2);
    }
}

8. 跳台阶

  一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

import java.util.Scanner;
/**
 * @description: 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
 * @author: Liu Wen
 * @create: 2020-03-07 17:07
 **/
public class Main8 {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int target = scanner.nextInt();
        int result = JumpFloor(target);
        System.out.println(result);
    }
    /**
     * @Description:     递归
      * @param target
     * @return int
     * @date 20.3.7 17:09
     */
    public static int JumpFloor(int target) {
        if(target==0){
            return 0;
        }
        if(target==1){
            return 1;
        }
        if(target==2){
            return 2;
        }
        return JumpFloor(target-1)+JumpFloor(target-2);
    }
}

9. 变态跳台阶

  一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

import java.util.Scanner;
/**
 * @description: 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。
 *                求该青蛙跳上一个n级的台阶总共有多少种跳法。
 * @author: Liu Wen
 * @create: 2020-03-07 18:20
 **/
public class Main9 {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int target = scanner.nextInt();
        int result = JumpFloorII1(target);
        System.out.println(result);
    }
    /**
     * @Description:   方法1: 递归           1   2   3   4   5
      * @param target                        1   2   4   8   16
     * @return int
     * @date 20.3.7 18:22
     */
    public static int JumpFloorII(int target) {
        if(target == 0){
            return 0;
        }
        if(target == 1){
            return 1;
        }
        return 2*JumpFloorII(target-1);
    }
    /**
     * @Description:           方法 2    : 1   2   3   4   5  ... n
      * @param target                       1   2   4   8   16 ... 2^(n-1)
     * @return int
     * @date 20.3.7 19:07
     */
    public static int JumpFloorII1(int target) {
        int result = (int)Math.pow(2,target-1);
        return result;
    }
}

10. 矩阵覆盖

  我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

import java.util.Scanner;
/**
 * @description: 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。
 * 请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
 * @author: Liu Wen
 * @create: 2020-03-07 19:11
 **/
public class Main10 {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int result = RectCover(n);
        System.out.println(result);
    }
    /**
     * @Description:          1  2  3  4  5  6  7  8
      * @param n              1  2  3  5  8  13 21 34
     * @return int
     * @date 20.3.7 19:16
     */
    public static int RectCover(int n) {
        if(n == 0){
            return 0;
        }
        if(n == 1 || n == 2){
            return n;
        }
        return RectCover(n-1)+RectCover(n-2);
    }
}

11. 二进制中1的位数

  输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

import java.util.Scanner;
/**
 * @description: 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
 * @author: Liu Wen
 * @create: 2020-03-08 13:45
 **/
public class Main11 {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int result = NumberOf12(n);
        System.out.println(result);
    }
    /**
     * @Description:    方法2:n&n-1执行的次数可以确定1的个数  这个方法绝了
     * @param n                   eg: 7: 0111      1次
     * @return int                      6: 0110   n&n-1 → 0110    2次
     *                                                      0101  n&n-1 → 0100    三次
     * @date 20.3.8 13:52                                                 0011   n&n-1 → 0000
     */
    public static int NumberOf12(int n) {
        int count=0;
        while(n!=0){
            n = n&n-1;
            count++;
        }
        return count;
    }
    /**
     * @Description:   方法1:转成二进制再数1
      * @param n
     * @return int
     * @date 20.3.8 13:48
     */
    public static int NumberOf1(int n) {
        String string = Integer.toBinaryString(n);
        int count = 0;
        for (int i = 0; i < string.length(); i++) {
            if(string.charAt(i)=='1'){
                count++;
            }
        }
        return count;
    }
}

12. 数值的整数次方

  定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0

/**
 * @description: 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
 * @author: Liu Wen
 * @create: 2020-03-08 14:00
 **/
public class Main12 {
    public static void main(String[] args){
        double base = 5;
        int exponent = -6;
        double result = Power(base,exponent);
        System.out.println(result);
    }
    /**
     * @Description:   方法1:递归解法
      * @param base
    * @param exponent
     * @return double
     * @date 20.3.8 14:04
     */
    public static double Power1(double base, int exponent) {
        if(exponent<0){
            return 1/(base*Power1(base,-exponent-1));
        }
        if(exponent==0){
            return 1;
        }
        if(exponent==1){
            return base;
        }
        return base*Power1(base,exponent-1);
    }
    /**
     * @Description:       方法1:数学公式
      * @param base
     * @param exponent
     * @return double
     * @date 20.3.8 14:02
     */
    public static double Power(double base, int exponent) {
        return Math.pow(base,exponent);
    }
}

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

  输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

import java.util.ArrayList;
/**
 * @description:   调整数组顺序使奇数位于偶数前面
 * @author: Liu Wen
 * @create: 2020-03-08 15:32
 **/
public class Main13 {
    public static void main(String[] args){
        int[] array = {1,2,3,4,5,6,7,8,9};
        reOrderArray1(array);
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i]+" ");
        }
    }
    /**
     * @Description:    方法3:类似冒泡排序 (稳定)
      * @param array    要想保证原有次序,则只能顺次移动或相邻交换。
     * @return void
     * @date 20.3.8 15:58
     */
    public static void reOrderArray2(int[] array) {
        for (int i = 0; i < array.length-1; i++) {
            for (int j = 0; j < array.length-1; j++) {
                if(array[j]%2==0&&array[j+1]%2==1){
                    int tmp = array[j];
                    array[j]=array[j+1];
                    array[j+1]=tmp;
                }
            }
        }
    }
    /**
     * @Description:   方法2: 类似插入排序(稳定)
      * @param array    在遍历的过程中,将奇数移动到前面已经拍好的奇数序列中即可
     * @return void
     * @date 20.3.8 15:47
     */
    public static void reOrderArray1(int[] array) {
        int m = array.length;
        int count = 0;                   //记录已经摆好位置的奇数的个数
        for (int i = 0; i < m; i++) {
            if (array[i] % 2 == 1) {
                int j = i;
                while (j > count) {    //将奇数移动到前面已经拍好的奇数序列中即可
                    int tmp = array[j];
                    array[j] = array[j-1];
                    array[j-1] = tmp;
                    j--;
                }
                count++;
            }
        }
    }
    /**
     * @Description:  方法:1.定义两个arrayList数组,分别用于装奇数和偶数
      * @param array
     * @return void
     * @date 20.3.8 15:35
     */
    public static void reOrderArray(int [] array) {
        ArrayList<Integer> arrayList1 = new ArrayList<>();
        ArrayList<Integer> arrayList2 = new ArrayList<>();
        ArrayList<Integer> arrayList = new ArrayList<>();
        for (int i = 0; i < array.length; i++) {
            if(array[i]%2==1){
                arrayList1.add(array[i]);
            }else {
                arrayList2.add(array[i]);
            }
        }
//        System.out.println("arrayList1:"+arrayList1);
//        System.out.println("arrayList2:"+arrayList2);
        for (int i = 0; i < arrayList1.size(); i++) {
            arrayList.add(arrayList1.get(i));
        }
        System.out.println(arrayList);
        for (int i = 0; i < arrayList2.size(); i++) {
            arrayList.add(arrayList2.get(i));
        }
        for (int i = 0; i < arrayList.size(); i++) {
            array[i]=arrayList.get(i);
        }
    }
}

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

  输入一个链表,输出该链表中倒数第k个结点。

/**
 * @description: Good good study,day day up!
 * @author: Liu Wen
 * @create: 2020-03-08 16:25
 **/
public class Main {
    public static void main(String[] args){
        ListNode head = new ListNode(0);
        ListNode node1 = new ListNode(1);
        ListNode node2 = new ListNode(2);
        ListNode node3 = new ListNode(3);
        ListNode node4 = new ListNode(4);
        ListNode node5 = new ListNode(5);
        ListNode node6 = new ListNode(6);
        head.next = node1;node1.next=node2;node2.next=node3;
        node3.next=node4;node4.next=node5;node5.next=node6;
        ListNode result = FindKthToTail1(head,4);
        if(result!=null)
        System.out.println(result.value);
    }
    /**
     * @Description:  方法2:两个指针(类似于指针),一个先走k步,然后两个同时走。
      * @param head
     * @param i
     * @return com.liuwen.牛客网.ListNode
     * @date 20.3.8 22:28
     */
    public static ListNode FindKthToTail1(ListNode head, int i) {
        ListNode listNode1 = head;
        ListNode listNode2 = head;
        while (i>0){
            i--;
            if(listNode1!=null) {
                listNode1 = listNode1.next;
            }else {
                return null;
            }
        }
        while (listNode1!=null){
            listNode1=listNode1.next;
            listNode2=listNode2.next;
        }
        return listNode2;
    }
    /**
     * @Description:   方法1:先遍历,得到链表的长度size,再遍历到size-k即可。
      * @param head
     * @param k
     * @return com.liuwen.牛客网.ListNode
     * @date 20.3.8 20:58
     */
    public static ListNode FindKthToTail(ListNode head,int k) {
        ListNode result = null;
        int count = 0;
        ListNode tmp = head;
        while (tmp!=null){
            count++;
            tmp = tmp.next;
        }
        int flag = count-k;
        if(flag>=0){
            result = head;
            while (flag>0){
                result = result.next;
                flag--;
            }
        }
        return result;
    }
}

15. 反转链表

  输入一个链表,反转链表后,输出新链表的表头。

/**
 * @description: 输入一个链表,反转链表后,输出新链表的表头。
 * @author: Liu Wen
 * @create: 2020-03-08 22:38
 **/
public class Main15 {
    public static void main(String[] args){
        ListNode head = new ListNode(0);
        ListNode node1 = new ListNode(1);
        ListNode node2 = new ListNode(2);
        ListNode node3 = new ListNode(3);
        ListNode node4 = new ListNode(4);
        ListNode node5 = new ListNode(5);
        ListNode node6 = new ListNode(6);
        head.next = node1;node1.next=node2;node2.next=node3;
        node3.next=node4;node4.next=node5;node5.next=node6;
        ListNode newHead = ReverseList(head);
        ListNode tmp = newHead;
        while (tmp!=null) {
            System.out.print(tmp.value+"->");
            tmp = tmp.next;
        }
    }
    /**
     * @Description:   递归方法
      * @param head    它利用递归走到链表的末端,然后再更新每一个node的next值,实现链表的反转。
     * @return com.liuwen.牛客网.ListNode
     * @date 20.3.8 22:50
     */
    public static ListNode ReverseList1(ListNode head) {
        if(head==null||head.next==null)
            return head;
        ListNode newHead = ReverseList(head.next);
        //**********************************//    head→→→head.next
        head.next.next=head;    //     head.next ← 1     →    2  ↓
        head.next=null;         //        null  ←  1     ←    2  ↓
        return newHead;         //         ← head.next.next ←←←  ↓
    }
    /**
     * @Description:   方法1:常规方法
      * @param head
     * @return com.liuwen.牛客网.ListNode
     * @date 20.3.8 22:39
     */
    public static ListNode ReverseList(ListNode head) {
        ListNode newHead = null;
        if(head==null){
            return newHead;
        }
        ListNode cur = head;
        newHead = null;
        ListNode tmp = null;
        while (cur!=null){
            //      0→1→2→3→4→5→6→null
            //null←0←1←2←3←4←5←6
            tmp = cur;
            cur = cur.next;
            tmp.next = newHead;
            newHead = tmp;
        }
        return newHead;
    }
}

16. 合并两个排序的链表

  输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

/**
 * @description: 输入两个单调递增的链表,输出两个链表合成后的链表,
 *                当然我们需要合成后的链表满足单调不减规则。
 * @author: Liu Wen
 * @create: 2020-03-08 23:17
 **/
public class Main16 {
    public static void main(String[] args){
        //链表1
        ListNode head1 = new ListNode(1);
        ListNode node1 = new ListNode(3);
        ListNode node2 = new ListNode(4);
        ListNode node3 = new ListNode(6);
        ListNode node4 = new ListNode(8);
        head1.next = node1;node1.next=node2;node2.next=node3;node3.next=node4;
        //链表2
        ListNode head2 = new ListNode(1);
        ListNode node5 = new ListNode(2);
        ListNode node6 = new ListNode(5);
        ListNode node7 = new ListNode(7);
        ListNode node8 = new ListNode(9);
        head2.next = node5;node5.next=node6;node6.next=node7;node7.next=node8;
        //合并得到一个新链表
        ListNode newHead = Merge(head1,head2);
        ListNode tmp = newHead;
        while (tmp!=null) {
            System.out.print(tmp.value+"->");
            tmp = tmp.next;
        }
    }
    /**
     * @Description:    方法1:类似于合并两个有序数组  step1:确定头结点;
      * @param list1                                  step2:依次比较两个链表的大小,小的先取下来;
     * @param list2                                 step3:直到其中一个链表取完了,比较结束;
     * @return com.liuwen.牛客网.ListNode            step4:取完剩下的一个链表。
     * @date 20.3.8 23:17
     */
    public static ListNode Merge(ListNode list1,ListNode list2) {
        if(list1==null&&list2==null){
            return null;
        }
        if(list1==null||list2==null){
            return list1==null?list2:list1;
        }
        ListNode newHead = null;
        if(list1.value<list2.value){   //step1:确定头结点;
            newHead = list1;
            list1=list1.next;
        }else {
            newHead = list2;
            list2 = list2.next;
        }
        ListNode tmp = newHead;
        while (list1!=null&&list2!=null){
            if(list1.value<=list2.value){   //step2:依次比较两个链表的大小,小的先取下来;
                tmp.next = list1;
                list1=list1.next;
            }else {
                tmp.next = list2;
                list2 = list2.next;
            }
            tmp = tmp.next;
        }                                     //tep3:直到其中一个链表取完了,比较结束;
        if (list1!=null){                     //step4:取完剩下的一个链表。
            tmp.next=list1;
        }
        if (list2!=null){
            tmp.next=list2;
        }
        return newHead;
    }
}

17. 树的子结构

  
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

/**
 * @description: 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
 * @author: Liu Wen
 * @create: 2020-03-09 22:29
 **/
public class Main17 {
    public static void main(String[] args){
        //初始化树1
        TreeNode root1 = new TreeNode(1);       //      root:    1
        TreeNode node2 = new TreeNode(2);       //             /   \
        TreeNode node3 = new TreeNode(3);       //            2     3
        TreeNode node4 = new TreeNode(4);       //           / \     \
        TreeNode node5 = new TreeNode(5);       //          4   5     6
        TreeNode node6 = new TreeNode(6);
        root1.left = node2;root1.right = node3;
        node2.left = node4;node2.right = node5;
        node3.right = node6;
        //初始化树2
        TreeNode root2 = new TreeNode(2);       //      root:     2
        TreeNode node22 = new TreeNode(4);       //             /   \
        TreeNode node23 = new TreeNode(5);       //            4     5
        TreeNode node26 = new TreeNode(6);
        root2.left = node22;
        root2.right = node23;
        
        boolean res = HasSubtree(root1,root2);
        System.out.println(res);
    }
    /**
     * @Description:    递归
      * @param root1
     * @param root2
     * @return boolean
     * @date 20.3.9 22:29
     */
    public static boolean HasSubtree(TreeNode root1,TreeNode root2) {
        boolean result = false;
        if(root1==null||root2==null){
            return result;
        }
        if(root1.val==root2.val){
            result = isTree1HasTree2(root1,root2);  //判断以root为起点的树是否包含树2
        }
        if(!result){          //如果没有找到,则判断树1的左字节点是否是树2的根节点
            result = HasSubtree(root1.left,root2);
        }
        if(!result){          //如果还没有找到,则判断树1的左字节点是否是树2的根节点
            result = HasSubtree(root1.right,root2);
        }
        return result;
    }
    //判断树1含有树2吗?
    public static boolean isTree1HasTree2(TreeNode root1, TreeNode root2) {
        if(root2==null){          //递归截止条件1(如果Tree2已经遍历完了都能对应的上,返回true)
            return true;
        }
        if(root1==null){          //递归截止条件2(如果Tree2还没有遍历完,Tree1却遍历完了。返回false)
            return false;
        }
        if(root1.val!=root2.val){ //若节点出现不一致,直接返回false
            return false;
        }         //如果根节点一致,分别比较左右子结点。
        return isTree1HasTree2(root1.left,root2.left)&&isTree1HasTree2(root1.right,root2.right);
    }
}

18. 二叉树的镜像

  输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

/**
 * @description: 操作给定的二叉树,将其变换为原二叉树的镜像。
 * @author: Liu Wen
 * @create: 2020-03-09 22:30
 **/
public class Main18 {
    public static void main(String[] args){
        TreeNode root1 = new TreeNode(8);       //      root:    8
        TreeNode node2 = new TreeNode(6);       //             /   \
        TreeNode node3 = new TreeNode(10);       //           6     10
        TreeNode node4 = new TreeNode(5);       //           / \     \
        TreeNode node5 = new TreeNode(7);       //          5   7    11
        TreeNode node6 = new TreeNode(11);
        root1.left = node2;root1.right = node3;
        node2.left = node4;node2.right = node5;
        node3.right = node6;

        Mirror(root1);
    }
    /**
     * @Description:   方法1:迭代
     * /* 先前序遍历这棵树的每个结点,如果遍历到的结点有子结点,就交换它的两个子节点,
     * 当交换完所有的非叶子结点的左右子结点之后,就得到了树的镜像
      * @param root1
     * @return void
     * @date 20.3.9 22:51
     */
    public static void Mirror(TreeNode root1) {
        if(root1==null){
            return;
        }
        TreeNode temp = root1.left;
        root1.left = root1.right;
        root1.right = temp;

        Mirror(root1.left);
        Mirror(root1.right);
    }
}

19. 顺时针打印矩阵

  输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

import java.util.ArrayList;
/**
 * @description: 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,
 * 例如,如果输入如下4 X 4矩阵:
 *        1  2   3   4
 *       5  6   7   8
 *      9  10  11  12
 *     13  14  15  16   则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
 *
 * @author: Liu Wen
 * @create: 2020-03-09 22:53
 **/
public class Main19 {
    public static void main(String[] args){
        int[][] matrix = {{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
        ArrayList<Integer> result = printMatrix(matrix);
        System.out.println(result);
    }
    /**
     * @Description: 思想,用左上和右下的坐标定位出一次要旋转打印的数据,一次旋转打印结束后,往对角分别前进和后退一个单位。
     *     提交代码时,主要的问题出在没有控制好后两个for循环,需要加入条件判断,防止出现单行或者单列的情况。
     * * @param matrix
     * @return java.util.ArrayList
     * @date 20.3.9 22:57
     */
    public static ArrayList<Integer> printMatrix(int [][] matrix) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        if(matrix==null){
            return arrayList;
        }
        int left = 0;int top = 0;
        int right = matrix[0].length-1;
        int bottom = matrix.length-1;
        while (left<=right&&top<=bottom){
            for (int i = left; i <= right; i++) {
                arrayList.add(matrix[top][i]);
            }
            for (int i = top+1; i <= bottom ; i++) {
                arrayList.add(matrix[i][right]);
            }
            if(bottom!=top) {    //需要加入条件判断,防止出现单行的情况。
                for (int i = right - 1; i >= left; i--) {
                    arrayList.add(matrix[bottom][i]);
                }
            }
            if(right!=left) {   //需要加入条件判断,防止出现单列的情况。
                for (int i = bottom - 1; i > top; i--) {
                    arrayList.add(matrix[i][left]);
                }
            }
            left++;top++;right--;bottom--;
        }
        return arrayList;
    }
    /**
     * @Description:     可以模拟魔方逆时针旋转的方法,一直做取出第一行的操作
      * @param matrix     输出并删除第一行后,再进行一次逆时针旋转。    重复上述操作
     * @return java.util.ArrayList
     * @date 20.3.9 23:05
     */
    public static ArrayList<Integer> printMatrix1(int[][] matrix) {
        return null;
    }
}

20. 包含min函数的栈

  定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
注意:保证测试中不会当栈为空的时候,对栈调用pop()或者min()或者top()方法。

import java.util.Iterator;
import java.util.Stack;
/**
 * @description: 定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
 * 注意:保证测试中不会当栈为空的时候,对栈调用pop()或者min()或者top()方法。
 * @author: Liu Wen
 * @create: 2020-03-09 23:07
 **/
public class Main20 {
    public static void main(String[] args){
        push(3);push(1);push(2); push(5);
        System.out.println("top():"+top());  //查看栈顶
        System.out.println("min():"+min());  //拿出最小值
    }
    /**
     * @Description:   方法1:使用迭代器,找出最小值
     * @param
     * @return int
     * @date 20.3.9 23:17
     */
    static Stack<Integer> stack = new Stack<>();
    public static void push(int node) {
        stack.push(node);
    }
    public static void pop() {
        stack.pop();
    }
    public static int top() {
        return stack.peek();
    }
    public static int min() {
        int min = stack.peek();
        Iterator iterator = stack.iterator();  //使用迭代器,找出最小值
        while (iterator.hasNext()){
            int temp = (Integer)iterator.next();
            if(min > temp){
                min = temp;
            }
        }
        return min;
    }
    /**
     * @Description:  应用一个辅助栈:
     *                 入栈,如果A栈的压入比B栈压入大,B栈不压,,,,小于等于,AB栈同时压入。
     *                 出栈,如果AB栈顶元素不等,A出,B不出。
     *                 这样,B栈就会始终装有最小值。
      * @param node
     * @return
     * @date 20.3.9 23:18
     */
    static Stack<Integer> stackA = new Stack<>();
    static Stack<Integer> stackB = new Stack<>();          //辅助栈
    public static void push1(int node) {
        stackA.push(node);
        if(stackB.isEmpty()){
            stackB.push(node);
        }else {
            if(node<=stackB.peek()){
                stackB.push(node);
            }
        }
    }
    public static void pop1() {
        if(stackA.peek()==stackB.peek()){
            stackB.pop();
        }
        stackA.pop();
    }
    public static int top1() {
        return stackA.peek();
    }
    public static int min1() {
        return stackB.peek();
    }
}

21. 栈的压入、弹出序列

  输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

import java.util.Stack;
/**
 * @description: 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。
 * @author: Liu Wen
 * @create: 2020-03-10 11:52
 **/
public class Main21 {
    public static void main(String[] args){
        int[] A = {1,2,3,4,5};
        int[] B = {4,5,3,1,2};
        boolean res = IsPopOrder(A,B);
        System.out.println(res);
    }
    /**
     * @Description: 假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,
     *  序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。
     * (注意:这两个序列的长度是相等的)
      * @param pushA
     * @param popA
     * @return boolean
     * @date 20.3.10 11:53
     */
    public static boolean IsPopOrder(int [] pushA,int [] popA) {
        boolean res = false;
        if(pushA.length!=popA.length){
            return res;
        }    //定义一个栈A来模仿整个流程
        Stack<Integer> A = new Stack<>();          //pushA {1,2,3,4,5}
        A.push(pushA[0]);                          //popA  {4,5,3,2,1}
        int pushIndex = 1;
        for (int i = 0; i < popA.length; i++) {
            while (A.peek()!=popA[i]&&pushIndex<pushA.length){
                A.push(pushA[pushIndex++]);
            }
            if(A.peek()==popA[i]){
                A.pop();
                continue;
            }
            return res;
        }
        res = !res;
        return res;
    }
}

22. 从上往下打印二叉树

  从上往下打印出二叉树的每个节点,同层节点从左至右打印。

import java.util.ArrayList;
import java.util.LinkedList;
/**
 * @description: 从上往下打印出二叉树的每个节点,同层节点从左至右打印。
 * @author: Liu Wen
 * @create: 2020-03-10 14:38
 **/
public class Main22 {
    public static void main(String[] args){
        TreeNode root = new TreeNode(1);       //      root:     1
        TreeNode node2 = new TreeNode(2);       //             /   \
        TreeNode node3 = new TreeNode(3);       //            2     3
        TreeNode node4 = new TreeNode(4);       //           / \   / \
        TreeNode node5 = new TreeNode(5);       //          4   5 7   6
        TreeNode node6 = new TreeNode(6);
        TreeNode node7 = new TreeNode(7);
        root.left = node2;root.right = node3;
        node2.left = node4;node2.right = node5;
        node3.right = node6;node3.left = node7;

        ArrayList<Integer> arrayList = PrintFromTopToBottom(root);
        System.out.println(arrayList);

    }
    /**
     * @Description:  层序遍历     使用队列结构
      * @param root
     * @return java.util.ArrayList
     * @date 20.3.10 14:39
     */
    public static ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> result = new ArrayList<>();
        if (root==null){
            return result;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()){
            TreeNode tmp = queue.remove();
            result.add(tmp.val);
            if(tmp.left!=null){
                queue.add(tmp.left);
            }
            if(tmp.right!=null){
                queue.add(tmp.right);
            }
        }
        return result;
    }
}

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

  输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

import java.util.ArrayList;
/**
 * @description: 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。
 *  如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
 * @author: Liu Wen
 * @create: 2020-03-10 14:56
 **/
public class Main23 {
    public static void main(String[] args){
        int[] arr = {1,3,2,6,5,4};
        boolean result = VerifySquenceOfBST(arr);
        System.out.println(result);
    }
    /**
     * @Description: BST的后序序列的合法序列是,对于一个序列S,最后一个元素是x (也就是根),如果去掉最后一个元素的序列为T,那么T满足:T可以分成两段,前一段(左子树)小于x,后一段(右子树)大于x,
     * 且这两段(子树)都是合法的后序序列。完美的递归定义。
      * @param sequence
     * @return boolean
     * @date 20.3.10 15:02
     */
    public static boolean VerifySquenceOfBST(int [] sequence) {
        if (sequence.length < 0) {
            return false;
        }
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < sequence.length; i++) {
            list.add(sequence[i]);
        }
        return VerifySeq(list);
    }
    private static boolean VerifySeq(ArrayList<Integer> sequence) {
        if(sequence.size()<1){
            return true;
        }
        int last = sequence.get(sequence.size()-1);
        //在二叉搜索树中左子树节点小于根节点
        int i = 0;
        ArrayList<Integer> arrayListLeft = new ArrayList<>();
        ArrayList<Integer> arrayListRight = new ArrayList<>();
        //找出左子树
        while (i<sequence.size()&&sequence.get(i)<last){
            arrayListLeft.add(sequence.get(i++));
        }
        //找出右子树
        while (i<sequence.size()&&sequence.get(i)>last){
            arrayListRight.add(sequence.get(i++));
        }
        //这里要用while,不能用if
        while ((arrayListLeft.size()+arrayListRight.size())<sequence.size()-1){
            return false;
        }
        // # 判断左子树是否为二叉搜索树
        int[] left = new int[arrayListLeft.size()];
        for (int j = 0; j < arrayListLeft.size(); j++) {
            left[j]=arrayListLeft.get(j);
        }
        // # 判断右子树是否为二叉搜索树
        int[] right = new int[arrayListRight.size()];
        for (int j = 0; j < arrayListRight.size(); j++) {
            right[j]=arrayListRight.get(j);
        }
        return VerifySquenceOfBST(left)&&VerifySquenceOfBST(right);
    }
}

24. 二叉树中和为某一值的路径

  输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

package com.liuwen.牛客网;

import java.util.ArrayList;

/**
 * @description: 输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。
 * 路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
 * (注意: 在返回值的list中,数组长度大的数组靠前)
 * @author: Liu Wen
 * @create: 2020-03-10 15:12
 **/
public class Main24 {
    public static void main(String[] args){
        TreeNode root = new TreeNode(1);       //      root:     1
        TreeNode node2 = new TreeNode(2);       //             /   \
        TreeNode node3 = new TreeNode(3);       //            2     3
        TreeNode node4 = new TreeNode(4);       //           / \   / \
        TreeNode node5 = new TreeNode(5);       //          4   5 3   4
        TreeNode node6 = new TreeNode(3);
        TreeNode node7 = new TreeNode(4);
        root.left = node2;root.right = node3;
        node2.left = node4;node2.right = node5;
        node3.right = node6;node3.left = node7;
        int target = 7;
        ArrayList<ArrayList<Integer>> result = FindPath1(root,target);
        System.out.println(result.toString());
    }

    /**
     * @Description:           方法1:递归(代码超级简洁)
     * @date 20.3.20 23:16
     */
    static ArrayList<ArrayList<Integer>> arrayLists = new ArrayList<>();
    static ArrayList<Integer> arrayList = new ArrayList<>();
    public static ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {
        if(root==null){
            return arrayLists;
        }
        arrayList.add(root.val);
        target = target-root.val;
        if(target==0&&root.left==null&&root.right==null){
            arrayLists.add(new ArrayList<>(arrayList));    //重新new一个新的arrayList
        }
        FindPath(root.left,target);
        FindPath(root.right,target);
        // 当前路径不对,(递归)去掉最后一个节点,从其父节点开始重新寻找
        arrayList.remove(arrayList.size()-1);
        return arrayLists;
    }

    /**
     * @Description:                 方法2:利用传参返回
     * @date 20.3.24 23:59
     */
    public static ArrayList<ArrayList<Integer>> FindPath1(TreeNode root, int target) {
        ArrayList<ArrayList<Integer>> arrayLists = new ArrayList<>();
        ArrayList<Integer> arrayList = new ArrayList<>();
        if(root==null){
            return arrayLists;
        }
        int sum = 0;
        helper(root,target,arrayLists,arrayList,sum);
        return arrayLists;
    }

    private static void helper(TreeNode root, int target, ArrayList<ArrayList<Integer>> arrayLists, ArrayList<Integer> arrayList, int sum) {
        if(root==null){
            return;
        }
        sum += root.val;
        arrayList.add(root.val);
        if(sum==target && root.left==null && root.right==null){  //截止条件
            arrayLists.add(new ArrayList<>(arrayList));
        }
        helper(root.left,target,arrayLists,arrayList,sum);
        helper(root.right,target,arrayLists,arrayList,sum);
        arrayList.remove(arrayList.size()-1);
    }
}

25. 复杂链表的复制

  输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

package com.liuwen.牛客网;

/**
 * @description:    复杂链表的复制
 * @author: Liu Wen
 * @create: 2020-03-24 19:56
 **/
public class Main25 {
    public static void main(String[] args){
        RandomListNode root = new RandomListNode(0);
        RandomListNode node1 = new RandomListNode(1);
        RandomListNode node2 = new RandomListNode(2);
        RandomListNode node3 = new RandomListNode(3);
        RandomListNode node4 = new RandomListNode(4);
        // 0→1→2→3
        root.next=node1;node1.next=node2;node2.next=node3;node3.next=node4;
        root.random=node2;node1.random=node4;node3.random=node1;
        print(root);
        RandomListNode proot = Clone(root);
        print(proot);
    }
        /**
     * @Description:          方法2:递归完成(使用Map集合来装)
     * @date 20.3.25 18:26
     */
    public static HashMap<RandomListNode,RandomListNode> hashMap = new HashMap<>();
    public static RandomListNode Clone(RandomListNode node) {
        if(node==null)
            return null;
        if(hashMap.containsKey(node)){
            return hashMap.get(node);        //用于返回random结点
        }
        RandomListNode copyNode = new RandomListNode(node.label);
        hashMap.put(node,copyNode);
        copyNode.next = Clone(node.next);
        copyNode.random = Clone(node.random);
        return copyNode;
    }
    /*
     *解题思路:
     *1、遍历链表,复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面;
     *2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
     *3、拆分链表,将链表拆分为原链表和复制后的链表.
     */
    public static RandomListNode Clone(RandomListNode pHead) {
        if(pHead==null){
            return null;
        }
        //step1、遍历链表,复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面;
        RandomListNode cur = pHead;
        while (cur!=null){
            RandomListNode copyNode = new RandomListNode(cur.label); //复制新节点
            RandomListNode nextNode = cur.next;
            cur.next = copyNode;
            copyNode.next = nextNode;
            cur = nextNode;
        }
        //step2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
        cur = pHead;
        while (cur!=null){
            if(cur.random!=null){
                cur.next.random = cur.random.next;
            }
            cur = cur.next.next;
        }
        //step3、拆分链表,将链表拆分为原链表和复制后的链表.
        cur = pHead;
        RandomListNode newHead = cur.next;
        while (cur!=null){
            RandomListNode copyNode = cur.next;
            cur.next = copyNode.next;
            if(copyNode.next!=null){
                copyNode.next=copyNode.next.next;
            }
            cur= cur.next;
        }
        return newHead;
    }

    //节点定义
    public static class RandomListNode {
        int label;
        RandomListNode next = null;
        RandomListNode random = null;

        RandomListNode(int label) {
            this.label = label;
        }
    }
    //打印链表
    public static void print(RandomListNode root){
        if(root==null)
            return;
        RandomListNode tmp = root;
        while (tmp!=null){
            System.out.print(tmp.label+"→");
            tmp = tmp.next;
        }
    }

}

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

  输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

package com.liuwen.牛客网;

/**
 * @description:    二叉搜索树与双向链表
 * @author: Liu Wen
 * @create: 2020-03-24 19:56
 **/
public class Main26 {
    public static void main(String[] args){
        TreeNode root = new TreeNode(4);       //      root:     4
        TreeNode node2 = new TreeNode(2);       //             /   \
        TreeNode node3 = new TreeNode(6);       //            2     6
        TreeNode node4 = new TreeNode(1);       //           / \   / \
        TreeNode node5 = new TreeNode(3);       //          1   3 5   7
        TreeNode node6 = new TreeNode(5);
        TreeNode node7 = new TreeNode(7);
        root.left = node2;root.right = node3;
        node2.left = node4;node2.right = node5;
        node3.left = node6;node3.right = node7;
        inOrder(root);
        TreeNode result = Convert(root);
        if(result!=null)
        System.out.println(result.val);
    }
    /**
     * @Description:         方法1:递归(解题思路)
     *                      1.将左子树构造成双链表,并返回链表头节点。
     *                      2.定位至左子树双链表最后一个节点。(用于3)
     *                      3.如果左子树链表不为空的话,将当前root追加到左子树链表。
     *                      4.将右子树构造成双链表,并返回链表头节点。
     *                      5.如果右子树链表不为空的话,将该链表追加到root节点之后。
     *                      6.根据左子树链表是否为空确定返回的节点。
     * @date 20.3.25 00:33
     */
    public static TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree==null)
            return null;
        TreeNode leftNode = Convert(pRootOfTree.left);
        TreeNode position = leftNode;
        while (position!=null&&position.right!=null){
            position = position.right;
        }
        if(leftNode!=null){
            position.right=pRootOfTree;
            pRootOfTree.left=position;
        }
        TreeNode rightNode = Convert(pRootOfTree.right);
        if(rightNode!=null){
            pRootOfTree.right=rightNode;
            rightNode.left=pRootOfTree;
        }
        return leftNode==null?pRootOfTree:leftNode;
    }
    /**
     * @Description:         方法2:中序遍历法非递归(利用Stack)
     * @date 20.3.25 00:41
     */
    public static TreeNode Convert1(TreeNode root) {
        return null;
    }

    //中序遍历
    public static void inOrder(TreeNode root){
        if(root==null){
            return;
        }
        inOrder(root.left);
        System.out.print(root.val+" ");
        inOrder(root.right);
    }
}

27. 字符串的排列

  输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

import java.util.ArrayList;
import java.util.Collections;
/**
 * @description: 字符串的排列
 * @author: Liu Wen
 * @create: 2020-03-11 18:25
 **/
public class Main27 {
    public static void main(String[] args){
        String string = "abc";
        ArrayList<String> arrayList = Permutation(string);
        System.out.println(arrayList);
    }
    /**
     * @Description:   方法:利用数组的全排列
      * @param str     (要用数组,所以转换为char[],然后再去重)
     * @return java.util.ArrayList
     * @date 20.3.12 15:56
     */
    public static ArrayList<String> Permutation(String str) {
        ArrayList<String> arrayList = new ArrayList<>();
        if(str!=null&&str.length()>0){
            char[] chars = str.toCharArray();
            perm(chars,0,arrayList);
        }
        Collections.sort(arrayList);
        return arrayList;
    }
    //进行数组的全排列(将全排列结果存在参数arrayList中)
    private static void perm(char[] chars, int L, ArrayList<String> arrayList) {
        if (L==chars.length-1){    //递归结束条件:当只有一个数字做全排列的时候,则就只有一种可能。
            String val = String.valueOf(chars);     //直接将字符数组转换为字符串
            if(!arrayList.contains(val)){
                arrayList.add(val);
            }
        }
        for (int j = L; j < chars.length; j++) {
            swap(chars,L,j);
            perm(chars,L+1,arrayList);
            swap(chars,L,j);
        }
    }
    //交换数组的两个元素
    private static void swap(char[] str, int i, int j) {
            char temp = str[i];
            str[i] = str[j];
            str[j] = temp;
    }
}

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

  数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
 * @description: 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
 *    例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,
 *    超过数组长度的一半,因此输出2。如果不存在则输出0。
 * @author: Liu Wen
 * @create: 2020-03-11 18:34
 **/
public class Main28 {
    public static void main(String[] args){
        int[] array = {1,2,3,2,4,2,5,2,3};
        int ans = MoreThanHalfNum_Solution3(array);
        System.out.println(ans);
    }
    /**
     * @Description:   方法4:方法3的进阶    candidate    候选数
      * @param array                         times        次数
     * @return int
     * @date 20.3.11 18:37
     */
    public static int MoreThanHalfNum_Solution3(int [] array) {
        int candidate = 0;
        int times = 0;     // {1,2,3,2,4,2,5,2,3}
        for (int i = 0; i < array.length; i++) {
            if(times==0) {
                candidate = array[i];
                times++;
            }else {
                if(candidate==array[i]){
                    times++;
                }else {
                    times--;
                }
            }
        }
   // 判断candidate出现的次数是不是超过了一半。
        int count = 0;
        for (int i = 0; i < array.length; i++) {
            if(array[i]==candidate){
                count++;
            }
        }
        if(count>array.length/2) {   //超出一半
            return candidate;
        }
        return 0;
    }
    /**
     * @Description:  方法3: 双指针;
     *                        两两删除不等数字,判断最后剩下的数字出现的次数是不是超过了一半。
      * @param array
     * @return int
     * @date 20.3.11 18:36
     */
    public static int MoreThanHalfNum_Solution2(int [] array) {
        if(array.length==0){
            return 0;
        }
        ArrayList<Integer> arrayList = new ArrayList<>();
        ArrayList<Integer> arrayIndex = new ArrayList<>();
        int left = 0; int right = array.length-1;
        for (int i = 0; i < array.length; i++) {
            arrayList.add(array[i]);
        }
        while (left<right){
            if(arrayList.get(left)!=arrayList.get(right)){
                arrayIndex.add(arrayList.get(left));         //两两不等数字记下来
                arrayIndex.add(arrayList.get(right));
                left++;right--;
            }else {
                left++;
            }
        }
        for (int i = 0; i < arrayIndex.size(); i++) {
            arrayList.remove(arrayIndex.get(i));            //两两删除不等数字
        }
        //如果arrayList有值,判断最后剩下的数字出现的次数是不是超过了一半
        if(arrayList.size()>0) {
            int count = 0;
            for (int i = 0; i < array.length; i++) {
                if (array[i] == arrayList.get(0)) {
                    count++;
                }
            }
            if (count > array.length / 2) {     //超出一半
                return arrayList.get(0);
            }
        }
        return 0;
    }
    /**
     * @Description:     方法2:HashMap存储
      * @param array
     * @return int
     * @date 20.3.11 18:35
     */
    public static int MoreThanHalfNum_Solution1(int [] array) {
        if(array.length==0){
            return 0;
        }
        HashMap<Integer,Integer> hashMap = new HashMap<>();
        for (int i = 0; i < array.length; i++) {
            if(hashMap.containsKey(array[i])){
                int value = hashMap.get(array[i]);
                value++;
                hashMap.put(array[i],value);
            }else {
                hashMap.put(array[i],1);
            }
        }
        for(Map.Entry<Integer, Integer> entry : hashMap.entrySet()){
            if(entry.getValue()>array.length/2){       //如果有value值,即某个数出现的次数大于数组的一半
                return entry.getKey();                 //则返回key
            }
//            System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue());
        }
        return 0;
    }
    /**
     * @Description:   方法1:排序,中间的数一定是
      * @param array
     * @return int
     * @date 20.3.11 18:34
     */
    public static int MoreThanHalfNum_Solution(int [] array) {
        if(array.length==0){
            return 0;
        }
        Arrays.sort(array);
        int mid = array[array.length/2];
        int count = 0;
        for (int i = 0; i < array.length; i++) {
            if(array[i]==mid){
                count++;
            }
        }
        if(count>array.length/2) {   //超出一半
            return mid;
        }
        return 0;
    }
}

29. 最小的K个数

  输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。

import java.util.ArrayList;
import java.util.Collections;
import java.util.PriorityQueue;
/**
 * @description: 输入n个整数,找出其中最小的K个数。
 * 例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
 * @author: Liu Wen
 * @create: 2020-03-10 20:05
 **/
public class Main29 {
    public static void main(String[] args){
        int[] input = {4,5,1,6,2,7,3,8};
        int k = 4;
        ArrayList<Integer> result = GetLeastNumbers_Solution2(input,k);
        System.out.println(result);
    }
    /**
     * @Description:     方法3:用优先队列储存一个长度为K的队列,
     *                          每次把队列头部的数据和遍历数据做对比即可
      * @param input
     * @param k
     * @return java.util.ArrayList
     * @date 20.3.11 18:48
     */
    public static ArrayList<Integer> GetLeastNumbers_Solution2(int[] input, int k) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        if(k>input.length||k<=0){
            return arrayList;
        }   // 定义一个优先队列
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(((o1, o2) -> o2-o1));
        for (int i = 0; i < input.length; i++) {
            //先把队列装到k个元素
            if(priorityQueue.size()<k){
                priorityQueue.add(input[i]);
                continue;
            }
//            System.out.println(priorityQueue);
            // 移除最大值,塞入最小值
            if(priorityQueue.size()>=k&&priorityQueue.peek()>input[i]){
                priorityQueue.poll();
                priorityQueue.add(input[i]);
            }
        }
        return new ArrayList<>(priorityQueue);
    }
    /**
     * @Description:    方法2:排好序,找出最小的k个数
      * @param input
     * @param k
     * @return java.util.ArrayList
     * @date 20.3.11 18:47
     */
    public static ArrayList<Integer> GetLeastNumbers_Solution1(int[] input, int k) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        if(k>input.length||k<=0){
            return arrayList;
        }
        for (int i = 0; i < input.length-1; i++) {
            for (int j = 0; j < input.length-i-1; j++) {
                if(input[j]>input[j+1]){
                    int temp = input[j];
                    input[j] = input[j+1];
                    input[j+1] = temp;
                }
            }
        }
        for (int i = 0; i < k; i++) {
            arrayList.add(input[i]);
//            System.out.print(input[i]+" ");
        }
        return arrayList;
    }

    /**
     * @Description:    方法1:利用工具类将集合排好序,取出前k个即可
      * @param input
     * @param k
     * @return java.util.ArrayList
     * @date 20.3.10 20:08
     */
    public static ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        if (input.length==0||k<1||k>input.length){
            return arrayList;
        }
        for (int i = 0; i < input.length; i++) {
            arrayList.add(input[i]);
        }
        Collections.sort(arrayList);
        ArrayList<Integer> result = new ArrayList<>();
        for (int i = 0; i < k; i++) {
            result.add(arrayList.get(i));
        }
        return result;
    }
}

30. 连续子数组的最大和

  HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

import java.util.ArrayList;
import java.util.Collections;
/**
 * @description: 连续子数组的最大和
 * @author: Liu Wen
 * @create: 2020-03-10 20:20
 **/
public class Main30 {
    public static void main(String[] args){
        int[] array = {2,8,1,5,9};
        int ans = FindGreatestSumOfSubArray1(array);
        System.out.println(ans);
    }
    /**
     * @Description:   方法2:动态规划
     *                   F(i):以array[i]为末尾元素的子数组的和的最大值,子数组的元素的相对位置不变
     *                   F(i)= max(F(i-1)+array[i] , array[i])
     *                   res=max(res,F(i))    res:所有子数组的和的最大值
     * @param array
     * @return int
     * @date 20.3.10 21:36
     */
    public static int FindGreatestSumOfSubArray1(int[] array) {
        if(array.length==0){
            return 0;
        }
        int Fn = array[0];       //包含array[i]的连续数组最大值
        int res = array[0];      //记录当前所有子数组的和的最大值
        for (int i = 1; i < array.length; i++) {
            Fn = Math.max(Fn+array[i],array[i]);
            res = Math.max(Fn,res);
        }
        return res;
    }
    /**
     * @Description:  方法1:暴力搜索。把以每个元素为起始点的子数组的和都算出来,保留最大值。
      * @param array
     * @return int
     * @date 20.3.10 20:21
     */
    public static int FindGreatestSumOfSubArray(int[] array) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        if(array.length==0){
            return 0;
        }
        for (int i = 0; i <= array.length-1; i++) {
            int tmp = 0;
            int max = array[i];
            if(tmp>array[i]){
                tmp = array[i];
            }
            for (int j = i; j <= array.length-1; j++) {
                tmp = tmp+array[j];
                if(max<=tmp){
                    max = tmp;
                }
            }
            arrayList.add(max);
        }
        Collections.sort(arrayList);
        return arrayList.get(arrayList.size()-1);
    }
}

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

  求出1-13的整数中1出现的次数,并算出100-1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

/**
 * @description:   从1到n整数中1出现的次数
 * @author: Liu Wen
 * @create: 2020-03-10 21:41
 **/
public class Main31 {
    public static void main(String[] args){
        int n = 13;
        int res = NumberOf1Between1AndN_Solution1(n);
        System.out.println(res);
    }
 /**
     * @Description:     方法1:遍历。
      * @param n           遍历的过程中将数字转化为字符串,再数每个字符串中1的个数
     * @return int
     * @date 20.3.10 21:50
     */
    public static int NumberOf1Between1AndN_Solution(int n) {
        int count = 0;
        for (int i = 1; i <= n; i++) {
            String string = String.valueOf(i);
            for (int j = 0; j < string.length(); j++) {
                if(string.charAt(j)=='1'){
                    count++;
                }
            }
        }
        return count;
    }
    /**
     * @Description:   方法2:设定整数点(如1、10、100等等)作为位置点i(对应n的各位、十位、百位等等),分别对每个数位上有多少包含1的点进行分析
      * @param n                int a = n/i,b = n%i;
     * @return int              count=count+(a+8)/10*i+(a%10==1)*(b+1);
     * @date 20.3.10 21:57
     */
    public static int NumberOf1Between1AndN_Solution1(int n) {
        int count=0;
        for(int i=1;i<=n;i*=10)
        {
            //i表示当前分析的是哪一个数位
            int a = n/i;int b = n%i;
            if(a%10==1) {
                count = count + (a + 8) / 10 * i + b + 1;
            }else {
                count = count + (a + 8) / 10 * i;
            }
        }
        return count;
    }
}

32. 把数组排成最小的数

  输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

import java.util.Arrays;
import java.util.Comparator;
/**
 * @description: 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
 * 例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
 * @author: Liu Wen
 * @create: 2020-03-10 22:03
 **/
public class Main32 {
    public static void main(String[] args){
        int[] array = {1,2,3};
        String ans = PrintMinNumber(array);
        System.out.println(ans);
    }
    /**
     * @Description:     方法2: 朝着能使数最小的方式排序
     *                    实现了Comparator接口的compare方法,将集合元素按照compare方法的规则进行排序
      * @param numbers
     * @return java.lang.String
     * @date 20.3.10 22:09
     */
    public static String PrintMinNumber(int [] numbers) {
        String string = new String();
        String[] strings = new String[numbers.length];
        for (int i = 0; i < numbers.length; i++) {
            strings[i] = String.valueOf(numbers[i]);
        }
        Arrays.sort(strings, new Comparator<String>() {  //自动实现Comparator接口
            @Override
            public int compare(String o1, String o2) {   //自定义比较器(代码没有侵入性)
                return (o1+o2).compareTo(o2+o1);
            }
        });
        for (int i = 0; i < strings.length; i++) {
            string = string+strings[i];
        }
        return string;
    }
}

33. 丑数

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

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

  在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).

package com.liuwen.牛客网;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @description:    第一个只出现一次的字符:在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到
 * @author: Liu Wen         第一个只出现一次的字符,并返回它的位置,如果没有则返回 -1(需要区分大小写).
 * @create: 2020-03-15 14:43
 **/
public class Main34 {
    public static void main(String[] args){
        String string = "google";
        int res = FirstNotRepeatingChar(string);
        System.out.println(res);
    }
    /**
     * @Description:     方法2:使用map的键值对来存字母及字母出现得次数
     * @date 20.3.15 15:03
     */
    public static int FirstNotRepeatingChar1(String string) {
        if(string.length()==0)
            return -1;
//        Map map = new LinkedHashMap<>();   //按加入的顺序排列
        Map<Character,Integer> map = new HashMap<>();   //按字母排序
        for (int i = 0; i < string.length(); i++) {
            if(map.containsKey(string.charAt(i))){
                int value = map.get(string.charAt(i));
                map.put(string.charAt(i),++value);
            }else {
                map.put(string.charAt(i),1);
            }
        }
//        for (Character c:map.keySet()) {
//            if (map.get(c)==1){
//                return string.indexOf(c);
//            }
//        }
        for (int i = 0; i < string.length(); i++) {
            if(map.get(string.charAt(i))==1){
                return i;
            }
        }
        return -1;
    }

    /**
     * @Description:      方法1:熟练调用String的方法
     * @date 20.3.15 15:02
     */
    public static int FirstNotRepeatingChar(String str) {
        if(str.length()==0)
            return -1;
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if(!str.substring(0,i).contains(c+"")&&!str.substring(i+1).contains(c+"")){
              return i;
            }
        }
        return -1;
    }
}

35. 数组中的逆序对

  在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

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

  输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

package com.liuwen.牛客网;

/**
 * @description:   输入两个链表,找出它们的第一个公共结点。
 * @author: Liu Wen
 * @create: 2020-03-16 16:58
 **/
public class Main36 {
    public static void main(String[] args){
        // 0→1→2→3→4→5→6
        ListNode head1 = new ListNode(0);
        ListNode node1 = new ListNode(1);
        ListNode node2 = new ListNode(2);
        ListNode node3 = new ListNode(3);
        ListNode node4 = new ListNode(4);
        ListNode node5 = new ListNode(5);
        ListNode node6 = new ListNode(6);
        head1.next = node1;node1.next=node2;node2.next=node3;
        node3.next=node4;node4.next=node5;node5.next=node6;
        //   9→8→3→4→5→6
        ListNode head2 = new ListNode(9);
        ListNode node8 = new ListNode(8);
        ListNode node9 = new ListNode(3);
        ListNode node10 = new ListNode(4);
        ListNode node11 = new ListNode(5);
        ListNode node12 = new ListNode(6);
        head2.next = node8;node8.next = node9;node9.next = node10;
        node10.next = node11;node11.next = node12;

        ListNode result = FindFirstCommonNode(head1,head2);
        if(result!=null)
            System.out.println("第一个公共结点:" + result.val);
    }
    /**
     * @Description:  找出2个链表的长度,然后让长的先走两个链表的长度差,然后再一起走。
     * @date 20.3.16 17:49         (因为2个链表用公共的尾部)。类似快慢指针。
     */

    public static ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if(pHead1==null||pHead2==null){
            return null;
        }
        int size1 = getSize(pHead1);
        int size2 = getSize(pHead2);
        ListNode temp1 = pHead1;
        ListNode temp2 = pHead2;
        if(size1>size2){
            for (int i = 0; i < (size1 - size2); i++) {
                temp1 = temp1.next;
            }
        }else {
            for (int i = 0; i < (size2 - size1); i++) {
                temp2 = temp2.next;
            }
        }
        while (temp1.val!=temp2.val&&temp1.next!=null&&temp2.next!=null){
            temp1 = temp1.next;
            temp2 = temp2.next;
        }
        if(temp1.val==temp2.val){
            return temp1;
        }else
        return null;
    }

    private static int getSize(ListNode pHead1) {
        if(pHead1==null){
            return 0;
        }
        ListNode temp = pHead1;
        int count = 0;
        while (temp!=null){
            count++;
            temp = temp.next;
        }
        return count;
    }

    public static class ListNode {
        int val;
        ListNode next = null;

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

37. 数字在排序数组中出现的次数

  统计一个数字在排序数组中出现的次数。

package com.liuwen.牛客网;

/**
 * @description:      统计一个数字在排序数组中出现的次数。
 * @author: Liu Wen
 * @create: 2020-03-16 17:41
 **/
public class Main37 {
    public static void main(String[] args){
        int [] array = {1,3,3,3,3,4,5};
        int k = 2;
        int result = GetNumberOfK1(array,k);
        System.out.println(result);
    }
    /**
     * @Description:      方法1:简单的遍历。用count计算次数。
     * @date 20.3.16 17:46
     */
    public static int GetNumberOfK(int [] array , int k) {
        int count = 0;
        for (int i = 0; i < array.length; i++) {
            if(array[i]>k){              //当k已经小于array[i],说明后面已经不可能有k了,提前结束遍历
                break;
            }
            if(array[i]==k){
                count++;
            }
        }
        return count;
    }

    /**
     * @Description:        方法2:二分查找(因为是有序数组)    二分查找代码练习
     * @date 20.3.16 17:53         step1:二分查找出第一个k; → 递归实现二分
     *                             step2:二分查找出最后一个k; → 循环实现二分
     */
    public static int GetNumberOfK1(int[] array, int k) {
        if(array.length==0){
            return 0;
        }
        int left = findFirstk(array,k,0,array.length-1);   //递归实现二分
        int right = findLastk(array,k,0,array.length-1);    //循环实现二分
        if(left==-1)      //未找到数,返回0;
            return 0;
        return right-left+1;
    }

    private static int findFirstk(int[] array, int k,int left,int right) {
        if(left>right){
            return -1;
        }
        int mid = left+(right-left)/2;
        if(array[mid]>k){
            int res = findFirstk(array,k,left,mid-1);
            return res;
        }else if(array[mid]<k){
            int res = findFirstk(array,k,mid+1,right);
            return res;
        }else if(mid-1>=0&&array[mid-1]==k){   //****关键****
            int res = findFirstk(array,k,left,mid-1);
            return res;
        }else {
            return mid;
        }
    }
    //{1,2,2,2,3,4,5,5,6,7,8,8,9};
    public static int findLastk(int[] array, int k,int left,int right) {
        int mid = left+(right-left)/2;
        while (left<=right){
            if(array[mid]>k){
                right = mid-1;
            }else if (array[mid]<k){
                left = mid+1;
            }else if(mid+1<=right&&array[mid+1]==k){    //****关键****
                left = mid+1;
            }else {
                return mid;
            }
            mid = left+(right-left)/2;
        }
        return -1;
    }

}

38. 二叉树的深度

  输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

package com.liuwen.牛客网;

import java.util.*;

/**
 * @description:        二叉树的深度
 * @author: Liu Wen
 * @create: 2020-03-16 18:07
 **/
public class Main38 {

    public static void main(String[] args){
        TreeNode root = new TreeNode(1);       //      root:     1
        TreeNode node2 = new TreeNode(2);       //             /   \
        TreeNode node3 = new TreeNode(3);       //            2     3
        TreeNode node4 = new TreeNode(4);       //           / \     \
        TreeNode node5 = new TreeNode(5);       //          4   5     6
        TreeNode node6 = new TreeNode(6);       //                   /
        TreeNode node7= new TreeNode(7);        //                  7
        root.left = node2;root.right = node3;
        node2.left = node4;node2.right = node5;
        node3.right = node6;node6.left = node7;

        int result = TreeDepth1(root);
        System.out.println(result);
    }
    /**
     * @Description:          方法1:递归解法
     * @date 20.3.16 18:10
     */
    public static int TreeDepth(TreeNode root) {
        if(root==null){
            return 0;
        }
        int leftLength = TreeDepth(root.left)+1;
        int rightLength = TreeDepth(root.right)+1;
        return Math.max(leftLength,rightLength);
    }
    /**
     * @Description:      方法2:层序遍历。来计算二叉树的层数
     * @date 20.3.16 18:19
     */
    public static int TreeDepth1(TreeNode root) {
        int depth = 0;
        if(root==null){
            return depth;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        int count = 0;             //记录当前层的个数
        int nextcount = 1;         //当前层个数
        while (!queue.isEmpty()){
            TreeNode treeNode = queue.poll();
            count++;
            if(treeNode.left!=null){
                queue.add(treeNode.left);
            }
            if(treeNode.right!=null){
                queue.add(treeNode.right);
            }
            if(count==nextcount){
                nextcount = queue.size();
                count = 0;
                depth++;
            }
        }
        return depth;
    }

    //树节点定义
    public static class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;

        public TreeNode(int val) {
            this.val = val;

        }
    }

}

39. 平衡二叉树

  输入一棵二叉树,判断该二叉树是否是平衡二叉树。

package com.liuwen.牛客网;

/**
 * @description:    输入一棵二叉树,判断该二叉树是否是平衡二叉树。
 * @author: Liu Wen
 * @create: 2020-03-16 18:21
 **/
public class Main39 {
    public static void main(String[] args){        //1 2 3 4 5 # # # # 6
        TreeNode root = new TreeNode(1);       //      root:     1
        TreeNode node2 = new TreeNode(2);       //             /   \
        TreeNode node3 = new TreeNode(3);       //            2     3
        TreeNode node4 = new TreeNode(4);       //           / \
        TreeNode node5 = new TreeNode(5);       //          4   5
        TreeNode node6 = new TreeNode(6);       //             /
        root.left = node2;root.right = node3;       //            6
        node2.left = node4;node2.right = node5;
        node5.left = node6;
        boolean result = IsBalanced_Solution(root);
        System.out.println(result);
    }
    /**
     * @Description:          方法1:平衡二叉树的左右子树深度差 <=1
     * @date 20.3.16 18:23
     */
    public static boolean IsBalanced_Solution(TreeNode root) {
        if (root==null){
            return true;
        }
        int leftLength = getDepth(root.left);
        int rightLength = getDepth(root.right);
        return (leftLength-rightLength)==0||(rightLength-leftLength)==1||(leftLength-rightLength)==1||(rightLength-leftLength)==0;
    }
    private static int getDepth(TreeNode root) {
        if(root==null){
            return 0;
        }
        int leftDepth = getDepth(root.left)+1;
        int rightDepth = getDepth(root.right)+1;
        return Math.max(leftDepth,rightDepth);
    }

    //树节点定义
    public static class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;

        public TreeNode(int val) {
            this.val = val;

        }
    }
}

40. 数组中只出现一次的数字

  一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

package com.liuwen.牛客网;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;

/**
 * @description:     一个整型数组里除了两个数字之外,其他的数字都出现了两次。
 * @author: Liu Wen       请写程序找出这两个只出现一次的数字。
 * @create: 2020-03-15 14:44
 **/
public class Main40 {
    public static void main(String[] args) {
        int[] array = {2,4,3,6,3,2,5,5};
        int[] num1 = new int[1];            //num1,num2分别为长度为1的数组。传出参数
        int[] num2 = new int[1];            //将num1[0],num2[0]设置为返回结果
        FindNumsAppearOnce(array, num1, num2);
    }

    /**
     * @Description: 方法1:排序后异或,比较相邻两值,同出0;
     * @date 20.3.15 15:11
     */
    public static void FindNumsAppearOnce(int[] array, int num1[], int num2[]) {
            if(array.length==0){
                return;
            }
            Arrays.sort(array);                     //排序后异或,比较相邻两值
            int index = 0;
            int[] arr = new int[2];
            for (int i = 0; i < array.length-1; i++) {
                if((array[i]^array[i+1])==0){       //说明两值一样
                    i = i+1;
                }else {
                    arr[index++] = array[i];
                }
            }
            if(arr[1]==0){
                arr[1]=array[array.length-1];
            }
            System.out.println(Arrays.toString(arr));
            num1[0] = arr[0];
            num2[0] =arr[1];
    }
    /**
     * @Description: 方法2:使用map的键值对。找出value等于1的key即可。
     * @date 20.3.15 15:12
     */
    public static void FindNumsAppearOnce1(int[] array, int[] num1, int[] num2) {
        if(array.length<2){
            return;
        }
        HashMap<Integer,Integer> hashMap = new LinkedHashMap<>();
        for (int i = 0; i < array.length; i++) {
            if(hashMap.containsKey(array[i])){
                int value = hashMap.get(array[i]);
                hashMap.put(array[i],++value);
            }else {
                hashMap.put(array[i],1);
            }
        }
        int[] arr = new int[2];
        int index = 0;
        for (Integer val:hashMap.keySet()) {
            if(hashMap.get(val)==1){
                arr[index]=val;
                index++;
            }
        }
        num1[0] = arr[0];
        num2[0] = arr[1];
    }
}

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

  小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

package com.liuwen.牛客网;

import com.sun.tools.javac.Main;

import java.util.ArrayList;

/**
 * @description:    和为S的连续正数序列
 * @author: Liu Wen 输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
 * @create: 2020-03-16 19:21
 **/
public class Main41 {
    public static void main(String[] args){
        int sum = 100;
        ArrayList<ArrayList<Integer>> arrayLists = FindContinuousSequence(sum);
        System.out.println(arrayLists);
    }
    /**
     * @Description:          双指针思想
     * @date 20.3.16 19:28
     */
    public static ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> arrayLists = new ArrayList<>();
        if(sum<1){
            return arrayLists;
        }
        int left = 1;
        int right = 2;
        int SUM;
        while (left<right){
            SUM = (left+right)*(right-left+1)/2;        //求和公式
            if(SUM>sum){
                left++;
            }
            if(SUM<sum){
                right++;
            }
            if(SUM == sum){
                ArrayList<Integer> arrayList = new ArrayList<>();
                for (int i = left; i <=right ; i++) {
                    arrayList.add(i);
                }
                arrayLists.add(arrayList);
                left++;
            }
        }
        return arrayLists;
    }
}

42. 和为S的两个数字

  输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

43. 左旋转字符串

  汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

44. 翻转单词顺序列

  牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

/**
 * @description:    翻转单词顺序列
 * @author: Liu Wen
 * @create: 2020-03-11 23:31
 **/
public class Main44 {
    public static void main(String[] args){
        String str = "student. a am I";
        String result = ReverseSentence(str);
        System.out.println(result);
    }
    /**
     * @Description:  方法1:先整个反转,再逐个反转。
      * @param str
     * @return java.lang.String
     * @date 20.3.11 23:32
     */   //student. a am I  →  I am a student.
    public static String ReverseSentence(String str) {
        if(str.trim().equals("")){         //用于删除字符串的头尾空白符。
            return str;
        }
        StringBuffer stringBuffer = new StringBuffer();
        StringBuffer result = new StringBuffer();
        for (int i = 0; i < str.length(); i++) {
            stringBuffer.append(str.charAt(i));
        }
        stringBuffer.reverse();
        String string = stringBuffer.toString();
        String[] strings = string.split(" ");
        for (int i = 0; i < strings.length; i++) {
            StringBuffer newStringBuffer = new StringBuffer();
            newStringBuffer.append(strings[i]);
            newStringBuffer.reverse();
            if(i==strings.length-1){
                result.append(newStringBuffer);
            }else {
                result.append(newStringBuffer + " ");
            }
        }
        return result.toString();
    }
}

45. 扑克牌顺子

  LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。

package com.liuwen.牛客网;

/**
 * @description:   扑克牌顺子
 * @author: Liu Wen
 * @create: 2020-03-24 11:45
 **/
public class Main45 {
    public static void main(String[] args){
        int[] numbers = {};
        boolean result = isContinuous(numbers);
        System.out.println(result);
    }
    /**
     * @Description: 满足条件 1.数组长度为5;  2.除0外没有重复的数字(牌) ; 3.max - min <5 ;
     * @date 20.3.24 11:46
     */
    public static boolean isContinuous(int [] numbers) {
        if(numbers.length!=5){
            return false;
        }
        int[] duplication = new int[14];   //用于登记除0外重复的数字(牌)(1~13+大小王=14)
        int max = -1;
        int min = 15;
        for (int i = 0; i < 5; i++) {
            duplication[numbers[i]]++;
            if(numbers[i]==0){             //如果是大小王,跳过本次循环
                continue;
            }
            if(duplication[numbers[i]]>1){
                return false;
            }
            max = max>numbers[i]?max:numbers[i];
            min = min<numbers[i]?min:numbers[i];
        }
        if(max-min<5){
            return true;
        }
        return false;
    }
}

46. 孩子们的游戏(圆圈中最后剩下的数)

  每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)。如果没有小朋友,请返回-1

package com.liuwen.牛客网;

/**
 * @description:   孩子们的游戏(圆圈中最后剩下的数)
 * @author: Liu Wen
 * @create: 2020-03-24 11:45
 **/
public class Main46 {
    public static void main(String[] args){
        int n = 20;      //20个小朋友 0~19
        int m = 3;       //从0数到2
        int result = LastRemaining_Solution1(n,m);
        System.out.println(result);
    }
    /**
     * @Description:            方法2:递归;f[n]表示n个人玩游戏报m退出最后胜利者的编号
     *                          eg:   0  1  2  3  4  5  6  7  8  9  (n=10,m=4)
     * @date 20.3.24 12:10            6  7  8  √  0  1  2  3  4  5  (假设2是最后的胜利者,则(2+m)%10=6)
     */
    public static int LastRemaining_Solution(int n, int m) {
        if(n==0){
            return -1;
        }
        if(n==1){
            return 0;
        }
        return (LastRemaining_Solution(n-1,m)+m)%n;  //f(n) = [f(n-1)+m]%n
    }

    /**
     * @Description:     :方法1:使用数组来模拟环,思路还是比较简单,但是各种下标要理清
     * @date 20.3.24 11:46
     */
    public static int LastRemaining_Solution1(int n, int m) {
        if(n==0){
            return -1;
        }
        if(n==1){
            return 0;
        }
        int Sum = n;
        int i = -1;         //这样i才可以从0开始
        int count = 0;
        int[] N = new int[n];
        while (Sum>0){
            i++;
            if(i>=n){
                i=0;        //模拟环
            }
            if(N[i]==-1){   //如果是已经删除的对象,跳过
                continue;
            }
            count++;
            if(count==m){   //找到待删除的对象。
                N[i]=-1;    //删除
                count = 0;
                Sum--;
            }
        }
        return i;
    }
}

47. 求1+2+3+…+n

  求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

/**
 * @description: 求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、
 *                               switch、case等关键字及条件判断语句(A?B:C)。
 * @author: Liu Wen
 * @create: 2020-03-09 00:25
 **/
public class Main47 {
    public static void main(String[] args){
        int result = Sum_Solution(10);
        System.out.println(result);
    }
    /**
     * @Description:  方法1:递归
      * @param n
     * @return int
     * @date 20.3.9 00:27
     */
    public static int Sum_Solution(int n) {
        if(n == 1){
            return 1;
        }
        return n+Sum_Solution(n-1);
    }
}

48. 不用加减乘除做加法

  写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

/**
 * @description: 写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号
 * @author: Liu Wen
 * @create: 2020-03-09 00:46
 **/
public class Main48 {
    public static void main(String[] args){
        int result = Add1(12,13);
        System.out.print(result);
    }
    /**
     * @Description:   方法2:位操作
      * @param num1 num2
     * @param i1
     * @return int
     * @date 20.3.9 00:58
     */
    public static int Add1(int num1,int num2) {     // 加法计算分为三步
        while(num2!=0){//进位为0计算结束//    eg:5->101,7->111       5+7=?
            int tmp = num1^num2;       //step1:个位相加,不算进位,得到010,二进制每位相加就相当于各位做异或操作,101^111。
            num2 = (num1&num2)<<1;     //step2:计算进位值,得到1010,相当于个位做与操作得到101,再向左移一位得到1010,(101&111)<<1。
            num1 = tmp;                //step3:重复上述两步,个位相加 010^1010=1000,进位值为100=(010&1010)<<1。
        }                              //继续重复上述两步:1000^100 = 1100,进位值为0,跳出循环,1100为最终结果。
        return num1;
    }
    /**
     * @Description:   方法1:数学工具。虽然符合题目要求,但是失去了这道题目的意义。
      * @param num1
     * @param num2
     * @return int
     * @date 20.3.9 00:51
     */
    public static int Add(int num1,int num2) {
        return Math.addExact(num1,num2);
    }
}

49. 把字符串转换成整数

  将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0.

50. 数组中重复的数字

  在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

package com.liuwen.牛客网;

/**
 * @description:      数组中重复的数字
 * @author: Liu Wen
 * @create: 2020-03-15 12:19
 **/
public class Main50 {
    public static void main(String[] args){
        int[] nums = {2,1,3,1,4};
        int[] duplication = new int[1];
        boolean res = duplicate1(nums,nums.length,duplication);
        System.out.println(res);
    }
    /**
     * @Description:             方法2:使用boolean只占一位
     * @date 20.3.15 14:38
     */
    public static boolean duplicate1(int numbers[],int length,int [] duplication) {
        boolean result = false;
        boolean[] buer = new boolean[length];
        for (int i = 0; i < length; i++) {
            if(buer[numbers[i]]==true){       //**关键**//
                result = true;
                duplication[0] = numbers[i];
                break;
            }else {
                buer[numbers[i]] = true;
            }
        }
        return result;
    }

    /**
     * @Description:     方法1:最简单的方法:
     *                        最直接的想法就是构造一个容量为N的辅助数组B,原数组A中每个数对应B中下标,
     *                        首次命中,B中对应元素+1。如果某次命中时,B中对应的不为0,说明,前边已经
     *                        有一样数字了,那它就是重复的了。
     * @date 20.3.15 12:23
     */
    public static boolean duplicate(int numbers[],int length,int [] duplication) {
        for (int i = 0; i < numbers.length; i++) {
            for (int j = i+1; j < numbers.length-1; j++) {
                if(numbers[i]==numbers[j]) {
                    duplication[0] = numbers[i];
                    return true;
                }
            }
        }
        return false;
    }
}

51. 构建乘积数组

  给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];).

package com.liuwen.牛客网;

import java.util.Arrays;

/**
 * @description:     构建乘积数组
 * @author: Liu Wen
 * @create: 2020-03-24 17:51
 **/
public class Main51 {
    public static void main(String[] args){
        int[] A = {1,2,3,4,5};
        int[] result = multiply1(A);
        System.out.println(Arrays.toString(result));
    }

    /**
     * @Description:      方法2:矩阵,先从上往下计算下三角(计算出B[i]的一部分),
     *                          再从下往上计算上三角(计算出B[i]的另一部分)  算法复杂度O(2n)
     *                    B0     1   A1   A2 ... An-2  An-1
     *                    B1     A0   1   A2 ... An-2  An-1
     *                    B2     A0  A1   1  ... An-2  An-1
     *                    ..     A0  A1   A2  1  An-2  An-1
     *                    Bn-2   A0  A1   A2 ...   1   An-1
     *                    Bn-1   A0  A1   A2 ... An-2   1
     * @date 20.3.24 18:05
     */
    public static int[] multiply1(int[] A) {
        int length = A.length;
        int[] B = new int[length];
            //先计算下三角
            B[0] = 1;
            for (int i = 1; i < length; i++) {
                B[i] = B[i-1]*A[i-1];
            }
            //再计算上三角
            int tmp = 1;
            for (int i = length-2; i >=0 ; i--) {
                tmp *= A[i+1];
                B[i] *= tmp;
            }
        return B;
    }

    /**
     * @Description:      方法1:两个for循环解决(算法复杂度n^2)
     * @date 20.3.24 18:03
     */
    public static int[] multiply(int[] A) {
        int[] B = new int[A.length];
        for (int i = 0; i < A.length; i++) {
            int muliply = 1;
            for (int j = 0; j < A.length; j++) {
               if(i==j){
                   continue;
               }
               muliply *= A[j];
            }
            B[i]=muliply;
        }
        return B;
    }
}

52. 正则表达式匹配

  请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配

53. 表示数值的字符串

  请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。

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

  请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

55. 链表中环的入口结点

   给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

package com.liuwen.牛客网;

/**
 * @description:     给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
 * @author: Liu Wen
 * @create: 2020-03-23 21:55
 **/
public class Main55 {
    public static void main(String[] args){
        //6->1->2->3->4->5
        //         ↑     ↓
        //         8<-7<-6
        ListNode root = new ListNode(6);
        ListNode node1 = new ListNode(1);
        ListNode node2 = new ListNode(2);
        ListNode node3 = new ListNode(3);
        ListNode node4 = new ListNode(4);
        ListNode node5 = new ListNode(5);
        ListNode node6 = new ListNode(6);
        ListNode node7 = new ListNode(7);
        ListNode node8 = new ListNode(8);
        root.next = node1;   node4.next = node5;
        node1.next = node2;  node5.next = node6;
        node2.next = node3;  node6.next = node7;
        node3.next = node4;  node7.next = node8;
        node8.next = node3;

        ListNode result = EntryNodeOfLoop(root);
        System.out.println(result.value);
    }
    /**
     * @Description:        先判断是否有环(快慢指针),有环则(慢慢指针)开始查找。
     *                       1、设置快慢指针,假如有环,他们最后一定相遇。
     *                       2、两个指针分别从链表头和相遇点继续出发,每次走一步,最后一定相遇与环入口。
     * @date 20.3.23 22:33
     */
    public static ListNode EntryNodeOfLoop(ListNode pHead) {
        if(pHead==null){
            return null;
        }
        //step1:先判断是否有环;
        boolean flag = false;
        ListNode slow = pHead;
        ListNode quick = pHead;
        while (quick!=null&&quick.next!=null){
            slow = slow.next;
            quick = quick.next.next;
            if(slow==quick){
                flag = true;
                break;
            }
        }
        //step2:有环则找入口点;   相遇点离环的开始处的距离等于链表头到环开始处的距离,
        if(flag){
           slow = pHead;
           while (slow.next!=null){
               if(slow==quick){
                   return slow;
               }
               slow = slow.next;
               quick = quick.next;
           }
        }
        //step3:无环则返回null。
        return null;
    }
}

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

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

package com.liuwen.牛客网;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;

/**
 * @description:   删除链表中重复的结点
 * @author: Liu Wen
 * @create: 2020-03-22 18:53
 **/
public class Main56 {
    public static void main(String[] args){

        ListNode node1 = new ListNode(1);
        ListNode node2 = new ListNode(1);
        ListNode node3 = new ListNode(3);
        ListNode node4 = new ListNode(3);
        ListNode node5 = new ListNode(3);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;

        print(node1);
        System.out.println("去重后:");

        ListNode result = deleteDuplication(node1);
        print(result);
    }
    /**
     * @Description: 1. 首先添加一个头节点,以方便碰到第一个,第二个节点就相同的情况  (秒啊)
     *               2. 设置 pre,cur指针,pre指针指向当前确定不重复的那个节点,而cur指针相当于工作指针,一直往后面搜索。
     * @date 20.3.23 20:04
     */
    public static ListNode deleteDuplication(ListNode pHead) {
        if(pHead==null||pHead.next==null){
            return pHead;
        }
        ListNode root = new ListNode(-1);
        root.next = pHead;
        ListNode pre = root;             //pre指针指向当前确定不重复的那个节点
        ListNode cur = pHead;            //cur指针相当于当前工作指针
        while (cur!=null){
            if(cur.next!=null && cur.val==cur.next.val){
                while (cur.next!=null && cur.val==cur.next.val){
                    cur = cur.next;
                }
                pre.next = cur.next;
                cur = cur.next;
            }else {
                pre = pre.next;
                cur = cur.next;
            }
        }
        return root.next;
    }

    //打印节点
    public static void print(ListNode node){
        ListNode tmp = node;
        while (tmp!=null){
            System.out.print(tmp.val+"->");
            tmp=tmp.next;
        }
    }
    //树节点定义
    public static class ListNode {
        int val;
        ListNode next = null;

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

57. 二叉树的下一个结点

  给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

package com.liuwen.牛客网;

/**
 * @description:        经典:二叉树的下一个结点
 * @author: Liu Wen
 * @create: 2020-03-22 18:47
 **/
public class Main57 {
    public static void main(String[] args){
        TreeLinkNode pNode = new TreeLinkNode(10);    //            10
        TreeLinkNode node1 = new TreeLinkNode(1);     //        //      \\
        TreeLinkNode node2 = new TreeLinkNode(2);     //       1         2
        TreeLinkNode node3 = new TreeLinkNode(3);     //     //  \\    //
        TreeLinkNode node4 = new TreeLinkNode(4);     //    3     4    5
        TreeLinkNode node5 = new TreeLinkNode(5);     //   //    //     \\
        TreeLinkNode node6 = new TreeLinkNode(6);     //  6      7       8
        TreeLinkNode node7 = new TreeLinkNode(7);     //       //
        TreeLinkNode node8 = new TreeLinkNode(8);     //       9
        TreeLinkNode node9 = new TreeLinkNode(9);//中序排列结果:6 3 1 9 7 4 10 5 8 2
        pNode.left=node1;    node1.next=pNode;
        pNode.right=node2;   node2.next=pNode;
        node1.left=node3;    node3.next=node1;
        node1.right=node4;   node4.next=node1;
        node2.left=node5;    node5.next=node2;
        node3.left=node6;    node6.next=node3;
        node4.left=node7;    node7.next=node4;
        node5.right=node8;   node8.next=node5;
        node7.left=node9;    node9.next=node7;
        TreeLinkNode result = GetNext(node5);
        if(result!=null)
        System.out.println(result.val);
//        inOder(pNode);
    }
    /**
     * @Description:         找出二叉树中序遍历顺序的下一个结点并且返回。
     * @date 20.3.22 18:47   注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
     */
    public static TreeLinkNode GetNext(TreeLinkNode pNode) {
        if(pNode==null){
            return null;
        }
        //1、有右子树的,那么下个结点就是右子树最左边的点
        if(pNode.right!=null){
            pNode = pNode.right;
            while (pNode.left!=null){
                pNode = pNode.left;
            }
            return pNode;
        }
        //2、没有右子树的,也可以分成两类,
        while (pNode.next!=null) {        //向上一直找父节点的父节点
            //①是父节点的左孩子,那么父节点就是下一个节点;
            if (pNode.next.left == pNode) {
                return pNode.next;
            }
            //②是父节点的右孩子,找他的父节点
            pNode = pNode.next;
        }
        return null;
    }

    //中序遍历
    private static void inOder(TreeLinkNode pNode) {
        if (pNode==null){
            return;
        }
        inOder(pNode.left);
        System.out.print(pNode.val+"  ");
        inOder(pNode.right);
    }
    //树节点定义
    public static class TreeLinkNode {
        int val;
        TreeLinkNode left = null;
        TreeLinkNode right = null;
        TreeLinkNode next = null;

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


58. 对称的二叉树

  请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

package com.liuwen.牛客网;

/**
 * @description:    对称的二叉树
 * @author: Liu Wen
 * @create: 2020-03-22 17:53
 **/
public class Main58 {
    public static void main(String[] args){
        TreeNode pRoot = new TreeNode(5);     //       5            5
        TreeNode node1 = new TreeNode(3);     //    3     3      3     3
        TreeNode node2 = new TreeNode(3);     //   5 1   1 5   5  1  1   5
        TreeNode node3 = new TreeNode(5);
        TreeNode node4 = new TreeNode(1);
        TreeNode node5 = new TreeNode(1);
        TreeNode node6 = new TreeNode(5);
        pRoot.left=node1;pRoot.right=node2;
        node1.left=node3;node1.right=node4;
        node2.left=node5;node2.right=node6;
        boolean result = isSymmetrical(pRoot);
        System.out.println(result);
    }
    /**
     * @Description:    如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
     * @date 20.3.22 17:54
     */
    public static boolean isSymmetrical(TreeNode pRoot){
        if(pRoot==null){
            return true;                 //空树也是镜像对称的
        }
        return helper(pRoot.left,pRoot.right);
    }

    private static boolean helper(TreeNode left, TreeNode right) {
        if(left==null&&right==null){     //递归截止条件 return true;
            return true;
        }
        if(right==null||left==null){     //有一个为null,镜像怎么可能还对称 return false;
            return false;
        }
        if(left.val!=right.val){         //两个值不一样,镜像之后怎么会相等 return false;
            return false;
        }
        return helper(left.left,right.right)&&helper(left.right,right.left);
    }
}

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

  请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

package com.liuwen.牛客网;

import java.util.ArrayList;
import java.util.Stack;

/**
 * @description:         请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,
 *                       第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
 * @author: Liu Wen
 * @create: 2020-03-22 14:03
 **/
public class Main59 {
    public static void main(String[] args){
        TreeNode pRoot = new TreeNode(5);     //       5
        TreeNode node1 = new TreeNode(3);     //    3     7
        TreeNode node2 = new TreeNode(7);     //   2 4   6 8
        TreeNode node3 = new TreeNode(2);
        TreeNode node4 = new TreeNode(4);
        TreeNode node5 = new TreeNode(6);
        TreeNode node6 = new TreeNode(8);
        pRoot.left=node1;pRoot.right=node2;
        node1.left=node3;node1.right=node4;
        node2.left=node5;node2.right=node6;
        ArrayList<ArrayList<Integer>> res = Print(pRoot);
        System.out.println(res);
    }
    /**
     * @Description:         思路:层序遍历。   两个栈
     * @date 20.3.22 14:09
     */
    public static ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> arrayLists = new ArrayList<>();
        if(pRoot==null){
            return arrayLists;
        }
        Stack<TreeNode> stack1 = new Stack<>();
        Stack<TreeNode> stack2 = new Stack<>();
        stack1.add(pRoot);
        while (!stack1.isEmpty()||!stack2.isEmpty()) {
            if (!stack1.isEmpty()) {
                ArrayList<Integer> arrayList = new ArrayList<>();
                while (!stack1.isEmpty()) {
                    TreeNode node = stack1.pop();
                    arrayList.add(node.val);
                    if (node.left != null) {             //先left再right
                        stack2.push(node.left);
                    }
                    if (node.right != null) {
                        stack2.push(node.right);
                    }
                }
                arrayLists.add(arrayList);
            } else {
                ArrayList<Integer> arrayList = new ArrayList<>();
                while (!stack2.isEmpty()) {
                    TreeNode node = stack2.pop();
                    arrayList.add(node.val);
                    if (node.right != null) {            //先right再left
                        stack1.push(node.right);
                    }
                    if (node.left != null) {
                        stack1.push(node.left);
                    }
                }
                arrayLists.add(arrayList);
            }
        }
        return arrayLists;
    }

    public static class TreeNode {
        int val;
        TreeNode left = null;
        TreeNode right = null;
        public TreeNode(int val) {
            this.val = val;
        }
    }
}

60. 把二叉树打印成多行

  从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

package com.liuwen.牛客网;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

/**
 * @description:    把二叉树打印成多行
 * @author: Liu Wen
 * @create: 2020-03-21 19:35
 **/
public class Main60 {
    public static void main(String[] args){
        TreeNode pRoot = new TreeNode(5);     //       5
        TreeNode node1 = new TreeNode(3);     //    3     7
        TreeNode node2 = new TreeNode(7);     //   2 4   6 8
        TreeNode node3 = new TreeNode(2);
        TreeNode node4 = new TreeNode(4);
        TreeNode node5 = new TreeNode(6);
        TreeNode node6 = new TreeNode(8);
        pRoot.left=node1;pRoot.right=node2;
        node1.left=node3;node1.right=node4;
        node2.left=node5;node2.right=node6;
        ArrayList<ArrayList<Integer>> res = Print(pRoot);
        System.out.println(res);
    }
    /**
     * @Description:     从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
     * @date 20.3.21 19:40
     */
    public static ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> arrayLists = new ArrayList<>();
        if(pRoot==null){
            return arrayLists;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(pRoot);
        ArrayList<Integer> arrayList = new ArrayList<>();
        int count = 0;int nextCount = 0;int cur = 1;
        while (!queue.isEmpty()){
            TreeNode ans = queue.poll();
            count++;
            arrayList.add(ans.val);
            if(ans.left!=null){
                queue.add(ans.left);
                nextCount++;
            }
            if(ans.right!=null){
                queue.add(ans.right);
                nextCount++;
            }
            if(count==cur){
                count=0;
                arrayLists.add(arrayList);
                arrayList = new ArrayList<>();
                cur = nextCount;
                nextCount = 0;
            }
        }
        return arrayLists;
    }
    public static class TreeNode {
        int val;
        TreeNode left = null;
        TreeNode right = null;

        public TreeNode(int val) {
            this.val = val;
        }
    }

}

61. 序列化二叉树

  请实现两个函数,分别用来序列化和反序列化二叉树。
  二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。
  二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

62. 二叉搜索树的第k个结点

  给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。

package com.liuwen.牛客网;

/**
 * @description:     二叉搜索树的第k个结点
 * @author: Liu Wen
 * @create: 2020-03-21 12:26
 **/
public class Main62 {
    public static void main(String[] args){
        //5,3,7,2,4,6,8
        TreeNode pRoot = new TreeNode(5);     //       5
        TreeNode node1 = new TreeNode(3);     //    3     7
        TreeNode node2 = new TreeNode(7);     //   2 4   6 8
        TreeNode node3 = new TreeNode(2);
        TreeNode node4 = new TreeNode(4);
        TreeNode node5 = new TreeNode(6);
        TreeNode node6 = new TreeNode(8);
        pRoot.left=node1;pRoot.right=node2;
        node1.left=node3;node1.right=node4;
        node2.left=node5;node2.right=node6;

//        inOrder(pRoot);   //中序排列测试

        int k = 1;
        TreeNode result = KthNode(pRoot,k);
        System.out.println("第"+k+"小的结点:"+result.val);
    }
    /**
     * @Description:            方法1:中序排列(递归)
     * @date 20.3.21 13:20
     */
    static int index = 0;       //去掉static方可测试通过
    public static TreeNode KthNode(TreeNode root, int k) {
        if(root!=null){
            TreeNode node = KthNode(root.left,k);
            if(node!=null)
                return node;
            index++;
            if(index==k){
                return root;
            }
            node = KthNode(root.right,k);
            if(node!=null)
                return node;
        }
        return null;
    }


    //树节点定义
    public static class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;

        public TreeNode(int val) {
            this.val = val;
        }
    }

    //中序排列测试
    private static void inOrder(TreeNode root) {
        if(root==null){return;}
        inOrder(root.left);
        System.out.print(root.val+"  ");
        inOrder(root.right);
    }

}

63. 数据流中的中位数

  如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

64. 滑动窗口的最大值

  给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

package com.liuwen.牛客网;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;


/**
 * @description:         滑动窗口的最大值:给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。
 * @author: Liu Wen
 * @create: 2020-03-15 10:31
 **/
public class Main64 {
    public static void main(String[] args){
        int[] array = {2,3,4,2,6,2,5,1};
        int size = 3;
        ArrayList<Integer> res = maxInWindows2(array,size);
        System.out.println(res);
    }
    /**
     * @Description:     方法3:用一个双端队列(算法复杂度n)),队列第一个位置保存当前窗口的最大值,当窗口滑动一次
     *                   step1.判断当前最大值是否过期;
     *                   step2.新增加的值从队尾开始比较,把所有比他小的值丢掉
     * @date 20.3.15 10:56
     */
    public static ArrayList<Integer> maxInWindows2(int[] num, int size) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        if (num.length<1||size>num.length||size<1){
            return arrayList;
        }
        Deque<Integer> deque = new ArrayDeque<>();                 //双端队列,用于存num数组的索引
        for (int i = 0; i < num.length; i++) {
            while (!deque.isEmpty()&&num[deque.getLast()]<num[i]){  //保持双端队列的升序
                deque.pollLast();
            }
            deque.addLast(i);                                        //往双端队列里添加索引
            while (!deque.isEmpty()&&deque.getFirst()<=i-size){      //保持双端队列的长度为size
                deque.pollFirst();
            }
            if(i>=size-1){
                arrayList.add(num[deque.getFirst()]);
            }
        }
        return arrayList;
    }
    /**
     * @Description:       方法2:双指针,容易理解(但是算法复杂度和方法1一样,为k*n)
     * @date 20.3.15 13:52
     */
    public static ArrayList<Integer> maxInWindows1(int[] num, int size) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        if (num.length<1||size>num.length||size<1){
            return arrayList;
        }
        int left = 0;int right = size-1;
        while (right<num.length){
            int max = caqulateMax(num,left,right);
            arrayList.add(max);
            left++;right++;
        }
        return arrayList;
    }
    private static int caqulateMax(int[] num, int left, int right) {
        int max = num[left];
        for (int i = left; i <= right ; i++) {
            if(max<num[i]){
                max = num[i];
            }
        }
        return max;
    }
    /**
     * @Description:       方法1:遍历 num.length-size 次数组,每次找出size个长度中的最大值,保存下来
     * @date 20.3.15 10:34
     */
    public static ArrayList<Integer> maxInWindows(int [] num, int size){
        ArrayList<Integer> arrayList = new ArrayList<>();
        if (num.length<1||size>num.length||size<1){
            return arrayList;
        }
        for (int i = 0; i <= num.length - size; i++) {
            int max = num[i];
            for (int j = i; j < i + size; j++) {
                max = Math.max(max,num[j]);
            }
            arrayList.add(max);
        }
        return arrayList;
    }
}

65. 矩阵中的路径

  请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如
a b c e
s f c s 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因字符串
a d e e 的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

package com.liuwen.牛客网;

/**
 * @description:    矩阵中的路径
 * @author: Liu Wen
 * @create: 2020-03-21 11:22
 **/
public class Main65 {
    public static void main(String[] args){
        char[] matrix = {'a','b','c','e','s','f','c','s','a','d','e','e'};
        int rows = 3;
        int cols = 4;
        char[] str = {'b','c','c','e','d'};
        boolean result = hasPath(matrix,rows,cols,str);
        System.out.println(result);
    }
    /**
     * @Description:    回朔法:
     *               0.根据给定数组,初始化一个标志位数组,初始化为false,表示未走过,true表示已经走过,不能走第二次
     *               1.根据行数和列数,遍历数组,先找到一个与str字符串的第一个元素相匹配的矩阵元素,进入judge
     *               2.根据i和j先确定一维数组的位置,因为给定的matrix是一个一维数组
     *               3.确定递归终止条件:越界,当前找到的矩阵值不等于数组对应位置的值,已经走过的,这三类情况,都直接false,说明这条路不通
     *               4.若k,就是待判定的字符串str的索引已经判断到了最后一位,此时说明是匹配成功的
     *               5.下面就是本题的精髓,递归不断地寻找周围四个格子是否符合条件,只要有一个格子符合条件,就继续再找这个符合条件的格子的四周是否存在符合条件的格子,直到k到达末尾或者不满足递归条件就停止。
     *               6.走到这一步,说明本次是不成功的,我们要还原一下标志位数组index处的标志位,进入下一轮的判断。
     * @date 20.3.21 12:12
     */
    public static boolean hasPath(char[] matrix, int rows, int cols, char[] str) {
        boolean[] buer = new boolean[matrix.length];
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                //循环遍历二维数组,找到起点等于str第一个元素的值,再递归判断四周是否有符合条件的----回溯法
                if(helper(matrix,i,j,rows,cols,buer,str,0)){
                    return true;
                }
            }
        }
        return false;
    }
    private static boolean helper(char[] matrix, int i, int j, int rows, int cols, boolean[] buer, char[] str, int k) {
        //先根据i和j计算匹配的第一个元素转为一维数组的位置
        int index = cols*i+j;
        //递归终止条件   ①越界  ②当前找到的矩阵值不等于数组对应位置的值    ③已经走过的
        if(i<0||i>=rows||j<0||j>=cols||matrix[index]!=str[k]||buer[index]==true){
            return false;
        }
        //若k已经到达str末尾了,说明之前的都已经匹配成功了,直接返回true即可
        if(k==str.length-1){
            return true;
        }
        //要走的第一个位置置为true,表示已经走过了
        buer[index] = true;
        //回溯,递归寻找,每次找到了就给k加一,找不到,还原           *****************
        if(helper(matrix,i+1,j,rows,cols,buer,str,k+1)||
           helper(matrix,i-1,j,rows,cols,buer,str,k+1)||
           helper(matrix,i,j+1,rows,cols,buer,str,k+1)||
           helper(matrix,i,j-1,rows,cols,buer,str,k+1)){
            return true;
        }
        //走到这,说明这一条路不通,还原,再试其他的路径
        buer[index]=false;
        return false;
    }
}

66. 机器人的运动范围

  地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

package com.liuwen.牛客网;

/**
 * @description:     机器人的运动范围
 * @author: Liu Wen
 * @create: 2020-03-21 11:05
 **/
public class Main66 {
    public static void main(String[] args){
        int threshold = 4;
        int rows = 4;
        int cols = 4;
        int result = movingCount(threshold,rows,cols);
        System.out.println(result);
    }
    /**
     * @Description: version_3:回溯法
     *                  核心思路:
     *              1.从(0,0)开始走,每成功走一步标记当前位置为true,然后从当前位置往四个方向探索,
     *              返回1 + 4 个方向的探索值之和。
     *              2.探索时,判断当前节点是否可达的标准为:
     *                  1)当前节点在矩阵内;
     *                  2)当前节点未被访问过;
     *                  3)当前节点满足limit限制
     * @date 20.3.21 11:07
     */
    public static int movingCount(int threshold, int rows, int cols) {
        boolean[][] buer = new boolean[rows][cols];
        return helper(threshold,rows,cols,0,0,buer);
    }

    private static int helper(int threshold, int rows, int cols, int r, int c, boolean[][] buer) {
        if(r<0||r>=rows||c<0||c>=cols||bitSum(r)+bitSum(c)>threshold||buer[r][c]){
            return 0;
        }
        buer[r][c]=true;
        return helper(threshold,rows,cols,r-1,c,buer) +
               helper(threshold,rows,cols,r+1,c,buer) +
               helper(threshold,rows,cols,r,c-1,buer) +
               helper(threshold,rows,cols,r,c+1,buer) +
               1;
    }

    private static int bitSum(int num) {       //数位之和
        int count = 0;
        while (num != 0){
            count+=num%10;
            num = num/10;
        }
        return count;
    }
}

67.剪绳子

  给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],…,k[m]。请问k[0]xk[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

package com.liuwen.牛客网;

/**
 * @description:    剪绳子            动态规划题
 * @author: Liu Wen
 * @create: 2020-03-15 12:13
 **/
public class Main67 {
    public static void main(String[] args){
        int target = 4;
        int res = cutRope1(target);
        System.out.println(res);
    }
    /**
     * @Description:       方法2:动态规划求解问题的四个特征:
     *                      ①求一个问题的最优解;
     *                      ②整体的问题的最优解是依赖于各个子问题的最优解;
     *                      ③小问题之间还有相互重叠的更小的子问题;
     *                      ④从上往下分析问题,从下往上求解问题;
     * @date 20.3.15 12:17
     */
    public static int cutRope1(int target) {
        if(target<2){
            return 0;
        }
        if(target==2){
            return 1;
        }
        if(target==3){
            return 2;
        }
         /*
        下面3行是n>=4的情况,跟n<=3不同,4可以分很多段,比如分成1、3,
        这里的3可以不需要再分了,因为3分段最大才2,不分就是3。记录最大的。
         */
        int[] dp  = new int[target+1];
        dp[0]=0;
        dp[1]=1;
        dp[2]=2;
        dp[3]=3;
        int res = 0;
        for (int i = 4; i < target+1; i++) {
            for (int j = 0; j <= i/2; j++) {
                res = Math.max(res,dp[j]*dp[i-j]);
            }
            dp[i] = res;
        }
        return dp[target];
    }

    /**
     * @Description:    方法1:找2和3的个数(一个数被拆分,要么是2,要么是3 ,它们的乘积才最大)
     *           贪心算法:不是对所有问题都能得到整体最优解,关键是贪心策略的选择,
     *        选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
     * @date 20.3.15 12:15
     */
    public static int cutRope(int target) {
        if(target < 2){
            return 0;
        }
        if(target == 2){
            return 1;
        }
        if(target == 3){
            return 2;
        }
        int numOf2 = target%3;
        int numOf3 = target/3;
        int sum = 1;
        if(numOf2==0){
            for (int i = 0; i < numOf3; i++) {
                sum *=3;
            }
        }
        if(numOf2==1){
            for (int i = 0; i < numOf3-1; i++) {
                sum *=3;
            }
            sum*=4;
        }
        if(numOf2==2){
            for (int i = 0; i < numOf3; i++) {
                sum *= 3;
            }
            sum *=2;
        }
        return sum;
    }
}

你可能感兴趣的:(五.数据结构与算法篇)