Daily LeetCode – day0056 0707. Design Linked List

// 0707. Design Linked List
class MyLinkedList {
    private Node head;
    private Node tail;
    private int size;

    public MyLinkedList() {
        this.head = null;
        this.tail = null;
        this.size = 0;
    }

    public int get(int index) {
        if (index < 0 || index >= this.size) return -1;
        Node curr = this.head;
        while (index > 0) {
            --index;
            curr = curr.next;
        }
        return curr.val;
    }

    public Node getNodeAt(int index) {
        Node curr = head;
        while (index > 0) {
            --index;
            curr = curr.next;
        }
        return curr;
    }

    public void addAtHead(int val) {
        Node node = new Node(val);
        if (this.size == 0) {
            this.head = node;
            this.tail = node;
        } else {
            node.next = this.head;
            this.head = node;
        }
        ++this.size;
    }

    public void addAtTail(int val) {
        Node node = new Node(val);
        if (this.size == 0) {
            this.head = node;
            this.tail = node;
        } else {
            this.tail.next = node;
            this.tail = node;
        }
        ++this.size;
    }

    public void addAtIndex(int index, int val) {
        if (index < 0 || index > this.size) return;
        if (index == 0) {
            addAtHead(val);
        } else if (index == this.size) {
            addAtTail(val);
        } else {
            Node previous = getNodeAt(index - 1);
            Node next = previous.next;
            Node curr = new Node(val);
            previous.next = curr;
            curr.next = next;
            ++this.size;
        }
    }

    public void deleteFirst() {
        if (this.size == 0) return;
        else if (this.size == 1) {
            this.head = null;
            this.tail = null;
        } else {
            Node curr = this.head;
            Node next = curr.next;
            curr.next = null;
            this.head = next;
        }
        --this.size;
    }

    public void deleteLast() {
        if (this.size == 0) return;
        else if (this.size == 1) {
            this.head = null;
            this.tail = null;
        } else {
            Node secondLast = getNodeAt(this.size - 2);
            secondLast.next = null;
            this.tail = secondLast;
        }
        this.size--;
    }

    public void deleteAtIndex(int index) {
        if (index < 0 || index >= this.size) return;
        if (index == 0) {
            deleteFirst();
        } else if (index == this.size - 1) {
            deleteLast();
        } else {
            Node previous = getNodeAt(index - 1);
            Node curr = previous.next;
            previous.next = previous.next.next;
            --this.size;
        }
    }
}

class Node {
    Node next = null;
    int val = 0;

    public Node(int val) {
        this.val = val;
    }
}
学习笔记:
今天这是一道设计题。
设计链表,链表是个比较有逻辑的数据结构。
先看了老外的代码,借鉴了不少,还进行了优化改良。
这里其实一共8个函数:
根据下标得到值,
根据下标得到结点,(题目没要求)
在头上加结点,
在尾上加结点,
根据下标加结点,
在头上删结点,(题目没要求)
在尾上删结点,(题目没要求)
根据下标删结点。

没要求的都会在其他函数中用得上,所以也就有了。
暂时没有去考虑双向链表的话怎么写,以后有空了得写写看。
发表在 每日LeetCode | 一条评论

Daily LeetCode – day0055 1640. Check Array Formation Through Concatenation

import java.util.HashMap;

// 1640. Check Array Formation Through Concatenation
class Solution {
    public boolean canFormArray(int[] arr, int[][] pieces) {
        HashMap<Integer, Integer> headIndex = new HashMap<>();
        for (int i = 0; i < pieces.length; ++i) {
            headIndex.put(pieces[i][0], i);
        }
        for (int i = 0; i < arr.length; ) {
            if (headIndex.containsKey(arr[i])) {
                Integer index = headIndex.get(arr[i]);
                headIndex.remove(arr[i]);
                for (int j = 0; j < pieces[index].length && i < arr.length; ++j) {
                    if (arr[i] == pieces[index][j]) {
                        ++i;
                    } else {
                        return false;
                    }
                }
            } else {
                return false;
            }
        }
        return true;
    }
}
学习笔记:
这道题本身不简单,主要他里面的数值保证全部不重复那就变成简单题了。
把每个小数组的头放入哈希表,把小数组的下标当成值。
之后就是循环根据头去查下标找到小数组,然后判断后面能不能对得上就行了。
发表在 每日LeetCode | 留下评论

Daily LeetCode – day0054 0854. K-Similar Strings

// 0854. K-Similar Strings
class Solution {
    public int kSimilarity(String s1, String s2) {
        if (s1.equals(s2)) return 0;
        int moves = 0;
        int len = s1.length();
        Queue<String> queue = new LinkedList<>();
        queue.offer(s1);
        HashSet<String> hashSet = new HashSet<>();
        hashSet.add(s1);
        int newShortest = closer(s1, s2);
        int oldShortest = newShortest;
        while (!queue.isEmpty()) {
            ++moves;
            oldShortest = newShortest;
            int queueSize = queue.size();
            for (int i = 0; i < queueSize; ++i) {
                String front = queue.poll();
                if (closer(front, s2) == oldShortest) {
                    for (int j = 0; j < len - 1; ++j) {
                        for (int k = j + 1; k < len; ++k) {
                            String swapped = swap(front, j, k);
                            if (!hashSet.contains(swapped)) {
                                if (s2.equals(swapped)) {
                                    return moves;
                                }
                                int distance = closer(swapped, s2);
                                if (distance < oldShortest) {
                                    if (distance <= newShortest) {
                                        newShortest = distance;
                                        hashSet.add(swapped);
                                        queue.offer(swapped);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return -1;
    }

    String swap(String origin, int i1, int i2) {
        char[] charArray = origin.toCharArray();
        char temp = charArray[i1];
        charArray[i1] = charArray[i2];
        charArray[i2] = temp;
        return new String(charArray);
    }

    int closer(String s, String s2) {
        int ret = 0;
        for (int i = 0; i < s.length(); ++i) {
            if (s.charAt(i) != s2.charAt(i)) ++ret;
        }
        return ret;
    }
}
学习笔记:
这道题是困难题,用的知识点可以中等就解决,使用宽度优先搜索。
但是简单的宽度优先搜索越来越多,就肯定超时。
所以需要剪枝,剪枝的方案就是:能一次交换就完成2个字符的,就不完成1个。
所以我写了个closer函数,判断当前字符串到结果的距离。必须越来越近。
越来越近还是会超时的,必须要一次进两步的时候就进两步。
把交换完成2个字符的放进队列,但这样会出现bug,那就是最后几步只能一步步前进的情况。
所以还是要记录一个上一轮最短距离和本轮新最短距离的变量,如果发现这一轮有缩短2步的,那么之后只缩短1步的情况就不加队列了。并且再新的一轮中,要把上一轮前期那些只缩短1步的垃圾情况过滤,不让他们搞循环浪费更多时间。
发表在 每日LeetCode | 留下评论

Daily LeetCode – day0053 0698. Partition to K Equal Sum Subsets

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

// 0698. Partition to K Equal Sum Subsets
class Solution {
    Map<Integer, Boolean> hashMap = new HashMap<>();

    public boolean canPartitionKSubsets(int[] nums, int k) {
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        if (sum % k != 0) return false;
        return backtrack(nums, 0, 0, k, 0, sum / k);
    }

    private boolean backtrack(int[] nums, int start, int bucket, int k, int used, int target) {
        if (k == 0) return true;
        if (bucket == target) {
            boolean res = backtrack(nums, 0, 0, k - 1, used, target);
            hashMap.put(used, res);
            return res;
        }
        if (hashMap.containsKey(used)) {
            return hashMap.get(used);
        }
        for (int i = start; i < nums.length; ++i) {
            if (((used >> i) & 1) == 1) continue;
            if (bucket + nums[i] > target) continue;
            bucket += nums[i];
            used |= 1 << i;
            if (backtrack(nums, i + 1, bucket, k, used, target)) return true;
            bucket -= nums[i];
            used ^= 1 << i;
        }
        return false;
    }
}
学习笔记:
这是一道标着中等的困难题,真把我做抑郁了。
说标中等因为搜索算法也可以算出来,但我用搜索算法每次跑到155/161个用例就超时。怎么改都不行只能更差不能更好。后来用了位运算,因为说了最多16个数字,所以用位运算来记录哪个数字用没用。
有空还是应该把这题搞懂搞清楚,太难了。
发表在 每日LeetCode | 留下评论

Daily LeetCode – day0052 1636. Sort Array by Increasing Frequency

import java.util.*;

// 1636. Sort Array by Increasing Frequency
class Solution {
    public int[] frequencySort(int[] nums) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        for (int num : nums) {
            if (hashMap.containsKey(num)) {
                hashMap.put(num, hashMap.get(num) + 1);
            } else {
                hashMap.put(num, 1);
            }
        }
        TreeSet<Pair> treeSet = new TreeSet<>();
        for (Map.Entry<Integer, Integer> e : hashMap.entrySet()) {
            treeSet.add(new Pair(e.getKey(), e.getValue()));
        }
        int[] ans = new int[nums.length];
        int i = 0;
        for (Pair p : treeSet) {
            for (int j = 0; j < p.frequency; ++j) {
                ans[i] = p.value;
                ++i;
            }
        }
        return ans;
    }
}

class Pair implements Comparable<Pair> {
    int value;
    int frequency;

    public Pair(int value, int frequency) {
        this.value = value;
        this.frequency = frequency;
    }

    @Override
    public int compareTo(Pair o) {
        if (frequency == o.frequency) return o.value - value;
        return frequency - o.frequency;
    }
}
学习笔记:
这就是一道经典的自定义排序题,还用到了hashmap特别考验扎实基本功。
先要按照频率从少到多排,相同数字再从大到小排。
发表在 每日LeetCode | 留下评论

Daily LeetCode – day0051 0827. Making A Large Island

import java.util.HashSet;

// 0827. Making A Large Island
class Solution {
    public int largestIsland(int[][] grid) {
        int n = grid.length;
        int fillNumber = 2;
        int[][] dxdy = new int[][]{{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == 1) {
                    dfs(i, j, fillNumber, grid, dxdy, n);
                    ++fillNumber;
                }
            }
        }
        int[] areas = new int[fillNumber + 1];
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                ++areas[grid[i][j]];
            }
        }
        if (areas[2] == 0) return 1;
        if (areas[0] == 0) return n * n;
        int ans = 0;
        HashSet<Integer> around = new HashSet<>();
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == 0) {
                    int total = 1;
                    around.clear();
                    if (i - 1 >= 0 && grid[i - 1][j] != 0) around.add(grid[i - 1][j]);
                    if (j + 1 < n && grid[i][j + 1] != 0) around.add(grid[i][j + 1]);
                    if (i + 1 < n && grid[i + 1][j] != 0) around.add(grid[i + 1][j]);
                    if (j - 1 >= 0 && grid[i][j - 1] != 0) around.add(grid[i][j - 1]);
                    for (Integer a : around) {
                        total += areas[a];
                    }
                    if (total > ans) ans = total;
                }
            }
        }
        return ans;
    }

    void dfs(int x, int y, int fillNumber, int[][] grid, int[][] dxdy, int n) {
        grid[x][y] = fillNumber;
        for (int[] d : dxdy) {
            int nx = x + d[0];
            int ny = y + d[1];
            if (nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] == 1) {
                dfs(nx, ny, fillNumber, grid, dxdy, n);
            }
        }
    }
}
学习笔记:
这是一道深度优先搜索的题目,说是困难题,但可能我深搜掌握的还可以。还用到了集合去重。
首先把每个1都多源深搜,标注成不同数字从2开始的岛屿。
然后从每个0开始尝试,0可以联通上下左右的岛屿,但是如果0的上下左右的岛屿有编号相同那就只能加一遍,所以用到了集合去重,0变成陆地后还需要额外加一。
发表在 每日LeetCode | 留下评论

Daily LeetCode – day0050 1624. Largest Substring Between Two Equal Characters

// 1624. Largest Substring Between Two Equal Characters
class Solution {
    public int maxLengthBetweenEqualCharacters(String s) {
        int[] firstSeen = new int[123];
        for (int i = 97; i < 123; ++i) firstSeen[i] = -1;
        char[] charArray = s.toCharArray();
        int ans = -1;
        for (int i = 0; i < charArray.length; ++i) {
            if (firstSeen[charArray[i]] != -1) {
                if (i - firstSeen[charArray[i]] - 1 > ans) {
                    ans = i - firstSeen[charArray[i]] - 1;
                }
            } else {
                firstSeen[charArray[i]] = i;
            }
        }
        return ans;
    }
}
学习笔记:
这是一道简单的字符串数组题。
原本我以为两个字母之间不允许有和两端字母相同的字母,所以做成了上一个和下一个相同之间字母的距离了,结果跑出来错了。原来并不是,这道题本质是求头一个和尾一个相同字母之间的最大距离。
发表在 每日LeetCode | 留下评论

Daily LeetCode – day0049 0850. Rectangle Area II

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

// 0850. Rectangle Area II
class Solution {
    public int rectangleArea(int[][] rectangles) {
        int ans = 0;
        int L = 1;
        int R = -1;
        int px = 0;
        int M = 1000000007;
        TreeMap<Integer, List<int[]>> map = new TreeMap<>();
        TreeMap<Integer, Integer> ymap = new TreeMap<>();
        for (int i = 0; i < rectangles.length; i++) {
            map.computeIfAbsent(rectangles[i][0], o -> new ArrayList<>()).add(new int[]{i, L});
            map.computeIfAbsent(rectangles[i][2], o -> new ArrayList<>()).add(new int[]{i, R});
        }
        for (int x : map.keySet()) {
            int py = 0, cnt = 0, sum = 0;
            for (int y : ymap.keySet()) {
                if (cnt > 0) {
                    sum += y - py;
                }
                py = y;
                cnt += ymap.get(y);
            }
            ans += (long) (x - px) * sum % M;
            ans %= M;
            px = x;
            for (int[] m : map.get(x)) {
                ymap.merge(rectangles[m[0]][1], m[1], Integer::sum);
                ymap.merge(rectangles[m[0]][3], m[1] * -1, Integer::sum);
            }
        }
        return ans;
    }
}
学习笔记:
这是一道超级困难题,就需要横向和纵向都使用线性扫描。
光横向x轴线性扫描就够烦的了,要考虑很多东西,怎么排序的细节。
发表在 每日LeetCode | 留下评论

Daily LeetCode – day0048 0672. Bulb Switcher II

// 0672. Bulb Switcher II
class Solution {
    public int flipLights(int n, int presses) {
        if (presses == 0) {
            return 1;
        } else if (n == 1) {
            return 2;
        } else if (n == 2) {
            if (presses == 1) {
                return 3;
            } else {
                return 4;
            }
        } else {
            if (presses == 1) {
                return 4;
            } else if (presses  == 2) {
                return 7;
            } else {
                return 8;
            }
        }
    }
}
学习笔记:
今天这不是一道编程题了,完全变成了高智商数学推理逻辑题。
发表在 每日LeetCode | 留下评论

Daily LeetCode – day0047 1619. Mean of Array After Removing Some Elements

import java.util.PriorityQueue;

// 1619. Mean of Array After Removing Some Elements
class Solution {
    public double trimMean(int[] arr) {
        int len = arr.length;
        int len5Percent = len / 20;
        PriorityQueue<Integer> biggest = new PriorityQueue<>();
        PriorityQueue<Integer> smallest = new PriorityQueue<>((Integer i1, Integer i2) -> i2 - i1);
        int sum = 0;
        for (int i = 0; i < len5Percent; ++i) {
            sum += arr[i];
            biggest.offer(arr[i]);
            smallest.offer(arr[i]);
        }
        for (int i = len5Percent; i < len; ++i) {
            sum += arr[i];
            if (arr[i] < smallest.peek()) {
                smallest.poll();
                smallest.offer(arr[i]);
            }
            if (arr[i] > biggest.peek()) {
                biggest.poll();
                biggest.offer(arr[i]);
            }
        }
        for (Integer i : biggest) {
            sum -= i;
        }
        for (Integer i : smallest) {
            sum -= i;
        }
        return sum / 18.0 / len5Percent;
    }
}
学习笔记:
这是一道数组的简单题,可以使用排序,但是时间复杂度比较高。
维护一个5%个元素的优先队列才是比较合理的做法。可惜这道简单题的数据太简单了,导致用优先队列做时间不但没减少,反而比一般得要多。
发表在 每日LeetCode | 留下评论