leetcode刷题笔记续

文章目录

字符串
替换空格
//请实现一个函数,把字符串 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)

}
实现 strStr() 双指针 字符串 字符串匹配
// 实现 strStr() 函数。
//
// 给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如
// 果不存在,则返回 -1 。
//
//
//
// 说明:
//
// 当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
//
// 对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。
//
//
//
// 示例 1:
//
//
// 输入:haystack = "hello", needle = "ll"
// 输出:2
//
//
// 示例 2:
//
//
// 输入:haystack = "aaaaa", needle = "bba"
// 输出:-1
//
//
// 示例 3:
//
//
// 输入:haystack = "", needle = ""
// 输出:0
//
//
//
//
// 提示:
//
//
// 0 <= haystack.length, needle.length <= 5 * 104
// haystack 和 needle 仅由小写英文字符组成
//
// Related Topics 双指针 字符串 字符串匹配
// 👍 973 👎 0

package leetcode.editor.cn;

// Java:实现 strStr()
public class ImplementStrstr {
    public static void main(String[] args) {
        Solution solution = new ImplementStrstr().new Solution();
        // TO TEST
        System.out.println(solution.strStr("hello", "ll"));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /*
         * 在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始) 匹配后返回第一个位置
         */
        public int strStr(String haystack, String needle) {
            int n = haystack.length(), m = needle.length();
            for (int i = 0; i + m <= n; i++) {
                boolean flag = true;
                for (int j = 0; j < m; j++) {
                    if (haystack.charAt(i + j) != needle.charAt(j)) {
                        flag = false;
                        break;
                    }
                }
                if (flag) {
                    return i;
                }
            }
            return -1;
        }
    }
    // 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)

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        String str = in.nextLine();
        char[] ch = str.toCharArray();
        for(int i=ch.length-1;i>=0;i--){
            StringBuffer s = new StringBuffer();
            while(i>=0&&ch[i]!=' '){
                s.append(ch[i--]);
            }
            System.out.print(s.reverse());
            if(i<=0) break;
            System.out.print(" ");
        }
    }
}

和为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;
    }
    
    public int lengthOfLongestSubstring(String s) {
        // write code here
        int i = 0, j = 0, l = 0;
        if (s.length() == 0 || s.equals(" ")) {
            return 0;
        }
        List<Character> set = new LinkedList<Character>();
        for (int z = 0; z < s.length(); z++) {
            while (set.contains(s.charAt(z))) {
                set.remove(0);
            }
            set.add(s.charAt(z));
            if (set.size() > l) {
                l = set.size();
            }
        }
        return l;

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

}
找到字符串中所有字母异位词 滑动窗口
// 给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
//
// 异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
//
//
//
// 示例 1:
//
//
// 输入: s = "cabbage", p = "abc"
// 输出: [0,6]
// 解释:
// 起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
// 起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
//
//
// 示例 2:
//
//
// 输入: s = "abab", p = "ab"
// 输出: [0,1,2]
// 解释:
// 起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
// 起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
// 起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。
//
//
//
//
// 提示:
//
//
// 1 <= s.length, p.length <= 3 * 10⁴
// s 和 p 仅包含小写字母
//
// Related Topics 哈希表 字符串 滑动窗口 👍 767 👎 0

package leetcode.editor.cn;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

// Java:找到字符串中所有字母异位词
public class FindAllAnagramsInAString {
    public static void main(String[] args) {
        Solution solution = new FindAllAnagramsInAString().new Solution();
        // TO TEST
        System.out.println(solution.findAnagrams("abab", "ab"));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /*
         * 设置两个哈希表,一个用来存储目标字符串,进行contains比较,一个用来做滑动窗口,设置两个指针,不行,好麻烦,不好比较,复杂度较高
         */
        public List<Integer> findAnagrams1(String s, String p) {
            HashMap<Character, Integer> pMap = new HashMap<>();
            HashMap<Character, Integer> sMap = new HashMap<>();
            List<Integer> res = new LinkedList<>();
            for (int i = 0; i < p.length(); i++) {
                pMap.put(p.charAt(i), pMap.getOrDefault(p.charAt(i), 0) + 1);
            }
            // 滑动窗口的前后指针
            int left = 0, right = 1;
            for (int i = 1; i < s.length(); i++) {
                if (pMap.containsKey(s.charAt(i)) && pMap.get(s.charAt(i)) != 0) {
                    right++;
                    pMap.put(s.charAt(i), pMap.get(s.charAt(i)) - 1);
                } else {
                    left++;
                    pMap.put(s.charAt(i), pMap.getOrDefault(p.charAt(i), 0) + 1);
                }
                int flag = 1;
                for (int j = 0; j < p.length(); j++) {
                    if (pMap.get(p.charAt(j)) != 0) {
                        flag = 0;
                    }
                }
                if ((right - left + 1) == p.length() && flag == 1) {
                    res.add(left);
                    left++;
                    right = left;
                }
            }
            return res;
        }

        /*
         * 1.使用长度为26的数组记录字母出现的次数
         * 2.记录两个字符串的字母频次
         * 3.如果两个记录的数组相等,第一个异位词索引为0
         * 4.遍历字符串索引[p.len,s.len),在s的字符数组中增加新字母,去除旧字母
         * 5.判断两个数组是否相等,相等,返回索引
         */
        public List<Integer> findAnagrams(String s, String p) {
            LinkedList<Integer> res = new LinkedList<>();
            if (p.length() > s.length()) {
                return res;
            }
            int slen = s.length(), plen = p.length();
            int[] sCnt = new int[26];
            int[] pCnt = new int[26];
            Arrays.fill(sCnt, 0);
            Arrays.fill(pCnt, 0);
            for (int i = 0; i < plen; i++) {
                pCnt[p.charAt(i) - 'a']++;
                sCnt[s.charAt(i) - 'a']++;
            }
            if (Arrays.equals(sCnt, pCnt)) {
                res.add(0);
            }
            for (int i = plen; i < slen; i++) {
                sCnt[s.charAt(i - plen) - 'a']--;
                sCnt[s.charAt(i) - 'a']++;
                if (Arrays.equals(sCnt, pCnt)) {
                    res.add(i - plen + 1);
                }
            }
            return res;
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
最长不含重复字符的子字符串 滑动窗口
// Java:最长不含重复字符的子字符串
public class ZuiChangBuHanZhongFuZiFuDeZiZiFuChuanLcof {
    public static void main(String[] args) {
        Solution solution = new ZuiChangBuHanZhongFuZiFuDeZiZiFuChuanLcof().new Solution();
        // TO TEST
        System.out.println(solution.lengthOfLongestSubstring("aab"));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /**
         * 如果是两次遍历,复杂度为n^2,使用滑动窗口来做,哈希表存储滑动窗口的值的话,就可以把查找的复杂度降为1,定义两个指针,指向滑动窗口的两端
         *
         */
        public int lengthOfLongestSubstring(String s) {
            char[] ch = s.toCharArray();
            HashMap<Character, Integer> hashMap = new HashMap<>();
            int a = 0, b = 0;
            int maxLength = 0;
            for (int i = 0; i < ch.length;) {
                if (hashMap.containsKey(ch[i])) {
                    // 当哈希表中包含了字符的时候,滑动窗口的左边向右移动一位
                    hashMap.remove(ch[a++]);
                } else {
                    b++;
                    // i++要在这里加,否则会导致有重复的元素时,后面的元素无法添加到哈希表中
                    hashMap.put(ch[i++], 1);
                }
                maxLength = Math.max(hashMap.size(), maxLength);
            }
            return maxLength;

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

}
最长无重复子数组
// 最长无重复子数组,子数组
// 使用哈希表存储元素,如果发现有重复的元素,就顺序删除
// 存储数组下标
public static int maxLength(int[] arr) {
    // write code here
    // key存储元素,value存储下标
    HashMap<Integer, Integer> map = new HashMap<>();
    int left = 0, right = 0, maxLength = 0;
    for (int i = 0; i < arr.length; i++) {
        if (map.containsKey(arr[i])) {
            // 挨个移除哈希表中的元素
            while (map.containsKey(arr[i])) {
                map.remove(arr[left++]);
            }
        } else {
            right++;
        }
        map.put(arr[i], i);
        maxLength = Math.max(maxLength, map.size());
    }
    return maxLength;
}
最后一个单词的长度
//给你一个字符串 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().equals(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)

}

将字符串按照字典序进行排序

  1. 调用系统方法进行排序
import java.util.*;
import java.io.*;
public class Main{
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        String[] s = new String[n];
        for(int i=0;i<n;i++){
            s[i]=br.readLine();
        }
        Arrays.sort(s);
        for(int i=0;i<n;i++){
            System.out.println(s[i]);
        }
    }
}
  1. 重写comparator比较接口
import java.util.*;
import java.io.*;
public class Main{
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        List<String> list = new ArrayList<>();
        String s = "";
        while((s=br.readLine()) != null){
            list.add(s);
        }
        br.close();
        list.sort(new Comparator<String>(){
            @Override
            public int compare(String s1,String s2){
                int i=0;
                while(i<s1.length() && i<s2.length()){
                    if(s1.charAt(i)!=s2.charAt(i)){
                        return s1.charAt(i)>s2.charAt(i)?1:-1;
                    }
                    i++;
                }
                if(s1.length() == s2.length()){
                    return 0;
                }else{
                    return (s1.length()>s2.length())?1:-1;
                }
            }
        });
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }
        
    }
}
表示数值的字符串 中等
//请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。 
//
// 数值(按顺序)可以分成以下几个部分: 
//
// 
// 若干空格 
// 一个 小数 或者 整数 
// (可选)一个 'e' 或 'E' ,后面跟着一个 整数 
// 若干空格 
// 
//
// 小数(按顺序)可以分成以下几个部分: 
//
// 
// (可选)一个符号字符('+' 或 '-') 
// 下述格式之一:
// 
// 至少一位数字,后面跟着一个点 '.' 
// 至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字 
// 一个点 '.' ,后面跟着至少一位数字 
// 
// 
// 
//
// 整数(按顺序)可以分成以下几个部分: 
//
// 
// (可选)一个符号字符('+' 或 '-') 
// 至少一位数字 
// 
//
// 部分数值列举如下: 
//
// 
// ["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"] 
// 
//
// 部分非数值列举如下: 
//
// 
// ["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"] 
// 
//
// 
//
// 示例 1: 
//
// 
//输入:s = "0"
//输出:true
// 
//
// 示例 2: 
//
// 
//输入:s = "e"
//输出:false
// 
//
// 示例 3: 
//
// 
//输入:s = "."
//输出:false 
//
// 示例 4: 
//
// 
//输入:s = "    .1  "
//输出:true
// 
//
// 
//
// 提示: 
//
// 
// 1 <= s.length <= 20 
// s 仅含英文字母(大写和小写),数字(0-9),加号 '+' ,减号 '-' ,空格 ' ' 或者点 '.' 。 
// 
// Related Topics 字符串 
// 👍 246 👎 0

package leetcode.editor.cn;
//Java:表示数值的字符串
public class BiaoShiShuZhiDeZiFuChuanLcof{
    public static void main(String[] args) {
        Solution solution = new BiaoShiShuZhiDeZiFuChuanLcof().new Solution();
        // TO TEST
        System.out.println(solution.isNumber("1 "));
//        String s = " sd m  ";
//        System.out.println(":"+s.trim()+":");
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
            /**
             * 直接抄题解看看,无从下手。常规解法
             * 只要有一个天剑不满足就判断为false
             * 1.定义四个flag,是否有字符:hasNum,是否有e:hasE,是否有正负符号:hasSign,是否有点:hasDot
             * 2.使用trim处理字符串前后空格
             * 3.循环遍历字符串
             *  当前字符为数字:hasNum=true,索引向后移动到非数字或遍历到末尾位置,遍历到末尾就结束循环
             *  当前字符是e或者E:如果e已经出现或者当前e之前没有出现数字,返回false;否则hasE=true,将其他三个flag置为false,开始遍历e后面的新数字
             *  当前字符是+或者-:如果已经出现过+或者-或者已经出现过.,返回false;否则令hasSign=true
             *  当前字符是.:如果已经出现过.,或者已经出现过e或者E,返回false;否则令hasDot=true;
             *  当前字符是 :结束循环,可能是末尾的空格,也可能是字符串中间的空格,在循环外继续处理
             *  其他字符返回false
             * 4.如果当前索引到了末尾,还要满足hasNum为true才可以返回true,因为如果字符串全是符号没有数字不行,e后面没有数字也不行,没有符号是可以的
             * 5.如果字符串中间有空格,索引index与字符串长度不相等,返回false
             * @param s
             * @return
             */
            public boolean isNumber(String s) {
                //去掉字符串首尾的空格
                String ss = s.trim();
                char[] ch = ss.toCharArray();
                //索引
                int index=0,n=ss.length();
                //是否有数字,e,正负符号,点 全部置为false
                boolean hasNum=false,hasE=false,hasSign=false,hasDot=false;
                while(index < n) {
                    //判断当前字符是否为数字
                    while(index<n&&Character.isDigit(ch[index])){
                        hasNum=true;
                        index++;
                    }
                    //字符串全是数字的情况
                    if(index==n) break;
                    char c = ch[index];
                    //判断当前字符是否为e或者E
                    if(c=='e'||c=='E'){
                        if(hasE||!hasNum){
                            return false;
                        }else{
                            hasE=true;
                            //其他置为false,防止冲突
                            hasDot=false;
                            hasNum=false;
                            hasSign=false;
                        }
                    }
                    else if(c == '+'||c=='-'){
//                        如果已经出现过+或-或者已经出现过数字或者已经出现过'.',返回flase
                        if(hasSign||hasNum||hasDot){
                            return false;
                        }else{
                            hasSign=true;
                        }
                    }
                    else if(c=='.'){
//                        如果已经出现过'.'或者已经出现过'e'或'E',返回false
                        if(hasDot||hasE){
                            return false;
                        }else{
                            hasDot = true;
                        }
                    }else if(c == ' '){ //当字符串中间有空格时
                        break;
                    }
                    else{
                        //其他字符
                        return false;
                    }
                    index++;
                }
//                如果当前索引index与字符串长度相等,说明遍历到了末尾,但是还要满足hasNum为true才可以最终返回true,因为如果字符串里全是符号没有数字的话是不行的,而且e后面没有数字也是不行的,但是没有符号是可以的
                return hasNum&&index==n;

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

}
字符串的排列 全排列 中等
//输入一个字符串,打印出该字符串中字符的所有排列。 
//
// 
//
// 你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。 
//
// 
//
// 示例: 
//
// 输入:s = "abc"
//输出:["abc","acb","bac","bca","cab","cba"]
// 
//
// 
//
// 限制: 
//
// 1 <= s 的长度 <= 8 
// Related Topics 字符串 回溯 👍 448 👎 0

package leetcode.editor.cn;

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

//Java:字符串的排列
public class ZiFuChuanDePaiLieLcof{
    public static void main(String[] args) {
        Solution solution = new ZiFuChuanDePaiLieLcof().new Solution();
        // TO TEST
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
        /**
         * @description: 全排列,进行回溯
         * 递归深度为字符串长度时跳出递归
         * path路径
         * 标记已经使用过的used数组
         * 使用dfs算法,在回溯时使用移去路径最后一个元素,将标记数组标记元素改为false
         * @param: s
         * @return: java.lang.String[]
         * @author:
         * @create: 2021-11-11
         */
        // 使用set集合,防止重复元素
        HashSet<String> res = new HashSet<String>();
//        LinkedList<String> res = new LinkedList<String>();
    public String[] permutation1(String s) {

        // 定义走过深度
        int depth = 0;
        // 走过的路径
        StringBuilder str = new StringBuilder();
        // 定义已经走过的元素的布尔数组
        Boolean[] used = new Boolean[s.length()];
        // 将走过的元素布尔数组全部置为false
        Arrays.fill(used,false);
        // 深度优先遍历
        dfs(s,depth,str,used);
        String[] result = new String[res.size()];
        int i = 0;
        for (String re : res) {
            result[i++] = re;
        }
        return result;
    }

    private void dfs(String s, int depth, StringBuilder str, Boolean[] used) {
        // 走过的深度大于数组长度时
        if(depth > s.length()-1){
            res.add(str.toString());
            return;
        }
        // 遍历字符串
        for (int i = 0; i < s.length(); i++) {
            // 如果已经访问过该元素
            if(used[i]){
                continue;
            }
            // 访问过的元素置为true
            used[i] = true;
            str.append(s.charAt(i));
            dfs(s, depth+1, str, used);
            // 回溯操作
            used[i] = false;
            str.deleteCharAt(str.length() - 1);
        }
            }

            LinkedList<String> ress = new LinkedList<>();
            char[] c;
            public String[] permutation(String s){
                c = s.toCharArray();
                dfss(0);
                return ress.toArray(new String[ress.size()]);
            }

            private void dfss(int x) {
                if(x == c.length - 1){
                    ress.add(String.valueOf(c)); //添加排列方案
                    return;
                }
                HashSet<Character> set = new HashSet<>();
                // 从位置x开始
                for (int i = x; i < c.length; i++) {
                    if(set.contains(c[i])){ // 重复,因此剪枝
                        continue;
                    }
                    set.add(c[i]);
                    swap(i, x); //交换,将c[i]固定在第x位
                    dfss(x + 1); //开启固定第x+1位字符
                    swap(i, x); //恢复交换
                }
            }
            void swap(int a, int b) {
                char tmp = c[a];
                c[a] = c[b];
                c[b] = tmp;
            }
        }



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

}
电话号码的字母组合 全排列 中等
// 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
//
// 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
//
//
//
//
//
// 示例 1:
//
//
// 输入:digits = "23"
// 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
//
//
// 示例 2:
//
//
// 输入:digits = ""
// 输出:[]
//
//
// 示例 3:
//
//
// 输入:digits = "2"
// 输出:["a","b","c"]
//
//
//
//
// 提示:
//
//
// 0 <= digits.length <= 4
// digits[i] 是范围 ['2', '9'] 的一个数字。
//
// Related Topics 哈希表 字符串 回溯 👍 1680 👎 0

package leetcode.editor.cn;

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

// Java:电话号码的字母组合
public class LetterCombinationsOfAPhoneNumber {
    public static void main(String[] args) {
        Solution solution = new LetterCombinationsOfAPhoneNumber().new Solution();
        // TO TEST
        System.out.println(solution.letterCombinations("23"));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
        // 2:abc 3:def 4:ghi 5:jkl 6:mno 7:pqrs 8:tuv 9:wxyz
        // 输入:digits = "23"
        // 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
        // 使用回溯,维护一个字符串,表示已有的字母排列,将其中的一个字母插入到已有的字母的后面,处理完电话号码中的所有数字,得到一个完整的字母排列,然后进行回退操作,遍历其余的字母排列
        public List<String> letterCombinations(String digits) {
            LinkedList<String> list = new LinkedList<>();
            HashMap<String, String> map = new HashMap<>();
            if (digits.length() == 0) {
                return list;
            }
            map.put("2", "abc");
            map.put("3", "def");
            map.put("4", "ghi");
            map.put("5", "jkl");
            map.put("6", "mno");
            map.put("7", "pqrs");
            map.put("8", "tuv");
            map.put("9", "wxyz");
            StringBuilder str = new StringBuilder();
            int index = 0;
            back(list, map, digits, index, str);
            return list;
        }

        public void back(LinkedList<String> list, HashMap<String, String> map, String digits, int index,
            StringBuilder str) {
            if (index == digits.length()) {
                list.add(str.toString());
                return;
            }
            String s = map.get(digits.charAt(index) + "");
            for (int i = 0; i < s.length(); i++) {
                str.append(s.charAt(i));
                back(list, map, digits, index + 1, str);
                str.deleteCharAt(str.length() - 1);
            }
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
把数字翻译成字符串 动态规划 中等
// 给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可
// 能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
//
//
//
// 示例 1:
//
// 输入: 12258
// 输出: 5
// 解释: 12258有5种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi"和"mzi"
//
//
//
// 提示:
//
//
// 0 <= num < 2³¹
//
// Related Topics 字符串 动态规划 👍 324 👎 0

package leetcode.editor.cn;

// Java:把数字翻译成字符串
public class BaShuZiFanYiChengZiFuChuanLcof {
    public static void main(String[] args) {
        Solution solution = new BaShuZiFanYiChengZiFuChuanLcof().new Solution();
        // TO TEST
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /**
         * @description: 0-25,0-'z';12258有5种不同的翻译,分别是1,2,2,5,8"bccfi", 12,2,5,8"mcfi", 1,22,5,8"bwfi",
         *               1,2,25,8"bczi"和12,25,8"mzi";动态规划。最多两位数,深度优先搜索 ,广度优先搜索
         *               动态规划:f(i)=f(i-2)+f(i-1)若数字x(i-1)x(i)可被翻译;f(i)=f(i-1)x(i-1)x(i)不可被翻译
         *               状态定义:dp[i]代表以xi为结尾的数字的翻译方案数量 转移方程:若x(i)和x(i-1)组成的两位数字可以被翻译,dp[i] =
         *               dp[i-1]+dp[i-2]否则dp[i]=dp[i-1].当x(i-1)=0时,组成的两位数是无法被翻译的,区间为10-25
         *               初始状态:dp[0]=dp[1]=1,即无数字和第一位数字的翻译方法数量都为1 当 numnum 第 1, 21,2 位的组成的数字 \in [10,25]∈[10,25] 时,显然应有
         *               22 种翻译方法,即 dp[2] = dp[1] + dp[0] = 2dp[2]=dp[1]+dp[0]=2 ,而显然 dp[1] = 1dp[1]=1 ,因此推出 dp[0] =
         *               1,dp[0]=1 返回值:dp[n],此数字的翻译方案数量。
         * 
         * @param: num
         * @return: int
         * @author:
         * @create: 2021-11-15
         */
        // 字符串遍历
        public int translateNum1(int num) {
            // 将数字转化为字符串,方便遍历实现动态规划
            String str = String.valueOf(num);
            // 动态规划的前两个数字
            int a = 1, b = 1;
            // 通过字符串切片获取数字组合,对比字符串ASCII吗判断字符串对应数字区间
            for (int i = 2; i <= str.length(); i++) {
                String tmp = str.substring(i - 2, i);
                int c = tmp.compareTo("10") >= 0 && tmp.compareTo("25") <= 0 ? a + b : a;
                b = a;
                a = c;
            }
            return a;

            //
        }

        // 数字求余
        /**
         * @description: 利用求余运算和求整运算,获取数字num的各位数字 通过求余和求整运算实现从右到左的遍历运算 动态规划
         * @param: null
         * @return:
         * @author:
         * @create: 2021-11-15
         */
        public int translateNum(int num) {
            int a = 1, b = 1, x, y = num % 10;
            while (num != 0) {
                num /= 10;
                x = num % 10;
                int tmp = 10 * x + y;
                int c = (tmp >= 10 && tmp <= 25) ? a + b : a;
                b = a;
                a = c;
                y = x;
            }
            return a;
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
正则表达式匹配 字符串 动态规划

*匹配任意字符,.匹配一个字符

有mxn个状态,因此使用动态规划

*的前面任意字符状态置为1,.前面一个字符置为1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j9vMoMlB-1644460788550)(笔记.assets/image-20211120213304675.png)]

从第二行开始每一行进行遍历

// 请实现一个函数用来匹配包含'. '和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配
// 是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但与"aa.a"和"ab*a"均不匹配。
//
// 示例 1:
//
// 输入:
// s = "aa"
// p = "a"
// 输出: false
// 解释: "a" 无法匹配 "aa" 整个字符串。
//
//
// 示例 2:
//
// 输入:
// s = "aa"
// p = "a*"
// 输出: true
// 解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
//
//
// 示例 3:
//
// 输入:
// s = "ab"
// p = ".*"
// 输出: true
// 解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。
//
//
// 示例 4:
//
// 输入:
// s = "aab"
// p = "c*a*b"
// 输出: true
// 解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
//
//
// 示例 5:
//
// 输入:
// s = "mississippi"
// p = "mis*is*p*."
// 输出: false
//
//
// s 可能为空,且只包含从 a-z 的小写字母。
// p 可能为空,且只包含从 a-z 的小写字母以及字符 . 和 *,无连续的 '*'。
//
//
// 注意:本题与主站 10 题相同:https://leetcode-cn.com/problems/regular-expression-matching/
//
// Related Topics 递归 字符串 动态规划 👍 296 👎 0

package leetcode.editor.cn;

// Java:正则表达式匹配
public class ZhengZeBiaoDaShiPiPeiLcof {
    public static void main(String[] args) {
        Solution solution = new ZhengZeBiaoDaShiPiPeiLcof().new Solution();
        // TO TEST
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /**
         * 状态定义:动态规划矩阵dp[i][j],字符串的前i个字符和p的前j个字符 转移方程:dp[0][0]表示空字符串,字符串的前i个字符是s[i-1]和p的前j个字符p[j-1]
         * 当p的第j个字符为*时,dp[i][j]满足任意一种情况时为true 1.dp[i][j-2]:将字符组合p的前一个字符当做没有出现时,能否匹配
         * 2.dp[i-1][j]且s[i-1]=p[j-2]:s的当前字符和p的前一个字符能否匹配; 3.dp[i-1][j且p[j-2]='.':让字符'.'多出现一次时,能否匹配 当p[j-1] !=
         * '*'时,dp[i][j]在以下任意一种情况时为true 1.dp[i-1][j-1]且s[i-1]=p[j-1]:让p的当前字符多出现一次时,能不能匹配上
         * 2.dp[i-1][j-1]且p[j-1]='.':将字符'.'看作s的当前字符时,能不能匹配 初始化:先初始化dp矩阵首行,避免状态转移时的索引越界 dp[0][0]=true:两个空字符串能够匹配
         * dp[0][j]=dp[0][j-2]且p[j-1]='*':字符串s为空字符串时,p的偶数位为*时才能够匹配(让p的奇数位出现0次,保证p是空字符串),循环遍历字符胡灿p,步长为2(只看p的偶数位是否为*)
         * 返回值:dp矩阵右下角字符,代表字符串s和p能否匹配
         */
        public boolean isMatch(String s, String p) {
            int m = s.length() + 1, n = p.length() + 1;
            boolean[][] dp = new boolean[m][n];
            dp[0][0] = true;
            for (int j = 2; j < n; j++) {
                dp[0][j] = dp[0][j - 2] && p.charAt(j - 1) == '*';
            }
            for (int i = 1; i < m; i++) {
                for (int j = 1; j < n; j++) {
                    dp[i][j] = p.charAt(j - 1) == '*'
                        ? dp[i][j - 2] || dp[i - 1][j] && (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.')
                        : dp[i - 1][j - 1] && (p.charAt(j - 1) == '.' || s.charAt(i - 1) == p.charAt(j - 1));
                }
            }
            return dp[m - 1][n - 1];
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
括号生成 dfs 动态规划 回溯 中等
// 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
//
//
//
// 示例 1:
//
//
// 输入:n = 3
// 输出:["((()))","(()())","(())()","()(())","()()()"]
//
//
// 示例 2:
//
//
// 输入:n = 1
// 输出:["()"]
//
//
//
//
// 提示:
//
//
// 1 <= n <= 8
//
// Related Topics 字符串 动态规划 回溯 👍 2289 👎 0

package leetcode.editor.cn;

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

// Java:括号生成
public class GenerateParentheses {
    public static void main(String[] args) {
        Solution solution = new GenerateParentheses().new Solution();
        // TO TEST
        System.out.println(solution.generateParenthesis(3));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 括号组合
        // 动态规划和回溯
        // 输入:n = 3
        // 输出:["((()))","(()())","(())()","()(())","()()()"]

        // 当前左右括号都有大于0个可以使用的时候,才能产生分支
        // 产生左右分支的时候,只看当前是否还有左右括号可以使用
        // 产生右括号的时候,还受到左分支的限制,右边剩余可以使用的括号数量一定得在严格大于左边剩余的数量的时候,才可以产生分支
        // 在左边和右边剩余的括号数都等于0的时候结算
        public List<String> generateParenthesis(int n) {
            LinkedList<String> list = new LinkedList<>();
            if (n == 0) {
                return list;
            }
            int left = n, right = n;
            dfs(list, left, right, "");
            return list;
        }

        private void dfs(LinkedList<String> list, int left, int right, String str) {
            if (left == 0 && right == 0) {
                list.add(str);
                return;
            }
            if (left > right) {
                return;
            }
            if (left > 0) {
                dfs(list, left - 1, right, str + "(");
            }
            if (right > 0) {
                dfs(list, left, right - 1, str + ")");
            }
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
字母异位词分组 哈希表 排序
// 给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
//
// 字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。
//
//
//
// 示例 1:
//
//
// 输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
// 输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
// "abt":"bat"
// ant:nat tan
// aet:"ate","eat","tea"
// 示例 2:
//
//
// 输入: strs = [""]
// 输出: [[""]]
//
//
// 示例 3:
//
//
// 输入: strs = ["a"]
// 输出: [["a"]]
//
//
//
// 提示:
//
//
// 1 <= strs.length <= 10⁴
// 0 <= strs[i].length <= 100
// strs[i] 仅包含小写字母
//
// Related Topics 哈希表 字符串 排序 👍 971 👎 0

package leetcode.editor.cn;

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

// Java:字母异位词分组
public class GroupAnagrams {
    public static void main(String[] args) {
        Solution solution = new GroupAnagrams().new Solution();
        // TO TEST
        List<List<String>> res = solution.groupAnagrams(new String[] {"eat", "tea", "tan", "ate", "nat", "bat"});
        System.out.println(res);

    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
        // 输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
        // "abt":"bat"
        // ant:nat tan
        // aet:"ate","eat","tea"
        /*
         * 哈希表的结构为:string:list
         * 将排序后的字符串作为key存入数组中,如果有相同的key,就获取哈希表的值列表,将新的值添加到列表中
         * 最后返回哈希表的所有值
         */
        public List<List<String>> groupAnagrams(String[] strs) {
            HashMap<String, List<String>> map = new HashMap<>();
            for (String str : strs) {
                char[] ch = str.toCharArray();
                Arrays.sort(ch);
                List<String> list = map.getOrDefault(Arrays.toString(ch), new ArrayList<>());
                list.add(str);
                map.put(Arrays.toString(ch), list);
            }
            return new ArrayList<List<String>>(map.values());
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
单词拆分
// 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。
//
// 注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
//
//
//
// 示例 1:
//
//
// 输入: s = "leetcode", wordDict = ["leet", "code"]
// 输出: true
// 解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。
//
//
// 示例 2:
//
//
// 输入: s = "applepenapple", wordDict = ["apple", "pen"]
// 输出: true
// 解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。
//   注意,你可以重复使用字典中的单词。
//
//
// 示例 3:
//
//
// 输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
// 输出: false
//
//
//
//
// 提示:
//
//
// 1 <= s.length <= 300
// 1 <= wordDict.length <= 1000
// 1 <= wordDict[i].length <= 20
// s 和 wordDict[i] 仅有小写英文字母组成
// wordDict 中的所有字符串 互不相同
//
// Related Topics 字典树 记忆化搜索 哈希表 字符串 动态规划 👍 1348 👎 0

package leetcode.editor.cn;

import java.util.HashSet;
import java.util.List;

// Java:单词拆分
public class WordBreak {
    public static void main(String[] args) {
        Solution solution = new WordBreak().new Solution();
        // 输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
        // TO TEST
        // LinkedList<String> str = new LinkedList<String>(){"cats", "dog", "sand", "and", "cat"};
        // System.out.println(solution.wordBreak("catsandog", str));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 字典树 记忆化搜索 哈希表 字符串 动态规划
        // 输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
        // 输出: false
        // 1.初始化dp为false,长度为n+1,n为字符串长度,dp[i]表示字符串的前i位是否可以用单词表示
        // 2.dp[0]=true,空字符可以被表示
        // 3.遍历字符串的所有子串,遍历开始索引i,比哪里区间[0,n)
        // 遍历结束索引j,遍历区间[i+1,n+1):若dp[i[=true,且s[i,j)在list中:dp[j]=true.解释:dp[i]=true说明s的前i为可以用dict表示,说明s的前j位可以表示
        // 4.返回dp[n]

        /*
         * 枚举s[0,i-1]中的分割点j,看s[0,j-1]组成的字符串s1(默认j=0时s为空串1)和s[j..i-1]组成的字符串s2是否都合法, 如果两个字符串都合法,按照s1和s2拼接的字符串也应该合法
         * 对于字符串s1是否合法可以由dp[j]得知,所以只需要看s2是否合法 dp[i] = dp[j]&&check(s[j,i-1])
         * 简单的剪枝:枚举分割点的时候倒着枚举,如果分割点j到i的长度大于字典列表中最长的单词的长度,结束枚举 边界条件:dp[0] = true
         */
        public boolean wordBreak(String s, List<String> wordDict) {
            HashSet<String> set = new HashSet<>();
            int maxLength = 0;
            for (String str : wordDict) {
                set.add(str);
                maxLength = Math.max(maxLength, str.length());
            }
            boolean[] dp = new boolean[s.length() + 1];
            dp[0] = true;
            for (int i = 1; i <= s.length(); i++) {
                for (int j = i; j >= 0 && i - j <= maxLength; j--) {
                    if (dp[j] && set.contains(s.substring(j, i))) {
                        dp[i] = true;
                        break;
                    }
                }
            }
            return dp[s.length()];
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
实现 Trie (前缀树)
// Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼
// 写检查。
//
// 请你实现 Trie 类:
//
//
// Trie() 初始化前缀树对象。
// void insert(String word) 向前缀树中插入字符串 word 。
// boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回
// false 。
// boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否
// 则,返回 false 。
//
//
//
//
// 示例:
//
//
// 输入
// ["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
// [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
// 输出
// [null, null, true, false, true, null, true]
//
// 解释
// Trie trie = new Trie();
// trie.insert("apple");
// trie.search("apple"); // 返回 True
// trie.search("app"); // 返回 False
// trie.startsWith("app"); // 返回 True
// trie.insert("app");
// trie.search("app"); // 返回 True
//
//
//
//
// 提示:
//
//
// 1 <= word.length, prefix.length <= 2000
// word 和 prefix 仅由小写英文字母组成
// insert、search 和 startsWith 调用次数 总计 不超过 3 * 10⁴ 次
//
// Related Topics 设计 字典树 哈希表 字符串 👍 1035 👎 0

package leetcode.editor.cn;

// Java:实现 Trie (前缀树)
public class ImplementTriePrefixTree {
    public static void main(String[] args) {
        Solution solution = new ImplementTriePrefixTree().new Solution();
        // TO TEST
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Trie {
        //
        // Trie() 初始化前缀树对象。
        // void insert(String word) 向前缀树中插入字符串 word 。
        // boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回
        // false 。
        // boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否
        // 则,返回 false 。
        // 哈希表存储
        /*
         * 每个节点包含字段:1.指向子节点的指针数组children 2.布尔字段isEnd,表示这个节点是否为字符串的结尾
         * 插入字符串:1.子节点存在。沿着指针移动到子节点,继续处理下一个字符。2.子节点不存在。创建一个新的子节点,记录在children数组的对应位置上,然后沿着指针移动到子节点,继续搜索下一个字符。。重复上面步骤,
         * 直到处理字符串的最后一个字符,然后将当前节点标记为字符串的结尾
         * 查找前缀:从字典树的根开始,查找前缀。1.子节点存在。沿着指针移到子节点,继续搜索下一个字符。2.子节点不存在,说明字典树不包含该前缀吗,返回空指针。。重复上面的步骤,直到返回空指针或者搜索完前缀的最后一个字符
         * 若搜索到了前缀的末尾,说明字典树中存在该前缀。若前缀末尾对应节点的isEnd为真,说明字典树中存在该字符串
         * 
         */
        private Trie[] children;
        private boolean isEnd;

        public Trie() {
            children = new Trie[26];
            isEnd = false;
        }

        public void insert(String word) {
            Trie node = this;
            for (int i = 0; i < word.length(); i++) {
                char ch = word.charAt(i);
                int index = ch - 'a';
                if (node.children[index] == null) {
                    node.children[index] = new Trie();
                }
                node = node.children[index];
            }
            node.isEnd = true;
        }

        public boolean search(String word) {
            Trie node = searchPrefix(word);
            return node != null && node.isEnd;
        }

        private Trie searchPrefix(String word) {
            Trie node = this;
            for (int i = 0; i < word.length(); i++) {
                char ch = word.charAt(i);
                int index = ch - 'a';
                if (node.children[index] == null) {
                    return null;
                }
                node = node.children[index];
            }
            return node;
        }

        public boolean startsWith(String prefix) {
            return searchPrefix(prefix) != null;
        }
    }

    /**
     * Your Trie object will be instantiated and called as such: Trie obj = new Trie(); obj.insert(word); boolean
     * param_2 = obj.search(word); boolean param_3 = obj.startsWith(prefix);
     */
    // leetcode submit region end(Prohibit modification and deletion)

}
字符串解码
// 给定一个经过编码的字符串,返回它解码后的字符串。
//
// 编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
//
// 你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
//
// 此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
//
//
//
// 示例 1:
//
//
// 输入:s = "3[a]2[bc]"
// 输出:"aaabcbc"
//
//
// 示例 2:
//
//
// 输入:s = "3[a2[c]]"
// 输出:"accaccacc"
//
//
// 示例 3:
//
//
// 输入:s = "2[abc]3[cd]ef"
// 输出:"abcabccdcdcdef"
//
//
// 示例 4:
//
//
// 输入:s = "abc3[cd]xyz"
// 输出:"abccdcdcdxyz"
//
//
//
//
// 提示:
//
//
// 1 <= s.length <= 30
// s 由小写英文字母、数字和方括号 '[]' 组成
// s 保证是一个 有效 的输入。
// s 中所有整数的取值范围为 [1, 300]
//
// Related Topics 栈 递归 字符串 👍 1022 👎 0

package leetcode.editor.cn;

import java.util.Stack;

// Java:字符串解码
public class DecodeString {
    public static void main(String[] args) {
        Solution solution = new DecodeString().new Solution();
        // TO TEST
        // System.out.println(solution.decodeString("3[z]2[2[y]pq4[2[jk]e1[f]]]ef"));
        System.out.println(solution.decodeString("3[a]2[bc]"));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /*
         * 3[a2[c]] 使用栈存储字符出现的次数和字符 accaccacc 3 a 2 c 3 acc
         * 1.辅助栈st,遍历字符串s中的每个字符c。当c为数字时,将数字字符转换为数字multi,用于后续倍数计算;当c为字母时,在res结果后面添加c;
         * 当c为[时,将当前multi和res入栈,分别置空置0(记录[前的临时结果res至栈,发现对应]后的拼接操作;记录此[前的倍数至栈,发现对应]后, 获取倍数字符串;进入新[后,res和multi重新记录)
         * 2.当c为]时,st出栈,拼接字符串res = last_res+cur_multi*res
         */
        public String decodeString(String s) {
            Stack<String> st = new Stack<String>();
            StringBuilder res = new StringBuilder();
            char[] chars = s.toCharArray();
            int multi = 0;
            for (int i = 0; i < chars.length; i++) {
                if (chars[i] >= '0' && chars[i] <= '9') {
                    multi = multi * 10 + Integer.parseInt(String.valueOf(chars[i]));
                }
                if (chars[i] >= 'a' && chars[i] <= 'z') {
                    res.append(chars[i]);
                }
                if (chars[i] == '[') {
                    st.push(String.valueOf(multi));
                    st.push(res.toString());
                    multi = 0;
                    res.delete(0, res.length());
                }
                if (chars[i] == ']') {
                    String last_res = st.pop();
                    int cur_multi = Integer.parseInt(st.pop());
                    StringBuilder tmp = new StringBuilder();
                    for (int j = 0; j < cur_multi; j++) {
                        tmp.append(res);
                    }
                    res.delete(0, res.length());
                    res.append(last_res);
                    res.append(tmp);
                }
            }
            return res.toString();

        }
    }
    // 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)

}
不重复的三元组

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

//给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重
//复的三元组。 
//
// 注意:答案中不可以包含重复的三元组。 
//
// 
//
// 示例 1: 
//
// 
//输入:nums = [-1,0,1,2,-1,-4]
//输出:[[-1,-1,2],[-1,0,1]]
// 
//
// 示例 2: 
//
// 
//输入:nums = []
//输出:[]
// 
//
// 示例 3: 
//
// 
//输入:nums = [0]
//输出:[]
// 
//
// 
//
// 提示: 
//
// 
// 0 <= nums.length <= 3000 
// -105 <= nums[i] <= 105 
// 
// Related Topics 数组 双指针 排序 
// 👍 3498 👎 0

package leetcode.editor.cn;

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

//Java:三数之和
public class ThreeSum{
    public static void main(String[] args) {
        Solution solution = new ThreeSum().new Solution();
        // TO TEST
        List<List<Integer>> list = solution.threeSum(new int[]{-2,0,1,1,2});
        System.out.println(list);
    }
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /**
         * 寻找没有重复的三元组的和为0
         * 暴力法n的三次方的时间复杂度
         *通过双指针动态消去无效解优化效率
         * 将nums排序
         * 固定3个指针中最小数值的指针,双指针i,j分设在数组所有k,len(nums)两端,通过双指针交替向中间移动,记录对于每个固定指针k的所有满足nums[k]+nums[i]+nums[j]==0的i,j组合:
         * 当nums[k]>0时直接break跳出;因为nums[j]>=nums[i]>=nums[k]>0,即3个数值都大于0,在此固定指针k之后不可能再找到结果
         * 在k>0且nums[k]==nums[k-1]时即跳过此元素nums[k];因为已经将nums[k-1]的所有组合加入到结果中,本次双指针搜索只会得到重复组合
         * i,j分设在数组索引k,len(nums)两端,当i<j时循环计算s=nums[k]+nums[i]+nums[j],并按照以下规则执行双指针移动:当s<0,i+=1并跳过所有重复的nums[i];当s》0时,j-=1并跳过所有重复的nums[j];当s==0时,记录组合k,i,j至res,执行i+=1和j-=1并跳过所有重复的nums[i]和nums[j],防止记录到重复组合>
         * @param nums
         * @return
         */
        public List<List<Integer>> threeSum1(int[] nums) {
            List<List<Integer>> list = new LinkedList<>();
            Arrays.sort(nums); //-4 -1 -1 0 1 2
            for (int i = 0; i < nums.length; i++) {
                for (int j = i+1; j < nums.length ; j++) {
                    for (int k = j+1; k < nums.length; k++) {
                        if(nums[i]+nums[j]+nums[k]==0){
                            LinkedList<Integer> l = new LinkedList<>();
                            l.add(nums[i]);
                            l.add(nums[j]);
                            l.add(nums[k]);
                            list.add(l);
                        }
                    }
                }
            }
            return list;
        }

        /**
         * 优化,将第三个指针从后往前看,第一个指针和第二个指针指向的数不能和它指向的数的前一个数相等,否则会造成有重复的三元组
         * 第二个指针需要保证在第三个指针的左边并且不能相等,当这三个指针指向的数的和大于0时可以跳过一个循环
         * @param nums
         * @return
         */
        public List<List<Integer>> threeSum2(int[] nums){
            List<List<Integer>> list = new ArrayList<>();
            Arrays.sort(nums); //-4 -1 -1 0 1 2
            for (int i = 0; i < nums.length; i++) {
                //和上一次枚举的数不相同
                if(i>0 && nums[i] == nums[i-1]){
                    continue;
                }
                //第三个指针指向数组的最右端
                int k = nums.length-1;
                //枚举第二个数
                for (int j = i+1; j < nums.length; j++) {
                    if(j>i+1&&nums[j] == nums[j-1]){
                        continue;
                    }
                    //保证j的指针在k的指针左边
                    while(j<k&&(nums[j]+nums[k]+nums[i])>0){
                        k--;
                    }
                    //当第二个指针和第三个指针相等时
                    if(j==k){
                        break;
                    }
                    if(nums[i]+nums[j]+nums[k]==0){
                        LinkedList<Integer> l = new LinkedList<>();
                        l.add(nums[i]);
                        l.add(nums[j]);
                        l.add(nums[k]);
                        list.add(l);
                    }

                }
            }
            return list;
        }
        /**
         * 将nums排序
         *              * 固定3个指针中最小数值的指针,双指针i,j分设在数组所有k,len(nums)两端,通过双指针交替向中间移动,记录对于每个固定指针k的所有满足nums[k]+nums[i]+nums[j]==0的i,j组合:
         *              * 当nums[k]>0时直接break跳出;因为nums[j]>=nums[i]>=nums[k]>0,即3个数值都大于0,在此固定指针k之后不可能再找到结果
         *              * 在k>0且nums[k]==nums[k-1]时即跳过此元素nums[k];因为已经将nums[k-1]的所有组合加入到结果中,本次双指针搜索只会得到重复组合
         *              * i,j分设在数组索引k,len(nums)两端,当i<j时循环计算s=nums[k]+nums[i]+nums[j],并按照以下规则执行双指针移动:当s<0,i+=1并跳过所有重复的nums[i];当s》0时,j-=1并跳过所有重复的nums[j];当s==0时,记录组合k,i,j至res,执行i+=1和j-=1并跳过所有重复的nums[i]和nums[j],防止记录到重复组合>
         *                  好难呀,我去
         */

        public List<List<Integer>> threeSum(int[] nums){
            List<List<Integer>> list = new ArrayList<>();
            //先给数组排序
            Arrays.sort(nums);
            int k=0,i=k+1,j=nums.length-1;
            //当第一个基准数大于0或者数组长度小于2时
            while(nums[k]<=0&&k<nums.length-2){
                //当第一个元素有重复时
                if(k>0&&nums[k]==nums[k-1]) {
                    k++;
                    continue;
                }
                i=k+1;
                j=nums.length-1;
                while(i<j){
                    int s = nums[k]+nums[j]+nums[i];
                    /*
                    if(s<0){
                        i++;
                        if(nums[i]==nums[i-1]) {
                            continue;
                        }
                    }
                    if(s>0){
                        j--;
                        if(j<nums.length-1&&nums[j]==nums[j+1]) {
                            continue;
                        }
                    }*/
                    if(s<0){
                        //将i进行加一操作,当第二个元素有重复时往前一位
                        while(i<j&&nums[i]==nums[++i]);
                    }
                    if(s>0){
                        //将j进行加一操作,当第三个元素有重复时我那个后一位
                        while(i<j&&nums[j]==nums[--j]);
                    }
                    if(s==0){
                        LinkedList<Integer> l = new LinkedList<>();
                        l.add(nums[k]);
                        l.add(nums[i]);
                        l.add(nums[j]);
                        list.add(l);
                        //如果不进行后面两步操作,会造成死循环
                        //左指针前进并且去重
                        while(i<j&&nums[i]==nums[++i]);
                        //右指针后退并且去重
                        while(i<j&&nums[j]==nums[--j]);
                    }
                }
                k++;
            }
            return list;
        }
    }
//leetcode submit region end(Prohibit modification and deletion)

}
数组中数字出现的次数 中等 位运算

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KUgzpOs2-1644460788551)(笔记.assets/image-20211121184723331.png)]

// 一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
//
//
//
// 示例 1:
//
// 输入:nums = [4,1,4,6]
// 输出:[1,6] 或 [6,1]
//
//
// 示例 2:
//
// 输入:nums = [1,2,10,4,1,4,3,3]
// 输出:[2,10] 或 [10,2]
//
//
//
// 限制:
//
//
// 2 <= nums.length <= 10000
//
//
//
// Related Topics 位运算 数组 👍 497 👎 0

package leetcode.editor.cn;

// Java:数组中数字出现的次数
public class ShuZuZhongShuZiChuXianDeCiShuLcof {
    public static void main(String[] args) {
        Solution solution = new ShuZuZhongShuZiChuXianDeCiShuLcof().new Solution();
        // TO TEST
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 输入:nums = [4,1,4,6]
        // 输出:[1,6] 或 [6,1]
        /**
         * 暴力法和哈希表统计法,时间复杂度和空间复杂度不满足 位运算, 异或运算:两个相同数字异或为0,满足交换律 将数组中的所有数字执行异或运算,留下的结果为出现一次的数字x
         * 1.遍历nums执行异或。对nums中所有数字执行异或,得到的结果是x异或y
         * 2.循环左移计算m:a&0001=1,a的第一位为1.初始化一个辅助变量m=1,通过与运算从右向左循环判断,可以获取整数x异或y的首位1,记录到m中 3.拆分nums为两个子数组
         * 4.分别遍历两个子数组执行异或。通过遍历判断nums中各数字和m做与运算的结果,可以将数组拆分为两个子数组,并分别对两个子数组遍历求异或,可以得到两个只出现一次的数字 5.返回值:返回只出现一次的数字x,y
         * 
         */
        public int[] singleNumbers(int[] nums) {
            int x = 0, y = 0, n = 0, m = 1;
            for (int num : nums) { // 1.遍历异或
                n ^= num;
            }
            while ((n & m) == 0) { // 2.循环左移,计算m
                m <<= 1;
            }
            for (int num : nums) { // 3.遍历nums分组
                if ((num & m) != 0) { // 4.当num&m != 0
                    x ^= num;
                } else { // 当num&m == 0
                    y ^= num;
                }
            }
            return new int[] {x, y};

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

}
下一个排列 双指针 中等
// 实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列(即,组合出下一个更大的整数)。
//
// 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
//
// 必须 原地 修改,只允许使用额外常数空间。
//
//
//
// 示例 1:
//
//
// 输入:nums = [1,2,3]
// 输出:[1,3,2]
//
//
// 示例 2:
//
//
// 输入:nums = [3,2,1]
// 输出:[1,2,3]
//
//
// 示例 3:
//
//
// 输入:nums = [1,1,5]
// 输出:[1,5,1]
//
//
// 示例 4:
//
//
// 输入:nums = [1]
// 输出:[1]
//
//
//
//
// 提示:
//
//
// 1 <= nums.length <= 100
// 0 <= nums[i] <= 100
//
// Related Topics 数组 双指针 👍 1487 👎 0

package leetcode.editor.cn;

// Java:下一个排列
public class NextPermutation {
    public static void main(String[] args) {
        Solution solution = new NextPermutation().new Solution();
        // TO TEST
        int[] nums = {3, 2, 1};
        solution.nextPermutation(nums);
        for (int num : nums) {
            System.out.print(num + " ");
        }
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 将给定数字序列重新排列成字典序中下一个更大的排列(即,组合出下一个更大的整数)。
        // 必须 原地 修改,只允许使用额外常数空间。
        // 双指针
        // 将后面的大数和前面的小数进行交换,就能获取一个更大的数
        // 下一个数增加的幅度尽可能小
        // 1.尽可能靠右的低位进行交换,从后往前查找
        // 2.将尽可能小的大数和前面的小数进行交换 123465----123564
        // 3.将大数交换到前面后,将大数后面的所有数重置为升序,升序排列就是最小的排列 123465-----123546
        // 123456 123465

        // 算法:
        // 1.从后向前查找第一个相邻升序的元素对,满足nums[i]<nums[j],此时j,end为降序
        // 2.在j,end从后向前查找第一个满足nums[i]<nums[k]的k,nums[i]和nums[k]就是小数和大数
        // 3.将nums[i]和nums[k]交换
        // 4.此时j,end为降序,逆置j,end,使其升序
        // 5.如果没有符合的相邻元素对,说明当前begin,end为一个降序,跳到4
        public void nextPermutation(int[] nums) {
            int i, j, k;
            for (j = nums.length - 1; j > 0; j--) {
                if (nums[j] > nums[j - 1]) {
                    break;
                }
            }
            if (j != 0) {
                i = j - 1;
                for (k = nums.length - 1; k > 0; k--) {
                    if (nums[i] < nums[k]) {
                        break;
                    }
                }
                swap(nums, i, k);
            }
            // 升序,冒泡排序
            for (int z = j; z < nums.length - 1; z++) {
                for (int r = j; r < nums.length - (z - j) - 1; r++) {
                    if (nums[r] > nums[r + 1]) {
                        swap(nums, r, r + 1);
                    }
                }
            }

        }

        private void swap(int[] nums, int a, int b) {
            int tmp = nums[a];
            nums[a] = nums[b];
            nums[b] = tmp;
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
允许重复选择元素的组合 回溯
// 给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的
// 唯一组合。
//
// candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。
//
// 对于给定的输入,保证和为 target 的唯一组合数少于 150 个。
//
//
//
// 示例 1:
//
//
// 输入: candidates = [2,3,6,7], target = 7
// 输出: [[7],[2,2,3]]
//
//
// 示例 2:
//
//
// 输入: candidates = [2,3,5], target = 8
// 输出: [[2,2,2,2],[2,3,3],[3,5]]
//
// 示例 3:
//
//
// 输入: candidates = [2], target = 1
// 输出: []
//
//
// 示例 4:
//
//
// 输入: candidates = [1], target = 1
// 输出: [[1]]
//
//
// 示例 5:
//
//
// 输入: candidates = [1], target = 2
// 输出: [[1,1]]
//
//
//
//
// 提示:
//
//
// 1 <= candidates.length <= 30
// 1 <= candidates[i] <= 200
// candidate 中的每个元素都是独一无二的。
// 1 <= target <= 500
//
//
//
//
// 注意:本题与主站 39 题相同: https://leetcode-cn.com/problems/combination-sum/
// Related Topics 数组 回溯 👍 12 👎 0

package leetcode.editor.cn;

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

// Java:允许重复选择元素的组合
public class Ygoe9J {
    public static void main(String[] args) {
        Solution solution = new Ygoe9J().new Solution();
        // TO TEST
        List<List<Integer>> res = solution.combinationSum(new int[] {2, 3, 5}, 8);
        System.out.println(res);
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 数字可以无限制重复被选取。如果至少一个所选数字
        // 回溯,标记数组,深度,回溯
        // 将标记置为true,depth+1,元素添加到路径中
        // 遍历之后,将标记置为false,元素删除
        // n^2
        List<List<Integer>> res = new LinkedList<>();

        public List<List<Integer>> combinationSum(int[] candidates, int target) {
            List<Integer> list = new ArrayList<>();
            Boolean[] flag = new Boolean[candidates.length];
            Arrays.fill(flag, false);
            int depth = 0;
            dfs(flag, depth, list, candidates, target);
            return res;
        }

        private void dfs(Boolean[] flag, int depth, List<Integer> list, int[] candidates, int target) {
            int sum = list.stream().mapToInt(e -> e).sum();
            if (target == sum) {
                List<Integer> listCopy = new ArrayList<>();
                listCopy.addAll(list);
                res.add(listCopy);
            }
            if (depth >= candidates.length) {
                return;
            }
            for (int i = 0; i < candidates.length; i++) {
                if (flag[i]) {
                    continue;
                }

                list.add(candidates[i]);
                flag[i] = true;
                dfs(flag, depth + 1, list, candidates, target);
                flag[i] = false;
                list.remove(list.size() - 1);
            }

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

}
组合总和 数组 回溯
// 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的
// 所有不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
//
// candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
//
// 对于给定的输入,保证和为 target 的不同组合数少于 150 个。
//
//
//
// 示例 1:
//
//
// 输入:candidates = [2,3,6,7], target = 7
// 输出:[[2,2,3],[7]]
// 解释:
// 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
// 7 也是一个候选, 7 = 7 。
// 仅有这两种组合。
//
// 示例 2:
//
//
// 输入: candidates = [2,3,5], target = 8
// 输出: [[2,2,2,2],[2,3,3],[3,5]]
//
// 示例 3:
//
//
// 输入: candidates = [2], target = 1
// 输出: []
//
//
// 示例 4:
//
//
// 输入: candidates = [1], target = 1
// 输出: [[1]]
//
//
// 示例 5:
//
//
// 输入: candidates = [1], target = 2
// 输出: [[1,1]]
//
//
//
//
// 提示:
//
//
// 1 <= candidates.length <= 30
// 1 <= candidates[i] <= 200
// candidate 中的每个元素都 互不相同
// 1 <= target <= 500
//
// Related Topics 数组 回溯 👍 1710 👎 0

package leetcode.editor.cn;

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

// Java:组合总和
public class CombinationSum {
    public static void main(String[] args) {
        Solution solution = new CombinationSum().new Solution();
        // TO TEST
        List<List<Integer>> res = solution.combinationSum(new int[] {2, 3, 5}, 8);
        System.out.println(res);
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 数组,回溯,dfs,可以使用多次
        // 输入: candidates = [2,3,5], target = 8
        // 输出: [[2,2,2,2],[2,3,3],[3,5]]
        // 深度,标记数组,
        // 这一类问题都需要先画出树形图,然后编码实现
        // 创建分支的时候做减法
        // 每一个箭头表示:从父亲节点的数值减去边上的数值,得到孩子节点的数值。边的值就是数组的每个元素的值
        // 减到0或者负数的时候停止:节点0和负数节点成为叶子节点
        // 所有从根节点到节点0的路径(从上到下)

        // 重复路径的处理
        // 在搜索的时候,按照某种顺序搜索。在每一次搜索的时候设置下一轮搜索的起点begin
        List<List<Integer>> res = new LinkedList<>();

        public List<List<Integer>> combinationSum(int[] candidates, int target) {
            LinkedList<Integer> list = new LinkedList<>();
            dfs(candidates, 0, target, list);
            return res;
        }

        private void dfs(int[] candidates, int begin, int target, LinkedList<Integer> list) {
            int sum = list.stream().mapToInt(e -> e).sum();
            if (target == 0) {
                LinkedList<Integer> rr = new LinkedList<>(list);
                res.add(rr);
            }
            if (target <= 0) {
                return;
            }
            for (int i = begin; i < candidates.length; i++) {
                list.add(candidates[i]);
                dfs(candidates, i, target - candidates[i], list);
                list.remove(list.size() - 1);
            }
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
合并区间 排序
// 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返
// 回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
//
//
//
// 示例 1:
//
//
// 输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
// 输出:[[1,6],[8,10],[15,18]]
// 解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
//
//
// 示例 2:
//
//
// 输入:intervals = [[1,4],[4,5]]
// 输出:[[1,5]]
// 解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
//
//
//
// 提示:
//
//
// 1 <= intervals.length <= 10⁴
// intervals[i].length == 2
// 0 <= starti <= endi <= 10⁴
//
// Related Topics 数组 排序 👍 1269 👎 0

package leetcode.editor.cn;

import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;

// Java:合并区间
public class MergeIntervals {
    public static void main(String[] args) {
        Solution solution = new MergeIntervals().new Solution();
        // TO TEST
        System.out.println(Arrays.deepToString(solution.merge(new int[][] {{1, 3}, {2, 6}, {8, 10}, {15, 18}})));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /*
         * 按照区间的左端点排序,再排序后的列表中,可以合并的区间一定是连续的
         * 数组res存储答案
         * 1.将列表中的区间按照左端点升序排序,将第一个区间加入res数组,按顺序考虑之后的区间
         * 2.当前区间的左端点再数组res中最后一个区间的右端点之后,它们不会重合,可以直接将这个区间加入res的末尾
         *  重合:用当前区间的右端点更新数组res的最后一个区间的右端点,将其置为二者较大的值
         */
        public int[][] merge(int[][] intervals) {
            LinkedList<int[]> res = new LinkedList<>();
            // 按照左端点升序排序
            Arrays.sort(intervals, new Comparator<int[]>() {
                @Override
                public int compare(int[] o1, int[] o2) {
                    return o1[0] - o2[0];
                }
            });
            res.add(intervals[0]);
            for (int[] interval : intervals) {
                if (interval[0] > res.getLast()[1]) {
                    res.add(interval);
                } else {
                    res.set(res.size() - 1,
                        new int[] {(res.get(res.size() - 1))[0], Math.max(res.get(res.size() - 1)[1], interval[1])});
                }
            }
            return res.toArray(new int[0][]);
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
子集 回溯
// 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
//
// 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
//
//
//
// 示例 1:
//
//
// 输入:nums = [1,2,3]
// 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
//
//
// 示例 2:
//
//
// 输入:nums = [0]
// 输出:[[],[0]]
//
//
//
//
// 提示:
//
//
// 1 <= nums.length <= 10
// -10 <= nums[i] <= 10
// nums 中的所有元素 互不相同
//
// Related Topics 位运算 数组 回溯 👍 1451 👎 0

package leetcode.editor.cn;

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

// Java:子集
public class Subsets {
    public static void main(String[] args) {
        Solution solution = new Subsets().new Solution();
        // TO TEST
        System.out.println(solution.subsets(new int[] {1, 2, 3}));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 定义depth(跳出),used数组(防止重复)
        LinkedList<List<Integer>> res = new LinkedList<>();

        public List<List<Integer>> subsets(int[] nums) {
            Boolean[] used = new Boolean[nums.length];
            Arrays.fill(used, false);
            LinkedList<Integer> list = new LinkedList<>();
            // res.add(list);
            dfs(nums, 0, used, list);
            return res;
        }

        private void dfs(int[] nums, int index, Boolean[] used, LinkedList<Integer> list) {
            res.add(new LinkedList<>(list));
            for (int i = index; i < nums.length; i++) {
                if (used[i]) {
                    continue;
                }
                used[i] = true;
                list.add(nums[i]);
                dfs(nums, i, used, list);
                used[i] = false;
                list.removeLast();
            }
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
最长连续序列 并查集 数组 哈希表
// 给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
//
// 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
//
//
//
// 示例 1:
//
//
// 输入:nums = [100,4,200,1,3,2]
// 输出:4
// 解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
//
// 示例 2:
//
//
// 输入:nums = [0,3,7,2,5,8,4,6,0,1]
// 输出:9
//
//
//
//
// 提示:
//
//
// 0 <= nums.length <= 10⁵
// -10⁹ <= nums[i] <= 10⁹
//
// Related Topics 并查集 数组 哈希表 👍 1060 👎 0

package leetcode.editor.cn;

import java.util.HashSet;

// Java:最长连续序列
public class LongestConsecutiveSequence {
    public static void main(String[] args) {
        Solution solution = new LongestConsecutiveSequence().new Solution();
        // TO TEST
        System.out.println(solution.longestConsecutive(new int[] {100, 4, 200, 1, 3, 2}));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 并查集 数组 哈希表
        // 给定一个未排序的整数数组 nums ,找出数字连续的最长序列
        // 输入:nums = [0,3,7,2,5,8,4,6,0,1]
        // 输出:9
        // 使用哈希表存储数组中的数,从x-1开始尝试匹配
        public int longestConsecutive(int[] nums) {
            HashSet<Integer> set = new HashSet<>();
            for (int num : nums) {
                set.add(num);
            }
            // 数组为空时,数组只有一个元素时
            int res = 0;
            for (int i = 0; i < nums.length; i++) {
                // 当哈希表不存在当前的的x-1的情况,计算从当前数开始的长度
                if (!set.contains(nums[i] - 1)) {
                    int len = 1;
                    int tmp = nums[i] + 1;
                    while (set.contains(tmp)) {
                        len++;
                        tmp++;
                    }
                    res = Math.max(res, len);
                }

            }
            return res;

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

}
除自身以外数组的乘积 数组 前缀和
// 给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之
// 外其余各元素的乘积。
//
//
//
// 示例:
//
// 输入: [1,2,3,4]
// 1 1 2 6
// 24 12 4 1
// 输出: [24,12,8,6]
//
//
//
// 提示:题目数据保证数组之中任意元素的全部前缀元素和后缀(甚至是整个数组)的乘积都在 32 位整数范围内。
//
// 说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。
//
// 进阶:
// 你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)
// Related Topics 数组 前缀和 👍 1034 👎 0

package leetcode.editor.cn;

import java.util.Arrays;

// Java:除自身以外数组的乘积
public class ProductOfArrayExceptSelf {
    public static void main(String[] args) {
        Solution solution = new ProductOfArrayExceptSelf().new Solution();
        // TO TEST
        System.out.println(Arrays.toString(solution.productExceptSelf(new int[] {1, 2, 3, 4})));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 输入: [1,2,3,4]
        // 输出: [24,12,8,6]
        /*
         * 前缀和
         * 当前位置的结果是它左部分的结果和右部分结果的乘积,需要遍历两次
         */
        public int[] productExceptSelf(int[] nums) {
            int[] LeftMul = new int[nums.length];
            int[] RightMul = new int[nums.length];
            Arrays.fill(LeftMul, 1);
            Arrays.fill(RightMul, 1);
            LeftMul[0] = 1;
            for (int i = 1; i < nums.length; i++) {
                LeftMul[i] = nums[i - 1] * LeftMul[i - 1];
            }
            RightMul[nums.length - 1] = 1;
            for (int i = nums.length - 2; i >= 0; i--) {
                RightMul[i] = nums[i + 1] * RightMul[i + 1];
            }
            int[] res = new int[nums.length];
            for (int i = 0; i < nums.length; i++) {
                res[i] = LeftMul[i] * RightMul[i];
            }
            return res;
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
和为 K 的子数组 数组 哈希表 前缀和
// 给你一个整数数组 nums 和一个整数 k ,请你统计并返回该数组中和为 k 的连续子数组的个数。
//
//
//
// 示例 1:
//
//
// 输入:nums = [1,1,1], k = 2
// 输出:2
//
//
// 示例 2:
//
//
// 输入:nums = [1,2,3], k = 3
// 输出:2
//
//
//
//
// 提示:
//
//
// 1 <= nums.length <= 2 * 10⁴
// -1000 <= nums[i] <= 1000
// -10⁷ <= k <= 10⁷
//
// Related Topics 数组 哈希表 前缀和 👍 1296 👎 0

package leetcode.editor.cn;

import java.util.HashMap;

// Java:和为 K 的子数组
public class SubarraySumEqualsK {
    public static void main(String[] args) {
        Solution solution = new SubarraySumEqualsK().new Solution();
        // TO TEST
        System.out.println(solution.subarraySum(new int[] {1, -1, 0}, 0));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /*
         * 双指针,滑动窗口,不行
         */
        public int subarraySum1(int[] nums, int k) {
            int left = 0, right = 0, sum = 0;
            int res = 0;
            int i = 0;
            // 会超出范围
            while (left < nums.length) {
                if (sum == k) {
                    res++;
                } else if (sum < k) {
                    right++;
                    sum += nums[i++];
                    continue;
                }
                sum -= nums[left++];

            }
            return res;
        }

        /*
         * pre[i]是[0.i]中所有数的和,pre[i] = pre[i-1]+nums[i]
         * [j..i]这个子数组和为k转化为pre[i]-pre[j-1]==k即pre[j-1]==pre[i]-k
         * 考虑以i结尾的和为k的连续子数组个数时只要统计有多少个前缀和为pre[i]-k的pre[j]
         * 建立哈希表mp,<和,出现次数>,从左往右更新哈希表mp,计算答案,以i结尾的答案mp[pre[i]-k],最后的答案就是所有下标结尾和为k
         * 的子数组个数之和
         * 
         */
        public int subarraySum(int[] nums, int k) {
            int count = 0, pre = 0;
            HashMap<Integer, Integer> map = new HashMap<>();
            map.put(0, 1);
            for (int i = 0; i < nums.length; i++) {
                pre += nums[i];
                if (map.containsKey(pre - k)) {
                    // map.put(pre - k, map.get(pre - k) + 1);
                    count += map.get(pre - k);
                }
                map.put(pre, map.getOrDefault(pre, 0) + 1);
            }
            return count;
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
最短无序连续子数组 栈 贪心 数组 双指针 排序 单调栈
// 给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
//
// 请你找出符合题意的 最短 子数组,并输出它的长度。
//
//
//
//
//
// 示例 1:
//
// 1 -1 1 1 -1 1
// 输入:nums = [2,6,4,8,10,9,15]
// 输出:5
// 解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
//
//
// 示例 2:
//
// 1 1 1 1
// 输入:nums = [1,2,3,4]
// 输出:0
//
//
// 示例 3:
//
//
// 输入:nums = [1]
// 输出:0
//
//
//
//
// 提示:
//
//
// 1 <= nums.length <= 10⁴
// -10⁵ <= nums[i] <= 10⁵
//
//
//
//
// 进阶:你可以设计一个时间复杂度为 O(n) 的解决方案吗?
//
//
// Related Topics 栈 贪心 数组 双指针 排序 单调栈 👍 775 👎 0

package leetcode.editor.cn;

// Java:最短无序连续子数组
public class ShortestUnsortedContinuousSubarray {
    public static void main(String[] args) {
        Solution solution = new ShortestUnsortedContinuousSubarray().new Solution();
        // TO TEST
        System.out.println(solution.findUnsortedSubarray(new int[] {1}));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /*
         * 设计双指针,一个从前往后查找,另一个从后往前查找
         * 1,2,3,3,3
         * 1,3,2,3
         * 1,3,3,2,3,3
         */
        public int findUnsortedSubarray1(int[] nums) {
            int left = 0, right = 0;
            for (int i = 0; i < nums.length - 1; i++) {
                if (nums[i + 1] <= nums[i]) {
                    left = i;
                    break;
                }
            }
            for (int i = nums.length - 1; i > 0; i--) {
                if (nums[i] <= nums[i - 1]) {
                    right = i;
                    break;
                }
            }
            return (right - left) == 0 || (nums[right] == nums[left]) ? 0 : (right - left) + 1;
        }

        /*
         * nums = [2,6,4,8,10,9,15]
         * 左端和右段是标准的升序数组,中段数组虽然是无序的,但是满足最小值大于左段的最大值,最大值小于右段的最小值
         * 从左到右维护一个最大值max,在进入右段之前,遍历到的nums[i]都是小于max的,要求的end就是遍历中最后一个小于max元素的位置
         * 从右到左维护一个最小值min,在进入左段之前,遍历到的nums[i]是大于min的,要求的begin是最后一个大于min元素的位置
         */
        public int findUnsortedSubarray(int[] nums) {
            int min = nums[nums.length - 1], max = nums[0];
            int end = 0, begin = 0;
            for (int i = 0; i < nums.length; i++) {
                if (nums[i] < max) {
                    end = i;
                } else {
                    max = nums[i];
                }
            }
            for (int i = nums.length - 1; i >= 0; i--) {
                if (nums[i] > min) {
                    begin = i;
                } else {
                    min = nums[i];
                }
            }
            return (end == begin) ? 0 : (end - begin + 1);
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
每日温度 栈 数组 单调栈
// 请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。
//
// 示例 1:
//
//
// 输入: temperatures = [73,74,75,71,69,72,76,73]
// 输出: [1,1,4,2,1,1,0,0]
//
//
// 示例 2:
//
//
// 输入: temperatures = [30,40,50,60]
// 输出: [1,1,1,0]
//
//
// 示例 3:
//
//
// 输入: temperatures = [30,60,90]
// 输出: [1,1,0]
//
//
//
// 提示:
//
//
// 1 <= temperatures.length <= 10⁵
// 30 <= temperatures[i] <= 100
//
// Related Topics 栈 数组 单调栈 👍 1011 👎 0

package leetcode.editor.cn;

import java.util.Arrays;
import java.util.Stack;

// Java:每日温度
public class DailyTemperatures {
    public static void main(String[] args) {
        Solution solution = new DailyTemperatures().new Solution();
        // TO TEST
        System.out.println(Arrays.toString(solution.dailyTemperatures(new int[] {73, 74, 75, 71, 69, 72, 76, 73})));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 输入: temperatures = [73,74,75,71,69,72,76,73]
        // 输出: [1,1,4,2,1,1,0,0]
        /*
         * 暴力法:反向遍历列表
         * 维护一个单调递减栈,从前往后
         * 答案是下标的距离
         * 
         */
        public int[] dailyTemperatures(int[] temperatures) {
            int len = temperatures.length;
            int[] ans = new int[len];
            // 单调栈,存储下标
            Stack<Integer> st = new Stack<>();
            for (int i = 0; i < len; i++) {
                while (!st.isEmpty() && temperatures[i] > temperatures[st.peek()]) {
                    int prevIndex = st.pop();
                    ans[prevIndex] = i - prevIndex;
                }
                st.push(i);
            }
            return ans;
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
根据身高重建队列 贪心 数组 排序
// 假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i
// 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
//
// 请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第
// j 个人的属性(queue[0] 是排在队列前面的人)。
//
//
//
//
//
//
// 示例 1:
//
//
// 输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
// 输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
// 解释:
// 编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。
// 编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。
// 编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 0 和 1 的人。
// 编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
// 编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。
// 编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
// 因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列。
//
//
// 示例 2:
//
//
// 输入:people = [[6,0],[5,0],[4,0],[3,2],[2,2],[1,4]]
// 输出:[[4,0],[5,0],[2,2],[3,2],[1,4],[6,0]]
//
//
//
//
// 提示:
//
//
// 1 <= people.length <= 2000
// 0 <= hi <= 10⁶
// 0 <= ki < people.length
// 题目数据确保队列可以被重建
//
// Related Topics 贪心 数组 排序 👍 1123 👎 0

package leetcode.editor.cn;

import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;

// Java:根据身高重建队列
public class QueueReconstructionByHeight {
    public static void main(String[] args) {
        Solution solution = new QueueReconstructionByHeight().new Solution();
        // TO TEST
        System.out.println(Arrays
            .deepToString(solution.reconstructQueue(new int[][] {{7, 0}, {7, 1}, {6, 1}, {5, 0}, {5, 2}, {4, 4}})));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /*
         * 当每个人身高都不相同时,将他们按照身高从小到大排序,就可以还原出原始的队列 根据第一个元素正向排序,根据第二个元素反向排序。或者根据第一个元素反向排序,根据第二个元素正向排序,能简化过程
         * 按照元素1进行降序排序,对于每个元素,在其之前的元素的个数,就是大于等于它的元素的数量,按照第二个元素正向排序, k大的尽量在后面,减少插入操作的次数
         */
        public int[][] reconstructQueue(int[][] people) {
            Arrays.sort(people, new Comparator<int[]>() {
                @Override
                public int compare(int[] o1, int[] o2) {
                    if (o1[0] != o2[0]) {
                        // 第一个元素不相等时,降序排序
                        return o2[0] - o1[0];
                    } else {
                        // 第一个元素相等时,第二个与三俗升序
                        return o1[1] - o2[1];
                    }
                }
            });
            // 保存结果
            LinkedList<int[]> list = new LinkedList<>();
            for (int i = 0; i < people.length; i++) {
                // 结果中元素个数大于第i个人前面应有人数时,将第i个人插入到结果的people[i][1]位置
                if (list.size() > people[i][1]) {
                    list.add(people[i][1], people[i]);
                } else {
                    // 结果集中元素个数小于等于第i个人前面应有人数时,将第i个人追加到结果集的后面
                    list.add(list.size(), people[i]);
                }
            }
            return list.toArray(new int[list.size()][]);
        }
    }
    // 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)

}
递归
数值的整数次方 中等
//实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。 
//
// 
//
// 示例 1: 
//
// 
//输入:x = 2.00000, n = 10
//输出:1024.00000
// 
//
// 示例 2: 
//
// 
//输入:x = 2.10000, n = 3
//输出:9.26100 
//
// 示例 3: 
//
// 
//输入:x = 2.00000, n = -2
//输出:0.25000
//解释:2-2 = 1/22 = 1/4 = 0.25 
//
// 
//
// 提示: 
//
// 
// -100.0 < x < 100.0 
// -231 <= n <= 231-1 
// -104 <= xn <= 104 
// 
//
// 
//
// 注意:本题与主站 50 题相同:https://leetcode-cn.com/problems/powx-n/ 
// Related Topics 递归 数学 
// 👍 207 👎 0

package leetcode.editor.cn;
//Java:数值的整数次方
public class ShuZhiDeZhengShuCiFangLcof{
    public static void main(String[] args) {
        Solution solution = new ShuZhiDeZhengShuCiFangLcof().new Solution();
        // TO TEST
        System.out.println(solution.myPow(2,-2));
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
            public double myPow(double x, int n) {
                /**
                 * java.lang.StackOverflowError
                 * n==0
                 * 解答失败:
                 *           测试用例:2.00000
                 *           -2
                 *           测试结果:2.00000
                 *           期望结果:0.25
                 */
                if(n==0) {
                    return 1;
                }
                //返回1/x^-n
                //n的最小值可以取到Integer.MIN_VALUE,如果直接取它的相反数还是它自己,会导致堆栈溢出,提一个x出来
                if(n<0){
                    return 1.0/(x*myPow(x,-n-1));
                }
                //如果n为奇数,x*x^n-1
                else if(n%2 == 1){
                    return x*myPow(x,n-1);
                }
                else{
                    //如果n为偶数
                    return myPow(x*x,n/2);
                }
            }

            /**
             * 当n<0时,不过会造成StackOverFlow
             * @param x
             * @param n
             * @return
             */
            private double mPow(double x, int n) {
                if(n >= -1){
                    return x;
                }
                return (x*mPow(x,n+1));
            }
        }
//leetcode submit region end(Prohibit modification and deletion)

}
求1+2+…+n 中等
// 求 1+2+...+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
//
//
//
// 示例 1:
//
// 输入: n = 3
// 输出: 6
//
//
// 示例 2:
//
// 输入: n = 9
// 输出: 45
//
//
//
//
// 限制:
//
//
// 1 <= n <= 10000
//
// Related Topics 位运算 递归 脑筋急转弯 👍 402 👎 0

package leetcode.editor.cn;

// Java:求1+2+…+n
public class Qiu12nLcof {
    public static void main(String[] args) {
        Solution solution = new Qiu12nLcof().new Solution();
        // TO TEST
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 初始化res变量记录结果
        int res = 0;

        public int sumNums(int n) {
            // 求 1+2+...+n
            // 开启递归函数,改写为sunNums(n-1)>0,将整体作为一个布尔量输出,否则会报错
            boolean x = n > 1 && sumNums(n - 1) > 0;
            res += n;
            return res;
        }
    }
    // 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. 写出子问题的递推关系
  3. 确定DP数组的计算顺序
  4. 空间优化
青蛙跳台阶问题 递归
//一只青蛙一次可以跳上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)

}
乘积最大子数组 数组
// 给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
//
//
//
// 示例 1:
//
// 输入: [2,3,-2,4]
// 输出: 6
// 解释: 子数组 [2,3] 有最大乘积 6。
//
//
// 示例 2:
//
// 输入: [-2,0,-1]
// 输出: 0
// 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
// Related Topics 数组 动态规划 👍 1462 👎 0

package leetcode.editor.cn;

// Java:乘积最大子数组
public class MaximumProductSubarray {
    public static void main(String[] args) {
        Solution solution = new MaximumProductSubarray().new Solution();
        // TO TEST
        System.out.println(solution.maxProduct(new int[] {-2, 3, -4}));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 输入: [2,3,-2,4]
        // 输出: 6
        // 解释: 子数组 [2,3] 有最大乘积 6。
        // 动态规划
        // 负数的情况就记录最小值
        public int maxProduct(int[] nums) {
            int max = 1, res = nums[0], min = 1;
            for (int num : nums) {
                int mx = max, mn = min;
                max = Math.max(mx * num, Math.max(num, mn * num));
                min = Math.min(mn * num, Math.min(num, mx * num));
                res = Math.max(max, res);
            }
            return res;
        }
    }
    // 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-tooVpVCf-1644460788553)(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 {
    
    /**
         * 模拟整个删除过程,构建一个长度为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;
        }
//        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)

}
礼物的最大价值 数组 动态规划 矩阵
//在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直
//到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物? 
//
// 
//
// 示例 1: 
//
// 输入: 
//[
//  [1,3,1],
//  [1,5,1],
//  [4,2,1]
//]
//输出: 12
//解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物 
//
// 
//
// 提示: 
//
// 
// 0 < grid.length <= 200 
// 0 < grid[0].length <= 200 
// 
// Related Topics 数组 动态规划 矩阵 
// 👍 185 👎 0

package leetcode.editor.cn;
//Java:礼物的最大价值
public class LiWuDeZuiDaJieZhiLcof{
    public static void main(String[] args) {
        Solution solution = new LiWuDeZuiDaJieZhiLcof().new Solution();
        // TO TEST
        int[][] nums = {{1,3,1},{1,5,1},{4,2,1}};
        System.out.println(solution.maxValue(nums));
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public int maxValue(int[][] grid) {
        // 输入:
        //[
        //  [1,3,1],
        //  [1,5,1],
        //  [4,2,1]
        //]
        //输出: 12
        //解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
        //】、
        /**
         * 1 4 5
         * 2 9 10
         * 6 11 12
         */
        for(int i=0;i<grid.length;i++){
            for (int j = 0; j < grid[0].length; j++) {
                if(i==0&&j==0) continue;
                if( i == 0){
                    grid[i][j] += grid[i][j-1];
                }
                else if(j == 0){
                    grid[i][j] += grid[i-1][j];
                }else{
                    grid[i][j] += Math.max(grid[i-1][j],grid[i][j-1]);
                }

            }
        }
        return grid[grid.length-1][grid[0].length-1];

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

}
不同路径 数学 动态规划 组合数学
// 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
//
// 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
//
// 问总共有多少条不同的路径?
//
//
//
// 示例 1:
//
//
// 输入:m = 3, n = 7
// 输出:28
//
// 示例 2:
//
//
// 输入:m = 3, n = 2
// 输出:3
// 解释:
// 从左上角开始,总共有 3 条路径可以到达右下角。
// 1. 向右 -> 向下 -> 向下
// 2. 向下 -> 向下 -> 向右
// 3. 向下 -> 向右 -> 向下
//
//
// 示例 3:
//
//
// 输入:m = 7, n = 3
// 输出:28
//
//
// 示例 4:
//
//
// 输入:m = 3, n = 3
// 输出:6
//
//
//
// 提示:
//
//
// 1 <= m, n <= 100
// 题目数据保证答案小于等于 2 * 10⁹
//
// Related Topics 数学 动态规划 组合数学 👍 1252 👎 0

package leetcode.editor.cn;

// Java:不同路径
public class UniquePaths {
    public static void main(String[] args) {
        Solution solution = new UniquePaths().new Solution();
        // TO TEST
        System.out.println(solution.uniquePaths(3, 7));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 机器人每次只能向下或者向右移动一步
        // 再上一次的基础上向下或者向右移动一步
        // 构建一个动态规划数组,在00的位置时为1,边缘位置时和上一个位置相等,其余为上方和左方元素相加
        public int uniquePaths(int m, int n) {
            int[][] dp = new int[m][n];
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (i == 0 && j == 0) {
                        dp[0][0] = 1;
                    } else if (i == 0) {
                        dp[i][j] = dp[i][j - 1];
                    } else if (j == 0) {
                        dp[i][j] = dp[i - 1][j];
                    } else {
                        dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                    }
                }
            }
            return dp[m - 1][n - 1];
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
最小路径和
// 给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
//
// 说明:每次只能向下或者向右移动一步。
//
//
//
// 示例 1:
//
//
// 输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
// 输出:7
// 解释:因为路径 1→3→1→1→1 的总和最小。
//
//
// 示例 2:
//
//
// 输入:grid = [[1,2,3],[4,5,6]]
// 输出:12
//
//
//
//
// 提示:
//
//
// m == grid.length
// n == grid[i].length
// 1 <= m, n <= 200
// 0 <= grid[i][j] <= 100
//
// Related Topics 数组 动态规划 矩阵 👍 1120 👎 0

package leetcode.editor.cn;

// Java:最小路径和
public class MinimumPathSum {
    public static void main(String[] args) {
        Solution solution = new MinimumPathSum().new Solution();
        // TO TEST
        System.out.println(solution.minPathSum(new int[][] {{1, 3, 1}, {1, 5, 1}, {4, 2, 1}}));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 动态规划数组
        // 取上方和左方较小的元素相加
        public int minPathSum(int[][] grid) {
            // int[][] res = new int[grid.length][grid[0].length];
            for (int i = 0; i < grid.length; i++) {
                for (int j = 0; j < grid[0].length; j++) {
                    if (i == 0 && j == 0) {
                        continue;
                    } else if (i == 0) {
                        grid[i][j] += grid[i][j - 1];
                    } else if (j == 0) {
                        grid[i][j] += grid[i - 1][j];
                    } else {
                        grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]);
                    }
                }
            }
            return grid[grid.length - 1][grid[0].length - 1];
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
最长回文子串 中等
//给你一个字符串 s,找到 s 中最长的回文子串。 
//
// 
//
// 示例 1: 
//
// 
//输入:s = "babad"
//输出:"bab"
//解释:"aba" 同样是符合题意的答案。
// 
//
// 示例 2: 
//
// 
//输入:s = "cbbd"
//输出:"bb"
// 
//
// 示例 3: 
//
// 
//输入:s = "a"
//输出:"a"
// 
//
// 示例 4: 
//
// 
//输入:s = "ac"
//输出:"a"
// 
//
// 
//
// 提示: 
//
// 
// 1 <= s.length <= 1000 
// s 仅由数字和英文字母(大写和/或小写)组成 
// 
// Related Topics 字符串 动态规划 
// 👍 4175 👎 0

package leetcode.editor.cn;

import com.sun.org.apache.xpath.internal.operations.Bool;

//Java:最长回文子串
public class LongestPalindromicSubstring{
    public static void main(String[] args) {
        Solution solution = new LongestPalindromicSubstring().new Solution();
        // TO TEST
        String s = "cbbd";
        System.out.println(solution.longestPalindrome(s));
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
            /**
             * 最长回文子串"babad",不一定两边往中间
             * 循环得出每个子串,对每个子串判断是否是回文子串n^3
             * Time Limit Exceeded
             * @param s
             * @return
             */
            public String longestPalindrome1(String s) {
                char[] ch = s.toCharArray();
                StringBuilder re = new StringBuilder();
                for (int i = 0; i < ch.length; i++) {
                    //列举出每一个子字符串
                    StringBuilder str = new StringBuilder();
                    str.append(ch[i]);
                    for (int j = i+1; j < ch.length; j++) {
                        str.append(ch[j]);
                        if(isPalindrome(str.toString())&&str.length()>re.length()){
//                            ccc的情况,不能使用append
                            re.replace(0,str.length(), String.valueOf(str));
                        }
                    }
                }
                //结果为一个字符
                if(re.length()==0){
                    re.append(ch[0]);
                }
                return re.toString();
            }
            //判断是否是回文字符串
            private Boolean isPalindrome(String str) {
//                System.out.println(str);
                char[] 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;
            }

            //判断是否是回文字符串
            private Boolean isPalindrome1(String str,int left,int right) {
//                System.out.println(str);
                char[] ch = str.toCharArray();
                right--;
                while(left<right){
                    if(ch[left]!=ch[right]){
                        return false;
                    }
                    left++;
                    right--;
                }
                return true;
            }

            /**
             * 依旧超时
             * @param s
             * @return
             */
            public String longestPalindrome2(String s){
                String ans = "";
                int max = 0;
                int len = s.length();
                for (int i = 0; i < len; i++) {
                    for (int j = i+1; j <= len; j++) {
                        //i----j-1
                        String test = s.substring(i,j);
                        if(isPalindrome(test)&&test.length()>max){
                            ans = s.substring(i,j);
                            max = Math.max(max,ans.length());
                        }
                    }
                }
                return ans;
            }

            public String longestPalindrome4(String s){
                int max = 1;
                int len = s.length();
                int begin=0;
                for (int i = 0; i < len; i++) {
                    for (int j = i+1; j <= len; j++) {
                        if(isPalindrome1(s,i,j)&&j-i>max){
                            begin = i;
                            max = j-i;
                        }
                    }
                }
                return s.substring(begin,begin+max);
            }

            /**
             * 使用动态规划的思想,p(i,j)=p(i+1,j-1)&&s[i]=s[j]
             * 01234
             * babad
             * 看左下角的元素
             * 子串左边界i\子串右边界j   0 1 2 3 4
                 *                 0 1 0 1 0 1
                 *                 1   1 0 1 0
                 *                 2     1 0 1
                 *                 3       1 0
                 *                 4         1
             * @param s
             * @return
             */
            public String longestPalindrome(String s){
                int len = s.length();
                char[] ch = s.toCharArray();
                if(len<2){
                    return s;
                }
                int begin = 0,maxlen=1;
                Boolean[][] dp = new Boolean[len][len];
                for (int i = 0; i < len; i++) {
                    dp[i][i] = true;
                }
                //一列一列的计算
                for (int j = 1; j < len; j++) {
                    for (int i = 0; i < j; i++) {
                        if(ch[i]==ch[j]){
                            //a bab
                            if((j-1)-(i+1)<1){
                                dp[i][j] = true;
                            }else{
                                dp[i][j] = dp[i+1][j-1];
                            }
                        }else{
                            dp[i][j] = false;
                        }
                        if(dp[i][j]&&j-i+1>maxlen){
                            begin = i;
                            maxlen=j-i+1;
                        }
                    }
                }
                return s.substring(begin,begin+maxlen);

            }



            public String longestPalindrome5(String s){
                int maxlen=1,begin=0;
                int len = s.length();
                if(len<2) return s;
                char[] ch = s.toCharArray();
                //表示子串是否是回文串
                Boolean[][] dp = new Boolean[len][len];
                for (int i = 0; i < len; i++) {
                    dp[i][i]=true;
                }

                //枚举字符串右边界,一列一列的填写
                for (int j = 1; j < len; j++) {
                    for (int i = 0; i < j; i++) {
                        if(ch[i] != ch[j]){
                            dp[i][j] = false;
                        }else{
                            //dp[i+1][j-1]只有一个字符,a并且s[i]=s[j]
                            if((j-1)-(i+1)<1){
                                dp[i][j]=true;
                            }else{
                                //a 和 bab都为true;
                                dp[i][j] = dp[i+1][j-1];
                            }
                        }
                        //记录回文长度和起始位置
                        if(dp[i][j] && j-i+1>maxlen){
                            maxlen=j-i+1;
                            begin=i;
                        }
                    }
                }
                return s.substring(begin,begin+maxlen);
            }


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

}
剪绳子 中等
//给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。
//请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18
//。 
//
// 示例 1: 
//
// 输入: 2
//输出: 1
//解释: 2 = 1 + 1, 1 × 1 = 1 
//
// 示例 2: 
//
// 输入: 10
//输出: 36
//解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36 
//
// 提示: 
//
// 
// 2 <= n <= 58 
// 
//
// 注意:本题与主站 343 题相同:https://leetcode-cn.com/problems/integer-break/ 
// Related Topics 数学 动态规划 
// 👍 310 👎 0

package leetcode.editor.cn;
//Java:剪绳子
public class JianShengZiLcof{
    public static void main(String[] args) {
        Solution solution = new JianShengZiLcof().new Solution();
        // TO TEST
        System.out.println(solution.cuttingRope(10));
    }
        //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
            // 输入: 10
//输出: 36
//解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
//            10=3+7 21 2+8 16
//

            /**
             * n长度的绳子,分为m段,m自己定义
             * 10
             * 2:1x10=10,2x8=16,3x7=21,4x6=24,5x5=25
             * 3:1x2x7=17 1x3x6=18 1x4x5=20 2x3x5=30 2x4x4=32 3x3x4=36
             * 4:1x2x3x4=24....
             * 1 2 3 4 5 6 7 8 9
             * 1 9,2 8,3 7,4 6,5 5
             * 1 1 8,1 2 7,1 3 6,1 4 5
             * 2 2 6,2 3 5,2 4 4
             * 3 3 4
             * 弄一个长度为m的数组,使用动态规划,当相等时存储值
             *没解出来,特殊情况太多了
             * @param n
             * @return
             */
            public int cuttingRope1(int n) {
                int m = 1,max=3,re=1;
                int[] nums=new int[n];
//                while(m++<max){
//                    int tmp=m;
//                    while(tmp-->0){
//
//                    }
//
//                }
                //动态定义变量,阿西吧
                //3
//                * 1 1 8,1 2 7,1 3 6,1 4 5
//                        * 2 2 6,2 3 5,2 4 4
//                        * 3 3 4
//                3:1 2, 1 1 1
                int i=1;
                int j=1;
                int k=n-1;
                while(i <= k){
                    while(j <= k){
                        while(i+j+k>n){
                            k--;
                        }
                        while(i+j+k<n){
                            j++;
                        }
                        if(i+j+k==n){
//                            解答失败:
//                            测试用例:3
//                            测试结果:1
//                            期望结果:2
//                            2
                            if(j==1&&i==1&&k!=0){
                                re = Math.max(re, i * 2);
                            }else{
                                re=Math.max(re,i*j*k);
                            }
//                            解答失败:
//       测试用例:4
//       测试结果:2
//       期望结果:4 1 1 2
                            k--;
                        }
                    }
                    i++;
                    j=i;
                    k=n-1;
                }
                return re;
            }

            /**
             * 1.用一个dp数组记录从0到n长度的绳子剪掉后的最大乘积,dp[i]表示长度为i的绳子剪成m段后的乘积,dp[2]=1
             * 2.第一段长度为j,把绳子从长度为2开始剪
             * 3.剪了第一段之后,(i-j)长度可以剪也可以不剪,取最大值max(j*(i-j),j*dp[i-j])
             * 4.第一段长度j取[2,i)
             * dp[i]=max(dp[i],max(j*(i-j),j*dp[i-j]))
             * 1
             * @param n
             * @return
             */
            public int cuttingRope(int n){
                int[] dp = new int[n+1];
                dp[2]=1;
                for (int i = 3; i < n+1; i++) {
                    for (int j = 2; j < i; j++) {
                        dp[i] = Math.max(dp[i],Math.max(j*(i-j),j*dp[i-j]));
                    }
                }
                return dp[n];
            }
}
//leetcode submit region end(Prohibit modification and deletion)

}
剪绳子 II 中等
//给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m - 1]
// 。请问 k[0]*k[1]*...*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘
//积是18。 
//
// 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。 
//
// 
//
// 示例 1: 
//
// 输入: 2
//输出: 1
//解释: 2 = 1 + 1, 1 × 1 = 1 
//
// 示例 2: 
//
// 输入: 10
//输出: 36
//解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36 
//
// 
//
// 提示: 
//
// 
// 2 <= n <= 1000 
// 
//
// 注意:本题与主站 343 题相同:https://leetcode-cn.com/problems/integer-break/ 
// Related Topics 数学 动态规划 
// 👍 135 👎 0

package leetcode.editor.cn;


import java.math.BigInteger;
import java.util.Arrays;

//Java:剪绳子 II
public class JianShengZiIiLcof{
    public static void main(String[] args) {
        Solution solution = new JianShengZiIiLcof().new Solution();
        // TO TEST
        System.out.println(solution.cuttingRope(120));
    }
        //leetcode submit region begin(Prohibit modification and deletion)

    class Solution {
                /**
                 * 大数越界的求余问题
                 * @param n
                 * @return
                 */
        public int cuttingRope(int n) {
            BigInteger[] dp = new BigInteger[n+1];
    //        dp[2] = 1;
            Arrays.fill(dp,BigInteger.valueOf(1));
            for (int i = 3; i < n+1; i++) {
                for (int j = 2; j < i; j++) {
                    dp[i] = dp[i].max(BigInteger.valueOf(j*(i-j)).max(dp[i-j].multiply(BigInteger.valueOf(j))));
                }
            }
    //        727011288
    //        953271190
            return dp[n].mod(BigInteger.valueOf(1000000007)).intValue();
        }



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

}
丑数 中等
// 我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
//
//
//
// 示例:
//
// 输入: n = 10
// 输出: 12
// 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
//
// 说明:
//
//
// 1 是丑数。
// n 不超过1690。
//
//
// 注意:本题与主站 264 题相同:https://leetcode-cn.com/problems/ugly-number-ii/
// Related Topics 哈希表 数学 动态规划 堆(优先队列) 👍 246 👎 0

package leetcode.editor.cn;

// Java:丑数
public class ChouShuLcof {
    public static void main(String[] args) {
        Solution solution = new ChouShuLcof().new Solution();
        // TO TEST
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /**
         * 哈希表,动态规划,堆(优先队列),估计有简化的数学公式,有点难,堆不会 2,3,5 分解质因数,1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。 n 不超过1690。
         * 如果一个数被2或3或5求余后的数位0,这个数就是丑叔 只包含质因子2 3 5,不断的分解分解在分解
         * 
         * 丑数=某较小丑数x某因子(2,3,5), 不确定x2,x3,x5哪个数最小,因此需要选择相乘之后结果更小的那个数
         */
        public int nthUglyNumber(int n) {
            /*int count = 1, num = 2;
            if(n == 1) {
                return 1;
            }
            while (count <= n) {
                if (num % 2 == 0 || num % 3 == 0 || num % 5 == 0) {
                    count++;
                    if (count == n) {
                        return num;
                    }
                }
                num++;
            }
            return num;*/
            int a = 0, b = 0, c = 0;
            int[] dp = new int[n];
            dp[0] = 1;
            for (int i = 0; i < n; i++) {
                int n2 = dp[a] * 2, n3 = dp[b] * 3, n5 = dp[c] * 5;
                dp[i] = Math.min(Math.min(n2, n3), n5);
                if (dp[i] == n2) {
                    a++;
                }
                if (dp[i] == n3) {
                    b++;
                }
                if (dp[i] == n5) {
                    c++;
                }
            }
            return dp[n - 1];
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
跳跃游戏 贪心 数组
// 给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
//
// 数组中的每个元素代表你在该位置可以跳跃的最大长度。
//
// 判断你是否能够到达最后一个下标。
//
//
//
// 示例 1:
//
//
// 输入:nums = [2,3,1,1,4]
// 输出:true
// 解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
//
//
// 示例 2:
//
//
// 输入:nums = [3,2,1,0,4]
// 输出:false
// 解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
//
//
//
//
// 提示:
//
//
// 1 <= nums.length <= 3 * 10⁴
// 0 <= nums[i] <= 10⁵
//
// Related Topics 贪心 数组 动态规划 👍 1591 👎 0

package leetcode.editor.cn;

// Java:跳跃游戏
public class JumpGame {
    public static void main(String[] args) {
        Solution solution = new JumpGame().new Solution();
        System.out.println(solution.canJump(new int[] {3, 2, 1, 0, 4}));
        // TO TEST
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 输入:nums = [2,3,1,1,4]
        // 输出:true
        // 解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
        // 2 4 3 4 8
        // 输入:nums = [3,2,1,0,4]
        // 输出:false
        // 解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
        // 3 3 3 3 8
        /*
         * 贪心 数组 动态规划
         * 1.如果一个起跳点的格子可以跳跃的距离为n,表示后面的n个格子都可以成为起跳点
         * 2.对每一个起跳点的格子都尝试跳一次,把能跳到最远的距离不断更新
         * 3.如果能一直跳到最后,成功
         * 跳出:当起跳点的位置小于当前位置时
         */
        public boolean canJump(int[] nums) {
            int k = 0;
            for (int i = 0; i < nums.length; i++) {
                if (i > k) {
                    return false;
                }
                k = Math.max(k, i + nums[i]);
            }
            return true;
        }

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

}
打家劫舍
// 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上
// 被小偷闯入,系统会自动报警。
//
// 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
//
//
//
// 示例 1:
//
//
// 输入:[1,2,3,1]
// 输出:4
// 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
// 偷窃到的最高金额 = 1 + 3 = 4 。
//
// 示例 2:
//
//
// 输入:[2,7,9,3,1]
// 输出:12
// 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
// 偷窃到的最高金额 = 2 + 9 + 1 = 12 。
//
//
//
//
// 提示:
//
//
// 1 <= nums.length <= 100
// 0 <= nums[i] <= 400
//
// Related Topics 数组 动态规划 👍 1848 👎 0

package leetcode.editor.cn;

// Java:打家劫舍
public class HouseRobber {
    public static void main(String[] args) {
        Solution solution = new HouseRobber().new Solution();
        // TO TEST
        // 2 2 3 4
        System.out.println(solution.rob(new int[] {2, 1, 1, 2}));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 相邻元素的最大值,连续
        // 从k个房子中能偷到的最大金额
        // 1.偷钱k-1间房子,最后一间不偷
        // 2.偷钱k-2间房子和最后一间
        // f(k)=max{f(k-1),H(k-1)+f(k-2)}
        // 当k=0时,没有房子 f(0)=0
        // 当k=1时,只有一个房子,f(1)=H(0)
        // 空间优化,使用两个变量存储中间结果
        public int rob1(int[] nums) {
            if (nums.length == 0) {
                return 0;
            }
            int[] dp = new int[nums.length + 1];
            dp[0] = 0;
            dp[1] = nums[0];
            for (int i = 2; i <= nums.length; i++) {
                dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i - 1]);
            }
            return dp[nums.length];
        }

        public int rob(int[] nums) {
            if (nums.length == 0) {
                return 0;
            }
            int pre = 0;
            int cur = nums[0];
            for (int i = 2; i <= nums.length; i++) {
                int tmp = Math.max(cur, pre + nums[i - 1]);
                pre = cur;
                cur = tmp;
            }
            return cur;
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
打家劫舍 III
// 在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“
// 房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
//
// 计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
//
// 示例 1:
//
// 输入: [3,2,3,null,3,null,1]
// 3 2 3 3 1 中左右
// 2 3 3 3 1
// 3 2 1 3 3
//
// 3
// / \
// 2 3
// \ \
// 3 1
//
// 输出: 7
// 解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
//
// 示例 2:
//
// 输入: [3,4,5,1,3,null,1]
//
//   3
// / \
// 4 5
// / \ \
// 1 3 1
//
// 输出: 9
// 解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.
//
// Related Topics 树 深度优先搜索 动态规划 二叉树 👍 1121 👎 0

package leetcode.editor.cn;

import java.util.HashMap;

// Java:打家劫舍 III
public class HouseRobberIii {
    public static void main(String[] args) {
        Solution solution = new HouseRobberIii().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 {
        // 树 深度优先搜索 动态规划
        // 1.偷前k-1间房子,最后一间不偷
        // 2.偷前k-2间房子和最后一间
        /*
         * 在不能同时选中由父子关系的点的情况下,能选中的点的最大权值和是多少
         * f(o)表示选择o节点的情况下,o节点的子树上被选择的节点的最大权值和;g(o)表示不选择o节点的情况下,o节点的子树上被选择的节点的最大权值和;l和r是o的左右孩子
         * 当o被选中时,o的左右孩子都不能被选中 f(o)=g(l)+g(r)
         * 当o不被选中时,o的左右孩子可以被选中,也可以不被选中,g(o)=max{f(l),g(l)}+max{f(r),g(r)}
         */
        HashMap<TreeNode, Integer> f = new HashMap<TreeNode, Integer>();
        HashMap<TreeNode, Integer> g = new HashMap<TreeNode, Integer>();

        public int rob(TreeNode root) {
            dfs(root);
            return Math.max(f.getOrDefault(root,0),g.getOrDefault(root,0));
        }

        /**
         * 对树进行遍历 1.偷前k-1间,最后一间不偷 2.偷前k-2间房子和最后一间 dp[k]=max(dp[k-1],dp[k-2]+H(k-1))
         */
        public void dfs(TreeNode root) {
            if (root == null) {
                return;
            }
            dfs(root.left);
            dfs(root.right);
//            当o被选中时,o的左右孩子都不能被选中 f(o)=g(l)+g(r)
//            当o不被选中时,o的左右孩子可以被选中,也可以不被选中,g(o)=max{f(l),g(l)}+max{f(r),g(r)}
            f.put(root, root.val + g.getOrDefault(root.left, 0) + g.getOrDefault(root.right, 0));
            g.put(root,Math.max(f.getOrDefault(root.left,0),g.getOrDefault(root.left,0))+Math.max(f.getOrDefault(root.right,0),g.getOrDefault(root.right,0)));
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
最大正方形
// 在一个由 '0' 和 '1' 组成的二维矩阵内,找到只包含 '1' 的最大正方形,并返回其面积。
//
//
//
// 示例 1:
//
//
// 输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"]
// ,["1","0","0","1","0"]]
// 输出:4
//
//
// 示例 2:
//
//
// 输入:matrix = [["0","1"],["1","0"]]
// 输出:1
//
//
// 示例 3:
//
//
// 输入:matrix = [["0"]]
// 输出:0
//
//
//
//
// 提示:
//
//
// m == matrix.length
// n == matrix[i].length
// 1 <= m, n <= 300
// matrix[i][j] 为 '0' 或 '1'
//
// Related Topics 动态规划
// 👍 790 👎 0

package leetcode.editor.cn;

// Java:最大正方形
public class MaximalSquare {
    public static void main(String[] args) {
        Solution solution = new MaximalSquare().new Solution();
        // TO TEST
        // 输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"]
        // ,["1","0","0","1","0"]]
        char[][] ch = {{'1', '0', '1', '0', '0'}, {'1', '0', '1', '1', '1'}, {'1', '1', '1', '1', '1'},
            {'1', '0', '0', '1', '0'}};
        System.out.println(solution.maximalSquare(ch));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /*
         * 1. 定义子问题 2. 写出子问题的递推关系 3. 确定DP数组的计算顺序 4. 空间优化 h(1)=1 h(2)=h(1)+ f(k) = f(k-1)+h(k) i+(k-1)=1(右,下,右下),使用dfs
         * 对于每一个元素,都需要满足右,下,右下都为1的关系 dp[i][j]=min(dp[i+1][j],dp[i][j+1],dp[i+1][j+1])+1的平方 1 4 9 16
         *  输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
         * 1 0 1 0 0
         * 1 0 1 1 1
         * 1 1 1 1 1
         * 1 0 0 1 0
         * 优化:加上一行一列初始化为0,默认为0,只需要将dp数组扩大一行一列
         */
        public int maximalSquare(char[][] matrix) {
            int[][] dp = new int[matrix.length][matrix[0].length];
            int max = 0;
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix[0].length; j++) {
                    if (matrix[i][j] == '1') {
                        dp[i][j] = 1;
                    } else {
                        dp[i][j] = 0;
                    }
                    max = (int)Math.max(Math.pow(dp[i][j], 2), max);
                }
            }
            /* 1 0 1 0 0
            * 1 0 1 1 1
            * 1 1 1 1 1
            * 1 0 0 1 0
            * 
            * 1 0 1 0 0
            * 1 0 1 1 1
            * 1 1 1 2 2
            * 1 0 0 1 0
            * */
            for (int i = 1; i < matrix.length; i++) {
                for (int j = 1; j < matrix[0].length; j++) {
                    if (matrix[i][j] == '1') {
                        dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
                    }
                    max = (int)Math.max(Math.pow(dp[i][j], 2), max);
                }
            }
            return max;

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

}
完全平方数
// 给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
//
// 给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。
//
// 完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
//
//
//
//
// 示例 1:
//
//
// 输入:n = 12
// 输出:3
// 解释:12 = 4 + 4 + 4
//
// 示例 2:
//
//
// 输入:n = 13
// 输出:2
// 解释:13 = 4 + 9
//
//
// 提示:
//
//
// 1 <= n <= 10⁴
//
// Related Topics 广度优先搜索 数学 动态规划 👍 1218 👎 0

package leetcode.editor.cn;

// Java:完全平方数
public class PerfectSquares {
    public static void main(String[] args) {
        Solution solution = new PerfectSquares().new Solution();
        // TO TEST
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 广度优先搜索 数学 动态规划
        /*
         * 给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。
         * 完全平方:h(k)
         * f(i)=f(i-j*j)
         * 
         */
        public int numSquares(int n) {
            int[] dp = new int[n + 1];
            for (int i = 1; i <= n; i++) {
                int min = Integer.MAX_VALUE;
                for (int j = 1; j * j <= i; j++) {
                    min = Math.min(min, dp[i - j * j]);
                }
                dp[i] = min + 1;
            }
            return dp[n];
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
最长递增子序列
// 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
//
// 子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序
// 列。
//
//
// 示例 1:
//
//
// 输入:nums = [10,9,2,5,3,7,101,18]
// 输出:4
// 解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
//
//
// 示例 2:
//
//
// 输入:nums = [0,1,0,3,2,3]
// 输出:4
//
//
// 示例 3:
//
//
// 输入:nums = [7,7,7,7,7,7,7]
// 输出:1
//
//
//
//
// 提示:
//
//
// 1 <= nums.length <= 2500
// -10⁴ <= nums[i] <= 10⁴
//
//
//
//
// 进阶:
//
//
// 你可以设计时间复杂度为 O(n²) 的解决方案吗?
// 你能将算法的时间复杂度降低到 O(n log(n)) 吗?
//
// Related Topics 数组 二分查找 动态规划 👍 2174 👎 0

package leetcode.editor.cn;

import java.util.Arrays;

// Java:最长递增子序列
public class LongestIncreasingSubsequence {
    public static void main(String[] args) {
        Solution solution = new LongestIncreasingSubsequence().new Solution();
        // TO TEST
        System.out.println(solution.lengthOfLIS(new int[] {10, 9, 2, 5, 3, 7, 101, 18}));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 输入:nums = [10,9,2,5,3,7,101,18]
        // 输出:4
        // 解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
        //
        // 从每一个数开始,求出它的序列的长度,时间复杂度和空间复杂度都是n的平方 二分查找 动态规划
        // 状态定义:dp[i]的值表示nums以nums[i]结尾的最长子序列长度
        // 转移方程:每轮计算dp[i]时,遍历[0,i)列表区间。
        // 1.当nums[i]>nums[j]时:nums[i]可以接在nums[j]之后,最长上升子序列长度为dp[j]+1
        // 2.当nums[i]<=nums[j]时:nums[i]无法接在nums[j]之后,上升子序列不成立,跳过 dp[i]=max(dp[i],dp[j]+1) dp[i] = max(dp[i],dp[j]+1) for
        // j in[0,i) 初始状态:所有元素都置为1 返回值:dp列表最大值

        public int lengthOfLIS(int[] nums) {
            if (nums.length == 0) {
                return 0;
            }
            int[] dp = new int[nums.length];
            Arrays.fill(dp, 1);
            int res = 0;
            for (int i = 0; i < nums.length; i++) {
                for (int j = 0; j < i; j++) {
                    if (nums[j] < nums[i]) {
                        dp[i] = Math.max(dp[i], dp[j] + 1);
                    }
                }
                res = Math.max(res, dp[i]);
            }
            return res;
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
最佳买卖股票时机含冷冻期
// 给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
//
// 设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
//
//
// 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
// 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
//
//
// 示例:
// 4-1
// 输入: [1,2,3,0,2]
// 输出: 3
// 解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
// Related Topics 数组 动态规划 👍 1034 👎 0

package leetcode.editor.cn;

// Java:最佳买卖股票时机含冷冻期
public class BestTimeToBuyAndSellStockWithCooldown {
    public static void main(String[] args) {
        Solution solution = new BestTimeToBuyAndSellStockWithCooldown().new Solution();
        // TO TEST
        System.out.println(solution.maxProfit(new int[] {1, 2, 3, 0, 2}));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 输入: [1,2,3,0,2]
        // 输出: 3
        // 解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
        // 必须在再次购买前出售掉之前的股票
        // 由3种选择:不买,买入或者卖出
        // 三种状态:1.持有一支股票,对应的累计最大收益为f[i][0] 卖出 第i-1天1的状态(买入)。 买入:第i-1条就是3的状态 f[i][0]=max(f[i-1][0],f[i-1][2]-price[i])
        // 2.目前不持有任何股票,处于冷冻期中,对应的累计最大收益为f[i][1] 卖出后冷冻状态 第i-1天为1的状态 f[i][1] = f[i-1][0]+price[i]
        // 3.不持有任何股票,不处于冷冻期中,f[i][2] 不买 第i-1天为2或者3的状态 f[i][2]=max(f[i-1][1],f[i-1][2])
        // max(f[i][0],f[i][1],f[i][2])
        // 第0天 f[0][0] = -price[0] f[0][1]=0 f[0][2]=0
        public int maxProfit(int[] prices) {
            int[][] dp = new int[prices.length][3];
            dp[0][0] = -prices[0];
            dp[0][1] = 0;
            dp[0][2] = 0;
            for (int i = 1; i < prices.length; i++) {
                dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2] - prices[i]);
                dp[i][1] = dp[i - 1][0] + prices[i];
                dp[i][2] = Math.max(dp[i - 1][1], dp[i - 1][2]);
            }
            return Math.max(Math.max(dp[prices.length - 1][0], dp[prices.length - 1][1]), dp[prices.length - 1][2]);
        }
    }
    // leetcode submit region end(Prohibit modification and deletion)

}
回文子串 字符串 动态规划
// 给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。
//
// 回文字符串 是正着读和倒过来读一样的字符串。
//
// 子字符串 是字符串中的由连续字符组成的一个序列。
//
// 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
//
//
//
// 示例 1:
//
//
// 输入:s = "abc"
// 输出:3
// 解释:三个回文子串: "a", "b", "c"
//
//
// 示例 2:
//
//
// 输入:s = "aaa"
// 输出:6
// 解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
//
//
//
// 提示:
//
//
// 1 <= s.length <= 1000
// s 由小写英文字母组成
//
// Related Topics 字符串 动态规划 👍 775 👎 0

package leetcode.editor.cn;

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

// Java:回文子串
public class PalindromicSubstrings {
    public static void main(String[] args) {
        Solution solution = new PalindromicSubstrings().new Solution();
        // TO TEST
        System.out.println(solution.countSubstrings("aaa"));
    }

    // leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        /*
         * 回文子串
         * 字符串的的排列+判断是否是回文字符串
         * 全排列:depth,used
         * 这是连续的子串,不能这样解答
         */
        public int countSubstrings1(String s) {
            List<StringBuilder> list = new LinkedList<>();
            int depth = 0, count = 0;
            boolean[] used = new boolean[s.length()];
            Arrays.fill(used, false);
            StringBuilder str = new StringBuilder();
            dfs(s, str, 0, list, depth, used);
            list.remove(0);
            for (StringBuilder sb : list) {
                if (isPali(sb)) {
                    count++;
                }
            }
            return count;
        }

        private boolean isPali(StringBuilder sb) {
            boolean f = true;
            for (int i = 0; i < sb.length() / 2; i++) {
                if (sb.charAt(i) != sb.charAt(sb.length() - i - 1)) {
                    f = false;
                }
            }
            return f;
        }

        private void dfs(String s, StringBuilder str, int begin, List<StringBuilder> list, int depth, boolean[] used) {
            list.add(new StringBuilder(str));
            if (depth == s.length()) {
                return;
            }
            for (int i = begin; i < s.length(); i++) {
                if (used[i])
                    continue;
                str.append(s.charAt(i));
                used[i] = true;
                dfs(s, str, i, list, depth + 1, used);
                used[i] = false;
                str.deleteCharAt(str.length() - 1);
            }

        }

        /*
         * 动态规划法
         * dp[i][j]表示字符串在[i,j]区间的子串是否是回文字符串
         * 状态转移方程:s[i]==s[j]&&(j-i<2||dp[i+1][j-1]),dp[i][j]=true,否则为false
         * 
         */
        public int countSubstrings(String s) {
            boolean[][] dp = new boolean[s.length()][s.length()];
            int ans = 0;
            for (int j = 0; j < s.length(); j++) {
                for (int i = 0; i <= j; i++) {
                    if (s.charAt(i) == s.charAt(j) && (j - i < 2 || dp[i + 1][j - 1])) {
                        dp[i][j] = true;
                        ans++;
                    }
                }
            }
            return ans;
        }
    }
    // 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)

}
复杂链表的复制 中等
//请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指
//向链表中的任意节点或者 null。 
//
// 
//
// 示例 1: 
//
// 
//
// 输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
//输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
// 
//
// 示例 2: 
//
// 
//
// 输入:head = [[1,1],[2,1]]
//输出:[[1,1],[2,1]]
// 
//
// 示例 3: 
//
// 
//
// 输入:head = [[3,null],[3,0],[3,null]]
//输出:[[3,null],[3,0],[3,null]]
// 
//
// 示例 4: 
//
// 输入:head = []
//输出:[]
//解释:给定的链表为空(空指针),因此返回 null。
// 
//
// 
//
// 提示: 
//
// 
// -10000 <= Node.val <= 10000 
// Node.random 为空(null)或指向链表中的节点。 
// 节点数目不超过 1000 。 
// 
//
// 
//
// 注意:本题与主站 138 题相同:https://leetcode-cn.com/problems/copy-list-with-random-point
//er/ 
//
// 
// Related Topics 哈希表 链表 
// 👍 314 👎 0

package leetcode.editor.cn;

import java.util.HashMap;

//Java:复杂链表的复制
public class FuZaLianBiaoDeFuZhiLcof {
    public static void main(String[] args) {
        Solution solution = new FuZaLianBiaoDeFuZhiLcof().new Solution();
        // TO TEST
    }
    //leetcode submit region begin(Prohibit modification and deletion)
// Definition for a Node.

    class Solution {
        /**
         * 分为两次复制,第一次复制next节点
         * 第二次复制random节点指针
         * 怎么找节点呢
         * 哈希表存储节点
         * 构建原链表系欸但和新链表对应节点的键值对映射关系,再遍历构建新链表各节点的next和random引用指向
         * 1.若头结点head为空节点,直接返回null
         * 2.初始化:哈希表dic,节点cur指向头节点
         * 3.复制链表:
         * 1.建立新节点,并向dic添加键值对(原本cur节点,新cur节点)
         * 2.cur遍历至原链表下一节点
         * 4.构建新链表的引用指向
         * 1.构建新节点的next和random引用指向
         * 2.cur遍历至原链表下一节点
         * 5.返回值:新链表的头节点dic[cur]
         *
         * @param head
         * @return
         */
        public Node copyRandomList1(Node head) {
            if (head == null) return null;
            HashMap<Node, Node> map = new HashMap<Node, Node>();
            Node cur = head;
            map.put(cur, head);
            //复制各个节点,建立原节点---新节点的map映射关系
            while (cur != null) {
                map.put(cur, new Node(cur.val));
                cur = cur.next;
            }
            cur = head;
            //构建新链表的next和random指向
            while (cur != null) {
                map.get(cur).next = map.get(cur.next);
                map.get(cur).random = map.get(cur.random);
ublic 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)

}
复杂链表的复制 中等
//请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指
//向链表中的任意节点或者 null。 
//
// 
//
// 示例 1: 
//
// 
//
// 输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
//输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
// 
//
// 示例 2: 
//
// 
//
// 输入:head = [[1,1],[2,1]]
//输出:[[1,1],[2,1]]
// 
//
// 示例 3: 
//
// 
//
// 输入:head = [[3,null],[3,0],[3,null]]
//输出:[[3,null],[3,0],[3,null]]
// 
//
// 示例 4: 
//
// 输入:head = []
//输出:[]
//解释:给定的链表为空(空指针),因此返回 null。
// 
//
// 
//
// 提示: 
//
// 
// -10000 <= Node.val <= 10000 
// Node.random 为空(null)或指向链表中的节点。 
// 节点数目不超过 1000 。 
// 
//
// 
//
// 注意:本题与主站 138 题相同:https://leetcode-cn.com/problems/copy-list-with-random-point
//er/ 
//
// 
// Related Topics 哈希表 链表 
// 👍 314 👎 0

package leetcode.editor.cn;

import java.util.HashMap;

//Java:复杂链表的复制
public class FuZaLianBiaoDeFuZhiLcof {
    public static void main(String[] args) {
        Solution solution = new FuZaLianBiaoDeFuZhiLcof().new Solution();
        // TO TEST
    }
    //leetcode submit region begin(Prohibit modification and deletion)
// Definition for a Node.

    class Solution {
        /**
         * 分为两次复制,第一次复制next节点
         * 第二次复制random节点指针
         * 怎么找节点呢
         * 哈希表存储节点
         * 构建原链表系欸但和新链表对应节点的键值对映射关系,再遍历构建新链表各节点的next和random引用指向
         * 1.若头结点head为空节点,直接返回null
         * 2.初始化:哈希表dic,节点cur指向头节点
         * 3.复制链表:
         * 1.建立新节点,并向dic添加键值对(原本cur节点,新cur节点)
         * 2.cur遍历至原链表下一节点
         * 4.构建新链表的引用指向
         * 1.构建新节点的next和random引用指向
         * 2.cur遍历至原链表下一节点
         * 5.返回值:新链表的头节点dic[cur]
         *
         * @param head
         * @return
         */
        public Node copyRandomList1(Node head) {
            if (head == null) return null;
            HashMap<Node, Node> map = new HashMap<Node, Node>();
            Node cur = head;
            map.put(cur, head);
            //复制各个节点,建立原节点---新节点的map映射关系
            while (cur != null) {
                map.put(cur, new Node(cur.val));
                cur = cur.next;
            }
            cur = head;
            //构建新链表的next和random指向
            while (cur != null) {
                map.get(cur).next = map.get(cur.next);
                map.get(cur).random = map.get(cur.random);
                cur = 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值