2021年9月10日

239. 滑动窗口问题

处理窗口的滑动路径

https://leetcode-cn.com/problems/sliding-window-maximum/

public int[] maxSlidingWindow(int[] nums, int k) {
        if( nums == null || k<1 || k > nums.length ) return null;
        int len = nums.length;
        int[] window = new int[len - k +1];
        int idx = 0;
        Deque<Integer> deque = new LinkedList<>();

        for(int i = 0; i<len; i++){
            // 非空说明里面有东西可以弹出,判断什么样的东西可以弹出
            // 进入的数字比较大,把里面的数字都弹出
            while(!deque.isEmpty() && nums[deque.peekLast()] < nums[i]){
                deque.pollLast();
            }
            // 不论如何,新遇到的数字都要加进去在里面进行计算
            deque.offer(i);
            // 当前位置减去窗口大小就是过期的idx(窗口前一个元素)
            if(deque.peekFirst() == i-k) {
                deque.pollFirst();
            }
            // 判断当前的i是否有效(i必须走到窗子的最右边那个元素才开始)
            if(i >= k-1) {
                window[idx++] = nums[deque.peekFirst()];
            }
        }
        return window;
    }

最长字串问题

中间扩撒法

https://leetcode-cn.com/problems/longest-palindromic-substring

public String longestPalindrome(String s) {
    if(s.length() == 1) return s;
    int len = 0;
    int resBegin = 0;
    int resLen = 1;
    int l = 0;
    int r = 0;

    for (int i = 0; i < s.length(); i++ ) {
        l = i-1;
        r = i+1;
        // 向左边扩容和向右边扩容
        while(l>=0 && s.charAt(l) == s.charAt(i)) {
            l--;
            len++;
        }
        while(r<s.length() && s.charAt(r) == s.charAt(i)) {
            r++;
            len++;
        }
        // 向两边一起扩容
        while( l>=0 && r<s.length() && s.charAt(l) == s.charAt(r) ) {
            l--;
            r++;
            len+=2;
        }
        if (len > resLen) {
            resLen = len;
            resBegin = l;
        }
        len = 1;
    }
    return s.substring(resBegin+1, resLen+resBegin+1);
}

42. 接雨水

双指针问题
https://leetcode-cn.com/problems/trapping-rain-water/

class Solution {
    public int trap(int[] height) {
        if (height == null || height.length == 0) return -1;
        // 找到最高的点,接水问题最高点很重要!
        int maxIdx = 0;
        int res = 0;
        for(int i = 0; i<height.length; i++) {    
           maxIdx = height[i] > height[maxIdx] ? i : maxIdx;
        }
        // 累加左边的水量,一个一个算
        int maxleft = 0;
        for(int i=0; i<maxIdx; i++) {
            if(height[i] > maxleft) {
                maxleft = height[i];
            }else {
                res += (maxleft - height[i]);
            }
        }
        // 累加右边的水量,一个一个算(从右边开始)
        int maxRight = 0;
        for(int i = height.length-1; i>maxIdx; i--) {
            if(maxRight < height[i]){
                maxRight = height[i];
            }else {
                res += (maxRight - height[i]);
            }
        }

        return res;
    }
}

判断是否二叉树 后续遍历的序列

链接:
二叉搜索树的后序遍历序列

class Solution {
    public boolean verifyPostorder(int[] postorder) {
        return verify(postorder, 0, postorder.length-1);
    }

    public static boolean verify(int[] postorder, int l, int r) {
        if( l >= r ) return true;

        // 找到前段落区域的划分点i-1,r是最右边的节点位置
        int i = 0;
        for (;i<r;i++) {
            if(postorder[i] > postorder[r]) break;
        }

        // 判断右树是否合格(右树都比根节点的数字大,遇到小的return false)
        for (int tmp = i; tmp<r; tmp++) {
            if(postorder[tmp] < postorder[r]) return false;
        }
        
        if(i==0 || i==r-1) {
            // i在两端,无法区分,只能说明最后的节点确实是根节点
            return verify(postorder, l, r-1);
        }else {
            // i落在中间的位置,需要将两边分开来判断
            return verify(postorder, l, i-1) && verify(postorder, i, r-1);
        }
    }
}

2021年9月11日

求二叉树的深度

dfs思想

https://leetcode-cn.com/problems/er-cha-shu-de-shen-du-lcof/

public static int inorder(TreeNode node) {
    if(node == null) return 0;
    return Math.max(inorder(node.left), inorder(node.right))+1;
}

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先【简单】

https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-zui-jin-gong-gong-zu-xian-lcof/

使用set和HashMap来进行处理,就变得简单了很多

class Solution {
    HashMap<TreeNode, TreeNode> fatherMap = new HashMap<>();
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // 加载好fatherMap,维护好哈希表
        loadFatherMap(root, null);

        // p节点的向上走的路径保存起来在HashSet的path中
        Set<TreeNode> path = new HashSet<>();
        while (fatherMap.containsKey(p)) {
            path.add(p);
            p = fatherMap.get(p);
        }

        // 找到q节点在path中的位置(一定存在,大家的父亲都是一样)
        while (!path.contains(q)) {
            q = fatherMap.get(q);
        }
        return q;
    }

    public void loadFatherMap(TreeNode cur, TreeNode father) {
        if (cur == null) return;
        fatherMap.put(cur, father);
        loadFatherMap(cur.left, cur);
        loadFatherMap(cur.right, cur);
    }
}

2021年9月13日

11. 盛最多水的容器

【数算】复习1_字符串

双指针思想,主要集中在找最多水的节点。
https://leetcode-cn.com/problems/container-with-most-water/

class Solution {
    public int maxArea(int[] height) {
        int maxArea = 0;
        int l = 0;
        int r = height.length-1;
        while(l<r) {
            maxArea = Math.max(maxArea, (r-l) *  Math.min(height[l], height[r]));
            if(height[l] < height[r]) {
                l++;
            }else {
                r--;
            }
        }
        return maxArea;
    }
}

merge合并算法(排序算法之一):

分治思想

public static void mergeSort(int[] arr, int left, int right) {
    //如果左边索引小于右边就可以一直分,l=r时,就是分到只剩一个数了
    if (left < right) {
        int mid = left + ((right - left) >> 1);
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        // 最关键的操作:合并
        merge(arr, left, mid, right);
    }

}
public static void merge(int[] arr, int l, int mid, int r) {
	// 建立中转数组
    int[] temp = new int[r - l + 1];
    int tIdx = 0;
    int i = l;
    int j = mid + 1;

    while (i <= mid && j <= r) {
        temp[tIdx++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
    }
    while (i <= mid) {
        temp[tIdx++] = arr[i++];
    }
    while (j <= r) {
        temp[tIdx++] = arr[j++];
    }

    tIdx = 0;
    while (l <= r) {
        arr[l++] = temp[tIdx++];
    }
}

前缀树的实现:

《实现搜索引擎的搜索关键词提示功能?》的很关键的算法
通过startsWith方法可以很便捷的确认接下来可能到达的路径是否可行,Trie也叫做字典树,关键在于【前缀匹配查找】
Trie 树的变体有很多,都可以在一定程度上解决内存消耗的问题。比如,缩点优化,就是对只有一个子节点的节点,而且此节点不是一个串的结束节点,可以将此节点与子节点合并。

1、Trie 树中用到了指针,所以,对缓存并不友好,性能上会打个折扣。
2、要求字符串的前缀重合比较多,不然空间消耗会变大很多。
3、但是在搜索的时候、自动补全的时候大有益处(平时的输入法就自带这个功能)

class Trie {

    public static class TrieNode{
        public int passCount;
        public int endCount;
        // 结点所走过的边
        public TrieNode[] paths; 

        public TrieNode(){
            passCount = 0;  
            endCount = 0;
            paths = new TrieNode[26];
        }
    }

    TrieNode root;
    /** Initialize your data structure here. */
    public Trie() {
        root = new TrieNode();
    }
    
    /** trie树中插入一个字符串 */
    public void insert(String word) {
        if (word == null || word.length() == 0) return;
        char[] chs = word.toCharArray();
        TrieNode cur = root;
        cur.passCount = 1;

        for (int i=0; i<chs.length; i++) {
            int idx = chs[i] - 'a';
            //不存在就铺个路,存在就往下走
            if (cur.paths[idx] == null) {
                cur.paths[idx] = new TrieNode();
            }
            cur = cur.paths[idx];
            cur.passCount++;
        }
        cur.endCount++;
    }
    
    /** Returns if the word is in the trie. */
    public boolean search(String word) {
        if (word == null || word.length() == 0) return false;
        char[] chs = word.toCharArray();
        TrieNode cur = root;
        for (int i = 0; i<chs.length; i++ ) {
            int idx = chs[i] - 'a';
            if (cur.paths[idx] == null) {
                return false;
            }
            cur = cur.paths[idx];
        }
        if (cur.endCount > 0 ) return true;
        return false;
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    public boolean startsWith(String prefix) {
        if (prefix == null) return false;

        char[] chs = prefix.toCharArray();
        TrieNode cur = root;
        for (int i = 0; i<chs.length; i++) {
            int idx = chs[i] - 'a';
            if (cur.paths[idx] == null) {
                return false;
            }
            cur = cur.paths[idx];
        }
        return true;
    }
}

/**
 * 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);
 */

763. 划分字母区间

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。

https://leetcode-cn.com/problems/partition-labels/

class Solution {
    public List<Integer> partitionLabels(String s) {
        // 创建map,让map装载每个字母的最大限度。
        HashMap<Character, Integer> map = new HashMap<>();
        List<Integer> list = new ArrayList<>();
        for(int i =0;i<s.length(); i++) {
            map.put(s.charAt(i),i);
        }
        // 用start来管理下一次开始的位置,不断找寻
        int start = 0;
        int last = 0;
        for(int i = 0; i<s.length(); i++) {
            last = Math.max(last, map.get(s.charAt(i)));
            if(last == i) {
                list.add(last - start + 1);
                start = last + 1;
            }
        }

        return list;
    }
}

小优化:讲HashMap表改为数组来作为缓存,更加节省空间

class Solution {
    public List<Integer> partitionLabels(String s) {
        // 创建map数组,让map装载每个字母的最大限度。
        int[] map = new int[26];
        List<Integer> list = new ArrayList<>();
        for(int i =0;i<s.length(); i++) {
            map[s.charAt(i)-'a'] = i;
        }
        // 用start来管理下一次开始的位置,不断找寻
        int start = 0;
        int last = 0;
        for(int i = 0; i<s.length(); i++) {
            last = Math.max(last, map[s.charAt(i)-'a']);
            if(last == i) {
                list.add(last - start + 1);
                start = last + 1;
            }
        }

        return list;
    }
}

821、字符最短距离:

这题主要是两头遍历的思想,本质还是贪心算法
【数算】复习1_字符串_02

class Solution {
    public int[] shortestToChar(String s, char c) {
        int[] res = new int[s.length()];
        int meet = Integer.MIN_VALUE >> 1;
        for (int i = 0; i<s.length(); i++) {
            // 只有当遇到了才更新meet为遇到的地址
            if ( s.charAt(i) == c ) meet = i;
            res[i] = i - meet;
        } 

        for (int i = s.length()-1; i>=0; i--) {
            if ( s.charAt(i) == c ) meet = i;
            int tmp = meet - i;
            // 取二者的最小值
            res[i] = Math.min(res[i], tmp);
        }

        return res;
    }
}

8. 字符串转换整数 (atoi)

巨有意思的题目:
对于这题,要考虑到各种情况的发生,特别是数字与非数字的处理,还有数字的越界问题,分门别类处理好问题就不大

https://leetcode-cn.com/problems/string-to-integer-atoi/

【数算】复习1_算法_03

class Solution {
    public int myAtoi(String s) {
        s = s.trim();
        if(s == "" || s.length() == 0) return 0;
        long res = 0;

        // 处理首位,只处理“+/-”符号,再对以下的进行处理
        int flag = 1;
        int i = 0;
        if (s.charAt(0) == '+') {
            flag = 1; i++;
        }else if (s.charAt(0) == '-') {
            flag = -1; i++;
        }

        for (;i < s.length(); i++) {
            char ch = s.charAt(i);
            if (ch<='9' && ch>='0') {
                // 1、属于数字:
                res = res*10 + (ch-'0');
            }else {
                // 2、输入的不属于数字
                return (int)res*flag;
            }
            if(res > Integer.MAX_VALUE && flag == -1) {
                // 1.1、 纯数字越界最大值
                return Integer.MIN_VALUE;
            }else if (res > Integer.MAX_VALUE && flag == 1) {
                // 1.2、 纯数字越界最小值 
                return Integer.MAX_VALUE;
            }
        }
        return (int)res * flag;
    }
}