整数|位运算
class Solution {
public int divide(int a, int b) {
if (a == Integer.MIN_VALUE) {
if (b == -1) {
return Integer.MAX_VALUE;
}
if (b == 1) {
return Integer.MIN_VALUE;
}
}
if (b == Integer.MIN_VALUE) {
if (a == Integer.MIN_VALUE) {
return 1;
}
return 0;
}
if (a == 0) {
return 0;
}
boolean reverse = false;
if (a > 0) {
a = -a;
reverse = !reverse;
}
if (b > 0) {
b = -b;
reverse = !reverse;
}
int left = 1;
int right = Integer.MAX_VALUE;
int ans = 0;
while (left <= right) {
int mid = left + ((right - left) >> 1);
boolean check = quickAdd(b, mid, a);
if (check) {
ans = mid;
if (mid == Integer.MAX_VALUE) {
return ans;
}
left = mid + 1;
} else {
right = mid - 1;
}
}
return reverse ? -ans : ans;
}
// 快速乘
public boolean quickAdd(int y, int z, int x) {
int result = 0;
int add = y;
while (z != 0) {
// 说明是奇数
if ((z & 1) != 0) {
if (result < x - add) {
return false;
}
result += add;
}
if (z != 1) {
if (add < x - add) {
return false;
}
add += add;
}
z >>= 1;
}
return true;
}
}
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public String addBinary(String a, String b) {
char[] chars1 = a.toCharArray();
char[] chars2 = b.toCharArray();
int idx1 = chars1.length - 1;
int idx2 = chars2.length - 1;
int mod = 0;
StringBuilder sb = new StringBuilder();
while (idx1 >= 0 || idx2 >= 0 || mod != 0) {
int v1 = idx1 >= 0 ? chars1[idx1] - '0' : 0;
int v2 = idx2 >= 0 ? chars2[idx2] - '0' : 0;
int val = (v1 + v2 + mod) % 2;
sb.append(val);
mod = (v1 + v2 + mod) / 2;
idx1--;
idx2--;
}
return sb.reverse().toString();
}
}
//leetcode submit region end(Prohibit modification and deletion)
剑指 Offer II 003. 前 n 个数字二进制中 1 的个数
// 时间复杂度:O(nlogn),不符合题目要求
class Solution {
public int[] countBits(int n) {
int[] ans = new int[n + 1];
for (int i = 0; i <= n; i++) {
ans[i] = count(i);
}
return ans;
}
public int count(int n) {
int res = 0;
while (n != 0) {
n = n & (n - 1);
res++;
}
return res;
}
}
//leetcode submit region end(Prohibit modification and deletion)
// 时间复杂度:O(n)
class Solution {
public int[] countBits(int n) {
// bits表示i的比特位为1的数量
int[] bits = new int[n + 1];
int highBit = 0;
for (int i = 1; i <= n; i++) {
// 找2的指数幂的最高位
if ((i & (i - 1)) == 0) {
highBit = i;
}
bits[i] = bits[i - highBit] + 1;
}
return bits;
}
}
如果是正好出现两次可以用异或。
题目要求不借用外部空间,那么不能排序也不能用hashmap,因为排序需要logn的空间和nlogn的时间复杂度。
只能用位计数,从0到31位上看每一位的1的个数,如果是重复的数,肯定是3
class Solution {
public int singleNumber(int[] nums) {
int ans = 0;
for (int i = 0; i < 32; ++i) {
int total = 0;
for (int num : nums) {
// 求得每一位的1的总数
total += ((num >> i) & 1);
}
if (total % 3 != 0) {
// 不是3的倍数那说明这一位肯定是我们需要的结果
ans |= (1 << i);
}
}
return ans;
}
}
意思是找到两个长度最大的不相交单词。
class Solution {
public int maxProduct(String[] words) {
int length = words.length;
int[] masks = new int[length];
for (int i = 0; i < length; i++) {
String word = words[i];
int wordLength = word.length();
for (int j = 0; j < wordLength; j++) {
masks[i] |= 1 << (word.charAt(j) - 'a');
}
}
int maxProd = 0;
for (int i = 0; i < length; i++) {
for (int j = i + 1; j < length; j++) {
if ((masks[i] & masks[j]) == 0) {
maxProd = Math.max(maxProd, words[i].length() * words[j].length());
}
}
}
return maxProd;
}
}
有很多解法,用双指针比较经典。
class Solution {
public int[] twoSum(int[] numbers, int target) {
int left = 0;
int right = numbers.length - 1;
while (left < right) {
if (numbers[left] + numbers[right] < target) {
left++;
} else if (numbers[left] + numbers[right] > target) {
right--;
} else {
return new int[]{left, right};
}
}
return null;
}
}
数组
✅剑指 Offer II 007. 数组中和为 0 的三个数
注意条件:数组不是有序的,那么不能直接使用双指针,可以考虑排序,因为题目要求的结果并不是索引,所以不用在意相对位置。
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<>();
for (int i = 0; i < nums.length - 2; i++) {
List<List<Integer>> twos = twoSum(nums, i + 1, -nums[i]);
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
if (twos.size() != 0) {
for (List<Integer> two : twos) {
List<Integer> three = new ArrayList<>();
three.add(nums[i]);
three.add(two.get(0));
three.add(two.get(1));
ans.add(three);
}
}
}
return ans;
}
public List<List<Integer>> twoSum(int[] nums, int start, int target) {
int left = start;
int right = nums.length - 1;
List<List<Integer>> ans = new ArrayList<>();
while (left < right) {
if (nums[left] + nums[right] == target) {
List<Integer> tmp = new ArrayList<>();
tmp.add(nums[left]);
tmp.add(nums[right]);
ans.add(tmp);
while (left < nums.length - 1 && nums[left] == nums[left + 1]) {
left++;
}
while (right > 0 && nums[right] == nums[right - 1]) {
right--;
}
left++;
right--;
} else if (nums[left] + nums[right] < target) {
left++;
} else {
right--;
}
}
return ans;
}
}
//leetcode submit region end(Prohibit modification and deletion)
剑指 Offer II 008. 和大于等于 target 的最短子数组
求连续子数组的最值,可以考虑用滑动窗口。
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int n = nums.length;
int ans = Integer.MAX_VALUE;
int start = 0, end = 0;
int sum = 0;
while (end < n) {
// 用end加
sum += nums[end];
// 用start减
while (sum >= s) {
ans = Math.min(ans, end - start + 1);
sum -= nums[start];
start++;
}
end++;
}
return ans == Integer.MAX_VALUE ? 0 : ans;
}
}
求连续子数组的个数,还是用滑动窗口,这题比较有技巧,要记住。
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
int n = nums.length, ret = 0;
int prod = 1, i = 0;
for (int j = 0; j < n; j++) {
prod *= nums[j];
while (i <= j && prod >= k) {
prod /= nums[i];
i++;
}
ret += j - i + 1;
}
return ret;
}
}
原本想用滑动窗口,但是发现元素可能为负数。
看了官方题解,还是要用前缀和+map,没啥意思。
public class Solution {
public int subarraySum(int[] nums, int k) {
int count = 0, pre = 0;
HashMap<Integer, Integer> mp = new HashMap<>();
mp.put(0, 1);
for (int i = 0; i < nums.length; i++) {
pre += nums[i];
if (mp.containsKey(pre - k)) {
count += mp.get(pre - k);
}
mp.put(pre, mp.getOrDefault(pre, 0) + 1);
}
return count;
}
}
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public int pivotIndex(int[] nums) {
// 假设位置为k
// nums[1] + ... + nums[k-1] = nums[k+1] + ... + nums[len]
// preSums[k-1] = preSums[len] - preSums[k]
// 要找到一个k,使得 preSums[k-1]+preSums[k] = total
int sum = 0;
for (int num : nums) {
sum += num;
}
if (nums[0] == sum) {
return 0;
}
int len = nums.length;
int[] preSums = new int[len];
preSums[0] = nums[0];
for (int i = 1; i < len; i++) {
preSums[i] = preSums[i - 1] + nums[i];
if (preSums[i] + preSums[i - 1] == sum) {
return i;
}
}
return -1;
}
}
//leetcode submit region end(Prohibit modification and deletion)
代码可以继续优化,不需要preSums数组。
class Solution {
public int pivotIndex(int[] nums) {
// 假设位置为k
// nums[1] + ... + nums[k-1] = nums[k+1] + ... + nums[len]
// preSums[k-1] = preSums[len] - preSums[k]
// 要找到一个k,使得 preSums[k-1]+preSums[k] = total
int sum = 0;
for (int num : nums) {
sum += num;
}
if (nums[0] == sum) {
return 0;
}
int len = nums.length;
int q = nums[0];
for (int i = 1; i < len; i++) {
// r就是pre[i],q就是pre[i-1]
int r = q + nums[i];
//preSums[i] = preSums[i - 1] + nums[i];
if (r + q == sum) {
return i;
}
q = r;
}
return -1;
}
}
class NumMatrix {
int[][] preSums;
public NumMatrix(int[][] matrix) {
int m = matrix.length;
int n = matrix[0].length;
preSums = new int[m + 1][n + 1];
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
preSums[i][j] = preSums[i - 1][j] + preSums[i][j - 1] - preSums[i - 1][j - 1] + matrix[i - 1][j - 1];
}
}
}
public int sumRegion(int row1, int col1, int row2, int col2) {
return preSums[row2+1][col2+1] - preSums[row1][col2+1] - preSums[row2+1][col1] + preSums[row1][col1];
}
}
字符串
// 暴力法,用字符数组,效率不是很理想
class Solution {
public boolean checkInclusion(String s1, String s2) {
// 子排列必须是连续的
// 那就用一个滑动窗口,在s2里找s1的异位词
int size = s1.length();
for (int i = 0; i <= s2.length() - size; i++) {
if (match(s1, s2.substring(i, i + size))) {
return true;
}
}
return false;
}
public boolean match(String s1, String s2) {
char[] chars1 = s1.toCharArray();
char[] chars2 = s2.toCharArray();
int[] counts = new int[26];
for (char c1 : chars1) {
counts[c1 - 'a']++;
}
for (char c2 : chars2) {
counts[c2 - 'a']--;
}
for (int count : counts) {
if (count != 0) {
return false;
}
}
return true;
}
}
// 果断参考了labuladong的题解
class Solution {
public boolean checkInclusion(String t, String s) {
Map<Character, Integer> need = new HashMap<>();
Map<Character, Integer> window = new HashMap<>();
for (char c : t.toCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
}
int left = 0, right = 0;
int valid = 0;
while (right < s.length()) {
char c = s.charAt(right);
right++;
// 进行窗口内数据的一系列更新
if (need.containsKey(c)) {
window.put(c, window.getOrDefault(c, 0) + 1);
if (window.get(c).equals(need.get(c))) {
valid++;
}
}
// 判断左侧窗口是否要收缩
while (right - left >= t.length()) {
// 在这里判断是否找到了合法的子串
if (valid == need.size()) {
return true;
}
char d = s.charAt(left);
left++;
// 进行窗口内数据的一系列更新
if (need.containsKey(d)) {
if (window.get(d).equals(need.get(d)))
valid--;
window.put(d, window.get(d) - 1);
}
}
}
// 未找到符合条件的子串
return false;
}
}
这就是上一题的另一个结果集。
import java.util.HashMap;
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public List<Integer> findAnagrams(String s, String p) {
Map<Character, Integer> win = new HashMap<>();
Map<Character, Integer> need = new HashMap<>();
int valid = 0;
for (char c : p.toCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
}
int left = 0;
int right = 0;
List<Integer> ans = new ArrayList<>();
while (right < s.length()) {
char cur = s.charAt(right);
right++;
if (need.containsKey(cur)) {
win.put(cur, win.getOrDefault(cur, 0) + 1);
if (win.get(cur).equals(need.get(cur))) {
valid++;
}
}
while (right - left >= p.length()) {
if (valid == need.size()) {
ans.add(left);
}
char out = s.charAt(left);
left++;
if (need.containsKey(out)) {
if (need.get(out).equals(win.get(out))) {
valid--;
}
win.put(out, win.get(out) - 1);
}
}
}
return ans;
}
}
//leetcode submit region end(Prohibit modification and deletion)
剑指 Offer II 016. 不含重复字符的最长子字符串
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> win = new HashMap<>();
int left = 0;
int right = 0;
int max = 0;
while (right < s.length()) {
char cur = s.charAt(right);
right++;
if (!win.containsKey(cur)) {
// 不存在,记录最大长度,注意这里的right在前面已经加过1了
max = Math.max(max, right - left);
} else {
// 已存在,找到跟cur相等的元素
char out = s.charAt(left);
// 过程中遇到的也通通删除
while (out != cur) {
win.remove(out);
out = s.charAt(++left);
}
// 此时left就是和right相等的,删除left
left++;
}
win.put(cur, 1);
}
return max;
}
}
和上面两题差别不大,区别在于,该题可以不用连续序列,所以判断出的条件有所不同。
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public String minWindow(String s, String t) {
Map<Character, Integer> win = new HashMap<>();
Map<Character, Integer> need = new HashMap<>();
for (char c : t.toCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
}
int left = 0;
int right = 0;
int min = Integer.MAX_VALUE;
int minLeft = 0;
int valid = 0;
while (right < s.length()) {
char cur = s.charAt(right);
right++;
if (need.containsKey(cur)) {
win.put(cur, win.getOrDefault(cur, 0) + 1);
if (need.get(cur).equals(win.get(cur))) {
valid++;
}
}
while (valid == need.size()) {
if (right - left < min) {
minLeft = left;
min = right - left;
}
char out = s.charAt(left);
left++;
if(need.containsKey(out)){
if (win.get(out).equals(need.get(out))) {
valid--;
}
win.put(out, win.get(out) - 1);
}
}
}
return min == Integer.MAX_VALUE ? "" : s.substring(minLeft, minLeft + min);
}
}
//leetcode submit region end(Prohibit modification and deletion)
class Solution {
public boolean isPalindrome(String s) {
// 先把所有字符转化成小写,并过滤掉空格和标点这类字符
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (Character.isLetterOrDigit(c)) {
sb.append(Character.toLowerCase(c));
}
}
// 然后对剩下的这些目标字符执行双指针算法,判断回文串
s = sb.toString();
// 一左一右两个指针相向而行
int left = 0, right = s.length() - 1;
while (left < right) {
if (s.charAt(left) != s.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
}
class Solution {
public boolean validPalindrome(String s) {
int left = 0;
int right = s.length() - 1;
while (left < right) {
if (s.charAt(left) == s.charAt(right)) {
// 正常的比较
left++;
right--;
} else {
// 不正常的,left或者right进一位,因为只可以删除一个字符,所以这里直接再做两次比较可以
return validPalindrome(s, left, right - 1) || validPalindrome(s, left + 1, right);
}
}
return true;
}
public boolean validPalindrome(String s, int left, int right) {
int l = left;
int r = right;
while (l < r) {
if (s.charAt(l) != s.charAt(r)) {
return false;
}
l++;
r--;
}
return true;
}
}
要用中心点法,枚举中心点,每次都想不到!
class Solution {
public int countSubstrings(String s) {
int n = s.length(), ans = 0;
for (int i = 0; i < 2 * n - 1; ++i) {
int l = i / 2, r = i / 2 + i % 2;
while (l >= 0 && r < n && s.charAt(l) == s.charAt(r)) {
--l;
++r;
++ans;
}
}
return ans;
}
}
链表
链表类问题,多想想用快慢指针。
剑指 Offer II 021. 删除链表的倒数第 n 个结点
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode slow = head;
ListNode fast = head;
int k = n;
while (k > 0) {
fast = fast.next;
k--;
}
// 如果正好走到头了,说明要删除的是第一个
if (fast == null) {
return head.next;
}
while (fast != null && fast.next != null) {
fast = fast.next;
slow = slow.next;
}
if (slow != null && slow.next != null) {
slow.next = slow.next.next;
}
return head;
}
}
又是典型的快慢指针。
//leetcode submit region begin(Prohibit modification and deletion)
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
ListNode second = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
// 如果有环,必然相遇
if (slow == fast) {
while (second != slow) {
second = second.next;
slow = slow.next;
}
return second;
}
}
//如果无环才会走到这里
return null;
}
}
//leetcode submit region end(Prohibit modification and deletion)
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode p1 = headA;
ListNode p2 = headB;
// 注意条件是p1!=p2,不能用p1!=null
// 因为如果没有交点的时候,p1和p2都走到最后的null时,此时要退出的,而不是继续给p1指向headB,p2指向headA
while (p1 != p2) {
p1 = p1 == null ? headB : p1.next;
p2 = p2 == null ? headA : p2.next;
}
return p1;
}
}
// 迭代写法,现在发现迭代写法很好写
class Solution {
public ListNode reverseList(ListNode head) {
ListNode p = null, q = head;
while (q != null) {
ListNode r = q.next;
q.next = p;
p = q;
q = r;
}
return p;
}
}
// 递归写法,真的是每次都想不到啊!!!!
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode last = reverseList(head.next);
head.next.next = head;
head.next = null;
return last;
}
}
// 先写一个翻转的
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode newL1 = reverse(l1);
ListNode newL2 = reverse(l2);
int mod = 0;
ListNode ans = new ListNode(-1);
ListNode pre = ans;
// [7,2,4,3] -> [3,4,2,7]
// [5,6,4] -> [4,6,5]
// 得到结果: [-1,7,0,8,7] -> [7,8,0,7]
while (newL1 != null || newL2 != null || mod != 0) {
int v1 = newL1 == null ? 0 : newL1.val;
int v2 = newL2 == null ? 0 : newL2.val;
int add = v1 + v2 + mod;
ans.next = new ListNode(add % 10);
mod = add / 10;
// 链表类的题目一定不能忘记,让指针动起来!
ans = ans.next;
newL1 = newL1 == null ? null : newL1.next;
newL2 = newL2 == null ? null : newL2.next;
}
//reverse [7,0,8,7]
return reverse(pre.next);
}
public ListNode reverse(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode tail = reverse(head.next);
head.next.next = head;
head.next = null;
return tail;
}
}
//leetcode submit region end(Prohibit modification and deletion)
题目要求不允许翻转,可以用栈来实现先入后出的效果。
class Solution {
public void reorderList(ListNode head) {
if (head == null) {
return;
}
ListNode mid = middleNode(head);
ListNode l1 = head;
ListNode l2 = mid.next;
mid.next = null;
l2 = reverseList(l2);
mergeList(l1, l2);
}
public ListNode middleNode(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
public void mergeList(ListNode l1, ListNode l2) {
ListNode l1_tmp;
ListNode l2_tmp;
while (l1 != null && l2 != null) {
l1_tmp = l1.next;
l2_tmp = l2.next;
l1.next = l2;
l1 = l1_tmp;
l2.next = l1;
l2 = l2_tmp;
}
}
}
//leetcode submit region begin(Prohibit modification and deletion)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
//找到中点,然后将后面的链表反转
// 1,2,3 3,2,1
// 1,2,3 4 3,2,1
ListNode l1 = head;
ListNode fast = head;
ListNode slow = head;
ListNode pre = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
pre = slow;
slow = slow.next;
}
if (pre != null) {
pre.next = null;
}
ListNode l2 = reverseList(slow);
while (l1 != null) {
if (l1.val != l2.val) {
return false;
}
l1 = l1.next;
l2 = l2.next;
}
return true;
}
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode last = reverseList(head.next);
head.next.next = head;
head.next = null;
return last;
}
}
//leetcode submit region end(Prohibit modification and deletion)
class Solution {
public Node flatten(Node head) {
dfs(head);
return head;
}
public Node dfs(Node node) {
Node last = null;
Node cur = node;
while (cur != null) {
Node next = cur.next;
if (cur.child != null) {
// 如果有child
Node childLast = dfs(cur.child);
next = cur.next;
// 将node和child连接起来
cur.next = cur.child;
cur.child.prev = cur;
// 将childLast和next连接
if (next != null) {
childLast.next = next;
next.prev = childLast;
}
// 将child置为空
cur.child = null;
last = childLast;
} else {
// 如果没有child,继续一直往下走
last = cur;
}
// 必须要走起来才能递归
cur = next;
}
return last;
}
}
class Solution {
public Node insert(Node head, int insertVal) {
Node node = new Node(insertVal);
// 链表为空
if (head == null) {
head = node;
head.next = node;
return head;
}
// 链表只有一个节点
if (head.next == head) {
head.next = node;
node.next = head;
return head;
}
Node cur = head, nxt = head.next;
// 链表不止一个节点,注意是一个循环链表,所以判断退出条件不能用null
while (nxt != head) {
// 在递增的区段里找到了合适的位置
if (cur.val <= insertVal && nxt.val >= insertVal) {
break;
}
// 在由增到减这个位置上可以插入
if (cur.val > nxt.val) {
if (insertVal > cur.val || insertVal < nxt.val) {
break;
}
}
cur = cur.next;
nxt = nxt.next;
}
cur.next = node;
node.next = nxt;
return head;
}
}
哈希表
剑指 Offer II 030. 插入、删除和随机访问都是 O(1) 的容器
insert和remove都可以直接用hash,都是O(1)
random要实现O(1),只能用数组或者list这个结构
但是arrayList只有查找是O(1),linkedList只有插入和删除是O(1)
可以使用arraylist,删除时将元素和队尾元素交换,插入时,直接插入队尾。
class RandomizedSet {
List<Integer> nums;
Map<Integer, Integer> indices;
Random random;
public RandomizedSet() {
nums = new ArrayList<Integer>();
indices = new HashMap<Integer, Integer>();
random = new Random();
}
public boolean insert(int val) {
if (indices.containsKey(val)) {
return false;
}
int index = nums.size();
nums.add(val);
indices.put(val, index);
return true;
}
public boolean remove(int val) {
if (!indices.containsKey(val)) {
return false;
}
int index = indices.get(val);
int last = nums.get(nums.size() - 1);
nums.set(index, last);
indices.put(last, index);
nums.remove(nums.size() - 1);
indices.remove(val);
return true;
}
public int getRandom() {
int randomIndex = random.nextInt(nums.size());
return nums.get(randomIndex);
}
}
class Solution {
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
if (s.equals(t)) {
return false;
}
Map<Character, Integer> need = new HashMap<>();
for (char ss : s.toCharArray()) {
need.put(ss, need.getOrDefault(ss, 0) + 1);
}
int valid = 0;
Map<Character, Integer> win = new HashMap<>();
for (char tt : t.toCharArray()) {
if (!need.containsKey(tt)) {
return false;
}
int target = need.get(tt);
int current = win.getOrDefault(tt, 0) + 1;
if (target == current) {
valid++;
}
win.put(tt, current);
}
return valid == need.size();
}
}
//leetcode submit region end(Prohibit modification and deletion)
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
// 问题在于怎么设置key
Map<String, List<String>> map = new HashMap<>();
for (String str : strs) {
// 思路一,对char数组进行排序,排序完之后肯定是相等的
char[] chars = str.toCharArray();
Arrays.sort(chars);
StringBuilder sb = new StringBuilder();
for(char c : chars){
sb.append(c);
}
List<String> values = map.getOrDefault(sb.toString(), new ArrayList<>());
values.add(str);
map.put(sb.toString(), values);
}
List<List<String>> ans = new ArrayList<>();
for (List<String> val : map.values()) {
ans.add(val);
}
return ans;
}
}
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
// 问题在于怎么设置key
Map<String, List<String>> map = new HashMap<>();
for (String str : strs) {
// 思路二,编码,字符和出现次数
char[] chars = str.toCharArray();
int[] counts = new int[26];
for (char c : chars) {
counts[c - 'a']++;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 26; i++) {
if (counts[i] > 0) {
sb.append(i + 'a').append(counts[i]);
}
}
List<String> values = map.getOrDefault(sb.toString(), new ArrayList<>());
values.add(str);
map.put(sb.toString(), values);
}
List<List<String>> ans = new ArrayList<>();
for (List<String> val : map.values()) {
ans.add(val);
}
return ans;
}
}
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
Map<Character, Integer> index = null;
public boolean isAlienSorted(String[] words, String order) {
index = new HashMap<>();
for (int i = 0; i < order.length(); i++) {
index.put(order.charAt(i), i);
}
for (int i = 0; i < words.length - 1; i++) {
if (compare(words[i], words[i + 1]) > 0) {
return false;
}
}
return true;
}
public int compare(String a, String b) {
int idx = 0;
while (idx < a.length() && idx < b.length()) {
if (index.get(a.charAt(idx)) < index.get(b.charAt(idx))) {
return -1;
} else if (index.get(a.charAt(idx)) > index.get(b.charAt(idx))) {
return 1;
} else {
idx++;
}
}
if (b.length() == a.length()) {
return 0;
} else if (b.length() > a.length()) {
return -1;
}
return 1;
}
}
//leetcode submit region end(Prohibit modification and deletion)
class Solution {
public int findMinDifference(List<String> timePoints) {
if (timePoints.size() > 1440) {
return 0;
}
Collections.sort(timePoints);
int min = Integer.MAX_VALUE;
for (int i = 0; i < timePoints.size() - 1; i++) {
min = Math.min(min, getMinutes(timePoints.get(i + 1)) - getMinutes(timePoints.get(i)));
}
min = Math.min(min, getMinutes(timePoints.get(0)) + 1440 - getMinutes(timePoints.get(timePoints.size() - 1)));
return min;
}
public int getMinutes(String str) {
int hours = 0;
int minutes = 0;
if (str.charAt(0) == '0') {
if (str.charAt(1) != '0') {
hours = Integer.parseInt(str.substring(1, 2));
}
} else {
hours = Integer.parseInt(str.substring(0, 2));
}
if (str.charAt(3) == '0') {
if (str.charAt(4) != '0') {
minutes = Integer.parseInt(str.substring(4, 5));
}
} else {
minutes = Integer.parseInt(str.substring(3, 5));
}
return hours * 60 + minutes;
}
}
栈
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
int ans = 0;
for (String token : tokens) {
// 如果是数字直接入栈
if (isNumberic(token)) {
stack.push(Integer.parseInt(token));
} else {
// 不是数字,说明是表达式,要从栈里取出两个元素
int v2 = stack.pop();
int v1 = stack.pop();
// 计算出结果再放入栈里
stack.push(caculate(v1, v2, token));
}
}
// 最后栈里应该就剩一个元素
return stack.pop();
}
public boolean isNumberic(String s) {
return !s.equals("+") && !s.equals("-") && !s.equals("*") && !s.equals("/");
}
public int caculate(int v1, int v2, String expr) {
if (expr.equals("+")) {
return v1 + v2;
} else if (expr.equals("-")) {
return v1 - v2;
} else if (expr.equals("/")) {
return v1 / v2;
}
return v1 * v2;
}
}
//leetcode submit region end(Prohibit modification and deletion)
class Solution {
public int[] asteroidCollision(int[] asteroids) {
Stack<Integer> stack = new Stack<>();
for (int asteroid : asteroids) {
if (asteroid < 0) {
// 此时说明小行星不会撞到任何东西
if (stack.isEmpty() || stack.peek() < 0) {
stack.push(asteroid);
continue;
}
// 遇到比自己小的,把这些都干掉
while (!stack.isEmpty() && stack.peek() > 0 && stack.peek() < -asteroid) {
stack.pop();
}
// 都干完了,只剩自己
if (stack.isEmpty() || stack.peek() < 0) {
stack.push(asteroid);
} else if (stack.peek() == -asteroid) {
// 没有同归于尽,只剩自己
stack.pop();
}
} else if (asteroid > 0) {
stack.push(asteroid);
}
}
int[] ans = new int[stack.size()];
for (int i = stack.size() - 1; i >= 0; i--) {
ans[i] = stack.pop();
}
return ans;
}
}
下一个更大元素问题,用单调栈。
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
Stack<Integer> stack = new Stack<>();
int len = temperatures.length;
int[] ans = new int[len];
for (int i = len - 1; i >= 0; i--) {
if (stack.isEmpty()) {
stack.push(i);
continue;
}
while (!stack.isEmpty() && temperatures[stack.peek()] <= temperatures[i]) {
stack.pop();
}
ans[i] = stack.isEmpty() ? 0 : stack.peek() - i;
stack.push(i);
}
return ans;
}
}
遍历每一个宽度,找到左边界和右边界,左边界和右边界都是要大于自己的高度。
class Solution {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
int[] left = new int[n];
int[] right = new int[n];
Deque<Integer> mono_stack = new ArrayDeque<Integer>();
for (int i = 0; i < n; ++i) {
while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
mono_stack.pop();
}
left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek());
mono_stack.push(i);
}
mono_stack.clear();
for (int i = n - 1; i >= 0; --i) {
while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
mono_stack.pop();
}
right[i] = (mono_stack.isEmpty() ? n : mono_stack.peek());
mono_stack.push(i);
}
int ans = 0;
for (int i = 0; i < n; ++i) {
ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]);
}
return ans;
}
}
// 方法一,强行解
class Solution {
public int maximalRectangle(String[] matrix) {
int m = matrix.length;
if (m == 0) {
return 0;
}
int n = matrix[0].length();
int[][] left = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i].charAt(j) == '1') {
left[i][j] = (j == 0 ? 0 : left[i][j - 1]) + 1;
}
}
}
int ret = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i].charAt(j) == '0') {
continue;
}
int width = left[i][j];
int area = width;
for (int k = i - 1; k >= 0; k--) {
width = Math.min(width, left[k][j]);
area = Math.max(area, (i - k + 1) * width);
}
ret = Math.max(ret, area);
}
}
return ret;
}
}
// 方法二:单调栈的优化
class Solution {
public int maximalRectangle(String[] matrix) {
int m = matrix.length;
if (m == 0) {
return 0;
}
int n = matrix[0].length();
int[][] left = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i].charAt(j) == '1') {
left[i][j] = (j == 0 ? 0 : left[i][j - 1]) + 1;
}
}
}
int ret = 0;
for (int j = 0; j < n; j++) { // 对于每一列,使用基于柱状图的方法
int[] up = new int[m];
int[] down = new int[m];
Deque<Integer> stack = new ArrayDeque<Integer>();
for (int i = 0; i < m; i++) {
while (!stack.isEmpty() && left[stack.peek()][j] >= left[i][j]) {
stack.pop();
}
up[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}
stack.clear();
for (int i = m - 1; i >= 0; i--) {
while (!stack.isEmpty() && left[stack.peek()][j] >= left[i][j]) {
stack.pop();
}
down[i] = stack.isEmpty() ? m : stack.peek();
stack.push(i);
}
for (int i = 0; i < m; i++) {
int height = down[i] - up[i] - 1;
int area = height * left[i][j];
ret = Math.max(ret, area);
}
}
return ret;
}
}
队列
class MovingAverage {
int size = 0;
Queue<Integer> queue = new LinkedList<>();
double total = 0;
/**
* Initialize your data structure here.
*/
public MovingAverage(int size) {
this.size = size;
queue = new ArrayDeque<Integer>();
}
public double next(int val) {
if (queue.size() == size) {
total -= queue.poll();
}
total += val;
queue.offer(val);
return total / queue.size();
}
}
class RecentCounter {
Queue<Integer> queue;
public RecentCounter() {
queue = new ArrayDeque<>();
}
public int ping(int t) {
queue.offer(t);
while (queue.peek() < t - 3000){
queue.poll();
}
return queue.size();
}
}
class CBTInserter {
Queue<TreeNode> candidate;
TreeNode root;
public CBTInserter(TreeNode root) {
this.candidate = new ArrayDeque<TreeNode>();
this.root = root;
Queue<TreeNode> queue = new ArrayDeque<TreeNode>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
if (!(node.left != null && node.right != null)) {
candidate.offer(node);
}
}
}
public int insert(int val) {
TreeNode child = new TreeNode(val);
TreeNode node = candidate.peek();
int ret = node.val;
if (node.left == null) {
node.left = child;
} else {
node.right = child;
candidate.poll();
}
candidate.offer(child);
return ret;
}
public TreeNode get_root() {
return root;
}
}
class Solution {
public List<Integer> largestValues(TreeNode root) {
List<Integer> ans = new ArrayList<>();
if (root == null) {
return ans;
}
Queue<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
// 一次for循环就是一层
int max = Integer.MIN_VALUE;
for (int i = 0; i < size; i++) {
TreeNode cur = queue.poll();
max = Math.max(max, cur.val);
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
}
ans.add(max);
}
return ans;
}
}
//leetcode submit region end(Prohibit modification and deletion)
class Solution {
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> queue = new ArrayDeque<TreeNode>();
queue.offer(root);
int ans = root.val;
while (!queue.isEmpty()) {
int size = queue.size();
// 一次for循环就是遍历一层
for (int i = 0; i < size; i++) {
TreeNode cur = queue.poll();
// 每一层的第一个元素就是最左边的元素
if (i == 0) {
ans = cur.val;
}
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
}
}
return ans;
}
}
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> ans = new ArrayList<>();
if (root == null) {
return ans;
}
Queue<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode cur = queue.poll();
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
if (i == size - 1) {
ans.add(cur.val);
}
}
}
return ans;
}
}