二叉搜索树
判断其是否是一个有效的二叉搜索树。
public boolean isValidBST(TreeNode root) {
return valid(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
private boolean valid(TreeNode root, long min, long max) {
if (root == null) {
return true;
}
if (root.val <= min || root.val >= max) {
return false;
}
boolean left = valid(root.left, min, root.val);
boolean right = valid(root.right, root.val, max);
return left && right;
}
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
public ListNode mergeKLists(ListNode[] lists) {
ListNode res = null;
for (ListNode list: lists) {
res = merge2Lists(res, list);
}
return res;
}
public ListNode merge2Lists(ListNode l1, ListNode l2) {
ListNode res = new ListNode(0);
ListNode tem = res;
while(l1 != null && l2 != null) {
if (l1.val > l2.val) {
res.next = l2;
l2 = l2.next;
} else {
res.next = l1;
l1 = l1.next;
}
res = res.next;
}
res.next = l1 == null? l2: l1;
return tem.next;
}
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity)以 正整数 作为容量capacity初始化 LRU 缓存int get(int key)如果关键字key存在于缓存中,则返回关键字的值,否则返回-1。void put(int key, int value)如果关键字key已经存在,则变更其数据值value;如果不存在,则向缓存中插入该组key-value。如果插入操作导致关键字数量超过capacity,则应该 逐出 最久未使用的关键字。
class LRUCache {
// 容量
private int capacity = 10;
// 当前存储数量
private int size = 0;
// 存储节点
private class ListNode {
int key;
int val;
ListNode pre;
ListNode next;
ListNode() {}
ListNode(int key, int val) { this.key = key; this.val = val; }
}
// Key - Value
private Map<Integer, ListNode> kv = new HashMap<>();
private ListNode head = new ListNode(0, 0);
private ListNode tail = head;
public LRUCache(int capacity) {
this.capacity = capacity;
}
public int get(int key) {
ListNode tem = kv.get(key);
if (tem == null) {
return -1;
}
updateNodeLocation(tem);
return tem.val;
}
public void put(int key, int value) {
ListNode node = kv.get(key);
if (node != null) {
node.val = value;
updateNodeLocation(node);
return;
}
ListNode tem = new ListNode(key, value);
tem.pre = tail;
tail.next = tem;
tail = tail.next;
kv.put(key, tem);
size++;
// 超过容量, 移除头部节点并从哈希表移除
if (size > capacity) {
kv.remove(head.next.key);
head.next.next.pre = head;
head.next = head.next.next;
size--;
}
}
private void updateNodeLocation(ListNode node) {
if (tail == node) return;
tail.next = node;
node.next.pre = node.pre;
node.pre.next = node.next;
node.pre = tail;
node.next = null;
tail = node;
}
}
#### 给你链表的头结点 `head` ,请将其按 **升序** 排列并返回 **排序后的链表** 。
```java
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode fast = head.next;
ListNode slow = head;
while(fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
ListNode tmp = slow.next;
slow.next = null;
ListNode left = sortList(head);
ListNode right = sortList(tmp);
ListNode pre = new ListNode(0);
ListNode h = pre;
while(left != null && right != null) {
if(left.val < right.val) {
h.next = left;
left = left.next;
} else {
h.next = right;
right = right.next;
}
h = h.next;
}
h.next = left == null? right: left ;
return pre.next;
}
给你一个链表,删除链表的倒数第 n **个结点,并且返回链表的头结点。
ListNode pre = new ListNode(0, head);
ListNode first = head;
ListNode second = pre;
for (int i = 0; i < n; i++) {
first = first.next;
}
while(first != null) {
first = first.next;
second = second.next;
}
second.next = second.next.next;
return pre.next;
链表,两两交换其中相邻的节点,并返回交换后链表的头节点。
ListNode pre = new ListNode(0, head);
ListNode tem = pre;
while (tem.next != null && tem.next.next != null) {
ListNode left = tem.next;
ListNode right = tem.next.next;
tem.next = right;
left.next = right.next;
right.next = left;
tem = left;
}
return pre.next;
反转链表
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while(cur != null) {
ListNode tem = cur.next;
cur.next = pre;
pre = cur;
cur = tem;
}
return pre;
}
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
- 每行的元素从左到右升序排列。
- 每列的元素从上到下升序排列。
public boolean searchMatrix(int[][] matrix, int target) {
int i = matrix.length - 1;
int j = 0;
while(i >= 0 && j < matrix[0].length - 1){
if (matrix[i][j] > target) {
i--;
} else if (matrix[i][j] < target) {
j++;
} else {
return true;
}
}
return false;
}
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
public void rotate(int[][] matrix) {
int n = matrix.length;
for (int i = 0; i < n/2; i++) {
int[] tem = matrix[i];
matrix[i] = matrix[n -1- i];
matrix[n -1-i] = tem;
}
for (int i = 0; i < n; i++) {
for (int j = 0; j< i; j++) {
int tem = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = tem;
}
}
}
给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法 。
public void setZeroes(int[][] matrix) {
Set<Integer> row_zero = new HashSet<>();
Set<Integer> col_zero = new HashSet<>();
int row = matrix.length;
int col = matrix[0].length;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (matrix[i][j] == 0) {
row_zero.add(i);
col_zero.add(j);
}
}
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (row_zero.contains(i) || col_zero.contains(j)) matrix[i][j] = 0;
}
}
}
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
public int firstMissingPositive(int[] nums) {
int len = nums.length;
for (int i = 0; i < len; i++) {
while (nums[i] > 0 && nums[i] <= len && nums[nums[i] - 1] != nums[i]) {
// 满足在指定范围内、并且没有放在正确的位置上,才交换
// 例如:数值 3 应该放在索引 2 的位置上
swap(nums, nums[i] - 1, i);
}
}
// [1, -1, 3, 4]
for (int i = 0; i < len; i++) {
if (nums[i] != i + 1) {
return i + 1;
}
}
// 都正确则返回数组长度 + 1
return len + 1;
}
private void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。
public int[] productExceptSelf(int[] nums) {
int len = nums.length;
int[] ans = new int[len];
ans[0] = 1;
for (int i = 1; i < len; i++) {
ans[i] = ans[i-1] * nums[i - 1];
}
int temp = 1;
for (int i = len -2; i >=0; i--) {
temp *= nums[i+1];
ans[i] = ans[i]*temp;
}
return ans;
}
给定一个整数数组 nums,将数组中的元素向右轮转 k **个位置,其中 k **是非负数。
public void rotate(int[] nums, int k) {
int len = nums.length ;
k = k % len;
reverse(nums, 0, len - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, len - 1);
}
private void reverse(int[] nums, int left, int right) {
while(left < right) {
int tem = nums[left];
nums[left] = nums[right];
nums[right] = tem;
left++;
right--;
}
}
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
int[][] res = new int[intervals.length][2];
int idx = -1;
for(int[] interval: intervals) {
if(idx < 0 || res[idx][1] < interval[0]) {
res[++idx] = interval;
} else {
res[idx][1] = Math.max(interval[1], res[idx][1]);
}
}
return Arrays.copyOf(res, idx + 1);
}
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
public int maxSubArray(int[] nums) {
int len = nums.length;
int[] dp = new int[len];
dp[0] = nums[0];
for (int i = 1; i < len ;i++) {
if (dp[i-1] < 0) {
dp[i] = nums[i];
} else {
dp[i] = dp[i - 1] + nums[i];
}
}
int res = dp[0];
for (int i = 0; i < len; i++) {
res = Math.max(res, dp[i]);
}
return res;
}
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
public String minWindow(String s, String t) {
int[] ctnS = new int[128];
int[] ctnT = new int[128];
int ansLeft = -1;
int ansRight = s.length();
for(int i = 0; i < t.length(); i++) {
ctnT[t.charAt(i)]++;
}
int left = 0;
for(int right = 0; right < s.length(); right++) {
ctnS[s.charAt(right)]++;
while(isCover(ctnT, ctnS)) {
if(right - left < ansRight - ansLeft) {
ansRight = right;
ansLeft = left;
}
ctnS[s.charAt(left)]--;
left++;
}
}
return ansLeft < 0? "": s.substring(ansLeft, ansRight + 1);
}
private boolean isCover(int[] ctnT, int[] ctnS ) {
for(char i = 'a'; i <= 'z'; i++) {
if(ctnS[i] < ctnT[i]) {
return false;
}
}
for(int i = 'A'; i <= 'Z'; i++) {
if(ctnS[i] < ctnT[i]) {
return false;
}
}
return true;
}
1 给你一个整数数组 nums 和一个整数 k ,请你统计并返回 *该数组中和为 k 的子数组的个数 。
public int subarraySum(int[] nums, int k) {
int count = 0;
int pre = 0;
HashMap<Integer, Integer> map = new HashMap<>();
map.put(0, 1);
for (int i = 0; i < nums.length; i++) {
pre += nums[i];
if (map.containsKey(pre - k)) {
count += map.get(pre-k);
}
map.put(pre, map.getOrDefault(pre - k, 0) + 1);
}
return count;
}
239. 滑动窗口最大值
给你一个整数数组 nums,有一个大小为 k **的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
public int[] maxSlidingWindow(int[] nums, int k) {
if ( nums == null || nums.length < 2 ) {
return nums;
}
int[] result = new int[nums.length - k + 1];
LinkedList<Integer> queue = new LinkedList<>();
for (int i = 0 ; i < nums.length; i++) {
while(queue.size() > 0 && nums[queue.peekLast()] <= nums[i]) {
queue.pollLast();
}
queue.addLast(i);
if (queue.peekFirst() <= i - k) {
queue.pollFirst();
}
if (i + 1 >= k) {
result[i - k + 1] = nums[queue.peekFirst()];
}
}
return result;
}
回溯
1、组合
77
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。
class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
if(k <= 0 || n < k) {
return res;
}
Deque<Integer> path = new ArrayDeque<>();
dfs(n, k, 1, path, res);
return res;
}
private void dfs(int n, int k, int begin, Deque<Integer> path, List<List<Integer>> res) {
if(path.size() == k) {
res.add(new ArrayList<>(path));
return;
}
for(int i = begin; i <=n; i++) {
path.addLast(i);
dfs(n, k, i+1, path, res);
path.removeLast();
}
}
}
2、组合总和 III
216
找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
class Solution {
public List<List<Integer>> combinationSum3(int k, int n) {
Deque<Integer> path = new ArrayDeque<>();
List<List<Integer>> ans = new ArrayList<>();
if(n<k || k<=0) {
return ans;
}
dfs(k, n, 1, path, ans);
return ans;
}
private void dfs(int k, int n, int begin, Deque<Integer> path, List<List<Integer>> res) {
if(k == path.size() && n==0) {
res.add(new ArrayList<>(path));
return;
}
for(int i = begin; i <= 9; i++) {
path.addLast(i);
dfs(k, n - i, i+1, path, res);
path.removeLast();
}
}
}
3 、17. 电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
class Solution {
public List<String> letterCombinations(String digits) {
List<String> combinations = new ArrayList<>();
if (digits == null || digits.length() == 0) {
return combinations;
}
HashMap<Character, String[]> map = new HashMap<>(){{
put('2', new String[]{"a", "b", "c"});
put('3', new String[]{"d", "e", "f"});
put('4', new String[]{"g", "h", "i"});
put('5', new String[]{"j", "k", "l"});
put('6', new String[]{"m", "n", "o"});
put('7', new String[]{"p", "q", "r", "s"});
put('8', new String[]{"t", "u", "v"});
put('9', new String[]{"w", "x", "y", "z"});
}};
Queue<String> queue = new LinkedList<>();
for(int i = 0; i < digits.length(); i++) {
dfs(queue, map.get(digits.charAt(i)));
}
for(String s: queue) {
combinations.add(s);
}
return combinations;
}
private void dfs(Queue<String> queue, String[] letters){
if (queue.size() == 0) {
for(String s: letters) {
queue.add(s);
}
} else {
int len = queue.size();
for(int i = 0; i < len; i++) {
String s = queue.poll();
for(String letter: letters) {
queue.add(s+letter);
}
}
}
}
}