力扣练习题

1、week3

1.1、有效的括号

20 - 有效的括号

public boolean isValid(String s) {
    Deque<Character> stack = new ArrayDeque<>();
    char[] chars = s.toCharArray();
    for (char c : chars) {
        if (c == '(' || c == '[' || c == '{') stack.addLast(c);
        else {
            if (stack.isEmpty()) return false;
            char top = stack.removeLast();
            if (c == ')' && top != '(') return false;
            if (c == ']' && top != '[') return false;
            if (c == '}' && top != '{') return false;
        }
    }
    return stack.isEmpty();
}

1.2、用队列实现栈

225 - 用队列实现栈

public class MyStack {

    private final Queue<Integer> q;

    public MyStack() {
        q = new LinkedList<>();
    }

    // 3214 -> 4321
    public void push(int x) {
        q.add(x);
        for (int i = 0; i < q.size() - 1; i++) q.add(q.remove());
    }

    public int pop() {
        return q.remove();
    }

    public int top() {
        return q.peek();
    }

    public boolean empty() {
        return q.isEmpty();
    }
}

1.3、用栈实现队列

232 - 用栈实现队列

public class MyQueue {

    private Stack<Integer> stack1; // 用来添加元素
    private Stack<Integer> stack2; // 用来缓存部分已经排好的元素
    private int front; // 需要保证 front 是 stack1 中最先进来的元素

    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }

    public void push(int x) {
        if (stack1.isEmpty()) front = x;
        stack1.push(x);
    }

    // [123 -> 123]
    public int pop() {
        if (!stack2.isEmpty()) return stack2.pop();
        while (stack1.size() != 1) stack2.push(stack1.pop());
        return stack1.pop();
    }

    public int peek() {
        if (!stack2.isEmpty()) return stack2.peek();
        return front;
    }

    public boolean empty() {
        return stack1.isEmpty() && stack2.isEmpty();
    }
}

2、week4

2.1、反转链表

206 - 反转链表

/**
 * 非递归实现,最终状态如下
 * cur、next -> null
 * prev -> newHead
 */
public static ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode cur = head;
    ListNode next;
    while (cur != null) {
        next = cur.next;
        cur.next = prev;
        prev = cur;
        cur = next;
    }
    return prev;
}

/**
 * 递归: 反转以 head 为头的链表, 并返回新的头结点
 */
public static ListNode reverseList(ListNode head) {
    if (head == null || head.next == null) return head;

    ListNode newHead = reverseList(head.next);
    head.next.next = head;
    head.next = null;

    return newHead;
}

2.2、移除链表元素

203 - 移除链表元素

/**
 * 使用 dummyHead
 */
public static ListNode removeElements(ListNode head, int val) {
    ListNode dummyHead = new ListNode(-1, head);

    ListNode prev = dummyHead;
    while (prev.next != null) {
        if (prev.next.val == val) prev.next = prev.next.next;
        else prev = prev.next;
    }

    return dummyHead.next;
}

/**
 * 递归解决问题
 */
public static ListNode removeElements(ListNode head, int val) {
    if (head == null) return null;
    head.next = removeElements(head.next, val);
    return head.val == val ? head.next : head;
}

3、week5 逆序数对

剑指 Offer 51 - 数组中的逆序对

image

/**
 * 归并解法
 */
public static int reversePairs(int[] nums) {
    return sort(nums);
}

private static int sort(int[] arr) {
    int[] temp = new int[arr.length];
    return process(arr, 0, arr.length - 1, temp);
}

private static int process(int[] arr, int l, int r, int[] temp) {
    // if (l >= r) return 0;
    if (r - l <= 15) return insertionSort(arr, l, r);

    int mid = l + (r - l) / 2;
    int left = process(arr, l, mid, temp);
    int right = process(arr, mid + 1, r, temp);

    if (arr[mid] > arr[mid + 1]) return left + right + merge(arr, l, mid, r, temp);
    return left + right;
}

private static int merge(int[] arr, int l, int mid, int r, int[] temp) {
    int res = 0;
    System.arraycopy(arr, l, temp, l, r - l + 1);

    int p1 = l;
    int p2 = mid + 1;
    int i = l;

    while (p1 <= mid && p2 <= r) {
        if (temp[p1] > temp[p2]) {
            // 输出逆序数对
            // for (int j = p1; j <= mid; j++) {
            //    System.out.println(String.format("[%d, %d]", temp[j], temp[p2]));
            // }
            res += mid - p1 + 1; // 后面区间的元素归并上来时, 和前面区间剩余元素形成逆序数对
            arr[i++] = temp[p2++];
        } else {
            arr[i++] = temp[p1++];
        }
    }
    while (p1 <= mid) arr[i++] = temp[p1++];
    while (p2 <= r) arr[i++] = temp[p2++];
    return res;
}

private static int insertionSort(int[] arr, int l, int r) {
    int res = 0;
    for (int i = l + 1; i <= r; i++) {
        int k = arr[i];
        int j;
        for (j = i; j - 1 >= l && arr[j - 1] > k; j--) {
            arr[j] = arr[j - 1];
        }
        arr[j] = k;
        res += i - j; // arr[i] 插入到了 arr[j] 的位置
    }
    return res;
}

4、week6

image

4.1、Select K 问题

215 - 数组中的第 K 个最大元素

/**
 * 求数组升序排序好后, 从右往左数第 K 个元素, 它的索引是 length - K
 * 复杂度: O(n)
 */
public static int findKthLargest(int[] arr, int k) {
    return selectK(arr, 0, arr.length - 1, arr.length - k, new Random());
}

private static int selectK(int[] arr, int l, int r, int k, Random random) {
    int p = partition(arr, l, r, random);

    if (p == k) return arr[p];

    if (p < k) return selectK(arr, p + 1, r, k, random);
    return selectK(arr, l, p - 1, k, random);
}

/**
 * 双路快速排序
 */
private static int partition(int[] arr, int l, int r, Random random) {
    int p = random.nextInt(r - l + 1) + l;
    swap(arr, l, p);

    int v = arr[l];
    int p1 = l + 1;
    int p2 = r;

    while (true) {
        while (p1 <= p2 && arr[p1] < v) p1++;
        while (p1 <= p2 && arr[p2] > v) p2--;

        if (p1 >= p2) break;

        swap(arr, p1++, p2--);
    }

    swap(arr, l, p2);
    return p2;
}

private static void swap(int[] arr, int a, int b) {
    int k = arr[a];
    arr[a] = arr[b];
    arr[b] = k;
}

4.2、Top K 问题

面试题 17.14. 最小 K 个数

/**
 * 求数组升序排序好后, 从左往右数共 K 个元素
 * 复杂度: O(n)
 */
public static int[] smallestK(int[] arr, int k) {
    if (k == 0) return new int[0];
    return selectK(arr, 0, arr.length - 1, k - 1, new Random());
}

private static int[] selectK(int[] arr, int l, int r, int k, Random random) {
    int p = partition(arr, l, r, random);

    if (p == k) return Arrays.copyOf(arr, k + 1);

    if (p < k) return selectK(arr, p + 1, r, k, random);
    return selectK(arr, l, p - 1, k, random);
}

/**
 * 双路快速排序
 */
private static int partition(int[] arr, int l, int r, Random random) {
    int p = random.nextInt(r - l + 1) + l;
    swap(arr, l, p);

    int v = arr[l];
    int p1 = l + 1;
    int p2 = r;

    while (true) {
        while (p1 <= p2 && arr[p1] < v) p1++;
        while (p1 <= p2 && arr[p2] > v) p2--;

        if (p1 >= p2) break;

        swap(arr, p1++, p2--);
    }

    swap(arr, l, p2);
    return p2;
}

private static void swap(int[] arr, int a, int b) {
    int k = arr[a];
    arr[a] = arr[b];
    arr[b] = k;
}

5、week7

5.1、爱吃香蕉的珂珂

875 - 爱吃香蕉的珂珂

public static int minEatingSpeed(int[] piles, int h) {
    int speedL = 1;
    int speedR = Arrays.stream(piles).max().getAsInt();
    int speedMid;

    // arr[speedL, speedR] 代表吃香蕉的速度, time = eatingTime(speed)
    // 二分搜索 time <= h 最大的 time 对应 speed
    // 每次循环开始时: speedL 还没看, speedR 可能是解, 因此当 speedL == speedR 时, speedR 就是解
    while (speedL < speedR) {
        speedMid = speedL + (speedR - speedL) / 2;
        int time = getEatingTime(piles, speedMid);
        if (time <= h) speedR = speedMid;
        else speedL = speedMid + 1;
    }

    return speedR;
}

private static int getEatingTime(int[] piles, int speed) {
    int times = 0;
    for (int pile : piles) {
        times += pile / speed + (pile % speed == 0 ? 0 : 1);
    }
    return times;
}

5.2、在 D 天内送达包裹的能力

1011 - 在 D 天内送达包裹的能力

public static int shipWithinDays(int[] weights, int days) {
    int capacityL = Arrays.stream(weights).max().getAsInt();
    int capacityR = Arrays.stream(weights).sum();
    int capacityMid;

    // arr[capacityL, capacityR] 代表运载能力, time = getShipTime(capacity)
    // 二分搜索 time <= days 最大的 time 对应 capacity
    // 每次循环开始时: capacityL 还没看, capacityR 可能是解, 因此当 capacityL == capacityR 时, capacityR 就是解
    while (capacityL < capacityR) {
        capacityMid = capacityL + (capacityR - capacityL) / 2;
        int times = getShipTime(weights, capacityMid);
        if (times <= days) capacityR = capacityMid;
        else capacityL = capacityMid + 1;
    }

    return capacityR;
}

private static int getShipTime(int[] weights, int maxCapacity) {
    int times = 0;
    int hasCapacity = 0;
    for (int weight : weights) {
        if (hasCapacity + weight <= maxCapacity) hasCapacity += weight;
        else {
            times++;
            hasCapacity = weight;
        }
    }
    return times + 1;
}

6、week8

6.1、两个数组的交集

349 - 两个数组的交集

public static int[] intersection(int[] nums1, int[] nums2) {
    TreeSet<Integer> set = new TreeSet<>();
    for (int num : nums1) set.add(num);

    ArrayList<Integer> list = new ArrayList<>();
    for (int num : nums2) {
        if (set.contains(num)) {
            list.add(num);
            set.remove(num);
        }
    }

    int[] res = new int[list.size()];
    for (int i = 0; i < list.size(); i++) res[i] = list.get(i);
    return res;
}

6.2、两个数组的交集 II

350 - 两个数组的交集 II

public static int[] intersect(int[] nums1, int[] nums2) {
    TreeMap<Integer, Integer> map = new TreeMap<>();
    for (int num : nums1) {
        if (!map.containsKey(num)) map.put(num, 1);
        else map.put(num, map.get(num) + 1);
    }

    ArrayList<Integer> list = new ArrayList<>();
    for (int num : nums2) {
        if (map.containsKey(num)) {
            list.add(num);
            map.put(num, map.get(num) - 1);
            if (map.get(num) == 0) map.remove(num);
        }
    }

    int[] res = new int[list.size()];
    for (int i = 0; i < list.size(); i++) res[i] = list.get(i);
    return res;
}

6.3、唯一摩尔斯密码词

804 - 唯一摩尔斯密码词

public static int uniqueMorseRepresentations(String[] words) {
    String[] codes = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.."};
    Set<String> set = new TreeSet<>();

    StringBuilder sb = new StringBuilder();
    for (String word : words) {
        for (int i = 0; i < word.length(); i++) {
            sb.append(codes[word.charAt(i) - 'a']);
        }
        set.add(sb.toString());
        sb.delete(0, sb.length());
    }

    return set.size();
}

7、week9

7.1、Select K 问题

215 - 数组中的第 K 个最大元素

// 用 Java 提供的 PriorityQueue
public static int findKthLargest(int[] arr, int k) {
    PriorityQueue<Integer> pq = new PriorityQueue<>();
    for (int i = 0; i < k; i++) pq.add(arr[i]);
    for (int i = k; i < arr.length; i++) {
        if (arr[i] > pq.peek()) {
            pq.remove();
            pq.add(arr[i]);
        }
    }

    return pq.peek();
}
// 自己实现最小堆
public static int findKthLargest(int[] arr, int k) {
    int[] minHeap = Arrays.copyOf(arr, k);
    int last = k - 1;
    for (int i = (last - 1) / 2; i >= 0; i--) siftDown(minHeap, i);

    for (int i = k; i < arr.length; i++) {
        if (arr[i] > minHeap[0]) {
            minHeap[0] = arr[i];
            siftDown(minHeap, 0);
        }
    }

    return minHeap[0];
}

private static void siftDown(int[] arr, int index) {
    int last = arr.length - 1;
    while (index * 2 + 1 <= last) {
        int smaller = index * 2 + 1;
        if (smaller + 1 <= last && arr[smaller + 1] < arr[smaller]) smaller++;

        if (arr[index] <= arr[smaller]) break;
        swap(arr, index, smaller);
        index = smaller;
    }
}

private static void swap(int[] arr, int a, int b) {
    int k = arr[a];
    arr[a] = arr[b];
    arr[b] = k;
}

7.2、Top K 问题

面试题 17.14. 最小 K 个数

// 用 Java 提供的 PriorityQueue
public static int[] smallestK(int[] arr, int k) {
    if (k == 0) return new int[0];

    PriorityQueue<Integer> pq = new PriorityQueue<>(Collections.reverseOrder());
    for (int i = 0; i < k; i++) pq.add(arr[i]);

    for (int i = k; i < arr.length; i++) {
        if (arr[i] < pq.peek()) {
            pq.remove();
            pq.add(arr[i]);
        }
    }

    int[] res = new int[k];
    for (int i = 0; i < k; i++) res[i] = pq.remove();
    return res;
}
// 自己实现最大堆
public static int[] smallestK(int[] arr, int k) {
    if (k == 0) return new int[0];

    int[] maxHeap = Arrays.copyOf(arr, k);
    int last = k - 1;
    for (int i = (last - 1) / 2; i >= 0; i--) siftDown(maxHeap, i);

    for (int i = k; i < arr.length; i++) {
        if (arr[i] < maxHeap[0]) {
            maxHeap[0] = arr[i];
            siftDown(maxHeap, 0);
        }
    }

    return maxHeap;
}

private static void siftDown(int[] arr, int index) {
    int last = arr.length - 1;
    while (index * 2 + 1 <= last) {
        int bigger = index * 2 + 1;
        if (bigger + 1 <= last && arr[bigger + 1] > arr[bigger]) bigger++;

        if (arr[index] >= arr[bigger]) break;
        swap(arr, index, bigger);
        index = bigger;
    }
}

private static void swap(int[] arr, int a, int b) {
    int k = arr[a];
    arr[a] = arr[b];
    arr[b] = k;
}

8、week11

8.1、区域和检索 I

303 - 区域和检索 - 数组不可变

// 通过前缀和解决
public class NumArray {

    private final int[] sum; // sum[i] 代表 arr[0...i] 的和

    public NumArray(int[] nums) {
        sum = new int[nums.length];
        sum[0] = nums[0];
        for (int i = 1; i < nums.length; i++) sum[i] = sum[i - 1] + nums[i];
    }

    public int sumRange(int left, int right) {
        if (left == 0) return sum[right];
        return sum[right] - sum[left - 1];
    }
}

8.2、区域和检索 II

307 - 区域和检索 - 数组可修改

// 通过前缀和解决
public class NumArray {

    private final int[] arr;
    private final int[] sum; // sum[i] 代表 arr[0...i] 的和

    public NumArray(int[] nums) {
        arr = Arrays.copyOf(nums, nums.length);
        sum = new int[nums.length];

        sum[0] = arr[0];
        for (int i = 1; i < arr.length; i++) sum[i] = sum[i - 1] + arr[i];
    }

    /**
     * O(n)
     */
    public void update(int index, int val) {
        int diff = val - arr[index];
        arr[index] = val;
        for (int i = index; i < sum.length; i++) sum[i] += diff;
    }

    public int sumRange(int left, int right) {
        if (left == 0) return sum[right];
        return sum[right] - sum[left - 1];
    }
}
// 通过线段树解决
public class NumArray {

    private final int[] data;
    private final int[] tree;

    public NumArray(int[] nums) {
        data = Arrays.copyOf(nums, nums.length);
        tree = new int[nums.length * 4];
        buildSegmentTree(0, 0, data.length - 1);
    }

    private void buildSegmentTree(int treeIndex, int l, int r) {
        if (l == r) {
            tree[treeIndex] = data[l];
            return;
        }

        int mid = l + (r - l) / 2;
        int leftTreeIndex = leftChild(treeIndex);
        int rightTreeIndex = rightChild(treeIndex);

        buildSegmentTree(leftTreeIndex, l, mid);
        buildSegmentTree(rightTreeIndex, mid + 1, r);

        tree[treeIndex] = tree[leftTreeIndex] + tree[rightTreeIndex];
    }

    public void update(int index, int val) {
        if (index < 0 || index >= data.length) {
            throw new IllegalArgumentException("index is illegal.");
        }
        if (data[index] == val) return;

        data[index] = val;
        update(0, 0, data.length - 1, index, val);
    }

    private void update(int treeIndex, int l, int r, int index, int val) {
        if (l == r) {
            tree[treeIndex] = val;
            return;
        }

        int mid = l + (r - l) / 2;
        int leftTreeIndex = leftChild(treeIndex);
        int rightTreeIndex = rightChild(treeIndex);

        if (index <= mid) update(leftTreeIndex, l, mid, index, val);
        else update(rightTreeIndex, mid + 1, r, index, val);

        tree[treeIndex] = tree[leftTreeIndex] + tree[rightTreeIndex];
    }

    public int sumRange(int left, int right) {
        // [0, data.length - 1], left <= right
        if (left < 0 || left >= data.length || right < 0 || right >= data.length || left > right) {
            throw new IllegalArgumentException("index is illegal.");
        }
        return query(0, 0, data.length - 1, left, right);
    }

    private int query(int treeIndex, int l, int r, int queryL, int queryR) {
        if (l == queryL && r == queryR) {
            return tree[treeIndex];
        }

        int mid = l + (r - l) / 2;
        int leftTreeIndex = leftChild(treeIndex);
        int rightTreeIndex = rightChild(treeIndex);

        if (queryR <= mid) return query(leftTreeIndex, l, mid, queryL, queryR);
        else if (queryL >= mid + 1) return query(rightTreeIndex, mid + 1, r, queryL, queryR);
        else {
            int leftSum = query(leftTreeIndex, l, mid, queryL, mid);
            int rightSum = query(rightTreeIndex, mid + 1, r, mid + 1, queryR);
            return leftSum + rightSum;
        }
    }

    private int leftChild(int index) {
        return index * 2 + 1;
    }

    private int rightChild(int index) {
        return index * 2 + 2;
    }
}

8.3、实现前缀树

208 - 实现 Trie(前缀树)

public class Trie {

    private class Node {
        public boolean isWord;
        public TreeMap<Character, Node> next;

        public Node(boolean isWord) {
            this.isWord = isWord;
            this.next = new TreeMap<>();
        }

        public Node() {
            this(false);
        }
    }

    private final Node root;

    public Trie() {
        root = new Node();
    }

    public void insert(String word) {
        Node cur = root;

        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (!cur.next.containsKey(c)) cur.next.put(c, new Node());
            cur = cur.next.get(c);
        }

        if (!cur.isWord) cur.isWord = true;
    }

    public boolean search(String word) {
        Node cur = root;

        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (!cur.next.containsKey(c)) return false;
            cur = cur.next.get(c);
        }

        return cur.isWord;
    }

    public boolean startsWith(String prefix) {
        Node cur = root;

        for (int i = 0; i < prefix.length(); i++) {
            char c = prefix.charAt(i);
            if (!cur.next.containsKey(c)) return false;
            cur = cur.next.get(c);
        }

        return true;
    }
}

8.4、添加与搜索单词

211 - 添加与搜索单词 - 数据结构设计

public class WordDictionary {

    private class Node {
        public boolean isWord;
        public TreeMap<Character, Node> next;

        public Node(boolean isWord) {
            this.isWord = isWord;
            this.next = new TreeMap<>();
        }

        public Node() {
            this(false);
        }
    }

    private final Node root;

    public WordDictionary() {
        root = new Node();
    }

    public void addWord(String word) {
        Node cur = root;

        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (!cur.next.containsKey(c)) cur.next.put(c, new Node());
            cur = cur.next.get(c);
        }

        if (!cur.isWord) cur.isWord = true;
    }

    public boolean search(String word) {
        return match(root, word, 0);
    }

    /**
     * 在以 node 为根节点的 Trie 中查找 word[index ... n - 1]
     * node 中存放的是已经查询过的, 未查询的在 node 的下面
     */
    private boolean match(Node node, String word, int index) {
        if (index == word.length()) return node.isWord;

        char c = word.charAt(index);

        if (c != '.') {
            if (!node.next.containsKey(c)) return false;
            return match(node.next.get(c), word, index + 1);
        } else {
            for (Node nextNode : node.next.values()) {
                if (match(nextNode, word, index + 1)) return true;
            }
            return false;
        }
    }
}

8.5、键值映射

677 - 键值映射

public class MapSum {

    private class Node {
        public int val;
        public TreeMap<Character, Node> next;

        public Node(int val) {
            this.val = val;
            next = new TreeMap<>();
        }

        public Node() {
            this(0);
        }
    }

    private final Node root;

    public MapSum() {
        root = new Node();
    }

    public void insert(String word, int val) {
        Node cur = root;

        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (!cur.next.containsKey(c)) cur.next.put(c, new Node());
            cur = cur.next.get(c);
        }

        cur.val = val;
    }

    public int sum(String prefix) {
        Node cur = root;

        for (int i = 0; i < prefix.length(); i++) {
            char c = prefix.charAt(i);
            if (!cur.next.containsKey(c)) return 0;
            cur = cur.next.get(c);
        }

        return sum(cur);
    }

    private int sum(Node node) {
        int sum = node.val;
        if (node.next.isEmpty()) return sum;

        for (Node nextNode : node.next.values()) sum += sum(nextNode);
        return sum;
    }
}

9、week13

9.1、字符串中的第一个唯一字符

387 - 字符串中的第一个唯一字符

public int firstUniqChar(String s) {
    int[] freq = new int[26];

    for (int i = 0; i < s.length(); i++) freq[s.charAt(i) - 'a']++;

    for (int i = 0; i < s.length(); i++) {
        if (freq[s.charAt(i) - 'a'] == 1) return i;
    }

    return -1;
}

9.2、区域和检索 II

307 - 区域和检索 - 数组可修改

public class NumArray {

    private int[] data;
    private int[] blocks;
    private int N;  // 元素总数
    private int B;  // 每组元素个数
    private int Bn; // 组数

    public NumArray(int[] nums) {
        N = nums.length;
        if (N == 0) return;

        B = (int) Math.sqrt(N);
        Bn = N / B + (N % B != 0 ? 1 : 0);

        data = Arrays.copyOf(nums, N);
        blocks = new int[Bn];
        for (int i = 0; i < N; i++) blocks[i / B] += data[i];
    }

    public void update(int index, int val) {
        if (index < 0 || index >= N) return;
        int diff = val - data[index];
        data[index] = val;
        blocks[index / B] += diff;
    }

    public int sumRange(int l, int r) {
        if (l < 0 || l >= N || r < 0 || r >= N || l > r) return 0;

        int sum = 0;
        int bStart = l / B;
        int bEnd = r / B;

        if (Math.abs(bStart - bEnd) <= 1) {
            for (int i = l; i <= r; i++) sum += data[i];
        } else {
            for (int i = l; i < (bStart + 1) * B; i++) sum += data[i]; // bStart 组
            for (int i = bStart + 1; i < bEnd; i++) sum += blocks[i];  // [bStart + 1 ... bEnd - 1] 组
            for (int i = bEnd * B; i <= r; i++) sum += data[i];        // bEnd 组
        }

        return sum;
    }
}
posted @ 2023-11-08 15:35  lidongdongdong~  阅读(16)  评论(0编辑  收藏  举报