中级算法
中级算法 - LeetBook - 力扣(LeetCode)全球极客挚爱的技术成长平台
三数之和
解法1
英雄哪里出来大佬的视频解法的Java实现
一层枚举加双指针
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
int n = nums.length;
if (n < 3 || nums[0] > 0 || nums[n - 1] < 0) {
return result;
}
if (nums[0] == 0 && nums[n - 1] == 0) {
List<Integer> temp = new ArrayList<>();
temp.add(0);
temp.add(0);
temp.add(0);
result.add(temp);
return result;
}
for (int i = 0; i < n - 2; i++) {
if (i != 0 && nums[i] == nums[i - 1]) {
continue;
}
if (nums[i] > 0) {
break;
}
int j = i + 1;
int k = n - 1;
while (j < k) {
int sum = nums[i] + nums[j] + nums[k];
if (sum > 0) {
k--;
} else if (sum < 0) {
j++;
} else {
List<Integer> temp = new ArrayList<>();
temp.add(nums[i]);
temp.add(nums[j]);
temp.add(nums[k]);
result.add(temp);
j++;
k--;
while (j < k && nums[j] == nums[j - 1]) {
j++;
}
while (j < k && nums[k] == nums[k + 1]) {
k--;
}
}
}
}
return result;
}
}
矩阵置零
解法1
- 遍历矩阵,取出所有0所对应的横坐标和纵坐标。
- 单独遍历取出的横纵坐标数组,对对应的行列置为0
- return
class Solution {
public void setZeroes(int[][] matrix) {
int[] x = new int[matrix.length * matrix[0].length];
int[] y = new int[matrix.length * matrix[0].length];
int countx = 0;
int county = 0;
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
if (matrix[i][j] == 0) {
x[countx++] = i;
y[county++] = j;
}
}
}
for (int i = 0; i < countx; i++) {
for (int j = 0; j < matrix[0].length; j++) {
matrix[x[i]][j] = 0;
}
}
for (int i = 0; i < county; i++) {
for (int j = 0; j < matrix.length; j++) {
matrix[j][y[i]] = 0;
}
}
}
}
字母异位词分组
解法1
- 将每个字符串排序后放入一个新的字符串数组,与原数组对应
- 遍历原数组
- 如果对应的排序后的数组对应值存在在map中,则将原数组的元素添加到map的value(List)里
- 如果不存在,则将原数组元素作为key,新数组元素作为value(放入list),put进map
- 遍历map取出所有value放入result(List)返回
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
String[] newstr = new String[strs.length];
List<List<String>> result = new ArrayList<>();
Map<String, List<String>> map = new HashMap<>();
for (int i = 0; i < strs.length; i++) {
char[] ss = strs[i].toCharArray();
Arrays.sort(ss);
newstr[i] = String.valueOf(ss);
}
for (int i = 0; i < strs.length; i++) {
if (map.containsKey(newstr[i])) {
map.get(newstr[i]).add(strs[i]);
continue;
}
List<String> list = new ArrayList<>();
list.add(strs[i]);
map.put(newstr[i], list);
}
// Iterator iterator = map.keySet().iterator();
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
result.add(entry.getValue());
}
return result;
}
}
解法2
- 遍历字符串数组
- 将元素排序,并在map中get元素的value
- 如果value为null,则将字符串和对应的ArrayList(空的)放入map中,并将ArrayList放入result中
- 向ArrayList中添加元素
- 返回result
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> result = new ArrayList<>();
Map<String, ArrayList<String>> map = new HashMap<>();
char[] ccc;
String sss;
ArrayList<String> temp = null;
for (String str : strs) {
ccc = str.toCharArray();
Arrays.sort(ccc);
sss = String.valueOf(ccc);
temp = map.get(sss);
if (temp == null) {
temp = new ArrayList<>();
map.put(sss, temp);
result.add(temp);
}
temp.add(str);
}
return result;
}
}
无重复字符的最长子串
解法1
- 使用HashMap和快慢指针
- 遍历字符串,将每一个字符都放入HashMap中,并计算当前最长子串,如果大于记录,则替换
- 返回记录的最大值
class Solution {
public int lengthOfLongestSubstring(String s) {
int slow = 0;
int max = 0;
Map<Character, Integer> map = new HashMap<>();
for (int fast = 0; fast < s.length(); fast++) {
char temp = s.charAt(fast);
if (map.containsKey(temp)) {
slow = Math.max(slow, map.get(temp));
}
max = Math.max(max, fast - slow + 1);
map.put(temp, fast + 1);
}
return max;
}
}
最长回文子串
解法1
- 将字符串转换为数组
- 对数组进行循环,当碰到回文中心时(例如:“bb”,“bab”),以回文中心为基准扩散寻找当前回文中心对应的最大回文序列
- 若寻找到的回文序列比已记录的回文序列长,则记录此回文序列的首尾下标
- 循环结束后返回所记录的首尾下标对应的最长回文序列
class Solution {
public String longestPalindrome(String s) {
char[] ccc = s.toCharArray();
int startIndex = 0;
int endIndex = 0;
for (int i = 0; i < ccc.length - 1; i++) {
if (ccc[i] == ccc[i + 1]) {
int start = i;
int end = i + 1;
while (start >= 0 && end < ccc.length && ccc[start] == ccc[end]) {
start--;
end++;
}
if ((end - start - 2) > (endIndex - startIndex)) {
startIndex = start + 1;
endIndex = end - 1;
}
}
if (i + 2 != ccc.length && ccc[i] == ccc[i + 2]) {
int start = i;
int end = i + 2;
while (start >= 0 && end < ccc.length && ccc[start] == ccc[end]) {
start--;
end++;
}
if ((end - start - 2) > (endIndex - startIndex)) {
startIndex = start + 1;
endIndex = end - 1;
}
}
}
String result = "";
for (int i = startIndex; i <= endIndex; i++) {
result += ccc[i];
}
return result;
}
}
解法2
- 优化:
- StringBuilder替换String
class Solution {
public String longestPalindrome(String s) {
char[] ccc = s.toCharArray();
int startIndex = 0;
int endIndex = 0;
for (int i = 0; i < ccc.length - 1; i++) {
if (ccc[i] == ccc[i + 1]) {
int start = i;
int end = i + 1;
while (start >= 0 && end < ccc.length && ccc[start] == ccc[end]) {
start--;
end++;
}
if ((end - start - 2) > (endIndex - startIndex)) {
startIndex = start + 1;
endIndex = end - 1;
}
}
if (i + 2 != ccc.length && ccc[i] == ccc[i + 2]) {
int start = i;
int end = i + 2;
while (start >= 0 && end < ccc.length && ccc[start] == ccc[end]) {
start--;
end++;
}
if ((end - start - 2) > (endIndex - startIndex)) {
startIndex = start + 1;
endIndex = end - 1;
}
}
}
StringBuilder result = new StringBuilder();
for (int i = startIndex; i <= endIndex; i++) {
result.append(ccc[i]);
}
return result.toString();
}
}
递增的三元子序列
解法1
- 遍历nums数组,并设置两个记录值,一个记录最小值,另一个记录中间值
- 如果当前数字比最小值小,那么给最小值赋值(if)
- 如果当前数字比中间值小,那么给中间值赋值(else if)
- 如果当前值大于中间值那么返回true
- 遍历结束返回false
class Solution {
public boolean increasingTriplet(int[] nums) {
int first = Integer.MAX_VALUE;
int second = Integer.MAX_VALUE;
for (int num : nums) {
if (num <= first) {
first = num;
} else if (num <= second) {
second = num;
} else {
return true;
}
}
return false;
}
}
两数相加
解法1
- 假设最终结果为list1,通过遍历将list2的结点的值全部加到list1上,并设置一个flag判断是否进位
- 如果list1长度小于list2,则循环到末尾后,将next指向list2指向的结点,然后返回
- 注意:
- 最高位进位要注意判断
- 一个链表循环完成后连续进位要注意判断
/**
* 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 ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode result = l1;
boolean ad = false;
while (l1 != null && l2 != null) {
l1.val += l2.val;
if (ad) {
l1.val += 1;
ad = false;
}
if (l1.val >= 10) {
ad = true;
l1.val -= 10;
}
if (l1.next == null && l2.next == null && ad) {
l1.next = new ListNode(1);
return result;
}
if (l1.next == null) {
l1.next = l2.next;
l1 = l1.next;
break;
}
l1 = l1.next;
l2 = l2.next;
}
while (l1 != null) {
if (ad) {
l1.val += 1;
ad = false;
}
if (l1.val >= 10) {
ad = true;
l1.val -= 10;
}
if (l1.next == null && ad) {
l1.next = new ListNode(1);
ad = false;
}
l1 = l1.next;
}
return result;
}
}
奇偶链表
解法1
- 初始化两个节点one指向head,two指向head.next
- 遍历链表
- 奇数节点连到one指向的节点,偶数节点连到two指向的节点
- 循环结束后,将two连到one之后
- return
/**
* 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 ListNode oddEvenList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode one = head;
ListNode two = head.next;
ListNode node1 = one;
ListNode node2 = two;
while (node2.next != null && node2.next.next != null) {
node1.next = node2.next;
node2.next = node2.next.next;
node1 = node1.next;
node2 = node2.next;
}
if (node2.next != null) {
node1.next = node2.next;
node1 = node1.next;
node2.next = null;
}
node1.next = two;
return one;
}
}
相交链表
解法1
思路:将两个链表首尾相接拼起来遍历,相同节点一定会出现在后面的相同位置
- 针对两种拼接方式的链表,取两个头节点,并且设置两个标志(不允许操作原链表),判断是否切换到另一个链表
- 当链表走到空并且判断标志表示并未切换时切换到另一个链表继续往后走
- 如果碰到两个ListNode相等,则相交,return当前结点
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode list1 = headA;
ListNode list2 = headB;
int isT1 = 0;
int isT2 = 0;
while (list1 != null && list2 != null) {
if(list1 == list2) {
return list1;
}
list1 = list1.next;
list2 = list2.next;
if (isT1 == 0 && list1 == null) {
list1 = headB;
isT1++;
}
if (isT2 == 0 && list2 == null) {
list2 = headA;
isT2++;
}
}
return null;
}
}
二叉树的中序遍历
解法1
- 递归
- 左根右
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
inOrder(result, root);
return result;
}
public void inOrder(List<Integer> result, TreeNode node) {
if (node == null) {
return;
}
inOrder(result, node.left);
result.add(node.val);
inOrder(result, node.right);
}
}
二叉树的锯齿形层次遍历
解法1
- 解法同二叉树的层次遍历
- 只需修改偶数行的add方法,增加一个下标0即可,从头部插入
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
zigzagLevelO(root, 0, result);
return result;
}
public void zigzagLevelO(TreeNode root, int level, List<List<Integer>> result) {
if (root == null) {
return;
}
if (level >= result.size()) {
result.add(new ArrayList<Integer>());
}
if (level % 2 == 0) {
result.get(level).add(root.val);
} else {
result.get(level).add(0, root.val);
}
zigzagLevelO(root.left, level + 1, result);
zigzagLevelO(root.right, level + 1, result);
}
}
从前序与中序遍历序列构造二叉树
解法1
大佬思路
前序遍历的顺序是:根左右
中序遍历的顺序是:左根右
所以前序序列的第一个元素为根节点元素,且右边最靠近根节点的元素为左子树的根节点的元素
所以从前序序列中拿到根节点元素,然后在中序序列中找个根节点的索引,
然后创建根节点,
然后以相同的方式,使用从序列开始的索引到根索引的位置创建左子树
然后以相同的方式,使用从根索引的位置到序列末尾的位置的序列创建右子树
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
private int preRootIndex = 0;
private int[] gPreOrder;
private int[] gInOrder;
private Map<Integer, Integer> map = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
gPreOrder = preorder;
gInOrder = inorder;
int length = gPreOrder.length;
for (int i = 0; i < length; i++) {
map.put(gInOrder[i], i);
}
return createNode(0, length - 1);
}
private TreeNode createNode(int inLeft, int inRight) {
if(inLeft > inRight) {
return null;
}
int rootVal = gPreOrder[preRootIndex];
preRootIndex++;
TreeNode node = new TreeNode(rootVal);
int inRootIndex = map.get(rootVal);
node.left = createNode(inLeft, inRootIndex - 1);
node.right = createNode(inRootIndex + 1, inRight);
return node;
}
}
填充每个节点的下一个右侧节点指针
解法1
- 递归
- 每个结点的左右节点进行递归
- 左的left连左的right
- 左的right连右的left
- 右的left连右的right
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node next;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, Node _left, Node _right, Node _next) {
val = _val;
left = _left;
right = _right;
next = _next;
}
};
*/
class Solution {
public Node connect(Node root) {
if (root != null) {
connectIt(root.left, root.right);
}
return root;
}
public void connectIt(Node left, Node right) {
if (left == null || left.next == right) {
return;
}
left.next = right;
connectIt(left.left, left.right);
connectIt(left.right, right.left);
connectIt(right.left, right.right);
}
}
二叉搜索树中第K小的元素
解法1
- 由于是二叉搜索树
- 中序遍历正好是从小到大的顺序
- 中序遍历取结果即可
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
private int count;
private int result = -1;
public int kthSmallest(TreeNode root, int k) {
count = k;
inOrder(root);
return result;
}
public void inOrder(TreeNode node) {
if (node == null) {
return;
}
inOrder(node.left);
count--;
if (count == 0) {
result = node.val;
return;
}
inOrder(node.right);
}
}
岛屿数量
解法1
递归
- 遍历网格
- 如果碰到1就count自加1并且进入递归:
- 按照上右下左的方向判断是否为1,如果为1再次调用自己
- 返回count计数
class Solution {
public int numIslands(char[][] grid) {
int count = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == '1') {
remove(grid, i, j);
count++;
}
}
}
return count;
}
public void remove(char[][] grid, int i, int j) {
if (i > 0 && grid[i - 1][j] == '1') {
grid[i - 1][j] = 0;
remove(grid, i - 1, j);
}
if (j < grid[0].length - 1 && grid[i][j + 1] == '1') {
grid[i][j + 1] = 0;
remove(grid, i, j + 1);
}
if (i < grid.length - 1 && grid[i + 1][j] == '1') {
grid[i + 1][j] = 0;
remove(grid, i + 1, j);
}
if (j > 0 && grid[i][j - 1] == '1') {
grid[i][j - 1] = 0;
remove(grid, i, j - 1);
}
}
}
电话号码的字母组合
解法1
- 设计一个拼接函数,用于拼接每种组合类型
- 用每个数字对应的字母对字符串进行拼接
- 进行递归
- 递归到末尾后,将字符串连接到List上
- 返回List
class Solution {
public List<String> letterCombinations(String digits) {
if (digits.length() == 0) {
return new ArrayList<>();
}
char[] ch = digits.toCharArray();
String str = "";
List<String> result = new ArrayList<>();
addList(result, 0, str, ch);
return result;
}
public void addList(List<String> result, int count, String str, char[] ch) {
if (count == ch.length) {
result.add(str);
return;
}
int temp = 0;
if (ch[count] > '7') {
temp = 1;
}
addList(result, count + 1, str + (char) ((ch[count] - '2') * 3 + 'a' + temp), ch);
addList(result, count + 1, str + (char) ((ch[count] - '2') * 3 + 1 + 'a' + temp), ch);
addList(result, count + 1, str + (char) ((ch[count] - '2') * 3 + 2 + 'a' + temp), ch);
if (ch[count] == '7' || ch[count] == '9') {
addList(result, count + 1, str + (char) ((ch[count] - '2') * 3 + 3 + 'a' + temp), ch);
}
}
}
解法2
- 优化:
- 使用StringBuilder替换String
- 每个StringBuilder使用完成后进行移除回溯
class Solution {
public List<String> letterCombinations(String digits) {
if (digits.length() == 0) {
return new ArrayList<>();
}
char[] ch = digits.toCharArray();
List<String> result = new ArrayList<>();
addList(result, 0, new StringBuilder(), ch);
return result;
}
public void addList(List<String> result, int count, StringBuilder str, char[] ch) {
if (count == ch.length) {
result.add(str.toString());
return;
}
int temp = 0;
if (ch[count] > '7') {
temp = 1;
}
addList(result, count + 1, str.append((char) ((ch[count] - '2') * 3 + 'a' + temp)), ch);
str.deleteCharAt(count);
addList(result, count + 1, str.append((char) ((ch[count] - '2') * 3 + 1 + 'a' + temp)), ch);
str.deleteCharAt(count);
addList(result, count + 1, str.append((char) ((ch[count] - '2') * 3 + 2 + 'a' + temp)), ch);
str.deleteCharAt(count);
if (ch[count] == '7' || ch[count] == '9') {
addList(result, count + 1, str.append((char) ((ch[count] - '2') * 3 + 3 + 'a' + temp)), ch);
str.deleteCharAt(count);
}
}
}
括号生成
解法1
- 递归回溯
- n记录还有几个左括号没添加,m记录还有就几个右括号没添加
- 当m和n均为0时向list中添加当前字符串
- 使用StringBuilder提高效率
- 返回List
class Solution {
public List<String> generateParenthesis(int n) {
List<String> result = new ArrayList<>();
createIt(result, 0, n, new StringBuilder(), 0);
return result;
}
public void createIt(List<String> result, int m, int n, StringBuilder str, int count) {
if (m == 0 && n == 0) {
result.add(str.toString());
return;
}
if (m > 0) {
str.append(')');
createIt(result, m - 1, n, str, count + 1);
str.deleteCharAt(count);
}
if (n > 0) {
str.append('(');
createIt(result, m + 1, n - 1, str, count + 1);
str.deleteCharAt(count);
}
}
}
全排列
解法1
- 遍历nums,将遍历到的每一位和后面的每一位进行交换,即可得出当前位的所有情况
- 遍历结束将数组转换为List放入List中
- return
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
findIt(result, 0, nums);
return result;
}
public void findIt(List<List<Integer>> result, int index, int[] nums) {
if (index == nums.length) {
List<Integer> list = new ArrayList<>();
for (int num : nums) {
list.add(num);
}
result.add(list);
return;
}
for (int i = index; i < nums.length; i++) {
re(nums, i, index);
findIt(result, index + 1, nums);
re(nums, i, index);
}
}
public void re(int [] nums, int i, int j) {
if (i == j) {
return;
}
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
子集
解法1
- 回溯
- 对每一位的每一种情况进行先选择再撤销
- 同时记录出现过的所有情况,所有情况均为集合的子集
- 返回List
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
findIt(result, new ArrayList<>(), nums, 0);
return result;
}
public void findIt(List<List<Integer>> result, List<Integer> list, int[] nums, int start) {
result.add(new ArrayList<>(list));
for (int i = start; i < nums.length; i++) {
list.add(nums[i]);
findIt(result, list, nums, i + 1);
list.remove(list.size() - 1);
}
}
}
单词搜索
解法1
- 遍历数组
- 找到起点即开始搜索四个方向,并标记已走过的点(坑,不标会有重复路径报错)
- 搜索成功返回true,否则返回false
class Solution {
public boolean exist(char[][] board, String word) {
char[] ch = word.toCharArray();
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (isT(board, ch, 0, i, j)) {
return true;
}
}
}
return false;
}
public boolean isT(char[][] board, char[] ch, int flag, int i, int j) {
if (i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != ch[flag]) {
return false;
}
if (flag == ch.length - 1) {
return true;
}
char temp = board[i][j];
board[i][j] = '-';
boolean result = isT(board, ch, flag + 1, i + 1, j) ||
isT(board, ch, flag + 1, i, j + 1) ||
isT(board, ch, flag + 1, i - 1, j) ||
isT(board, ch, flag + 1, i, j - 1);
board[i][j] = temp;
return result;
}
}
颜色分类
解法1
- 遍历把0移到左边,把2移到右边
class Solution {
public void sortColors(int[] nums) {
int left = 0;
int right = nums.length - 1;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0) {
int temp = nums[i];
nums[i] = nums[left];
nums[left] = temp;
left++;
} else if (nums[i] == 2) {
int temp = nums[i];
nums[i] = nums[right];
nums[right] = temp;
right--;
i--;
}
if (i >= right) {
break;
}
}
}
}
前 K 个高频元素
解法1
- 用一个HashMap存放每个元素及出现次数
- 用一个优先级队列来存放前k个高频元素(小顶堆)
- 遍历map,当队列满之后每次都判断从map中取出的元素出现次数和队首的元素出现此数
- 保留其中较高的那一个
- 循环结束,依次出队,即为前k个高频元素
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
public int compare(int[] m, int[] n) {
return m[1] - n[1];
}
});
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
int[] kv = new int[2];
kv[0] = entry.getKey();
kv[1] = entry.getValue();
if (queue.size() == k) {
if (queue.peek()[1] < kv[1]) {
queue.poll();
queue.offer(kv);
}
} else {
queue.offer(kv);
}
}
int[] result = new int[k];
for (int i = 0; i < k; i++) {
result[i] = queue.poll()[0];
}
return result;
}
}
数组中的第K个最大元素
解法1
- 用一个长度为20001的数组来计数
- 反向寻找
class Solution {
public int findKthLargest(int[] nums, int k) {
int[] hhh = new int[20001];
for (int num : nums) {
hhh[num + 10000]++;
}
for (int i = 20000; i >= 0; i--) {
k -= hhh[i];
if (k <= 0) {
return i - 10000;
}
}
return -1;
}
}
寻找峰值
解法1
二分查找
class Solution {
public int findPeakElement(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid + 1] < nums[mid]) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
在排序数组中查找元素的第一个和最后一个位置
解法1
二分查找
- 碰到相等的元素,把其确定为中点,向两侧遍历寻找临界点并返回
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] result = {-1, -1};
if (nums.length == 0) {
return result;
}
if (nums.length == 1 && nums[0] == target) {
result[0] = 0;
result[1] = 0;
return result;
}
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
left = mid;
right = mid;
while (left != 0 && nums[left - 1] == target) {
left--;
}
while (right != nums.length - 1 && nums[right + 1] == target) {
right++;
}
result[0] = left;
result[1] = right;
break;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return result;
}
}
合并区间
解法1
- 对intervals里面进行排序(按照第一个数字的大小排序)
- 循环遍历intervals,对于能和后一个区间合并的向后合并,前一个区间置为0
- 再次遍历intervals,将不为0的区间取出,放到一个新数组中
- return新数组即为结果
class Solution {
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
int count = intervals.length;
for (int i = 0; i < intervals.length - 1; i++) {
if (intervals[i][1] >= intervals[i + 1][0]) {
intervals[i + 1][0] = intervals[i][0];
if (intervals[i + 1][1] < intervals[i][1]) {
intervals[i + 1][1] = intervals[i][1];
}
intervals[i][0] = 0;
intervals[i][1] = 0;
count--;
}
}
int[][] result = new int[count][2];
int number = 0;
for (int i = 0; i < intervals.length; i++) {
if (intervals[i][0] == 0 && intervals[i][1] == 0) {
continue;
}
result[number][0] = intervals[i][0];
result[number][1] = intervals[i][1];
number++;
}
return result;
}
}
搜索旋转排序数组
解法1
找断点+二分查找
class Solution {
public int search(int[] nums, int target) {
if (nums.length == 1) {
if (nums[0] == target) {
return 0;
}
return -1;
}
int left = 0;
int right = nums.length - 1;
if (nums[left] == target) {
return left;
}
if (nums[right] == target) {
return right;
}
if (nums[right] < nums[left]) {
for (int i = 0; i < nums.length; i++) {
if (nums[i + 1] < nums[i]) {
if (nums[0] > target) {
left = i + 1;
break;
} else {
right = i;
break;
}
}
}
}
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
}
搜索二维矩阵 II
解法1
- 从右上角开始递归
- target大于当前数字左移
- target小于当前数字下移
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
if (matrix.length == 0) {
return false;
}
return hasIt(matrix, target, 0, matrix[0].length - 1);
}
public boolean hasIt(int[][]matrix, int target, int i, int j) {
if (i < 0 || j < 0 || i >= matrix.length || j >= matrix[0].length) {
return false;
}
if (matrix[i][j] == target) {
return true;
} else if (matrix[i][j] < target) {
return hasIt(matrix, target, i + 1, j);
} else {
return hasIt(matrix, target, i, j - 1);
}
}
}
跳跃游戏
解法1
- 动态规划
dp[i]:从i开始最多可以往后跳几步dp[i]=max(dp[i-1] - 1, nums[i])- 结束条件:
dp[i] + i + 1 >= nums.len(返回true) 或者dp[i]==0(返回false)
class Solution {
public boolean canJump(int[] nums) {
if (nums.length == 1) {
return true;
}
if (nums[0] == 0) {
return false;
}
for (int i = 1; i < nums.length - 1; i++) {
nums[i] = Math.max(nums[i - 1] - 1, nums[i]);
if (nums[i] == 0) {
return false;
}
if (i + nums[i] >= nums.length) {
return true;
}
}
return true;
}
}
不同路径
解法1
- 动态规划:
- 初始化:从(0,0)到首行、首列中的任意位置路径只有1条
- 状态转移条件:新的一行,或新的一列
- 状态转移表达式:
pathNum(i, j) = pathNum(i - 1, j) + pathNum(i, j - 1) - 退出条件:从节点(0,0)遍历到节点(m - 1, n - 1),pathNum(m - 1, n - 1)中即为最终解
class Solution {
public int uniquePaths(int m, int n) {
int[][] road = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (i == 0 || j == 0) {
road[i][j] = 1;
} else {
road[i][j] = road[i - 1][j] + road[i][j - 1];
}
}
}
return road[m - 1][n - 1];
}
}
零钱兑换
思路
解法1
public class Solution {
public int coinChange(int[] coins, int amount) {
int max = amount + 1;
int[] dp = new int[amount + 1];
Arrays.fill(dp, max);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.length; j++) {
if (coins[j] <= i) {
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
}
最长递增子序列
思路
最长递增子序列 - 最长递增子序列 - 力扣(LeetCode)
解法1
class Solution {
public int lengthOfLIS(int[] nums) {
int len = 1, n = nums.length;
if (n == 0) {
return 0;
}
int[] d = new int[n + 1];
d[len] = nums[0];
for (int i = 1; i < n; ++i) {
if (nums[i] > d[len]) {
d[++len] = nums[i];
} else {
int l = 1, r = len, pos = 0; // 如果找不到说明所有的数都比 nums[i] 大,此时要更新 d[1],所以这里将 pos 设为 0
while (l <= r) {
int mid = (l + r) >> 1;
if (d[mid] < nums[i]) {
pos = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
d[pos + 1] = nums[i];
}
}
return len;
}
}
二叉树的序列化与反序列化
解法1
中序、后序、先序和层次遍历解法 - 二叉树的序列化与反序列化 - 力扣(LeetCode)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if (root == null) {
return "null";
}
return root.val + "," + serialize(root.left) + "," + serialize(root.right);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
List<String> list = new ArrayList<String>(Arrays.asList(data.split(",")));
return createTree(list);
}
public TreeNode createTree(List<String> list) {
if ("null".equals(list.get(0))) {
list.remove(0);
return null;
}
TreeNode node = new TreeNode(Integer.valueOf(list.get(0)));
list.remove(0);
node.left = createTree(list);
node.right = createTree(list);
return node;
}
}
// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));
常数时间插入、删除和获取随机元素
解法1
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);
}
}
/**
* Your RandomizedSet object will be instantiated and called as such:
* RandomizedSet obj = new RandomizedSet();
* boolean param_1 = obj.insert(val);
* boolean param_2 = obj.remove(val);
* int param_3 = obj.getRandom();
*/
快乐数
解法1
把每次循环出的结果放到一个hashset中,如果有重复就可以提前return,出现重复就形成循环了,不会快乐了
例子的问题,居然没有暴力循环快,这是没想到的
class Solution {
public boolean isHappy(int n) {
HashSet<Integer> set = new HashSet<>();
while (n != 1) {
set.add(n);
int temp = n;
n = 0;
while (temp != 0) {
n += (temp % 10) * (temp % 10);
temp /= 10;
}
if (set.contains(n)) {
return false;
}
}
return true;
}
}
阶乘后的零
解法1
- 计算n以内能分解出几个5,最终有几个5就有几个0
class Solution {
public int trailingZeroes(int n) {
int count = 0;
while (n >= 5) {
count += n / 5;
n /= 5;
}
return count;
}
}
Excel表列序号
解法1
- 26进制转十进制
class Solution {
public int titleToNumber(String columnTitle) {
char[] ch = columnTitle.toCharArray();
int sum = 0;
for (int i = 0; i < ch.length; i++) {
sum = sum * 26 + (ch[i] - 'A' + 1);
}
return sum;
}
}
Pow(x, n)
解法1
- 折半计算,每次把n缩小一半,奇数少乘一次x
class Solution {
public double myPow(double x, int n) {
if (n == 0 || x == 1) {
return 1;
}
if (n < 0) {
x = 1 / x;
n = -1 * n;
}
double result = 1;
for (int i = n; i != 0; i /= 2) {
if (i % 2 != 0) {
result *= x;
}
x *= x;
}
return result;
}
}
x的平方根
解法1
二分查找
class Solution {
public int mySqrt(int x) {
int left = 0;
int right = x;
while (left <= right) {
int mid = left + (right - left) / 2;
long pow = (long) mid * mid;
if (pow == x) {
return mid;
} else if (pow > x) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return right;
}
}
两数相除
思路
解法1
class Solution {
public int divide(int dividend, int divisor) {
// 考虑被除数为最小值的情况
if (dividend == Integer.MIN_VALUE) {
if (divisor == 1) {
return Integer.MIN_VALUE;
}
if (divisor == -1) {
return Integer.MAX_VALUE;
}
}
// 考虑除数为最小值的情况
if (divisor == Integer.MIN_VALUE) {
return dividend == Integer.MIN_VALUE ? 1 : 0;
}
// 考虑被除数为 0 的情况
if (dividend == 0) {
return 0;
}
// 一般情况,使用类二分查找
// 将所有的正数取相反数,这样就只需要考虑一种情况
boolean rev = false;
if (dividend > 0) {
dividend = -dividend;
rev = !rev;
}
if (divisor > 0) {
divisor = -divisor;
rev = !rev;
}
List<Integer> candidates = new ArrayList<Integer>();
candidates.add(divisor);
int index = 0;
// 注意溢出
while (candidates.get(index) >= dividend - candidates.get(index)) {
candidates.add(candidates.get(index) + candidates.get(index));
++index;
}
int ans = 0;
for (int i = candidates.size() - 1; i >= 0; --i) {
if (candidates.get(i) >= dividend) {
ans += 1 << i;
dividend -= candidates.get(i);
}
}
return rev ? -ans : ans;
}
}
分数到小数
思路
解法1
class Solution {
public String fractionToDecimal(int numerator, int denominator) {
long numeratorLong = (long) numerator;
long denominatorLong = (long) denominator;
if (numeratorLong % denominatorLong == 0) {
return String.valueOf(numeratorLong / denominatorLong);
}
StringBuffer sb = new StringBuffer();
if (numeratorLong < 0 ^ denominatorLong < 0) {
sb.append('-');
}
// 整数部分
numeratorLong = Math.abs(numeratorLong);
denominatorLong = Math.abs(denominatorLong);
long integerPart = numeratorLong / denominatorLong;
sb.append(integerPart);
sb.append('.');
// 小数部分
StringBuffer fractionPart = new StringBuffer();
Map<Long, Integer> remainderIndexMap = new HashMap<Long, Integer>();
long remainder = numeratorLong % denominatorLong;
int index = 0;
while (remainder != 0 && !remainderIndexMap.containsKey(remainder)) {
remainderIndexMap.put(remainder, index);
remainder *= 10;
fractionPart.append(remainder / denominatorLong);
remainder %= denominatorLong;
index++;
}
if (remainder != 0) { // 有循环节
int insertIndex = remainderIndexMap.get(remainder);
fractionPart.insert(insertIndex, '(');
fractionPart.append(')');
}
sb.append(fractionPart.toString());
return sb.toString();
}
}
两整数之和
- 位运算
-
^是“半加”,“不进位加”,使用异或得到除去进位的那部分结果
-
只有1&1=1,其余都为0,所以可以使用&表示进位的那部分结果
-
因为&的结果表示的是进位,所以将&的结果向左移1位
-
若&结果不为null,继续上面操作直至&结果为null(即没有进位)
解法1
class Solution {
public int getSum(int a, int b) {
if (a == 0) {
return b;
}
return getSum((a & b) << 1, a ^ b);
}
}
逆波兰表达式求值
解法1
- 遍历数组
- 如果碰到符号则取出两个栈顶数字进行运算
- 如果碰到数字则入栈
- 最后栈顶则为结果,返回栈顶
class Solution {
public int evalRPN(String[] tokens) {
List<String> list = Arrays.asList("+", "-", "*", "/");
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < tokens.length; i++) {
if (!list.contains(tokens[i])) {
stack.push(Integer.valueOf(tokens[i]));
} else {
int o2 = stack.pop();
int o1 = stack.pop();
if (tokens[i].equals("+")) {
stack.push(o1 + o2);
}
else if (tokens[i].equals("-")) {
stack.push(o1 - o2);
}
else if (tokens[i].equals("*")) {
stack.push(o1 * o2);
}
else if (tokens[i].equals("/")) {
stack.push(o1 / o2);
}
}
}
if (stack.isEmpty()) {
return 0;
}
return stack.pop();
}
}
多数元素
解法1
- 由于一定存在结果,那么就可以采用计数的方式
- 遍历数组
- 如果当前数是正在计数的数,则count++
- 如果当前数不是正在计数的数,则count--
- 如果count为0了,则替换掉正在计数的数为当前数
- 返回正在计数的数
class Solution {
public int majorityElement(int[] nums) {
int temp = nums[0];
int count = 1;
for (int i = 1; i < nums.length; i++) {
if (count == 0) {
temp = nums[i];
count++;
} else if (nums[i] != temp) {
count--;
} else {
count++;
}
}
return temp;
}
}
任务调度器
思路
【任务调度器】C++ 桶子_配图理解 - 任务调度器 - 力扣(LeetCode)
解法1
class Solution {
public int leastInterval(char[] tasks, int n) {
Map<Character, Integer> freq = new HashMap<Character, Integer>();
// 最多的执行次数
int maxExec = 0;
for (char ch : tasks) {
int exec = freq.getOrDefault(ch, 0) + 1;
freq.put(ch, exec);
maxExec = Math.max(maxExec, exec);
}
// 具有最多执行次数的任务数量
int maxCount = 0;
Set<Map.Entry<Character, Integer>> entrySet = freq.entrySet();
for (Map.Entry<Character, Integer> entry : entrySet) {
int value = entry.getValue();
if (value == maxExec) {
++maxCount;
}
}
return Math.max((maxExec - 1) * (n + 1) + maxCount, tasks.length);
}
}