力扣练习题
1、week3
1.1、有效的括号
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、用队列实现栈
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、用栈实现队列
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、反转链表
/**
* 非递归实现,最终状态如下
* 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、移除链表元素
/**
* 使用 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 逆序数对
/**
* 归并解法
*/
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
4.1、Select 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 问题
/**
* 求数组升序排序好后, 从左往右数共 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、爱吃香蕉的珂珂
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 天内送达包裹的能力
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、两个数组的交集
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
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、唯一摩尔斯密码词
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 问题
// 用 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 问题
// 用 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
// 通过前缀和解决
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
// 通过前缀和解决
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、实现前缀树
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、添加与搜索单词
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、键值映射
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、字符串中的第一个唯一字符
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
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;
}
}
本文来自博客园,作者:lidongdongdong~,转载请注明原文链接:https://www.cnblogs.com/lidong422339/p/17817540.html