leecode更新

This commit is contained in:
markilue 2023-03-17 13:38:50 +08:00
parent 4d659677a1
commit 38f94367ee
7 changed files with 678 additions and 0 deletions

View File

@ -0,0 +1,136 @@
package com.markilue.leecode.hot100;
import com.markilue.leecode.listnode.ListNode;
import java.util.List;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-17 09:52
*@Description:
* TODO 力扣234 回文链表:
* 给你一个单链表的头节点 head 请你判断该链表是否为回文链表如果是返回 true 否则返回 false
*@Version: 1.0
*/
public class T69_IsPalindrome {
/**
* 思路:遍历一边用数组记录下来在进行判断?
* 速度击败17.97% 内存击败94.89% 16ms
* @param head
* @return
*/
public boolean isPalindrome(ListNode head) {
if (head == null) return true;
StringBuilder sb = new StringBuilder();
ListNode temp = head;
while (temp != null) {
sb.append(temp.val);
temp = temp.next;
}
return eval(sb);
}
public boolean eval(StringBuilder sb) {
int left = 0;
int right = sb.length() - 1;
while (left < right) {
if (sb.charAt(left) != sb.charAt(right)) return false;
left++;
right--;
}
return true;
}
boolean flag = true;
/**
* 将head传递下去?递归判断法
* 速度击败25.8% 内存击败15.82% 11ms
*
* @param head
* @return
*/
public boolean isPalindrome1(ListNode head) {
RecurEval(head, head);
return flag;
}
public ListNode RecurEval(ListNode cur, ListNode head) {
if (cur.next == null) {
if (cur.val == head.val) return head.next;
else {
flag = false;
return null;
}
}
ListNode listNode = RecurEval(cur.next, head);
if (listNode == null) return null;
if (cur.val == listNode.val) return listNode.next;
else {
flag = false;
return null;
}
}
/** O(n) time and O(1) space
* mid
* 1 -> 2 -> 3 -> 2 -> 1
* 1 -> 2 -> 3 -> 1 -> 2 reverse(mid.next)
* p1 p2
* 官方最快: 快慢指针+反转链表
* 速度击败63.33% 内存击败38.21% 5ms 更快的都是相同的方法
*/
public boolean isPalindrome2(ListNode head) {
if (head == null) {
return true;
}
ListNode mid = findMid(head);
ListNode p2 = reverse(mid.next);
ListNode p1 = head;
while (p2 != null) {//p2!=null是重点p2短一些
if (p1.val != p2.val) {
return false;
}
p1 = p1.next;
p2 = p2.next;
}
return true;
}
private ListNode findMid(ListNode head) {
ListNode fast = head, slow = head;
//必须加fast.next != null不然null pointer
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
private ListNode reverse(ListNode head) {
ListNode prev = null;
while(head != null) {
ListNode temp = head.next;
head.next = prev;
prev = head;
head = temp;
}
return prev;
}
}

View File

@ -0,0 +1,53 @@
package com.markilue.leecode.hot100;
import com.markilue.leecode.tree.TreeNode;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-17 10:19
*@Description:
* TODO 力扣236 二叉树的最近公共祖先:
* 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先
* 百度百科中最近公共祖先的定义为对于有根树 T 的两个节点 pq最近公共祖先表示为一个节点 x
* 满足 x pq 的祖先且 x 的深度尽可能大一个节点也可以是它自己的祖先
*@Version: 1.0
*/
public class T70_LowestCommonAncestor {
/**
* 速度击败31.65% 内存击败71.14% 7ms
* @param root
* @param p
* @param q
* @return
*/
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) return null;
if (root == p || root == q) return root;//提前碰到某个节点了
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left == null) return right;
if (right == null) return left;
return root;//两个都不相等就返回自己,证明一个在左边一个在右边
}
/**
* 比较快的那种写法
* @param root
* @param p
* @param q
* @return
*/
public TreeNode lowestCommonAncestor1(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) return root;
TreeNode left = lowestCommonAncestor1(root.left, p, q);
TreeNode right = lowestCommonAncestor1(root.right, p, q);
if (left != null && right != null) {return root;}
return left != null ? left : right;
}
}

View File

@ -0,0 +1,84 @@
package com.markilue.leecode.hot100;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-17 10:41
*@Description:
* TODO 力扣238 除自身以外数组的乘积:
* 给你一个整数数组 nums返回 数组 answer 其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积
* 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 整数范围内
* 请不要使用除法且在 O(n) 时间复杂度内完成此题
*@Version: 1.0
*/
public class T71_ProductExceptSelf {
/**
* 速度击败31.9% 内存击败6.68% 2ms
* @param nums
* @return
*/
public int[] productExceptSelf(int[] nums) {
int length = nums.length;
// L R 分别表示左右两侧的乘积列表
int[] L = new int[length];
int[] R = new int[length];
int[] answer = new int[length];
// L[i] 为索引 i 左侧所有元素的乘积
// 对于索引为 '0' 的元素因为左侧没有元素所以 L[0] = 1
L[0] = 1;
for (int i = 1; i < length; i++) {
L[i] = nums[i - 1] * L[i - 1];
}
// R[i] 为索引 i 右侧所有元素的乘积
// 对于索引为 'length-1' 的元素因为右侧没有元素所以 R[length-1] = 1
R[length - 1] = 1;
for (int i = length - 2; i >= 0; i--) {
R[i] = nums[i + 1] * R[i + 1];
}
// 对于索引 i nums[i] 之外其余各元素的乘积就是左侧所有元素的乘积乘以右侧所有元素的乘积
for (int i = 0; i < length; i++) {
answer[i] = L[i] * R[i]; //重点是这:左乘上右 ->left只考虑了左边right只考虑了右边两者相乘就是除自身以外的乘积
}
return answer;
}
/**
* 官方空间复杂度O(1)优化动态构造
* 速度击败100% 内存击败75.15% 1ms
* @param nums
* @return
*/
public int[] productExceptSelf1(int[] nums) {
int length = nums.length;
int[] answer = new int[length];
// answer[i] 表示索引 i 左侧所有元素的乘积
// 因为索引为 '0' 的元素左侧没有元素 所以 answer[0] = 1
answer[0] = 1;
for (int i = 1; i < length; i++) {
answer[i] = nums[i - 1] * answer[i - 1];
}
// R 为右侧所有元素的乘积
// 刚开始右边没有元素所以 R = 1
int R = 1;
for (int i = length - 1; i >= 0; i--) {
// 对于索引 i左边的乘积为 answer[i]右边的乘积为 R
answer[i] = answer[i] * R;
// R 需要包含右边所有的乘积所以计算下一个结果时需要将当前值乘到 R
R *= nums[i];
}
return answer;
}
}

View File

@ -0,0 +1,111 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
import java.util.ArrayDeque;
import java.util.Comparator;
import java.util.PriorityQueue;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-17 11:08
*@Description:
* TODO 力扣239 滑动窗口最大值:
* 给你一个整数数组 nums有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧你只可以看到在滑动窗口内的 k 个数字滑动窗口每次只向右移动一位
* 返回 滑动窗口中的最大值
*@Version: 1.0
*/
public class T72_MaxSlidingWindow {
@Test
public void test() {
int[] nums = {1, 3, -1, -3, 5, 3, 6, 7};
int k = 3;
System.out.println(maxSlidingWindow(nums, k));
}
@Test
public void test1() {
int[] nums = {1};
int k = 1;
System.out.println(maxSlidingWindow(nums, k));
}
/**
* 后续的一个最大值:
* 使用一个优先队列来维护最大值,过期元素去除
* @param nums
* @param k
* @return
*/
public int[] maxSlidingWindow(int[] nums, int k) {
PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o2[0] - o1[0];
}
});
for (int i = 0; i < k && i < nums.length; i++) {
queue.add(new int[]{nums[i], i});
}
if (nums.length < k) return new int[]{queue.peek()[0]};
int[] result = new int[nums.length - k + 1];
result[0] = queue.peek()[0];
for (int i = k; i < nums.length; i++) {
queue.add(new int[]{nums[i], i});
//移除过期元素
while (!queue.isEmpty() && i - queue.peek()[1] >= k) {
queue.poll();
}
result[i - k + 1] = queue.peek()[0];
}
return result;
}
/**
* 官方较快:维护一个单调队列即可
* 速度击败71.45% 内存击败46.7% 29ms
* @param nums
* @param k
* @return
*/
public int[] maxSlidingWindow1(int[] nums, int k) {
ArrayDeque<Integer> deque = new ArrayDeque<>();
int n = nums.length;
int[] res = new int[n - k + 1];
int idx = 0;
for(int i = 0; i < n; i++) {
// 根据题意i为nums下标是要在[i - k + 1, i] 中选到最大值只需要保证两点
// 1.队列头结点需要在[i - k + 1, i]范围内不符合则要弹出
while(!deque.isEmpty() && deque.peek() < i - k + 1){
deque.poll();
}
// 2.既然是单调就要保证每次放进去的数字要比末尾的都大否则也弹出
while(!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {//依次跟末尾比
deque.pollLast();
}
deque.offer(i);
// 因为单调当i增长到符合第一个k范围的时候每滑动一步都将队列头节点放入结果就行了
if(i >= k - 1){
res[idx++] = nums[deque.peek()];
}
}
return res;
}
}

View File

@ -0,0 +1,139 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-17 11:32
*@Description:
* TODO 力扣240 搜索二维矩阵II:
* 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 该矩阵具有以下特性
* 每行的元素从左到右升序排列
* 每列的元素从上到下升序排列
*@Version: 1.0
*/
public class T73_SearchMatrix {
@Test
public void test() {
int[][] matrix = {
{1, 4, 7, 11, 15},
{2, 5, 8, 12, 19},
{3, 6, 9, 16, 22},
{10, 13, 14, 17, 24},
{18, 21, 23, 26, 30}};
int target = 20;
System.out.println(searchMatrix(matrix, target));
}
@Test
public void test1() {
int[][] matrix = {
{-1, 3}};
int target = 3;
System.out.println(searchMatrix(matrix, target));
}
boolean[][] visited;
/**
* 思路:直接依次判断对角行不行来设计一个横行和一个竖行
* 速度击败5.38% 内存击败98.29% 77ms
* @param matrix
* @param target
* @return
*/
public boolean searchMatrix(int[][] matrix, int target) {
visited = new boolean[matrix.length][matrix[0].length];
return search(matrix, 0, 0, target);
}
public boolean search(int[][] matrix, int i, int j, int target) {
if (i < 0 || j < 0 || i >= matrix.length || j >= matrix[0].length) {
return false;
}
if (!visited[i][j]) {
visited[i][j] = true;
if (matrix[i][j] < target) {
return search(matrix, i + 1, j + 1, target) || search(matrix, i + 1, j, target) || search(matrix, i, j + 1, target);
} else if (matrix[i][j] > target) {
return search(matrix, i - 1, j, target) || search(matrix, i, j - 1, target);
} else {
return true;
}
}
return false;
}
/**
* 官方最快:Z 字形查找
* 时间复杂度O(m+n)
* TODO 合理性理由:
* 1)如果 matrix[x,y]=target说明搜索完成
* 2)如果 matrix[x,y]>target由于每一列的元素都是升序排列的那么在当前的搜索矩阵中所有位于第 y 列的元素都是严格大于target 因此我们可以将它们全部忽略即将 yyy 减少 111
* 3)如果 matrix[x,y]<targe由于每一行的元素都是升序排列的那么在当前的搜索矩阵中所有位于第 x行的元素都是严格小于 target的因此我们可以将它们全部忽略即将 xxx 增加 111
* 速度击败97.8% 内存击败81.78% 5ms
* @param matrix
* @param target
* @return
*/
public boolean searchMatrix1(int[][] matrix, int target) {
int i = 0;
int j = matrix[0].length - 1;
while (i < matrix.length && j >= 0) {
if (matrix[i][j] == target) {
return true;
}
if (matrix[i][j] > target) {
j--;
} else {
i++;
}
}
return false;
}
/**
* 官方第二快:二分法
* 速度击败38.68% 内存击败52.77% 6ms
* @param matrix
* @param target
* @return
*/
public boolean searchMatrix2(int[][] matrix, int target) {
for (int[] ints : matrix) {
if(search(ints,target,0,ints.length-1)!=-1)return true;
}
return false;
}
public int search(int[] matrix, int target, int start, int end) {
if (start == end) {
if (matrix[start] == target) return start;
else return -1;
}
int mid = start + ((end - start) >> 1);
if (matrix[mid] > target) {
return search(matrix, target, start, mid - 1);
} else if (matrix[mid] < target) {
return search(matrix, target, mid + 1, end);
} else {
return mid;
}
}
}

View File

@ -0,0 +1,46 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-17 12:16
*@Description:
* TODO 力扣279题 完全平方数:
* 给你一个整数 n 返回 和为 n 的完全平方数的最少数量
* 完全平方数 是一个整数其值等于另一个整数的平方换句话说其值等于一个整数自乘的积例如149 16 都是完全平方数 3 11 不是
*@Version: 1.0
*/
public class T74_NumSquares {
@Test
public void test(){
int n=12;
System.out.println(numSquares(n));
}
/**
* 思路:一次判断他用什么组合最少
* 速度击败43.45% 内存击败49.78% 41ms
* @param n
* @return
*/
public int numSquares(int n) {
int[] dp = new int[n + 1];
dp[1] = 1;
for (int i = 2; i < dp.length; i++) {
dp[i] = i;//最多为他自身
for (int j = 2; j * j <= i; j++) {//平方数
dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
}
}
return dp[n];
}
}

View File

@ -0,0 +1,109 @@
package com.markilue.leecode.test;
import java.util.HashMap;
import java.util.Scanner;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.test
*@Author: markilue
*@CreateTime: 2023-03-16 19:33
*@Description: TODO
*@Version: 1.0
*/
public class testAnt {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String input = sc.nextLine();
new testAnt().test(input);
// StringBuilder output = new StringBuilder();
// int left = 0;
//
// //去除前面的
// while (left < input.length() && input.charAt(left) == ' ') {
// left++;
// }
//
// boolean isCapital = true; // 首字母是否大写
// for (int i = left; i < input.length(); i++) {
// char c = input.charAt(i);
// if (c == ' ') {
// if (i > 0 && input.charAt(i - 1) == '.') {
// output.deleteCharAt(output.length() - 1);
// }
// // 跳过多余的空格
// while (i < input.length() - 1 && input.charAt(i + 1) == ' ') {
// i++;
// }
// if (input.charAt(i + 1) != '.') {
// output.append(' ');
// }
// } else if (c == '.') {
// // 句号后面要加一个空格
// output.append(". ");
// isCapital = true; // 下一句话的首字母要大写
// } else {
// if (isCapital) {
// // 首字母要大写
// output.append(Character.toUpperCase(c));
// isCapital = false;
// } else {
// output.append(c);
// }
// }
// }
//
// System.out.println(output.toString());
sc.close();
}
public void test(String str) {
int left = 0;
//去除前面的
while (left < str.length() && str.charAt(left) == ' ') {
left++;
}
StringBuilder sb = new StringBuilder();
sb.append(Character.toUpperCase(str.charAt(left++)));
boolean flag = false;
while (left < str.length()) {
char c = str.charAt(left);
if (c == '.') {
int temp = sb.length();
while (sb.charAt(--temp)==' ')sb.deleteCharAt(sb.length()-1);
sb.append(". ");
flag = true;
} else if (c == ' ') {
char c1 = str.charAt(left - 1);
if (c1 != ' ' && c1 != '.') {
sb.append(c);
}
} else {
if (flag) {
sb.append(Character.toUpperCase(c));
flag = false;
} else {
sb.append(c);
}
}
left++;
}
System.out.println(sb.toString());
}
}