剑指offer刷题

ArrayList和LinkedList
  1. ArrayList和LinkedList,一个是动态数组的数据结构,一个是链表的数据结构,它们都是对List接口的实现。前者是数组队列;后者是双向链表结构,可以作为堆栈、队列、双端队列
  2. 随机访问操作时,ArrayList O(1)比LinkedList O(n)的效率更高,LinkedList时线性的数据结构
  3. 在进行增加和删除操作时,LinkedList比ArrayList效率高,ArrayList是数组,有可能会对操作点之后所有数据的下标的索引造成影响,需要进行数据的移动
  4. 从利用效率上看,ArrayList自由性较低,在list列表的结尾预留一定容量空间,LinkedList的每一个元素需要消耗空间
  5. LinkedList不支持高效的随机元素访问
    当操作在一列数据的后面添加数据,随机的访问其中的元素是,使用ArrayList更好;
    在一列数据的前面或中间添加或删除数据,按照顺序访问其中元素时,应该使用LinkedList
    深入理解List的toArray()方法和toArray(T[] a)方法
    这两个方法都是将列表List中的元素转导出为数组,不同的是,toArray()方法导出的是Object类型数组,而toArray[T[] a]方法导出的是指定类型的数组。
StringBuilder和StringBuffer

String的值是不可变的,导致每次对String的操作都会生成新的String对象,效率低下,而且浪费有限的内存空间。

StringBuilder和StringBuffer类的对象能够被多次的修改,并且不产生新的未使用对象

StringBuilder类在Java5中被提出,和StringBuffer之间的最大不同在于StringBuilder的方法不是线程安全的(不能同步访问)

StringBuilder比StringBuffer有速度优势,所以多数情况下使用StringBuilder类。在应用程序要求线程安全的情况下,必须使用StringBuffer类

String可以空赋值,其他两种不行

操作少量数据可以使用String

常见的数据结构

数组,栈,链表,队列,树,图,堆,散列表等

数组:在内存中连续存储多个元素的结构,在内存中的分配也是连续的,数组中的元素通过数组的下标进行访问。数组的查询比较方便,但是添加和删除操作比较慢,需要移动其他的元素,固定大小

栈:先进先出,入栈,出栈,栈通常用于递归功能的实现比如斐波那契数列

队列:先进先出,在多线程阻塞队列管理中比较适用

链表:物理存储单元上非连续的、非顺序的存储结果,逻辑顺序通过链表的指针地址实现,包含数据域和指针域。链表可以任意加减元素,添加和删除操作比较快。但是因为包含大量的指针域,占用的空间比较大。查找元素耗时,适合数据量小,频繁增加,删除操作

二叉树:添加,删除元素都很快,在查找方面也有很多的算法优化,二叉树既有链表的好处,也有数组的好处。处理大批量的动态数据。二叉树有很多扩展的数据结构,包括平衡二叉树,红黑树,B+树等。Mysql的数据库索引结构使用的是B+树,HashMap的底层源码中使用红黑树

散列表:哈希表,key value进行访问的数据结构,通过key和value来映射到集合中的一个位置,很快就可以找到集合中对应的元素。Java中有些集合类就是借鉴的哈希原理进行构造。比如HashMap和HashTable等,hash表对于集合的查找元素是非常方便的,因为哈希表是基于数组的,在添加和删除元素比较慢。进一步就是数组结合链表,hashMap使用数组+链表+红黑树

堆:完全二叉树,大根堆和小根堆,一般用来做数组中的排序,称为堆排序

图:节点的又穷集合和边的结合,无向图和有向图。在存储数据上有比较复杂和高效的算法,分别有邻接举证、邻接表、十字链表、邻接多重表、边集数组等存储结构

排序
快排
/**
     * 快速排序
     * 1.随机选择数组的一个元素
     * 2.若当前元素大于基准书,右分区指示器左移一位,当前元素和右分区指示器所指元素交换,索引保持不变
     * 3.若当前元素小于等于基准书是,左分区指示器右移一位,索引右移
     *  1.当前元素大于等于左分区指示器所指元素,当前元素保持不发动,当前元素小于左分区所指元素,交换
     * @param arr
     * @param left
     * @param right
     */
public static void QuikSort(int[] arr, int left, int right) {
        int i=left,j=right;
        //当左边大于右边元素时
        if(i>=j){
            return;
        }
        //元素基准值
        int key=arr[left];
        while(i<j){
            while(arr[j]>=key&&i<j){
                j--;
            }
            //基准位置放j位置的值
            if(i<j) arr[i++]=arr[j];
            while(arr[i]<key&&i<j){
                i++;
            }
            //j位置放置i的值
            if(i<j) arr[j--]=arr[i];
        }
        //i位置放置基准值
        arr[i]=key;
        //左边元素进行递归
        QuikSort(arr,left,i-1);
        //右边元素进行递归
        QuikSort(arr,j+1,right);
    }

时间复杂度:在拆分时,极端情况:基准数左边的元素个数都为0,而右边n-1个。这个时候,就需要拆分n次,每次拆分整理的时间复杂度为n,最坏的时间复杂度为n2。C(n)=(n-1)+(n-2)+…+1=n(n-1)/2。最坏情况下,快速排序的时间复杂度为n2

最好的情况下就是每次拆分都能够从书序的中间拆分,拆分logn次,时间复杂度为nlogn

在随机取基准数的时候,数据是可能会发生变化的,快速排序不是稳定的

冒泡排序
/**
     * 冒泡排序
     * 1.从数组头开始,比较相邻的元素,如果第一个元素比第二个大,就交换他们
     * 2.对每一对相邻匀速做相同的工作
     * 3.重复
     */
    public static void BubbleSort(int[] nums){
        if(nums.length == 0) return;
        for (int i = 0; i < nums.length-1; i++) {
            for (int j = 0; j < nums.length-i-1; j++) {
                if(nums[j]>nums[j+1]){
                    int tmp = nums[j];
                    nums[j] = nums[j+1];
                    nums[j+1] = tmp;
                }

            }
        }
    }

时间复杂度为n^2

该算法在比较两个相同的数时没必要进行交换,是稳定的算法

选择排序
/**
     * 选择排序
     * 1.找到数组中最大或者最小的元素
     * 2.将它和数组的第一个元素交换位置,如果第一个元素就是最小的元素,它就和自己交换
     * 3.在剩下的元素中找到最大或者最小的元素,将它和数组的第二个元素交换位置,往复
     */
    public static void ChooseSort(int[] nums){
        if(nums.length == 0) return;
        for (int i = 0; i < nums.length; i++) {
            int min = i;
            for (int j = i+1; j < nums.length; j++) {
                //找到后面最小的元素
                if(nums[j]<nums[min]){
                    min = j;
                }
            }
            //交换
            int tmp = nums[min];
            nums[min] = nums[i];
            nums[i] = tmp;
        }
    }

时间复杂度 n^2

如果选择的元素比当前的元素小,剩下的元素中有和指定的元素相同的元素,破坏了稳定性。数组5,8,5,2,9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中两个5的相对前后顺序就被破坏了

插入排序
/**
     * 插入排序
     * 1.对于未排序的元素,在已排序序列中从左往右扫描,找到相应位置并插入
     * 2.为了给要插入的元素腾出空间,需要将插入位置之后的已排序元素都向后移动一位
     * @param
     */
    public static void InsertSort(int[] nums){
        if(nums.length == 0) return;
        //待排序的数据,该数据之前的已被排序
        int current;
        for (int i = 0; i < nums.length-1; i++) {
            //已被排序数据的做引
            int index = i;
            current = nums[index+1];
            //将当前元素后移一位
            while(index >= 0 && current < nums[index]){
                nums[index+1] = nums[index];
                index--;
            }
            //插入元素
            nums[index + 1] = current;

        }
    }

时间复杂度:最坏的情况下为n^2 最好的情况下为n

比较的时候发现,两个数相等的话,不会进行移动,前后两个数都是相等的,该算法是稳定的算法

希尔排序
/**
 * 希尔排序
 * 基于插入排序的快速的排序算法
 * 简单插入排序对于大规模乱序数组很慢
 * 希尔排序为了加速简单的改进了插入排序,也称为缩小增量排序
 * 希尔排序时吧待排序数组按一定的数量分组,对每组使用直接插入排序算法排序,然后缩小数量继续分组排序,随着数量的减少,每组包含的元素越来越多,当数量减至1是,整个数组恰被分为一组,排序便完成了,这个不断缩小的数量构成了一个增量序列
 * 4,5,3,7,6,3,2,1
 * 4 3 2 1 6 5 3 7
 * 2 1 3 3 4 5 6 7
 * 1 2 3 3 4 5 6 7
 * @param
 */
public static void ShellSort(int[] nums){
    int len = nums.length;
    if(len<2){
        return;
    }
    //当前待排序数据,该数据之前的已被排序
    int current;
    //增量
    int gap = len / 2;
    while(gap > 0){
        for (int i = gap; i < len; i++) {
            current = nums[i];
            //前面有序序列的索引
            int index = i - gap;
            while(index >= 0 && current < nums[index]){
                nums[index + gap] = nums[index];
                //有序序列的下一个
                index -= gap;
            }
            //插入
            nums[index + gap] = current;
        }
        //index相除取整
        gap = gap / 2;
    }
}

时间复杂度:和增量序列有关{n/2,(n/2)/2,…,1}

稳定性:插入排序时稳定的,但是在不同的插入排序中,相同元素可能在各自的插入排序中移动,5,2,2,1,第一次排序第一个元素5会和第三个元素2交换,第二个元素2会和第四个元素1交换,原序列中两个2的相对前后顺序就被破坏

不稳定

最小的k个数

堆 快速排序

//输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。 
//
// 
//
// 示例 1: 
//
// 输入:arr = [3,2,1], k = 2
//输出:[1,2] 或者 [2,1]
// 
//
// 示例 2: 
//
// 输入:arr = [0,1,2,1], k = 1
//输出:[0] 
//
// 
//
// 限制: 
//
// 
// 0 <= k <= arr.length <= 10000 
// 0 <= arr[i] <= 10000 
// 
// Related Topics 堆 分治算法 
// 👍 242 👎 0

package leetcode.editor.cn;

import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Queue;

//Java:最小的k个数
public class ZuiXiaoDeKgeShuLcof{
    public static void main(String[] args) {
        Solution solution = new ZuiXiaoDeKgeShuLcof().new Solution();
        // TO TEST
        int[] arr={0,0,2,3,2,1,1,2,0,4};
//        Sort(arr,0,arr.length-1);
        arr=solution.getLeastNumbers1(arr,5);
//        solution.Sort(arr,0,arr.length-1);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]+" ");
        }
    }

    //leetcode submit region begin(Prohibit modification and deletion)

    class Solution {
        /**
         * 排序后进行输出,快排
         * @param arr
         * @param k
         * @return
         */
        public int[] getLeastNumbers1(int[] arr, int k) {

            return QuikSearch(arr,k,0,arr.length-1);
        }
        public int[] QuikSearch(int[] arr, int k,int left,int right){
            //每快排一次,找到排序后下标为i的元素
            //k<j下次只需要切分left~j-1的元素
            //k>j 遍历数组j+1~right的元素
            //相等,返回数组
            int j=QuikSort(arr,left,right);
            if(k==j){
                return Arrays.copyOf(arr,k);
            }
            return k>j?QuikSearch(arr,k,j+1,right):QuikSearch(arr,k,left,j-1);
        }

        /**
         * 使用最后一个数作为基准值
         * @param arr
         * @param left
         * @param right
         */
        public int QuikSort(int[] arr,int left, int right){
            if(left>=right){
                return left;
            }
            int i=left,j=right;
            //元素基准值
            int key=arr[right];
            while(i<j){
                while(arr[j]>=key&&i<j){
                    j--;
                }
                //基准位置放j位置的值
                if(i<j) arr[i++]=arr[j];
                while(arr[i]<key&&i<j){
                    i++;
                }
                //j位置放置i的值
                if(i<j) arr[j--]=arr[i];


            }
            //i位置放置基准值
            arr[i]=key;
            return i;
        }
        /**
         * 使用快排会超时
         * @param arr
         * @param left
         * @param right
         */
        public void Sort(int[] arr, int left, int right) {
            int i=left,j=right;
            //当左边大于右边元素时
            if(i>=j){
                return;
            }
            //元素基准值
            int key=arr[left];
            while(i<j){
                while(arr[j]>key&&i<j){
                    j--;
                }
                //基准位置放j位置的值
                arr[i++]=arr[j];
                while(arr[i]<key&&i<j){
                    i++;
                }
                //j位置放置i的值
                arr[j--]=arr[i];
            }
            //i位置放置基准值
            arr[i]=key;
            //左边元素进行递归
            Sort(arr,left,i-1);
            //右边元素进行递归
            Sort(arr,j+1,right);
        }
        // n 1

        /**
         *大根堆(前K小)/小根堆(前K大)
         * 用一个容量为K的大根堆,每次poll出最大的数
         * 1.若目前堆的大小为k,将当前数字放入堆中
         * 2.否则判断当前元素与大根堆对丁元素的大小关系,如果当前元素比大根堆堆顶大,直接跳过
         * 反之,如果当前数字比大根堆堆顶小,先poll掉堆顶,再将该数字放入堆中
         * @param arr
         * @param k
         * @return
         */
        public int[] getLeastNumbers(int[] arr, int k)
        {
            if(k==0||arr.length==0){
                return new int[0];
            }
            //默认是小根堆,大根堆需要重写比较器
            Queue<Integer> pq=new PriorityQueue<>((v1,v2)->v2-v1);
            for (int num:arr) {
                if(pq.size()<k){
                    pq.offer(num);
                }else if(num<pq.peek()){
                    pq.poll();
                    pq.offer(num);
                }
            }
            //返回堆中的元素
            int[] res=new int[pq.size()];
            int idx=0;
            for (int num:pq) {
                res[idx++]=num;
            }
            return res;
        }
//        n nlogn

    }
//leetcode submit region end(Prohibit modification and deletion)

}
归并排序
/**
     * 归并排序
     * 分而治之,按照设定的与之进行分解成多个计算,然后将各个计算结果进行汇总
     * 若将两个有序集合合并成一个有序表,称为2-路归并
     *
     * @param
     */
    public static int[] MergeSort(int[] nums){
        if(nums.length < 2) return nums;
        int mid = nums.length / 2;
        //分成两组
        int[] left = Arrays.copyOfRange(nums,0,mid);
        int[] right = Arrays.copyOfRange(nums,mid,nums.length);
        //递归拆分
        return merge(MergeSort(left),MergeSort(right));
    }
    public static int[] merge(int[] left,int[] right){
        int[] result = new int[left.length+right.length];
        //i表示左边数组的索引,j表示右边
        for (int index = 0,i = 0,j = 0; index < result.length; index++) {
            if(i >= left.length){
                //说明左侧的数据已经全部取完,取右边的数据
                result[index] = right[j++];
            }else if(j >= right.length){
                //说明右侧的数据已经全部取完,取左边的数据
                result[index] = left[i++];
            }else if(left[i] > right[j]){
                //左边大于右边,取右边的
                int a = right[j++];
                result[index] = a;
            }else{
                //右边大于左边,取左边的
                result[index] = left[i++];
            }
        }
        return result;
    }

时间复杂度:总时间=分解时间+子序列排序时间+合并时间,分解时间是一个常数,可以忽略不计 nlogn

稳定性:在合并的时候,如果相等,选择前面的元素到辅助数组,所以归并排序时稳定的

堆排序
/**
 * 堆排序,这里的堆是一种特殊的二叉树,通常叫做二叉堆
 * 是完全二叉树,堆中某个节点的值总是不大于或不小于其父节点的值
 * 二叉堆:
 *  大根堆:每一个根节点都大于等于它的左右孩子节点,叫最大堆
 *  小根堆:每一个根节点都小于等于它的左右孩子节点,叫最小堆
 *  
 * @param args
 */

时间复杂度nlogn

不稳定

计数排序

非比较排序

桶排序

计数排序可以看成每个桶值存储相同元素,桶排序每个桶存储一定范围的元素,通过函数的某种映射关系,将待排序数组中的元素映射到各个对应的桶中,对每个桶中的元素进行排序,最后将非空桶中的元素逐个放入原序列中

  1. 找出数组中的最大值max和最小值min,可以确定数组所在范围min-max

  2. 根据数据范围确定桶的数量

    1. 若桶的数量太少,桶排序失效
    2. 若桶的数量太多,有的桶可能没有,没有数据造成数据浪费

    桶的数量由自己确定,尽量让元素平均分布到每一个桶中 (最大值-最小值)/每个桶所能放置多少个不同数值+1

  3. 确定桶的区间,一般是按照(最大值-最小值)/桶的数量划分,左闭右开

基数排序

按照从右往左的顺序,依次将每一位都当做一次关键字,然后按照该关键字对数组排序,同时每一轮排序都基于上轮排序后的结果,当我们将所有的位排序后,整个数组达到有序状态。基数排序不是基于比较的算法

  • 从高位开始进行排序
  • 从低位开始进行排序
基数排序 桶排序 计数排序

都是稳定的

  • 基数排序:根据每一位的关键字来分配桶
  • 桶排序:存储一定范围的值
  • 基数排序:每个桶只存储一个类型值,但是数量不限
二叉树
二叉树的深度
class Solution {
    /**
     * 递归方法,dfs
     * 左边和右边更深的
     * @param root
     * @return
     */
    public int maxDepth1(TreeNode root) {
        if(root==null) return 0;
        int leftDepth=0,rightDepth=0;
        if(root.left!=null){
            leftDepth=maxDepth1(root.left);
        }
        if(root.right!=null){
            rightDepth=maxDepth1(root.right);
        }
        return leftDepth>rightDepth?leftDepth+1:rightDepth+1;
    }

    /**
     * bfs
     * 一层一层进行拓展
     * @param root
     * @return
     */
    public int maxDepth(TreeNode root){
        if(root==null) return 0;
        LinkedList<TreeNode> que=new LinkedList<>();
        que.offer(root);
        int ans=0;
        while(!que.isEmpty()){
            int size=que.size();
            while(size>0){
                TreeNode tmp=que.poll();
                if(tmp.left!=null){
                    que.offer(tmp.left);
                }
                if(tmp.right!=null){
                    que.offer(tmp.right);
                }
                size--;
            }
            ans++;
        }
        return ans;
    }
}
二叉搜索树
//Java:二叉搜索树的后序遍历序列
public class ErChaSouSuoShuDeHouXuBianLiXuLieLcof{
    public static void main(String[] args) {
        Solution solution = new ErChaSouSuoShuDeHouXuBianLiXuLieLcof().new Solution();
        // TO TEST
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
        /**
         * 后序遍历:左右中
         * 二叉搜索树:左 < 中,右 > 中
         * 递归分治
         *  终止条件:i>=j,此子树节点数量<=1
         *  递推:
         *      1.划分左右子树,遍历数组元素,找到第一个大于根节点的节点[i,m-1][m,j-1]
         *      2.判断是否是二叉搜索树
         *          左子树区间所有节点小于根节点
         *          右子树区间所有节点大于根节点
         *          如果满足这两个条件可判断是否是二叉搜索树
         *       返回值,判断此树是否正确,此树的左子树是否正确,此树的右子树是否正确
         *
         * @param postorder
         * @return
         */
    public boolean verifyPostorder(int[] postorder) {
        return recur(postorder,0,postorder.length-1);
    }

    public boolean recur(int[] postorder, int i, int j) {
        if(i>=j) return true;
        int p=i;
//        for (int k = 0; k < j; k++) {
//            if(postorder[k]>postorder[j]){
//                m=k;
//            }
//        }
        while(postorder[p]<postorder[j]) p++;
        int m=p;
        while(postorder[p]>postorder[j]) p++;
        return p==j&&recur(postorder,i,m-1)&&recur(postorder,m,j-1);
    }
    }
二叉树的镜像
/**
     * BFS层次遍历
     * 栈或者队列都是可以的
     * 传进队列的顺序为右左
     * 再构造二叉树
     * @param root
     * @return
     */
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null) return null;
        LinkedList<TreeNode> que=new LinkedList<>();
        que.offer(root);
        while(!que.isEmpty()){
            TreeNode node=que.poll();
            if(node.right!=null){
                que.offer(node.right);
            }
            if(node.left!=null){
                que.offer(node.left);
            }
            TreeNode tmp=node.right;
            node.right=node.left;
            node.left=tmp;
        }
        return root;
    }
    // n n
    /**
     * 递归
     * 递归的交换左右节点,在root==null时退出
     * @param root
     * @return
     */
    public TreeNode mirrorTree1(TreeNode root){
        if(root==null) return null;
        TreeNode node=mirrorTree(root.left);
        root.left=mirrorTree(root.right);
        root.right=node;
        return root;
    }
对称二叉树
    /**
     * 递归方法,判断是否是对称的二叉树
     * dfs:深度优先遍历
     * left.left=right.right
     * left.right=right.left
     * 跳出循环:left=null,right==null
     * 特殊情况:root==null
     * @param root
     * @return
     */
    public boolean isSymmetric1(TreeNode root) {
//        没有考虑特殊情况
        if(root==null) return true;
        return isS(root.left,root.right);
    }
    public boolean isS(TreeNode left,TreeNode right){
//        三种情况
        if(left==null&&right==null){
            return true;
        }else if(left==null||right==null||left.val!=right.val){
            return false;
        }else{
            return isS(left.left,right.right)&&isS(left.right,right.left);
        }
    }
    // n遍历n个节点 n和树的深度有关,最坏情况下树变成一个链表结构



    /**
     *广度优先遍历
     * 定义两个队列
     * 首先从队列中取出两个节点进行比较
     * 将left的left节点和right的right节点放入队列
     * 将left的right节点和right的left节点放入队列
     * @param root
     * @return
     */
    public boolean isSymmetric(TreeNode root){
        if(root==null) return true;
        if(root.left==null&&root.right==null){
            return true;}
        LinkedList<TreeNode> que=new LinkedList<TreeNode>();
        que.offer(root.left);
        que.offer(root.right);
        while(!que.isEmpty()){
            TreeNode left=que.poll();
            TreeNode right=que.poll();
            if(left==null&&right==null) continue;
            if(left==null||right==null||left.val!=right.val){
                return false;
            }

            que.add(left.left);
            que.add(right.right);

            que.add(left.right);
            que.add(right.left);

        }
        return true;
    }
    //n n
}
二叉搜索树的公共祖先
class Solution {
    /**
     * BFS
     * 一个节点与另一个节点都为同一个节点的公共节点
     * 一个节点为一个节点的父节点
     * 一个节点与另一个节点的最近父节点不相同,需要网上进行比较
     * 左右中
     * (0 (3 5 *4) *2) (7 9 *8) *6
     * 0 *2 3 *4 5 *6 7 *8 9
     * *6 *2 0 *4 3 5 *8 7 9
     * 找出两个节点的祖先
     * 迭代
     * p,q比root值小,在root左侧
     * 比root值大,在root右侧
     * 一个大一个小,返回root
     * @param root
     * @param p
     * @param q
     * @return
     */
    public TreeNode lowestCommonAncestor1(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null) return null;
        LinkedList<TreeNode> que=new LinkedList<>();
        que.offer(root);
        while(!que.isEmpty()){
            TreeNode tmp=que.poll();
            if(tmp.val>p.val&&tmp.val>q.val){
                que.offer(tmp.left);
            }
            else if(tmp.val<p.val&&tmp.val<q.val){
                que.offer(tmp.right);
            }else{
                return tmp;
            }
        }
        return null;
    }
//    n 1
/**
 * dfs
 * p,q在root的右子树中递归root.right
 * p,q在root的左子树中递归root.left
 * 返回root
 * @param root
 * @param p
 * @param q
 * @return
 */
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){
    if(root.val<p.val&&root.val<q.val){
        //递归并返回
        return lowestCommonAncestor(root.right,p,q);
    }
    if(root.val>p.val&&root.val>q.val){
        return lowestCommonAncestor(root.left,p,q);
    }
    return root;
}
//    n n
}```
二叉树的最近公共祖先
/**
     * 找到p,q结点的祖先,比较得到最近的祖先
     * 通过递归对二叉树进行后序遍历,当遇到结点p或q时返回。
     * 从底至顶回溯,当结点p,q在结点root的异侧时,结点root为最近公共祖先,向上返回root
     * 返回值:
     * 1.当left和right同时为空,root的左右子树中都不包含p,q,返回null
     * 2.当left和right同时不为空,p,q分列在root的异侧,因此root为最近公共祖先,返回root
     * 3.当left为空,right不为空;p,q都不在root的左子树中,直接返回right,
     *  1.p,q其中一个在root的右子树中,此时right指向p
     *  2.p,q两节点都在root的右子树中,此时的right指向最近公共祖先结点
     * 4.当left不为空,right为空,与情况3同理
     * @param root
     * @param p
     * @param q
     * @return
     */
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null||root==p||root==q) return root;//树为空时直接返回null,p和q中有等于root的,它们的最近公共祖先即为root
        TreeNode left=lowestCommonAncestor(root.left,p,q);//递归左子节点,返回值记为 leftleft ;
        TreeNode right = lowestCommonAncestor(root.right,p,q);//递归右子节点,返回值记为 rightleft ;
        if(left==null&&right==null) return null;//当left和right同时为空,root的左右子树中都不包含p,q,返回null
        if(left==null) return right;//left为空,right不为空;p,q都不在root的左子树中,直接返回right,
        if(right==null) return left;//right为空,left不为空;p,q都不在root的右子树中,直接返回left,
        return root;// 当left和right同时不为空,p,q分列在root的异侧,因此root为最近公共祖先,返回root


    }
从上到下打印二叉树
/**
     * bfs
     * @param root
     * @return
     */
    public int[] levelOrder(TreeNode root) {
        if(root==null) return new int[0];
        LinkedList<TreeNode> que=new LinkedList<>();
        //add操作,访问元素,LinkedList或ArrayList都可以使用
        ArrayList<Integer> list=new ArrayList<>();
        que.add(root);
        while(!que.isEmpty()){
            TreeNode tmp=que.poll();

            list.add(tmp.val);
            if(tmp.left!=null){
                que.add(tmp.left);
            }
            if(tmp.right!=null){
                que.add(tmp.right);
            }
        }
        int[] num=new int[list.size()];
        for (int i = 0; i < list.size(); i++) {
            num[i]= list.get(i);
        }
        return num;
    }
层次遍历
/**
     * 二叉树的层次遍历
     * 一个队列用来做正常遍历
     * 一个链表用来存储二叉树每一层的节点
     * 每一层遍历完之后,将该层的链表加入双层链表中
     * 核心要点:每一层遍历时,循环的开始为链表的长度,避免因为链表做了改变之后长度变化
     * @param root
     * @return
     */
    public List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode> que=new LinkedList<>();
        //add操作,访问元素,LinkedList或ArrayList都可以使用
        List<List<Integer>> list=new ArrayList<>();
        if(root != null) que.add(root);
        while(!que.isEmpty()){
            //定义一个每一层节点的列表
            List<Integer> tmp=new ArrayList<>();
            //避免队列大小的改变
            for (int i = que.size(); i > 0 ; i--) {
                TreeNode node=que.poll();
                tmp.add(node.val);
                if(node.left != null) que.add(node.left);
                if(node.right != null) que.add(node.right);
            }
            list.add(tmp);
        }
        return list;
    }
}
层次遍历进阶版
/**
     * 第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印
     * 不对:每一层遍历,加入队列时改变顺序,或者遍历时改变顺序
     * 利用双端队列的两端都可以添加元素的特性,设打印列表(双端队列)tmp,规定:奇数层添加至tmp尾部,偶数层添加至tmp头部
     * @param root
     * @return
     */
    public List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode> que=new LinkedList<>();
        //add操作,访问元素,LinkedList或ArrayList都可以使用
        List<List<Integer>> list=new ArrayList<>();
        if(root != null) que.add(root);
        while(!que.isEmpty()){
            //定义一个每一层节点的列表
            LinkedList<Integer> tmp=new LinkedList<>();
            //避免队列大小的改变
            for (int i = que.size(); i > 0 ; i--) {
                TreeNode node=que.poll();
                //二叉树为偶数层或奇数层
                if(list.size()%2 == 0) tmp.addLast(node.val);
                else tmp.addFirst(node.val);
                if(node.left != null) que.add(node.left);
                if(node.right != null) que.add(node.right);
                }
            list.add(tmp);
        }
        return list;
    }
二叉树的前序遍历
//给你二叉树的根节点 root ,返回它节点值的 前序 遍历。 
//
// 
//
// 示例 1:
//
// 
//输入:root = [1,null,2,3]
//输出:[1,2,3]
// 
//
// 示例 2: 
//
// 
//输入:root = []
//输出:[]
// 
//
// 示例 3: 
//
// 
//输入:root = [1]
//输出:[1]
// 
//
// 示例 4: 
//
// 
//输入:root = [1,2]
//输出:[1,2]
// 
//
// 示例 5: 
//
// 
//输入:root = [1,null,2]
//输出:[1,2]
// 
//
// 
//
// 提示: 
//
// 
// 树中节点数目在范围 [0, 100] 内 
// -100 <= Node.val <= 100 
// 
//
// 
//
// 进阶:递归算法很简单,你可以通过迭代算法完成吗? 
// Related Topics 栈 树 深度优先搜索 二叉树 
// 👍 621 👎 0

package leetcode.editor.cn;

import com.sun.source.tree.Tree;

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;

//Java:二叉树的前序遍历
public class BinaryTreePreorderTraversal{
    public static void main(String[] args) {
        Solution solution = new BinaryTreePreorderTraversal().new Solution();
        // TO TEST
    }
        //leetcode submit region begin(Prohibit modification and deletion)
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
//    递归算法,迭代算法
    public List<Integer> preorderTraversal1(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        pre(root,res);
        return res;
    }
    public void pre(TreeNode root,List<Integer> res){
        if(root == null){
            return;
        }
        res.add(root.val);
        pre(root.left,res);
        pre(root.right,res);
    }
    //在递归的时候隐式的维护了一个栈,我们在迭代的时候需要显式的将这个栈模拟出来
    public List<Integer> preorderTraversal(TreeNode root){
        List<Integer> res = new ArrayList<Integer>();
        if(root == null){
            return res;
        }
        Deque<TreeNode> stack = new LinkedList<TreeNode>();
        TreeNode node = root;
        while(!stack.isEmpty() || node != null){
            while(node != null){
                res.add(node.val);
                stack.push(node);
                node = node.left;
            }
            node = stack.pop();
            node = node.right;
        }
        return res;
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
二分查找
统计一个数字在排序数组中出现的次数
//统计一个数字在排序数组中出现的次数。 
//
// 
//
// 示例 1: 
//
// 输入: nums = [5,7,7,8,8,10], target = 8
//输出: 2 
//
// 示例 2: 
//
// 输入: nums = [5,7,7,8,8,10], target = 6
//输出: 0 
//
// 
//
// 限制: 
//
// 0 <= 数组长度 <= 50000 
//
// 
//
// 注意:本题与主站 34 题相同(仅返回值不同):https://leetcode-cn.com/problems/find-first-and-last-
//position-of-element-in-sorted-array/ 
// Related Topics 数组 二分查找 
// 👍 139 👎 0

package leetcode.editor.cn;
//Java:在排序数组中查找数字 I
public class ZaiPaiXuShuZuZhongChaZhaoShuZiLcof{
    public static void main(String[] args) {
        Solution solution = new ZaiPaiXuShuZuZhongChaZhaoShuZiLcof().new Solution();
        // TO TEST
        int[] nums={5,7,7,8,8,10};
        System.out.println(solution.search(nums,8));
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
        /**
         * 二分查找
         * 比中间小就在左边查找,比中间大就在右边查找
         * @param nums
         * @param target
         * @return
         */
    public int search1(int[] nums, int target) {
        int count=0;
        for (int i = 0; i < nums.length; i++) {
            if(nums[i]==target){
                count++;
            }
        }
        return count;
//        二分查找
//        int left=0,right=nums.length;
//        int mid=(left+right)/2;
//        while(nums[mid]!=target){
//            if(nums[mid]>target){
//                right=mid--;
//            }
//            if(nums[mid]<target){
//                left=mid++;
//            }
//            mid=(left+right)/2;
//        }
//        return mid;
    }

        /**
         * 使用二分法分别找到左边界和右边界right-left-1
         */
        public int search(int[] nums, int target){
            //搜索右边界right
            int i=0,j=nums.length-1;
            while(i<=j){
                int m=(i+j)/2;
                if(nums[m]<=target){
                    i=m+1;
                }else{
                    j=m-1;
                }
            }
            int right=i;
            //若数组没有target,提前返回
            if(j>=0&&nums[j]!=target) return 0;
            //搜索左边界right
            i=0;
            j=nums.length-1;
            while(i<=j){
                int m=(i+j)/2;
                if(nums[m]<target) i=m+1;
                else j=m-1;
            }
            int left=j;
            return right-left-1;
        }
        //优化,将二分查找封装,分别二分查找target和target-1的右边界,将两结果相减并返回
    }
//leetcode submit region end(Prohibit modification and deletion)

}
搜索插入位置
//给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 
//
// 请必须使用时间复杂度为 O(log n) 的算法。 
//
// 
//
// 示例 1: 
//
// 
//输入: nums = [1,3,5,6], target = 5
//输出: 2
// 
//
// 示例 2: 
//
// 
//输入: nums = [1,3,5,6], target = 2
//输出: 1
// 
//
// 示例 3: 
//
// 
//输入: nums = [1,3,5,6], target = 7
//输出: 4
// 
//
// 示例 4: 
//
// 
//输入: nums = [1,3,5,6], target = 0
//输出: 0
// 
//
// 示例 5: 
//
// 
//输入: nums = [1], target = 0
//输出: 0
// 
//
// 
//
// 提示: 
//
// 
// 1 <= nums.length <= 104 
// -104 <= nums[i] <= 104 
// nums 为无重复元素的升序排列数组 
// -104 <= target <= 104 
// 
// Related Topics 数组 二分查找 
// 👍 1005 👎 0

package leetcode.editor.cn;
//Java:搜索插入位置
public class SearchInsertPosition{
    public static void main(String[] args) {
        Solution solution = new SearchInsertPosition().new Solution();
        // TO TEST
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public int searchInsert(int[] nums, int target) {
//        给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
        //使用二分查找
        int left=0,right=nums.length-1;
        int mid;
        while(left<=right){
            mid=(right+left)/2;
            if(nums[mid]==target){
                return mid;
            }
            if(nums[mid]>target) right=mid-1;
            else left=mid+1;
        }
        return left;
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
字符串
替换空格
//请实现一个函数,把字符串 s 中的每个空格替换成"%20"。 
//
// 
//
// 示例 1: 
//
// 输入:s = "We are happy."
//输出:"We%20are%20happy." 
//
// 
//
// 限制: 
//
// 0 <= s 的长度 <= 10000 
// 👍 108 👎 0

package leetcode.editor.cn;
//Java:替换空格
public class TiHuanKongGeLcof{
    public static void main(String[] args) {
        Solution solution = new TiHuanKongGeLcof().new Solution();
        // TO TEST
        System.out.println(solution.replaceSpace("hello w s"));
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
//        java中,字符串被设计为不可变的类型,无法直接修改字符串的某一位字符,需要新建一个字符串实现
//        1.初始化一个StringBuilder,记为res
//        2.遍历列表s中的每个字符c:
//            当c为空格时:向res后添加字符串%20
//             当c部位空格是:向res后添加字符c
//        3.将列表res转化为字符串后返回
//        执行耗时:0 ms,击败了100.00% 的Java用户
//        内存消耗:36.2 MB,击败了74.08% 的Java用户
    public String replaceSpace1(String s) {
        StringBuilder res=new StringBuilder();
        for(Character c:s.toCharArray()){
            if(c == ' ') res.append("%20");
            else res.append(c);
        }
        return res.toString();
    }
//    时间复杂度N,空间复杂度N

//        执行耗时:0 ms,击败了100.00% 的Java用户
//        内存消耗:36.4 MB,击败了32.12% 的Java用户
    public String replaceSpace2(String s) {
        return s.replace(" ","%20");
    }
//    N N

//        字符串直接相加
//        执行耗时:7 ms,击败了5.42% 的Java用户
//        内存消耗:38.4 MB,击败了5.01% 的Java用户
    public String replaceSpace(String s){
        String res = "";
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if(c == ' '){
                res+="%20";
            }else{
                res+=c;
            }
        }
        return res;
    }
// N N^2
}
//leetcode submit region end(Prohibit modification and deletion)

}
第一次只出现一次的字符
//在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。 
//
// 示例: 
//
// s = "abaccdeff"
//返回 "b"
//
//s = "" 
//返回 " "
// 
//
// 
//
// 限制: 
//
// 0 <= s 的长度 <= 50000 
// Related Topics 哈希表 
// 👍 103 👎 0

package leetcode.editor.cn;

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

//Java:第一个只出现一次的字符
public class DiYiGeZhiChuXianYiCiDeZiFuLcof{
    public static void main(String[] args) {
        Solution solution = new DiYiGeZhiChuXianYiCiDeZiFuLcof().new Solution();
        // TO TEST
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
        /**
         * hash法,将所有字符进行统计,最后返回
         * 遍历字符串s,使用哈希表统计“各个字符数量是否为1”
         * 字符数量大于1位false,1为true
         * @param s
         * @return
         */
    public char firstUniqChar1(String s) {
        HashMap<Character,Boolean> dic=new HashMap<>();
        char[] sc=s.toCharArray();
        for(char c:sc){
            dic.put(c,!dic.containsKey(c));
        }
        for(char c:sc){
            if(dic.get(c)) return c;
        }
        return ' ';
    }
    // n hashmap查找操作的复杂度为1 1 最多有26个不同的字符,需要占用O(26)=O(1)的额外空间

        /**
         * 有序哈希表
         * 在哈希表的基础上,有序哈希表的键值对是按照插入顺序排序的,可以通过遍历有序哈希表,实现搜索收个“数量为1的字符”
         * 减少了第二轮遍历的循环此树,当字符很长是,方法二效率更高
         * @param s
         * @return
         */
    public char firstUniqChar(String s){
        Map<Character,Boolean> dic=new LinkedHashMap<>();
        char[] sc=s.toCharArray();
        for(char c:sc){
            dic.put(c,!dic.containsKey(c));
        }
        for(Map.Entry<Character,Boolean> d:dic.entrySet()){
            if(d.getValue()) return d.getKey();
        }
        return ' ';
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
左旋转字符串
//字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数
//将返回左旋转两位得到的结果"cdefgab"。 
//
// 
//
// 示例 1: 
//
// 输入: s = "abcdefg", k = 2
//输出: "cdefgab"
// 
//
// 示例 2: 
//
// 输入: s = "lrloseumgh", k = 6
//输出: "umghlrlose"
// 
//
// 
//
// 限制: 
//
// 
// 1 <= k < s.length <= 10000 
// 
// Related Topics 字符串 
// 👍 124 👎 0

//Java:左旋转字符串
public class ZuoXuanZhuanZiFuChuanLcof{
    public static void main(String[] args) {
        Solution solution = new ZuoXuanZhuanZiFuChuanLcof().new Solution();
        // TO TEST
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
        /**
         * 字符串切片
         * 获取字符串s[n:]切片和s[:n]切片,使用“+”运算符拼接并返回
         * 等待结果超时
         * @param s
         * @param n
         * @return
         */
    public String reverseLeftWords1(String s, int n) {
        return s.substring(n,s.length())+s.substring(0,n);
    }
    // n:字符串切片函数为线性时间复杂度 n:两个字符串切片的总长度为N

    /**
     * 1.新建一个StringBuilder,记为res
     * 2.现象res添加“第n+1位至末位的字符”
     * 3.再向res添加首位至第n位的字符
     * 4.将res转化为字符串并返回
     * @param s
     * @param n
     * @return
     */
    public String reverseLeftWords2(String s, int n){
        StringBuilder res=new StringBuilder();
        for (int i = n; i < s.length(); i++) {
            res.append(s.charAt(i));
        }
        for (int i = 0; i < n; i++) {
            res.append(s.charAt(i));
        }
        return res.toString();
    }
    //n:线性遍历s n:新建的辅助res

    //使用求余运算简化代码
    public String reverseLeftWords(String s, int n){
        StringBuilder res=new StringBuilder();
        for (int i = n; i < n+s.length(); i++) {
            res.append(s.charAt(i % s.length()));
        }
        return res.toString();
    }

    /**
     * 若规定java只能用String
     * 使用字符串代替列表
     * @param s
     * @param n
     * @return
     */
    public String reverseLeftWords3(String s, int n){
        String res="";
        for (int i = n; i < s.length(); i++) {
            res+=s.charAt(i);
        }
        for (int i = 0; i < n; i++) {
            res+=s.charAt(i);
        }
        return res;
    }
    // n n

}
翻转单词顺序
//输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. ",
//则输出"student. a am I"。 
//
// 
//
// 示例 1: 
//
// 输入: "the sky is blue"
//输出: "blue is sky the"
// 
//
// 示例 2: 
//
// 输入: "  hello world!  "
//输出: "world! hello"
//解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
// 
//
// 示例 3: 
//
// 输入: "a good   example"
//输出: "example good a"
//解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
// 
//
// 
//
// 说明: 
//
// 
// 无空格字符构成一个单词。 
// 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 
// 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 
// 
//
// 注意:本题与主站 151 题相同:https://leetcode-cn.com/problems/reverse-words-in-a-string/ 
//
//
// 注意:此题对比原题有改动 
// Related Topics 字符串 
// 👍 105 👎 0

//Java:翻转单词顺序
public class FanZhuanDanCiShunXuLcof{
    public static void main(String[] args) {
        Solution solution = new FanZhuanDanCiShunXuLcof().new Solution();
        // TO TEST
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
        /**
         * 双指针
         * 倒序遍历字符串s,记录单词左右索引便捷i,j
         * 没确定一个单词的便捷,将其添加至单词列表res
         * 最终,将单词列表拼接为字符串,并返回
         * @param s
         * @return
         */
    public String reverseWords(String s) {
        s=s.trim();//删除首尾空格
        int j=s.length()-1,i=j;
        StringBuilder res=new StringBuilder();
        while(i>=0){
            while(i>=0&&s.charAt(i) !=' ') i--;//搜索首个空格
            res.append(s.substring(i+1,j+1)+" ");//添加单词
            while(i >= 0&&s.charAt(i) == ' ') i--;//跳过单词间空格
            j=i;//j指向下个单词的尾字符
        }
        return res.toString().trim();//转化为字符串并返回

    }
    //n:线性遍历字符串 n新建的StringBuilder中的字符串总长度
}
//leetcode submit region end(Prohibit modification and deletion)

和为s的连续序列
//输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。 
//
// 序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。 
//
// 
//
// 示例 1: 
//
// 输入:target = 9
//输出:[[2,3,4],[4,5]]
// 
//
// 示例 2: 
//
// 输入:target = 15
//输出:[[1,2,3,4,5],[4,5,6],[7,8]]
// 
//
// 
//
// 限制: 
//
// 
// 1 <= target <= 10^5 
// 
//
// 
// 👍 278 👎 0

package leetcode.editor.cn;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

//Java:和为s的连续正数序列
public class HeWeiSdeLianXuZhengShuXuLieLcof{
    public static void main(String[] args) {
        Solution solution = new HeWeiSdeLianXuZhengShuXuLieLcof().new Solution();
        // TO TEST
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
        /**
         * 方法一:求和公式
         * 方法二:滑动窗口
         * 设置序列的左边界和右边界,每轮判断滑动窗口内元素和与目标值target的大小关系,相等记录结果,大于target移动左边界i,小于target移动右边界j
         * 1.初始化:i=1,右边界j=2,元素和s=3
         * 2.循环:当i>=j时跳出:
         *  当s>target时,向右移动左边界i=i+1,更新元素和s;
         *  当s<target时;向右移动右边界j=j+1,更新元素和
         *  当s=target时,记录连续整数序列,并向右移动左边界i=i+1
         * 3.返回值:返回结果列表res
         * @param target
         * @return
         */
    public int[][] findContinuousSequence(int target) {
        int i=1,j=2,s=3;
        List<int[]> res=new ArrayList<>();
        while(i<j){
            if(s>target){
                //左边向右移动一位,减去移动前的数字
                s-=i;
                i++;
            }else if(s<target){
                //右边向右移动一位,加上移动后的数字
                j++;
                s+=j;
            }else{
                //处理结果
                int[] ans=new int[j-i+1];
                for (int k = i,z=0; k <=j ; k++) {
                    ans[k-i]=k;
                }
                res.add(ans);
                //窗口左边向右移动一位
                s-=i;
                i++;
            }
        }
        return res.toArray(new int[0][]);
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
无重复字符的最长子串
//给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 
//
// 
//
// 示例 1: 
//
// 
//输入: s = "abcabcbb"
//输出: 3 
//解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
// 
//
// 示例 2: 
//
// 
//输入: s = "bbbbb"
//输出: 1
//解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
// 
//
// 示例 3: 
//
// 
//输入: s = "pwwkew"
//输出: 3
//解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
//     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
// 
//
// 示例 4: 
//
// 
//输入: s = ""
//输出: 0
// 
//
// 
//
// 提示: 
//
// 
// 0 <= s.length <= 5 * 104 
// s 由英文字母、数字、符号和空格组成 
// 
// Related Topics 哈希表 字符串 滑动窗口 
// 👍 5734 👎 0

package leetcode.editor.cn;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

//Java:无重复字符的最长子串
public class LongestSubstringWithoutRepeatingCharacters{
    public static void main(String[] args) {
        Solution solution = new LongestSubstringWithoutRepeatingCharacters().new Solution();
        // TO TEST
        System.out.println(solution.lengthOfLongestSubstring("dvdf"));
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
            /**
             * 滑动窗口,使用哈希集合
             * 如果有重复的元素,左指针向右移动,找出每一位字符的最长不重复字符串
             * 如果没有重复的元素,一直将右边的元素加入到哈希集合中
             * @param s
             * @return
             */
    public int lengthOfLongestSubstring1(String s) {
        Set<Character> set=new HashSet<Character>();
        //rk为滑动窗口右边的坐标,ans为窗口的最大长度
        int rk=-1,ans=0;
        for (int i = 0; i < s.length(); i++) {
            //如果包含了重复的字符,除了第一个,其余都要移除滑动窗口收个字符
            if(i!=0){
                set.remove(s.charAt(i-1));
            }
            //滑动窗口最右边的值在范围内,哈希集合中不包含该字符时
            while(rk+1<s.length()&&!set.contains(s.charAt(rk+1))){
                set.add(s.charAt(rk+1));
                rk++;
            }
            if(ans<rk-i+1){
                ans=rk-i+1;
            }
        }
        return ans;
    }

    /**
     * 使用滑动窗口,哈希表,哈希表记录滑动窗口中的元素
     * 如果发现在哈希表中有当前的元素,就从前往后移除元素
     * 如果哈希表中没有当前的元素,就向后一次添加元素
     * @param s
     * @return
     */
    public int lengthOfLongestSubstring(String s){
        //滑动窗口
        char[] ch = s.toCharArray();
        int i=0,n=0,j=ch.length-1,l=0,max=0;
        Map<Character,Boolean> map = new HashMap<Character,Boolean>();
        while(i<=j&&n<ch.length){
            //当哈希表中包含该元素是,移除该元素,更改滑动窗口大小
            if(map.containsKey(ch[n])){
                map.remove(ch[i++]);
                l--;
            }
            //当哈希表中不包含该元素是,向后添加元素
            while(i<=j&&n<ch.length&&!map.containsKey(ch[n]))
            {
                map.put(ch[n++],true);
                l++;
                //更新滑动窗口的最大值
                max= Math.max(max, l);
            }

        }
        return max;
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
最后一个单词的长度
//给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中最后一个单词的长度。 
//
// 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 
//
// 
//
// 示例 1: 
//
// 
//输入:s = "Hello World"
//输出:5
// 
//
// 示例 2: 
//
// 
//输入:s = "   fly me   to   the moon  "
//输出:4
// 
//
// 示例 3: 
//
// 
//输入:s = "luffy is still joyboy"
//输出:6
// 
//
// 
//
// 提示: 
//
// 
// 1 <= s.length <= 104 
// s 仅有英文字母和空格 ' ' 组成 
// s 中至少存在一个单词 
// 
// Related Topics 字符串 
// 👍 339 👎 0

package leetcode.editor.cn;
//Java:最后一个单词的长度
public class LengthOfLastWord{
    public static void main(String[] args) {
        Solution solution = new LengthOfLastWord().new Solution();
        // TO TEST
        System.out.println(solution.lengthOfLastWord("hello world"));
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public int lengthOfLastWord1(String s) {
        //把字符串转换为字符数组
        //存储每个单词的长度,返回最后一个长度
        //使用指针进行求解,首指针,尾指针
        int left=0,right=0;
        char[] ch = s.toCharArray();
        for (int i=0;i<ch.length;) {
            left=i;
            right=i;
            while(i<ch.length&&ch[i]!=' '){
                right++;
                i++;
            }
            while(i<ch.length&&ch[i]==' ') i++;
        }
        return right-left;
    }

            /**
             * 从字符串末尾开始向前遍历,主要有两种情况
             * 1.从后往前遍历直到遍历到头或者遇到空格为止
             * 2.需要将末尾的空格过滤掉,再进行第一种情况的操作。
             * 先从后过滤掉空格找到按此尾部,再从尾部向前遍历,找到单词头部,最后两者相减,即单词的长度
             * @param s
             * @return
             */
    public int lengthOfLastWord(String s){
        int end = s.length()-1;
        while(end >=0 && s.charAt(end) == ' ') end--;
        if(end < 0) return 0;
        int start = end;
        while(start >=0 && s.charAt(start) != ' ') start--;
        return end-start;
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
验证回文串
//给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。 
//
// 说明:本题中,我们将空字符串定义为有效的回文串。 
//
// 
//
// 示例 1: 
//
// 
//输入: "A man, a plan, a canal: Panama"
//输出: true
//解释:"amanaplanacanalpanama" 是回文串
// 
//
// 示例 2: 
//
// 
//输入: "race a car"
//输出: false
//解释:"raceacar" 不是回文串
// 
//
// 
//
// 提示: 
//
// 
// 1 <= s.length <= 2 * 105 
// 字符串 s 由 ASCII 字符组成 
// 
// Related Topics 双指针 字符串 
// 👍 403 👎 0

package leetcode.editor.cn;

import java.util.Locale;

//Java:验证回文串
public class ValidPalindrome{
    public static void main(String[] args) {
        Solution solution = new ValidPalindrome().new Solution();
        // TO TEST
        System.out.println(solution.isPalindrome("A man, a plan, a canal: Panama"));
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public boolean isPalindrome1(String s) {
        //输入: "A man, a plan, a canal: Panama"
        //输出: true
        //解释:"amanaplanacanalpanama" 是回文串
        //将字符串全部转换为小写字符,去掉空格,比较该字符串是否为回文字符串
        //使用双指针进行字符串是否为回文字符串判断
        s = s.toLowerCase();
        char[] ch = s.toCharArray();
        int c=0;
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < ch.length; i++) {
            if((ch[i]>='a'&&ch[i]<='z')||(ch[i]>='0'&&ch[i]<='9')){
//                System.out.println(ch[i]);
                str.append(ch[i]);
            }
        }
//        System.out.println(str);
        ch = str.toString().toCharArray();
        for (int i = 0; i < ch.length/2; i++) {
            if(ch[i]!=ch[ch.length-1-i]){
                return false;
            }
        }
        return true;
    }
    //使用语言中的字符串翻转API得到逆序字符串,只要两个字符串相同,就是回文字符串
    public boolean isPalindrome(String s){
        s = s.toLowerCase();
        char[] ch = s.toCharArray();
        int c=0;
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < ch.length; i++) {
            if((ch[i]>='a'&&ch[i]<='z')||(ch[i]>='0'&&ch[i]<='9')){
//                System.out.println(ch[i]);
                str.append(ch[i]);
            }
        }
        StringBuilder str_rev = new StringBuilder(str).reverse();
        return str.toString().eq  uals(str_rev.toString());
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
最长公共前缀
//编写一个函数来查找字符串数组中的最长公共前缀。 
//
// 如果不存在公共前缀,返回空字符串 ""。 
//
// 
//
// 示例 1: 
//
// 
//输入:strs = ["flower","flow","flight"]
//输出:"fl"
// 
//
// 示例 2: 
//
// 
//输入:strs = ["dog","racecar","car"]
//输出:""
//解释:输入不存在公共前缀。 
//
// 
//
// 提示: 
//
// 
// 0 <= strs.length <= 200 
// 0 <= strs[i].length <= 200 
// strs[i] 仅由小写英文字母组成 
// 
// Related Topics 字符串 
// 👍 1642 👎 0

package leetcode.editor.cn;
//Java:最长公共前缀
public class LongestCommonPrefix{
    public static void main(String[] args) {
        Solution solution = new LongestCommonPrefix().new Solution();
        // TO TEST
    }
        //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /**
         * 最长公共前缀
         * 两个循环,第一个循环,如果字符为空结束
         * 第二个循环,比较每个字符串相应字符,如果都有这个字符,将其加入公共前缀中
         * 使用String存储公共前缀
         * 错误,字符串越界问题
         * @param strs
         * @return
         */
        public String longestCommonPrefix2(String[] strs) {
            StringBuilder st= new StringBuilder();
            int j=0;
            if(strs.length==0) return "";
            while(true){
                if(j>=strs[0].length()){
                    return st.toString();
                }
                char c=strs[0].charAt(j);
                for (int i = 0; i < strs.length; i++) {
                    if(strs[i].charAt(j)!=c||j>=strs[i].length()){
                        return st.toString();
                    }
                }
                st.append(c);
                j++;

            }
        }

            /**
             * 当字符串数组长度为0时啧公共前缀为空,直接返回
             * 零最长公共前缀ans的值为第一个字符串,进行初始化
             * 遍历后面的字符串,依次与ans进行比较,两两找出公共前缀
             * 如果查找过程中出现了ans为空的情况,啧公共前缀不存在直接返回
             * 时间复杂度为s,s是所有字符串的长度之和
             * @param strs
             * @return
             */
        public String longestCommonPrefix1(String[] strs){
            //字符串数组长度为0
            if(strs.length == 0){
                return "";
            }
            //初始化第一个字符串
            String ans = strs[0];
            for (int i = 1; i < strs.length; i++) {
                int j=0;
                for(;j<ans.length()&&j<strs[i].length();j++){
                    if(ans.charAt(j) != strs[i].charAt(j)){
                        break;
                    }
                }
                //减小字符串长度,得到最长公共前缀
                ans=ans.substring(0,j);
                //公共前缀不存在的情况
                if(ans.equals("")) return ans;
            }
            return ans;
        }

            /**
             * 纵向扫描
             * 从前往后遍历所有字符串的每一列,比较相同列上的字符是否相同,如果相同啧继续对下一列进行比较,如果不相同啧当前列不再属于公共前缀,当前列之前的部分为公共前缀
             * @param strs
             * @return
             */
        public String longestCommonPrefix(String[] strs){
            if(strs==null||strs.length==0) return "";
            for (int i = 0; i < strs[0].length(); i++) {
                for (int j = 1; j < strs.length; j++) {
                    if(i==strs[j].length()||strs[0].charAt(i)!=strs[j].charAt(i)){
                        return strs[0].substring(0,i);
                    }
                }
            }
            return strs[0];
        }
        // mn 1

    }
//leetcode submit region end(Prohibit modification and deletion)

}
数组
移动0
//给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 
//
// 示例: 
//
// 输入: [0,1,0,3,12]
//输出: [1,3,12,0,0] 
//
// 说明: 
//
// 
// 必须在原数组上操作,不能拷贝额外的数组。 
// 尽量减少操作次数。 
// 
// Related Topics 数组 双指针 
// 👍 1083 👎 0

package leetcode.editor.cn;
//Java:移动零
public class MoveZeroes{
    public static void main(String[] args) {
        Solution solution = new MoveZeroes().new Solution();
        // TO TEST
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
            /**
             * 将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序
             * 双指针
             * 如果遇到0就将其往后移动,使用两个指针记录位置一个指针移动,另一个指针记录得到0的位置
             * 创建两个指针i和j,第一次遍历的时候指针j用来记录当前有多少个非0元素。遍历时每遇到一个非0元素就将其往数组左边挪,第一次遍历完后,j指针的下标就指向了最后一个非0元素下标
             * 第二次遍历的时候,起始位置就从j开始到结束,将剩下的这段区域的元素全部置为0
             * @param nums
             */
            //这种方法是重新赋值
    public void moveZeroes1(int[] nums) {
        if(nums==null) return;
        //第一次遍历的时候,j指针记录非0的个数,只要是非0统统赋给nums[j]
        int j=0;
        for (int i = 0; i < nums.length; i++) {
            if(nums[i]!=0){
                nums[j++] = nums[i];
            }
        }
        //非0元素统计完了,剩下的都是0了
        //第二次遍历将末尾的元素都赋给0
        for (int i = j; i < nums.length; i++) {
            nums[i] = 0;
        }
    }

            /**
             * 一次遍历,快速排序
             * 将0作为中间点,把不等于0的放在中间点的左边,等于0的放到右边
             * 1 0 0 3 2
             * 1 2 0 3 0 i=1 j=4
             *超时
             *
             * @param nums
             */
            public void moveZeroes2(int[] nums){
                int i=0,j=nums.length-1;
                while(i<=j){
                    while(nums[j]<=0)
                        j--;
                    }
                    nums[i] = nums[j];
                    while(nums[i]>0){
                        i++;
                    }
                    nums[j]=0;
                }

            /**
             * 使用两个指针,实现快速排序
             * @param nums
             */
            //直接交换两个数
            public void moveZeroes(int[] nums){
                if(nums==null){
                    return;
                }
                //两个指针i和j
                int j=0;
                for (int i = 0; i < nums.length; i++) {
                    //当前元素!=0,就把其交换到左边,等于0的交换到右边
                    if(nums[i]!=0){
                        int tmp=nums[i];
                        nums[i]=nums[j];
                        nums[j++]=tmp;
                    }
                }
            }


}
//leetcode submit region end(Prohibit modification and deletion)

}
数组中重复的数字

//Java:数组中重复的数字
public class ShuZuZhongZhongFuDeShuZiLcof{
    public static void main(String[] args) {
        Solution solution = new ShuZuZhongZhongFuDeShuZiLcof().new Solution();
        int[] nums={2, 3, 1, 0, 0, 5, 4};
        System.out.println(solution.findRepeatNumber(nums));
        int[] nums1={2, 3, 1, 0, 4, 5, 6};
        System.out.println(solution.findRepeatNumber(nums1));
        int[] nums2={3, 3, 1, 0, 4, 5, 6};
        System.out.println(solution.findRepeatNumber(nums2));
        // TO TEST
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
//        1.最简单的方法
    public int findRepeatNumber1(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            for (int j = 0; i!=j&&j < nums.length; j++) {
                if(nums[i] == nums[j]){
                    return nums[i];
                }
            }
        }
        return -1;
    }
//    时间复杂度N*N,空间复杂度1

//    2.使用哈希集合
    public int findRepeatNumber2(int[] nums) {
        Set<Integer> dic=new HashSet<>();
        for (int i = 0; i < nums.length; i++) {
            if(dic.contains(nums[i])){
                return nums[i];
            }else{
                dic.add(nums[i]);
            }
        }
        return -1;
    }
//    时间复杂度N,空间复杂度N

//    在一个长度为n的数组中的所有数字都在0-n-1的范围内,数组元素的索引和值是一对多的关系,可以遍历数组并通过交换操作,使元素的索引与值一一对应nums[i]=i,通过索引映射对应的值
//        第一次遇见数字时,将其交换至索引x出,当第二次遇到数字x,一定有nums[x]=x,可以得到一组重复数字
//        算法流程:遍历数组,设置索引初始值i=0;
//                  1.若nums[i]=i,说明数字已在对应索引位置,无序交换,因此跳过
//                  2.nums[nums[i]]=nums[i]:代表索引nums[i]出的索引i处的元素值都为nums[i],找到一组重复值
//                  3.否则交换索引为i和nums[i]的元素值,将此数字交换至索引位置
    public int findRepeatNumber3(int[] nums) {
//        {2, 3, 1, 0, 0, 5, 4}该测试数据有问题
        for (int i = 0; i < nums.length; i++) {
            if(nums[i] == i){
                continue;
            }
            if(nums[nums[i]]==nums[i]){
                return nums[i];
            }
            int tmp = nums[i];
            nums[i]=nums[tmp];
            nums[tmp]=tmp;
        }
        return -1;
    }
        public int findRepeatNumber(int[] nums) {
            int i=0;
            while (i < nums.length) {
                if(nums[i] == i){
//                    只有当前索引处的值等于当前索引时,往下走
                    i++;
                    continue;
                }
//                i索引之前的数应已经按照顺序排列好了
                if(nums[nums[i]]==nums[i]){
                    return nums[i];
                }
//                交换num[i]和i索引处的值 nums[nums[i]]=nums[i]
                int tmp = nums[i];
                nums[i]=nums[tmp];
                nums[tmp]=tmp;
            }
            return -1;
        }
//    时间复杂度N,空间复杂度1
}
//leetcode submit region end(Prohibit modification and deletion)

}
找到数组中消失的数
//给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数
//字,并以数组的形式返回结果。 
//
// 
//
// 示例 1: 
//
// 
//输入:nums = [4,3,2,7,8,2,3,1]
//输出:[5,6]
// 
//
// 示例 2: 
//
// 
//输入:nums = [1,1]
//输出:[2]
// 
//
// 
//
// 提示: 
//
// 
// n == nums.length 
// 1 <= n <= 105 
// 1 <= nums[i] <= n 
// 
//
// 进阶:你能在不使用额外空间且时间复杂度为 O(n) 的情况下解决这个问题吗? 你可以假定返回的数组不算在额外空间内。 
// Related Topics 数组 
// 👍 756 👎 0

package leetcode.editor.cn;

import java.util.LinkedList;
import java.util.List;

//Java:找到所有数组中消失的数字
public class FindAllNumbersDisappearedInAnArray{
    public static void main(String[] args) {
        Solution solution = new FindAllNumbersDisappearedInAnArray().new Solution();
        // TO TEST
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
            /**
             * 把每一个元素都归位,如果有未归位的,将其加入结果数组中
             * [4,3,2,7,8,2,3,1]
             * 7 3 2 4 8 2 3 1
             * 3 3 2 4 8 2 7 1
             * 2 3 3 4 8 2 7 1
             * 3 2 3 4 8 2 7 1
             * return 3
             * 1 2 3 4 8 2 7 1+
             * 1 2 3 4 1 2 7 8
             * return 5
             * 1 2 3 4 5 2 7 8
             * return 2
             * 1 2 3 4 5 2 7 8
             * return 6
             * 1 2 3 4 5 6 7 8
             *
             * @param nums
             * @return
             */
    public List<Integer> findDisappearedNumbers1(int[] nums) {
        int i=1;
        List<Integer> list = new LinkedList<>();
        while(i<=nums.length){
            if(nums[i]==i){
                i++;
                continue;
            }
            if(nums[nums[i]]==nums[i]){
                list.add(i);
                nums[i]=i;
            }
            int tmp=nums[i];
            nums[i]=nums[nums[i]];
            nums[nums[i]]=tmp;
        }
        return list;
    }

            /**
             * 每遇到一个数i,就让nums[i]+nums.length
             * 最后再遍历一次,数组中元素小于等于nums.length为数组中消失的数
             * [4,3,2,7,8,2,3,1]
             * 4 3 2 11 8 2 3 1
             * 4 3 10 11 8 2 3 1
             * 4 11 10 11 8 2 3 1
             * ...
             * 12 19 18 11 8 2 10 9
             * @param nums
             * @return
             */
            public List<Integer> findDisappearedNumbers(int[] nums){
                int length=nums.length;
                for (int i = 0; i < length; i++) {
                    nums[(nums[i]-1)%length]+=length;
                }
                List<Integer> list = new LinkedList<>();
                for (int i = 0; i < length; i++) {
                    if(nums[i]<=length){
                        list.add(i+1);
                    }
                }
                return list;
            }
}
//leetcode submit region end(Prohibit modification and deletion)

}
加一
//给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。 
//
// 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 
//
// 你可以假设除了整数 0 之外,这个整数不会以零开头。 
//
// 
//
// 示例 1: 
//
// 
//输入:digits = [1,2,3]
//输出:[1,2,4]
//解释:输入数组表示数字 123。
// 
//
// 示例 2: 
//
// 
//输入:digits = [4,3,2,1]
//输出:[4,3,2,2]
//解释:输入数组表示数字 4321。
// 
//
// 示例 3: 
//
// 
//输入:digits = [0]
//输出:[1]
// 
//
// 
//
// 提示: 
//
// 
// 1 <= digits.length <= 100 
// 0 <= digits[i] <= 9 
// 
// Related Topics 数组 数学 
// 👍 729 👎 0

package leetcode.editor.cn;
//Java:加一
public class PlusOne{
    public static void main(String[] args) {
        Solution solution = new PlusOne().new Solution();
        // TO TEST
        int[] tmp = solution.plusOne(new int[]{8,9,9,9});
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public int[] plusOne1(int[] digits) {
        //考虑进位
        //当向前进一位的情况  [1,2,9]
        //需要增加数组的一个长度的情况 [9,9,9]
        int length = digits.length;
        int tmp=length-1;
        if(digits[tmp]+1 != 10) {
            digits[tmp]+=1;
            return digits;
        }
        while(digits[tmp]+1 == 10){
            digits[tmp] = 0;
            if(tmp-1>=0){
                if(digits[tmp-1]<9)
                {
                    digits[tmp-1] += 1;
                }else{
                    tmp-=1;
                }
            }else{
                digits = new int[digits.length+1];
                digits[0] = 1;
                return digits;
            }
        }

        return digits;

    }
    public int[] plusOne(int[] digits){
        for(int i=digits.length-1;i >= 0; i--){
            digits[i]++;
            digits[i] = digits[i] % 10;
            if(digits[i] != 0) return digits;
        }
        digits = new int[digits.length + 1];
        digits[0] = 1;
        return digits;
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
合并两个有序数组
//给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。 
//
// 初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nu
//ms2 的元素。 
//
// 
//
// 示例 1: 
//
// 
//输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
//输出:[1,2,2,3,5,6]
// 
//
// 示例 2: 
//
// 
//输入:nums1 = [1], m = 1, nums2 = [], n = 0
//输出:[1]
// 
//
// 
//
// 提示: 
//
// 
// nums1.length == m + n 
// nums2.length == n 
// 0 <= m, n <= 200 
// 1 <= m + n <= 200 
// -109 <= nums1[i], nums2[i] <= 109 
// 
// Related Topics 数组 双指针 排序 
// 👍 1045 👎 0

package leetcode.editor.cn;
//Java:合并两个有序数组
public class MergeSortedArray{
    public static void main(String[] args) {
        Solution solution = new MergeSortedArray().new Solution();
        solution.merge(new int[]{1,2,3,0,0,0},3,new int[]{2,5,6},3);
        // TO TEST
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
            //输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
//输出:[1,2,2,3,5,6]
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int ptr1=m-1,ptr2=n-1,ptr=m+n-1;
        while(ptr1 >= 0 && ptr2 >= 0){
            if(nums1[ptr1]<nums2[ptr2]) {
                nums1[ptr--] = nums2[ptr2--];
            }
            else {
                nums1[ptr--] = nums1[ptr1--];
            }
        }
        //将nums2数组熊下标0位置开始,拷贝到nums1数组中,从下标0位置开始,长度为ptr2+1
        System.arraycopy(nums2,0,nums1,0,ptr2+1);
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
杨辉三角
//给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。 
//
// 在「杨辉三角」中,每个数是它左上方和右上方的数的和。 
//
// 
//
// 
//
// 示例 1: 
//
// 
//输入: numRows = 5
//输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
// 
//
// 示例 2: 
//
// 
//输入: numRows = 1
//输出: [[1]]
// 
//
// 
//
// 提示: 
//
// 
// 1 <= numRows <= 30 
// 
// Related Topics 数组 动态规划 
// 👍 545 👎 0

package leetcode.editor.cn;

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

//Java:杨辉三角
public class PascalsTriangle{
    public static void main(String[] args) {
        Solution solution = new PascalsTriangle().new Solution();
        // TO TEST
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
//        nums[i][j] = nums[i+1][j-1]+nums[i+1][j]
            //输入: numRows = 5
//输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> list = new ArrayList<List<Integer>>();
        for (int i = 0; i < numRows; i++) {
            List<Integer> row = new ArrayList<Integer>();
            for (int j = 0; j <= i; j++) {
                if(j == 0 || j == i){
                    row.add(1);
                }else{
                    row.add(list.get(i-1).get(j-1)+list.get(i-1).get(j));
                }
            }
            list.add(row);
        }
        return list;
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
扑克牌中的顺子
//从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任
//意数字。A 不能视为 14。 
//
// 
//
// 示例 1: 
//
// 输入: [1,2,3,4,5]
//输出: True 
//
// 
//
// 示例 2: 
//
// 输入: [0,0,1,2,5]
//输出: True 
//
// 
//
// 限制: 
//
// 数组长度为 5 
//
// 数组的数取值为 [0, 13] . 
// Related Topics 数组 排序 
// 👍 154 👎 0

package leetcode.editor.cn;

import java.util.HashSet;
import java.util.Set;

//Java:扑克牌中的顺子
public class BuKePaiZhongDeShunZiLcof{
    public static void main(String[] args) {
        Solution solution = new BuKePaiZhongDeShunZiLcof().new Solution();
        // TO TEST
        System.out.println(solution.isStraight(new int[]{0,0,1,2,5}));
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
            /**
             * 大王和小王可以看作时任意牌
             * [0,0,1,2,5]
             * 0 1 2 4 5
             * 1 2 3 4 5
             * 计算大王小王牌的数量,先去掉重复的牌,再观察剩下的最大的数-最小的数 < 5 如果有重复的牌,直接返回false
             * @param nums
             * @return
             */
    public boolean isStraight(int[] nums) {
//        Set<Integer> set = new HashSet<>();
//        int joker = 0;
//        for (int i = 0; i < nums.length; i++) {
//            if(nums[i] == 0) joker++;
//            else set.add(nums[i]);
//        }
//        int max=nums[0],min=nums[0];
//        for (Integer s : set) {
//            if(max<s) max = s;
//            if(min == 0 && s != 0) min=s;
//            if(min > s) min = s;
//        }
//        if(max-min < 5) return true;
//        else return false;

        //大佬牛逼
        Set<Integer> repeat = new HashSet<>();
        int max = 0,min = 14;
        for(int num:nums){
            if(num == 0) continue; //跳过大小王
            max = Math.max(max,num); //最大的牌
            min = Math.min(min,num); //最小的牌
            if(repeat.contains(num)) return false; //如果有重复的牌,提前返回false
            repeat.add(num); //添加此牌到Set
        }
        return max-min < 5; //最大牌-最小牌<5满足条件
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
不重复的三元组

使用三次循环找到三元组,解决不重复的问题:对三元组的内容进行排序然后比对其中内容

全排列

画一个全排列的图

【】-【1】-【1,2】-【1,2,3】

【】-【1】-【1,3】-【1,3,2】

【】-【2】-【2,1】-【2,1,3】

【】-【2】-【2,3】-【2,3,1】

【】-【3】-【3,1】-【3,1,2】

【】-【3】-【3,2】-【3,2,1】

状态跳出:

  1. 递归深度为数组长度时跳出递归
  2. path路径
  3. 标记已使用过的数used数组

使用dfs算法,在回溯时使用移去路径最后一个元素,将标记数组标记元素改为false

//给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 
//
// 
//
// 示例 1: 
//
// 
//输入:nums = [1,2,3]
//输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
// 
//
// 示例 2: 
//
// 
//输入:nums = [0,1]
//输出:[[0,1],[1,0]]
// 
//
// 示例 3: 
//
// 
//输入:nums = [1]
//输出:[[1]]
// 
//
// 
//
// 提示: 
//
// 
// 1 <= nums.length <= 6 
// -10 <= nums[i] <= 10 
// nums 中的所有整数 互不相同 
// 
// Related Topics 数组 回溯 
// 👍 1553 👎 0

package leetcode.editor.cn;

import java.util.*;

//Java:全排列
public class Permutations{
    public static void main(String[] args) {
        Solution solution = new Permutations().new Solution();
        // TO TEST
        List<List<Integer>> list = solution.permute(new int[]{1,2,3});
        System.out.println(list);
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
            /**
             * 定义三个状态变量
             * 1.将结果画为树形,队规需要深度作为跳出条件
             * 2.每次走过的路径进行添加和删除,便于树形结构的回溯
             * 3.元素是否被使用过的布尔数组,和第二点向结合进行dfs的回溯
             * 牛啊
             * 注意在dfs函数中需要进行for循环寻找哪一个元素符合天剑进行元素的添加
             */
        List<List<Integer>> list = new LinkedList<>();
        public List<List<Integer>> permute(int[] nums) {

                //定义走过深度
                int depth = 0;
                //走过的路径,使用队列,这个定义是栈的推荐初始化定义
                Deque<Integer> path = new ArrayDeque<>();
                //定义已经走过的元素的布尔数组
                Boolean[] used = new Boolean[nums.length];
                Arrays.fill(used,false);
                dfs(nums,depth,path,used);
                return list;
            }
            //输入:nums = [1,2,3]
//输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
            private void dfs(int[] nums, int depth, Deque<Integer> path, Boolean[] used) {
                if(depth>nums.length-1){
                    //将队列转换为列表
                    list.add(new LinkedList<>(path));
                    return;
                }
                for (int i = 0; i < nums.length; i++) {
                    //钙元素已经被选择过
                    if(used[i]) continue;
                    //                1 2 3 回溯的时候不添加元素

                    used[i] = true;
                    //不能使用i进行路径元素的添加,因为前一个for循环遍历使为了找到合适的元素
                    //当路径为1 3 2时 选择3时i=2会导致indexoutof
                    //使用队列来存放元素
                    path.addLast(nums[i]);
                    dfs(nums,depth+1,path,used);
                    used[i] = false;
                    path.removeLast();
                }


            }
}
//leetcode submit region end(Prohibit modification and deletion)

}
模拟法
顺时针打印矩阵,
//Java:顺时针打印矩阵
public class ShunShiZhenDaYinJuZhenLcof{
    public static void main(String[] args) {
        Solution solution = new ShunShiZhenDaYinJuZhenLcof().new Solution();
        // TO TEST
        int[][] nums={{2,5,8},{4,0,-1}};
        int[] r=solution.spiralOrder(nums);
        for (int i = 0; i < r.length; i++) {
            System.out.println(r[i]);
        }
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
        /**
         * 1 2 3 4
         * 5 6 7 8
         * 9 10 11 12
         * 从左向右,从上向下,从左向右,从下向上
         * 1.当矩阵为空时,直接返回空列表[]
         * 2.初始化左右上下便捷l,r,t,b
         * 3.循环打印:从左向右,从上向下,从左向右,从下向上,
         *  1.根据便捷打印,将元素按顺序添加至res尾部
         *  2.边界向内收缩1,代表已经被打印
         *  3.判断是否打印完毕(边界是否相遇),若打印完毕啧跳出
         */
    public int[] spiralOrder(int[][] matrix) {
        if(matrix.length==0) return new int[]{};
        int[] nums=new int[matrix.length* matrix[0].length];
        int l=0,r=matrix[0].length-1;
        int t=0,b=matrix.length-1;
        int i=0;
        while(l<=r&&t<=b){
//            从左到右
            if(l<=r){
                for (int j = l; j <= r; j++) {
                    nums[i++]=matrix[t][j];
                }
                t++;
            }

//            从上到下
            if(t<=b){
                for (int j = t; j <= b; j++) {
                    nums[i++]=matrix[j][r];
                }
                r--;
            }
//            从右到左
            if(r>=l&&t<=b){
                for (int j = r; j >= l; j--) {
                    nums[i++]=matrix[b][j];
                }
                b--;
            }

//            从下到上
            if(b>=t&&r>=l){
                for (int j = b; j >= t; j--) {
                    nums[i++]=matrix[j][l];
                }
                l++;
            }
        }
        return nums;
        
    }
}
//leetcode submit region end(Prohibit modification and deletion)
//    2 5 8
//    4 0 -1
}
二维数组查找元素
        /**
         * 线性查找
         * 从数组的右上角开始查找。如果当前元素等于目标值,返回true
         * 当前元素大于目标值,则移到左边一列
         * 当前元素小于目标值,移到下边一行
         * @param matrix
         * @param target
         * @return
         */
        public boolean findNumberIn2DArray(int[][] matrix, int target){
            if(matrix==null||matrix.length==0||matrix[0].length==0) return false;
            int i=0;

            int j= matrix[0].length-1;
            while(i<= (matrix.length-1) && j>=0){
                if(matrix[i][j]>target){
                    j--;
                }else if(matrix[i][j]<target){
                    i++;
                }else{
                    return true;
                }
            }
            return false;
        }
}
//l
动态规划
青蛙跳台阶问题
//一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 
//
// 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。 
//
// 示例 1: 
//
// 输入:n = 2
//输出:2
// 
//
// 示例 2: 
//
// 输入:n = 7
//输出:21
// 
//
// 示例 3: 
//
// 输入:n = 0
//输出:1 
//
// 提示: 
//
// 
// 0 <= n <= 100 
// 
//
// 注意:本题与主站 70 题相同:https://leetcode-cn.com/problems/climbing-stairs/ 
//
// 
// Related Topics 递归 
// 👍 162 👎 0

//0 1 2 3 5

package leetcode.editor.cn;
//Java:青蛙跳台阶问题
public class QingWaTiaoTaiJieWenTiLcof{
    public static void main(String[] args) {
        Solution solution = new QingWaTiaoTaiJieWenTiLcof().new Solution();
        // TO TEST
    }
    //leetcode submit region begin(Prohibit modification and deletion)

    /**
     * 递推问题 f(n)=f(n-1)+f(n-2)
     */
    class Solution {
    public int numWays(int n) {
        int a=1,b=1,sum=0;
        for (int i = 0; i < n; i++) {
            sum=(a+b)%1000000007;
            a=b;
            b=sum;
        }
        return a;

    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
连续子数组的最大和
//Java:连续子数组的最大和
public class LianXuZiShuZuDeZuiDaHeLcof{
    public static void main(String[] args) {
        Solution solution = new LianXuZiShuZuDeZuiDaHeLcof().new Solution();
        // TO TEST
        int[] nums={-2,1,-3,4,-1,2,1,-5,4};
        System.out.println(solution.maxSubArray(nums));
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
        /**
         * 动态规划
         * 使用链表存储数据,存储0,
         * 向前加一个数,如果当前链表之和大于0,保存
         * 当前链表之和比之前小
         * 当前链表之和小于0
         * 当前链表之和比之前大 保存 不对
         * 存储前一个数的最优解,将当前树进行比较判断是否相加,如果大于原本的数,相加,不大于最优解变为原本的数
         * [-2,1,-3,4,-1,2,1,-5,4]
         * -2 1 -2 4 3 5 6 1 4
         * @param nums
         * @return
         */
    public int maxSubArray(int[] nums) {
        int tmp,max;
        tmp=max=nums[0];
        for (int i = 1; i < nums.length; i++) {
            if(tmp+nums[i]>nums[i]){
                tmp=tmp+nums[i];
            }else{
                tmp=nums[i];
            }
            if(tmp>max){
                max=tmp;
            }
        }
        return max;

    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
比特位计数
//给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。 
//
// 示例 1: 
//
// 输入: 2
//输出: [0,1,1] 
//
// 示例 2: 
//
// 输入: 5
//输出: [0,1,1,2,1,2] 
//
// 进阶: 
//
// 
// 给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗? 
// 要求算法的空间复杂度为O(n)。 
// 你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。 
// 
// Related Topics 位运算 动态规划 
// 👍 751 👎 0

package leetcode.editor.cn;
//Java:比特位计数
public class CountingBits{
    public static void main(String[] args) {
        Solution solution = new CountingBits().new Solution();
        // TO TEST
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
            /**
             * 位运算 动态规划
             * 对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
             * 将每个数转换为二进制数,计算其中1的个数
             * 动态规划 没看懂
             *对于任意整数x,零x=x&(x-1),该运算将x的二进制表示的最后一个1变成0.因此,对x重复该操作,直到x变成0,则操作次数即x的1位数
             * @param n
             * @return
             */
    public int[] countBits(int n) {
        int[] bits=new int[n+1];
        for (int i = 0; i <= n; i++) {
            bits[i] = countOnes(i);
        }
        return bits;
    }
    public int countOnes(int x){
        int ones=0;
        while(x>0){
            //将该数的二进制表示的最后一位1变为0
            x &= (x-1);
            //计算1的个数
            ones++;
        }
        return ones;
    }
    // nlogn 1
}
//leetcode submit region end(Prohibit modification and deletion)

}
圆圈中最后剩下的数字

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PzlfZFwU-1632130865675)(D:\project\leetcode\src\笔记.assets\image-20210812205332160.png)]

大佬牛掰class

//0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
// 
//
// 例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。 
//
// 
//
// 示例 1: 
//
// 
//输入: n = 5, m = 3
//输出: 3
// 
//
// 示例 2: 
//
// 
//输入: n = 10, m = 17
//输出: 2
// 
//
// 
//
// 限制: 
//
// 
// 1 <= n <= 10^5 
// 1 <= m <= 10^6 
// 
// Related Topics 递归 数学 
// 👍 419 👎 0

package leetcode.editor.cn;

import java.util.ArrayList;

//Java:圆圈中最后剩下的数字
public class YuanQuanZhongZuiHouShengXiaDeShuZiLcof{
    public static void main(String[] args) {
        Solution solution = new YuanQuanZhongZuiHouShengXiaDeShuZiLcof().new Solution();
        // TO TEST
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
//        0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
//            n = 5, m = 3

            /**
             * c = 0
             * 0 1 2 3 4  c = 3
             * 0 1 3 4  c=6%(list.size()=5)
             * 1 3 4 c =9%4
             * 1 3
             * 3
             * 找不出来了,看k神的
             * @param n
             * @param m
             * @return
             */
    public int lastRemaining1(int n, int m) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        for (int i = 0; i < n; i++) {
            list.add(i);
        }
        int c = 0;
        while(list.size()>1){
            c = c%(list.size()-1);
            if(list.get((c))%m==0){
                list.remove(c);
            }
        }
        return 1;

    }

            /**
             * 模拟整个删除过程,构建一个长度为n的链表,各个节点的值为对应的顺序索引,没轮删除第m个节点,直到链表长度为1是结束,返回最后剩余的节点
             * 但是模拟删除的时间复杂度为mn,太大了
             * 使用动态规划解决
             * 首轮删除环中第m个数字后,得到一个长度为n-1的数字换,删除的数字为(m-1)%n,删除后的数字环从下一个数字开始,t = m%n
             * 数字环 t,t+1,t+2,....,0,1,...,t-3,t-2
             * 设某个数字为x,可以得到递推关系为x->(x+t)%n
             * f(n) = (f(n-1)+t)%n = (f(n-1)+m%n)%n=(f(n-1)+m)%n
             * 动态规划解析:
             *  1.状态定义:设[i,m问题]的解为dp[i]
             *  2.转移方程:通过以下公式可从dp[i-1]递推得到dp[i]
             *      dp[i] = (dp[i-1]+m)%i
             *  3.初始状态:[1,m问题]的解恒为0,及dp[1]=0
             *  4.返回值:返回[n,m问题]的解dp[n]
             *  时间和空间复杂福 n 1
             * @param n
             * @param m
             * @return
             */
    public int lastRemaining(int n, int m){
        int x = 0;
        for (int i = 2; i <= n; i++) {
            x = (x+m)%i;
        }
        return x;
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
链表
链表翻转:判断是否为回文链表
//编写一个函数,检查输入的链表是否是回文的。 
//
// 
//
// 示例 1: 
//
// 输入: 1->2
//输出: false 
// 
//
// 示例 2: 
//
// 输入: 1->2->2->1
//输出: true 
// 
//
// 
//
// 进阶: 
//你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题? 
// Related Topics 链表 
// 👍 65 👎 0

package leetcode.editor.cn;
//Java:回文链表
public class PalindromeLinkedListLcci{
    public static void main(String[] args) {
        Solution solution = new PalindromeLinkedListLcci().new Solution();
        // TO TEST
        int[] nums={1,2, 3, 4, 5, 5, 4 ,3 ,2 ,1};
        ListNode head=new ListNode(nums[0]);
        ListNode tmp=head;
        for (int i = 1; i < nums.length; i++) {
            ListNode node=new ListNode(nums[i]);
            tmp.next=node;
            tmp=tmp.next;
        }
        System.out.println(solution.isPalindrome(head));
    }
    //leetcode submit region begin(Prohibit modification and deletion)
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    /**
     * 将链表中的数放在数组中,判断数组是否是回文的
     * 将链表前半部分进行反转,判断链表的数是否相同
     * 1 2 3 4 5 5 4 3 2 1
     * 5 4 3 2 1 5 4 3 2 1
     * pre cur next
     * pre=null
     * next=cur.next
     * cur.next=pre
     * pre=cur
     * cur=next
     * @param head
     * @return
     */
    public boolean isPalindrome(ListNode head) {
        //对链表前半部分进行反转
        //计算链表长度,不能使用回文链表中间两个数相等作为终止条件,有可能其它地方的两个数会相等
        ListNode tmp=head;
        int length=0;
        while(tmp!=null) {
            length++;
            tmp=tmp.next;
        }
//        pre是反转后的链表的头结点
        ListNode pre=null,cur=head;
        for (int i = 0; i < length/2; i++) {
            ListNode next=cur.next;
            cur.next=pre;
            pre=cur;
            cur=next;
        }
        ListNode head2;
        //链表长度可能为奇数可能为偶数,cur是待反转的结点即为后半部分链表的头结点
        if(length%2!=0) head2=cur.next;
        else head2=cur;
        while(pre!=null&&head2!=null){
            if(pre.val==head2.val){
                pre=pre.next;
                head2=head2.next;
            }else{
                return false;
            }
        }
        //比较完之后,如果某一个链表长度不为0,即链表前半部分和后半部分长度不一样
        return pre == null && head2 == null;

    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
链表相交
//给定两个(单向)链表,判定它们是否相交并返回交点。请注意相交的定义基于节点的引用,而不是基于节点的值。换句话说,如果一个链表的第k个节点与另一个链表的第j个
//节点是同一节点(引用完全相同),则这两个链表相交。 示例 1: 输入:intersectVal = 8, listA = [4,1,8,4,5], listB 
//= [5,0,1,8,4,5], skipA = 2, skipB = 3 输出:Reference of the node with value = 8 输入
//解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4
//,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。 示例 2: 输入:intersectVal = 2, listA = [0
//,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1 输出:Reference of the node with v
//alue = 2 输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为
// [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。 示例 3: 输入:intersectVal = 0, listA
// = [2,6,4], listB = [1,5], skipA = 3, skipB = 2 输出:null 输入解释:从各自的表头开始算起,链表 A 为 [
//2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。 解释:
//这两个链表不相交,因此返回 null。 注意: 如果两个链表没有交点,返回 null 。 在返回结果后,两个链表仍须保持原有的结构。 可假定整个链表结构中没有循
//环。 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。 Related Topics 链表 
// 👍 76 👎 0

package leetcode.editor.cn;
//Java:链表相交
public class IntersectionOfTwoLinkedListsLcci{
    public static void main(String[] args) {
        Solution solution = new IntersectionOfTwoLinkedListsLcci().new Solution();
        // TO TEST
    }
    //leetcode submit region begin(Prohibit modification and deletion)
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    /**
     * 链表相交,如果两个链表相交,尾结点一定相同
     * 可以通过计算相交链表的长度,计算出两个链表长度相同时的节点,再进行比较
     * @param headA
     * @param headB
     * @return
     */
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //某个链表为空的情况
        if(headA==null||headB==null) return null;
        //统计链表长度
        ListNode ptr1=headA,ptr2=headB;
        int sizeA=1,sizeB=1;
        while (ptr1.next!=null){
            ptr1=ptr1.next;
            sizeA++;
        }
        while(ptr2.next!=null){
            ptr2=ptr2.next;
            sizeB++;
        }
        if(ptr1!=ptr2) return null;
        //sizeA==sizeB
        while(sizeA!=sizeB){
            if(sizeA>sizeB){
                headA=headA.next;
                sizeA--;
            }
            if(sizeB>sizeA){
                headB=headB.next;
                sizeB--;
            }
        }
        if(headA!= headB){
            headA= headA.next;
            headB= headB.next;
        }
        return headA;
        
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
从尾到头打印链表
//输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。 
//
// 
//
// 示例 1: 
//
// 输入:head = [1,3,2]
//输出:[2,3,1] 
//
// 
//
// 限制: 
//
// 0 <= 链表长度 <= 10000 
// Related Topics 链表 
// 👍 142 👎 0

package leetcode.editor.cn;

import java.util.LinkedList;
import java.util.Stack;

//Java:从尾到头打印链表
public class CongWeiDaoTouDaYinLianBiaoLcof{
    public static void main(String[] args) {
        Solution solution = new CongWeiDaoTouDaYinLianBiaoLcof().new Solution();
        // TO TEST
        ListNode head = new ListNode(2);
        ListNode p1 = new ListNode(1);
        head.next = p1;
        ListNode p2 = new ListNode(3);
        p1.next = p2;
        int[] numbers=solution.reversePrint(head);
        for (int i = 0; i < numbers.length; i++) {
            System.out.println(numbers[i]);
        }
    }
    public static class ListNode {
        int val;
        ListNode next;
        ListNode(int x) { val = x; }
    }

    //leetcode submit region begin(Prohibit modification and deletion)
 
//   Definition for singly-linked list.

class Solution {

//        将链表反转之后,用数组存储
//    执行耗时:0 ms,击败了100.00% 的Java用户
//    内存消耗:39.2 MB,击败了37.30% 的Java用户
    public int[] reversePrint1(ListNode head) {
        ListNode pre=null;
        int length=0;
        while(head!=null){
            ListNode next = head.next;
            head.next=pre;
            pre=head;
            head=next;
            length++;
        }
        int i=0;
        int[] numbers = new int[length];
        while(pre!=null){
            numbers[i++]=pre.val;
            pre=pre.next;
        }
        return numbers;
    }
//    N N

//    递归法
//    每次传入head.next,以head == null为递归终止条件,这时候返回
//    回溯阶段是,将当前结点值加入列表
//    最终,将列表转化为数组
    LinkedList<Integer> list=new LinkedList<>();
    public int[] reversePrint2(ListNode head){
        reversePrintList(list,head);
        int[] numbers=new int[list.size()];
        for (int i = 0; i < list.size(); i++) {
            numbers[i] = list.get(i);
        }
        return numbers;
    }
    void reversePrintList(LinkedList<Integer> list,ListNode head) {
        if(head != null){
            reversePrintList(list,head.next);
            list.add(head.val);
        }
    }
//    N N

//    辅助栈法
//    入栈:遍历链表,将个节点值push入栈
//    出栈:个节点值pop出栈,存储于数组并返回
    public int[] reversePrint(ListNode head){
        Stack<Integer> st=new Stack<Integer>();
        while(head!=null){
            st.push(head.val);
            head=head.next;
        }
        int[] numbers=new int[st.size()];
        int i=0;
        while(!st.empty()){
            numbers[i]=st.pop();
            i++;
        }
        return numbers;
    }
}



//leetcode submit region end(Prohibit modification and deletion)

}
包含min函数的栈
class MinStack {
        /**
         * 使用两个栈,一个栈存储正常数据,另一个栈存储最小元素
         *  最小元素可以是在每个元素压入时都压入,也可以只压入较小的元素
         *  一种时占据空间,另一种取出时需要进行比较
         */

    /** initialize your data structure here. */
    Stack<Integer> st1;
    Stack<Integer> st2;
    public MinStack() {
        st1=new Stack<>();
        st2=new Stack<>();
    }
    
    public void push(int x) {
        st1.push(x);
        if(st2.empty()||st2.peek()>x){
            st2.push(x);
        }else{
            st2.push(st2.peek());
        }
    }
    
    public void pop() {
        st1.pop();
        st2.pop();
    }
    
    public int top() {
        return st1.peek();
    }
    
    public int min() {
        return st2.peek();
    }
}
其他
二进制中1的个数
public class Solution {
        /**
         *逐位判断
         * 根据与运算定义,舍二进制数字n,若n&1=0,n进制最右以为为0;若n&1=1,则n二进制最右以为为1
         * 循环判断:1.判断n最右以为是否为1,根据结果计数。2.将n右移以为(无符号右移)
         * 算法流程:
         *  1.初始化数量统计变量res=0
         *  2.循环逐位判断:当n=0时跳出
         *      1.res+=n&1:若n&1=1,则统计数res+1
         *      2.n >>> 1:将二进制数字n无符号右移一位
         * @param n
         * @return
         */
    // you need to treat n as an unsigned value
    public int hammingWeight1(int n) {
        int res=0;
        while(n != 0){
            res+=n&1;
            n >>>= 1;
        }
        return res;
    }
    //logn:该算法循环内部仅有移位,与,加等基本运算,占用1,逐位判断需要循环logn次 1:变量res使用常熟大小额外空间

        /**
         * 巧用n&(n-1)
         * n-1:二进制数字n最右边的1变为0,此1右边的0都编程1
         * n&(n-1):二进制数字n最右边的1变成0,其余不变
         * 1.初始化数量统计变量res
         * 2.循环消去最右边的1;当n=0时跳出。
         *  1.res+=1:统计变量+1
         *  2.n&=n-1:消去n最右边的1
         * 返回统计数量res
         * @param n
         * @return
         */
    public int hammingWeight(int n){
        int res=0;
        while(n != 0){
            res++;
            n &= n-1;
        }
        return res;
    }
    //M:n&(n-1)操作仅有减法和与运算,占用1;舍M为二进制n中1的个数,需循环M次,占用M 1:变量res使用常数大小额外空间
}
不用加减乘除做加法
//写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。 
//
// 
//
// 示例: 
//
// 输入: a = 1, b = 1
//输出: 2 
//
// 
//
// 提示: 
//
// 
// a, b 均可能是负数或 0 
// 结果不会溢出 32 位整数 
// 
// Related Topics 位运算 数学 
// 👍 202 👎 0

package leetcode.editor.cn;
//Java:不用加减乘除做加法
public class BuYongJiaJianChengChuZuoJiaFaLcof{
    public static void main(String[] args) {
        Solution solution = new BuYongJiaJianChengChuZuoJiaFaLcof().new Solution();
        // TO TEST
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
            /**
             * 使用位运算实现加法
             * a b 无进位和 进位
             * 0 0 0 0
             * 0 1 1 0
             * 1 0 1 0
             * 1 1 0 1
             * 无进位和与异或运算规律相同,进位和与运算规律相同(并需要左移一位)
             * n=a异或b
             * c = a&b<<1
             * s=a+b=>s=n+c
             * @param a
             * @param b
             * @return
             */
    public int add(int a, int b) {
        while(b != 0){ //当进位为0时跳出
            int c = (a&b)<<1; //c=进位
            a ^= b; //a = 非进位和
            b = c; //b = 进位
        }
        return a;
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
括号匹配深度
package leetcode.editor.cn;

import java.util.Scanner;

/**
 * 括号匹配深度
 * "" "()" "((()))"都是合法的括号序列
 * 1.空串的深度是0
 * 2.字符串x,y的深度是max(x,y)
 * 3.如果"X"的深度是x 字符串"(X)"的深度是x+1
 */
public class KuoHaoPiPeiShenDu {
    /**
     * 输出合法的括号序列 只包含'(' ')'
     * 从第一个字符开始向后遍历,碰到'(',count+1
     * 否则count-1,用max保存,max=Math.max(max,count)
     * max是上次循环保存的最大值
     */
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();
        int cnt = 0,max = 0;
        for (int j = 0; j < s.length(); j++) {
            if(s.charAt(j) == '('){
                cnt++;
            }else{
                cnt--;
            }
            max = Math.max(max,cnt);
        }
        sc.close();
        System.out.println(max);
    }
}

有效的括号
//给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。 
//
// 有效字符串需满足: 
//
// 
// 左括号必须用相同类型的右括号闭合。 
// 左括号必须以正确的顺序闭合。 
// 
//
// 
//
// 示例 1: 
//
// 
//输入:s = "()"
//输出:true
// 
//
// 示例 2: 
//
// 
//输入:s = "()[]{}"
//输出:true
// 
//
// 示例 3: 
//
// 
//输入:s = "(]"
//输出:false
// 
//
// 示例 4: 
//
// 
//输入:s = "([)]"
//输出:false
// 
//
// 示例 5: 
//
// 
//输入:s = "{[]}"
//输出:true 
//
// 
//
// 提示: 
//
// 
// 1 <= s.length <= 104 
// s 仅由括号 '()[]{}' 组成 
// 
// Related Topics 栈 字符串 
// 👍 2649 👎 0

package leetcode.editor.cn;

import java.util.HashMap;
import java.util.Stack;

//Java:有效的括号
public class ValidParentheses {
    public static void main(String[] args) {
        Solution solution = new ValidParentheses().new Solution();
        // TO TEST
        System.out.println(solution.isValid("{[]}"));
    }

    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /**
         * 使用一个栈弹出进行比对
         * {[]}测试用例无法通过
         * 一个栈无法解决,需要使用辅助栈
         *
         * @param s
         * @return
         */
        public boolean isValid1(String s) {
            Stack<Character> st = new Stack<>();
            if (s.equals("")) return true;
            for (int i = 0; i < s.length(); i++) {
                st.push(s.charAt(i));
            }
            while (st.size() >= 2) {
                char mc = st.pop();
                char c = st.peek();
                if (c == '(' && mc == ')' || c == '[' && mc == ']' || c == '{' && mc == '}') {
                    st.pop();
                } else {
                    return false;
                }
            }
            return true;
        }

        /**
         * 栈先入后出,若遇到左括号入栈,遇到右括号时将对应栈顶左括号出栈,遍历完所有括号后stack为空
         * 建立哈希表dic构建左右括号对应关系,查询两个括号对应只需要1时间复杂度
         * 1.如果c是左括号,入栈
         * 2.否则通过哈希表判断括号对应关系,若栈顶出栈括号与当前遍历括号不对应,提前返回false
         *
         * 测试用例:"["不通过,未考虑边界问题
         *
         *边界问题:
         * 1.栈为空:stack,pop会报错,给stack赋初值?:?的对应关系,此时当栈为空且c为右括号时,可以正常提前返回false
         * 2.字符串s以左括号结尾,stack中会遗留未出栈的左括号,最后需要返回len(stack)==1 判断是否是有效的括号组合
         * @param s
         * @return
         */
        public boolean isValid2(String s) {
            HashMap<Character, Character> dic = new HashMap<Character, Character>();
            dic.put(')', '(');
            dic.put(']', '[');
            dic.put('}', '{');
            Stack<Character> st = new Stack<Character>();
            int i = 0;
            st.push(s.charAt(i++));
            while (!st.isEmpty()) {
                if (s.charAt(i) == '(' || s.charAt(i) == '[' || s.charAt(i) == '{') {
                    st.push(s.charAt(i++));
                } else {
                    Character c = st.pop();
                    if (dic.get(s.charAt(i++)) != c || dic.containsKey(c)) {
                        return false;
                    }
                }

            }
            return true;
        }

        /**
         * 栈先入后出,若遇到左括号入栈,遇到右括号时将对应栈顶左括号出栈,遍历完所有括号后stack为空
         * 建立哈希表dic构建左右括号对应关系,查询两个括号对应只需要1时间复杂度
         * 1.如果c是左括号,入栈
         * 2.否则通过哈希表判断括号对应关系,若栈顶出栈括号与当前遍历括号不对应,提前返回false
         *
         *边界问题:
         * 1.栈为空:stack,pop会报错,给stack赋初值?:?的对应关系,此时当栈为空且c为右括号时,可以正常提前返回false
         * 2.字符串s以左括号结尾,stack中会遗留未出栈的左括号,最后需要返回len(stack)==1 判断是否是有效的括号组合
         * @param s
         * @return
         */
        public boolean isValid(String s) {

            HashMap<Character, Character> dic = new HashMap<Character, Character>();
            dic.put(')', '(');
            dic.put(']', '[');
            dic.put('}', '{');
            dic.put('?','?');
            if(s.length()>0&&!dic.containsValue(s.charAt(0))) return false;
            Stack<Character> st = new Stack<Character>();
            int i = 0;
            st.push('?');
            for(Character ch:s.toCharArray()){
                if (ch == '(' || ch == '[' || ch == '{') {
                    st.push(ch);
                } else {
                    Character c = st.pop();
                    if (dic.get(ch) != c ) {
                        return false;
                    }
                }

            }
            return st.size() == 1;
        }
    }
//leetcode submit region end(Prohibit modification and deletion)


}

示例 5:
//
//
//输入:s = “{[]}”
//输出:true
//
//
//
// 提示:
//
//
// 1 <= s.length <= 104
// s 仅由括号 ‘()[]{}’ 组成
//
// Related Topics 栈 字符串
// 👍 2649 👎 0

package leetcode.editor.cn;

import java.util.HashMap;
import java.util.Stack;

//Java:有效的括号
public class ValidParentheses {
public static void main(String[] args) {
Solution solution = new ValidParentheses().new Solution();
// TO TEST
System.out.println(solution.isValid("{[]}"));
}

//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    /**
     * 使用一个栈弹出进行比对
     * {[]}测试用例无法通过
     * 一个栈无法解决,需要使用辅助栈
     *
     * @param s
     * @return
     */
    public boolean isValid1(String s) {
        Stack<Character> st = new Stack<>();
        if (s.equals("")) return true;
        for (int i = 0; i < s.length(); i++) {
            st.push(s.charAt(i));
        }
        while (st.size() >= 2) {
            char mc = st.pop();
            char c = st.peek();
            if (c == '(' && mc == ')' || c == '[' && mc == ']' || c == '{' && mc == '}') {
                st.pop();
            } else {
                return false;
            }
        }
        return true;
    }

    /**
     * 栈先入后出,若遇到左括号入栈,遇到右括号时将对应栈顶左括号出栈,遍历完所有括号后stack为空
     * 建立哈希表dic构建左右括号对应关系,查询两个括号对应只需要1时间复杂度
     * 1.如果c是左括号,入栈
     * 2.否则通过哈希表判断括号对应关系,若栈顶出栈括号与当前遍历括号不对应,提前返回false
     *
     * 测试用例:"["不通过,未考虑边界问题
     *
     *边界问题:
     * 1.栈为空:stack,pop会报错,给stack赋初值?:?的对应关系,此时当栈为空且c为右括号时,可以正常提前返回false
     * 2.字符串s以左括号结尾,stack中会遗留未出栈的左括号,最后需要返回len(stack)==1 判断是否是有效的括号组合
     * @param s
     * @return
     */
    public boolean isValid2(String s) {
        HashMap<Character, Character> dic = new HashMap<Character, Character>();
        dic.put(')', '(');
        dic.put(']', '[');
        dic.put('}', '{');
        Stack<Character> st = new Stack<Character>();
        int i = 0;
        st.push(s.charAt(i++));
        while (!st.isEmpty()) {
            if (s.charAt(i) == '(' || s.charAt(i) == '[' || s.charAt(i) == '{') {
                st.push(s.charAt(i++));
            } else {
                Character c = st.pop();
                if (dic.get(s.charAt(i++)) != c || dic.containsKey(c)) {
                    return false;
                }
            }

        }
        return true;
    }

    /**
     * 栈先入后出,若遇到左括号入栈,遇到右括号时将对应栈顶左括号出栈,遍历完所有括号后stack为空
     * 建立哈希表dic构建左右括号对应关系,查询两个括号对应只需要1时间复杂度
     * 1.如果c是左括号,入栈
     * 2.否则通过哈希表判断括号对应关系,若栈顶出栈括号与当前遍历括号不对应,提前返回false
     *
     *边界问题:
     * 1.栈为空:stack,pop会报错,给stack赋初值?:?的对应关系,此时当栈为空且c为右括号时,可以正常提前返回false
     * 2.字符串s以左括号结尾,stack中会遗留未出栈的左括号,最后需要返回len(stack)==1 判断是否是有效的括号组合
     * @param s
     * @return
     */
    public boolean isValid(String s) {

        HashMap<Character, Character> dic = new HashMap<Character, Character>();
        dic.put(')', '(');
        dic.put(']', '[');
        dic.put('}', '{');
        dic.put('?','?');
        if(s.length()>0&&!dic.containsValue(s.charAt(0))) return false;
        Stack<Character> st = new Stack<Character>();
        int i = 0;
        st.push('?');
        for(Character ch:s.toCharArray()){
            if (ch == '(' || ch == '[' || ch == '{') {
                st.push(ch);
            } else {
                Character c = st.pop();
                if (dic.get(ch) != c ) {
                    return false;
                }
            }

        }
        return st.size() == 1;
    }
}

//leetcode submit region end(Prohibit modification and deletion)

}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值