LeetCode习题集 有些题可能直接略过了,整理一下之前刷leetcode

331. 验证二叉树的前序序列化

序列化二叉树的一种方法是使用前序遍历。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如 #。

     _9_
    /   \
   3     2
  / \   / \
 4   1  #  6
/ \ / \   / \
# # # #   # #

例如,上面的二叉树可以被序列化为字符串 "9,3,4,#,#,1,#,#,2,#,6,#,#",其中 # 代表一个空节点。

给定一串以逗号分隔的序列,验证它是否是正确的二叉树的前序序列化。编写一个在不重构树的条件下的可行算法。

每个以逗号分隔的字符或为一个整数或为一个表示 null 指针的 '#' 。

你可以认为输入格式总是有效的,例如它永远不会包含两个连续的逗号,比如 "1,,3" 。

示例 1:

输入: "9,3,4,#,#,1,#,#,2,#,6,#,#" 输出: true 示例 2:

输入: "1,#" 输出: false 示例 3:

输入: "9,#,#,1" 输出: false

只要保证初度和入度相等即可,读一个不管是否为数字就应该-1,如果为数字就表示可以有两个孩子+2 
先按照逗号把各个结点的值分开
如果开始的是空结点,并且还是偶数个结点返回false
如果开始时空结点,并且长度是1,说明只有一个根为空,返回true
然后循环数组,
每次循环count-1,count表示遍历的结点,count遇到普通节点,遍历了当前结点了就-1 ,
如果不是空结点,就可以多两个空结点,该节点的左右子树可以为空
如果count<0 说明结点多遍历了很多空结点,无法构成二叉树
class Solution {
   public boolean isValidSerialization(String preorder) {
        String[]temp = preorder.split(",");
        if(temp[0].equals("#")&&temp.length==1) return true;
        if(temp[0].equals("#")||temp.length%2==0) return false;
        int count = 1;
        for(int i =0;i<temp.length;i++)
        {   
            count--;
            if(count<0) return false;
            if(!temp[i].equals("#"))
                count+=2;
        }
        return count == 0;
    }
}

332. 重新安排行程

给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 出发。

说明:

如果存在多种有效的行程,你可以按字符自然排序返回最小的行程组合。例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前 所有的机场都用三个大写字母表示(机场代码)。 假定所有机票至少存在一种合理的行程。 示例 1:

输入: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]] 输出: ["JFK", "MUC", "LHR", "SFO", "SJC"] 示例 2:

输入: [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]] 输出: ["JFK","ATL","JFK","SFO","ATL","SFO"] 解释: 另一种有效的行程是 ["JFK","SFO","ATL","JFK","ATL","SFO"]。但是它自然排序更大更靠后。

map保存每个点的起始点到可以去的终点,多个终点就放到集合里面,这里用到的是前序队列,
    前序队列可以让终点的地点,保持一个自然顺序最小,因为这里要让自然顺序更小,
    
    dfs遍历每个结点,如果当前点的终点存在,就不断的dfs调用结点
class Solution {
  private Map<String, PriorityQueue<String>> map = new HashMap<>();

private List<String> resList = new LinkedList<>();

public List<String> findItinerary(List<List<String>> tickets) {
    for (List<String> ticket : tickets) {
        String src = ticket.get(0);
        String dst = ticket.get(1);
        if (!map.containsKey(src)) {
            PriorityQueue<String> pq = new PriorityQueue<>();
            map.put(src, pq);
        }
        map.get(src).add(dst);
    }
    dfs("JFK");
    return resList;
}

private void dfs(String src) {
    PriorityQueue<String> pq = map.get(src);
    while (pq != null && !pq.isEmpty())
        dfs(pq.poll());
    ((LinkedList<String>) resList).addFirst(src);
}
}

334. 递增的三元子序列

给定一个未排序的数组,判断这个数组中是否存在长度为 3 的递增子序列。

数学表达式如下:

如果存在这样的 i, j, k, 且满足 0 ≤ i < j < k ≤ n-1, 使得 arr[i] < arr[j] < arr[k] ,返回 true ; 否则返回 false 。 说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1) 。

示例 1:

输入: [1,2,3,4,5] 输出: true 示例 2:

输入: [5,4,3,2,1] 输出: false

class Solution {
      public boolean increasingTriplet(int[] nums) {
        if (nums==null||nums.length<3) return false;
        int big=Integer.MAX_VALUE,small=Integer.MAX_VALUE;
        for (int i:nums){
            // 通过if的结构保证递增!
            if (i<=small) small=i;
            // 走到这一步说明这个值大于前面的值(i>small)
            else if (i<=big) big=i;
            // 走到这一步说明这个值大于前面的两个值(i>big>small)
            else return true;
        }
        return false;
    }
}

335. 路径交叉

给定一个含有 n 个正数的数组 x。从点 (0,0) 开始,先向北移动 x[0] 米,然后向西移动 x[1] 米,向南移动 x[2] 米,向东移动 x[3] 米,持续移动。也就是说,每次移动后你的方位会发生逆时针变化。

编写一个 O(1) 空间复杂度的一趟扫描算法,判断你所经过的路径是否相交。

示例 1:

┌───┐
│   │
└───┼──>
    │

输入: [2,1,1,2] 输出: true 示例 2:

┌──────┐
│      │
│
│
└────────────>

输入: [1,2,3,4] 输出: false 示例 3:

┌───┐
│   │
└───┼>

输入: [1,1,1,1] 输出: true

判断四个方向是不是会超过以前走过的路程长度
    两个点减就是走的长度,
    四个方向的长度,走的长度要比以前反方向的长度长,就存在交叉
    如果大于4,说明是第二轮四个方向的循环,要判断上次是不是大于(上次逆方向的长度-上次同方向的长度)
    如果大于5的话,不仅要判断上面 4 的时候的一个方向,还要判断另一个方向
class Solution {
      public boolean isSelfCrossing(int[] x) {
        for (int i = 3; i < x.length; ++i) {
            if (x[i] >= x[i - 2] && x[i - 3] >= x[i - 1]) {
                return true;
            }
            if (i >= 4 && x[i-1] == x[i-3] && x[i] >= x[i-2] - x[i-4]) {
                return true;
            }
            if (i >= 5 && x[i-2] >= x[i-4] && x[i-3] >= x[i-1] && x[i-1] >= x[i-3] - x[i-5] && x[i] >= x[i-2] - x[i-4]) {
                return true;
            }
        }
        return false;
    }
}

336. 回文对

给定一组唯一的单词, 找出所有不同 的索引对(i, j),使得列表中的两个单词, words[i] + words[j] ,可拼接成回文串。

示例 1:

输入: ["abcd","dcba","lls","s","sssll"] 输出: [[0,1],[1,0],[3,2],[2,4]] 解释: 可拼接成的回文串为 ["dcbaabcd","abcddcba","slls","llssssll"] 示例 2:

输入: ["bat","tab","cat"] 输出: [[0,1],[1,0]] 解释: 可拼接成的回文串为 ["battab","tabbat"]

PS: 字符串反转构建字典树,再判定当前字符串在字典树中是否存在,如存在则证明存在其他串的子串镜像和该串相等,定义子串平均长度为k,则复杂度为O(N * K ^ 2)

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

  
    public List<List<Integer>> palindromePairs(String[] words) {
        if (words == null || words.length == 0) {
            return null;
        }

        ans = new ArrayList<>();
        TrieNode root = new TrieNode();

        for (int i = 0; i < words.length; i++) {
            addWord(root, words[i], i);
        }

        for (int i = 0; i < words.length; i++) {
            find(root, words[i], i);
        }
        return ans;
    }

    private static class TrieNode {
        //当前字符串的数组位置,下游节点,以及能构成当前串or回文子串节点的数组位置集合
        int index;
        TrieNode[] next;
        List<Integer> palindIndex;

        public TrieNode() {
            index = -1;
            next = new TrieNode[26];
            palindIndex = new ArrayList<>();
        }
    }

    private static void addWord(TrieNode root, String word, int index) {
        for (int i = word.length() - 1; i >= 0; i--) {
            int ch = word.charAt(i) - 'a';
            if (root.next[ch] == null) {
                root.next[ch] = new TrieNode();
            }

            if (isPalindrome(word, 0, i)) {
                root.palindIndex.add(index);
            }
            root = root.next[ch];
        }
        root.index = index;
        root.palindIndex.add(index);
    }

    private static void find(TrieNode root, String word, int index) {
        for (int i = 0; i < word.length(); i++) {
            //待匹配串比字典树子串长,如asadcc匹配树上的asad
            if (root.index != -1 && root.index != index && isPalindrome(word, i, word.length() - 1)) {
                ans.add(Arrays.asList(index, root.index));
            }
            //System.out.println("root index:" + root.index);
            if (root.next[word.charAt(i) - 'a'] == null) {
                return;
            }
            root = root.next[word.charAt(i) - 'a'];
        }
        //待匹配串比字典树子串短,如asad匹配树上的asadcc
        for (int i : root.palindIndex) {
            if (i != index) {
                ans.add(Arrays.asList(index, i));
            }
        }
    }

    private static boolean isPalindrome(String string, int l, int r) {
        while (l < r) {
            if (string.charAt(l++) != string.charAt(r--)) {
                return false;
            }
        }
        return true;
    }
}

338. 比特位计数

给定一个非负整数 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)来执行此操作。

	i & (i - 1)可以去掉i最右边的一个1(如果有),因此 i & (i - 1)是比 i 小的,
        而且i & (i - 1)的1的个数已经在前面算过了,所以i的1的个数就是 i & (i - 1)的1的个数加上1
class Solution {
    public int[] countBits(int num) {
        int[] res = new int[num + 1];
        for(int i = 1;i<= num;i++){  //注意要从1开始,0不满足
            res[i] = res[i & (i - 1)] + 1;
        }
        return res;
    }
}