leecode更新

This commit is contained in:
markilue 2023-04-07 14:20:15 +08:00
parent ee4c7618f8
commit 776e572905
3 changed files with 295 additions and 0 deletions

View File

@ -0,0 +1,100 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-07 10:18
*@Description:
* TODO 力扣406 根据身高重建队列:
* 假设有打乱顺序的一群人站成一个队列数组 people 表示队列中一些人的属性不一定按顺序
* 每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi 前面 正好 ki 个身高大于或等于 hi 的人
* 请你重新构造并返回输入数组 people 所表示的队列
* 返回的队列应该格式化为数组 queue 其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性queue[0] 是排在队列前面的人
*@Version: 1.0
*/
public class T88_ReconstructQueue {
@Test
public void test() {
int[][] people = {{7, 0}, {4, 4}, {7, 1}, {5, 0}, {6, 1}, {5, 2}};
// reconstructQueue1(people);
for (int[] ints : reconstructQueue1(people)) {
System.out.println(Arrays.toString(ints));
}
}
/**
* 思路:排序后使用linkedList插入
* 速度击败26.34% 内存击败70.6% 9ms
* @param people
* @return
*/
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0];
}
});
LinkedList<int[]> list = new LinkedList<>();
for (int[] person : people) {
list.add(person[1], person);
}
int[][] result = new int[people.length][];
return list.toArray(new int[people.length][]);
}
/**
* 先排序后腾挪
* 速度击败81.31% 内存击败17.3% 6ms
* @param people
* @return
*/
public int[][] reconstructQueue1(int[][] people) {
Arrays.sort(people, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] == o2[0] ? o1[1] - o2[1] : o1[0] - o2[0];
}
});
for (int i = people.length - 1; i >= 0; i--) {
int[] person = people[i];
//看看前面有几个和他相等的
int same = 0;
for (int j = i - 1; j >= 0; j--) {
if (people[j][0] == person[0]) same++;
if (people[j][0] < person[0]) break;//小了直接break
}
//不相等需要腾挪
if (same != person[1]) {
int length = person[1] - same;
System.arraycopy(people, i + 1, people, i, length);
people[i + length] = person;
}
}
return people;
}
}

View File

@ -0,0 +1,54 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-07 10:52
*@Description:
* TODO 力扣416 分割等和数组:
* 给你一个 只包含正整数 非空 数组 nums 请你判断是否可以将这个数组分割成两个子集使得两个子集的元素和相等
*@Version: 1.0
*/
public class T89_CanPartition {
@Test
public void test() {
// int[] nums={1,5,11,5};
int[] nums = {1, 2, 3, 5};
System.out.println(canPartition(nums));
}
/**
* 思路:本质上就是找一个总和一半的子序列
* @param nums
* @return
*/
public boolean canPartition(int[] nums) {
if (nums.length < 2) return false;
int sum = 0;
int max = 0;
for (int num : nums) {
sum += num;
max = Math.max(max, num);
}
if (sum % 2 != 0) return false;
int target = sum / 2;
if (max > target) return false;
boolean[] dp = new boolean[target + 1];
dp[0]=true;
for (int i = 0; i < nums.length; i++) {
for (int j = dp.length - 1; j >= nums[i]; j--) {
dp[j] |= dp[j - nums[i]];
}
}
return dp[target];
}
}

View File

@ -0,0 +1,141 @@
package com.markilue.leecode.hot100;
import com.markilue.leecode.tree.TreeNode;
import com.markilue.leecode.tree.TreeUtils;
import org.junit.Test;
import java.util.*;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-07 11:05
*@Description:
* TODO 力扣437 路径总和III:
* 给定一个二叉树的根节点 root 和一个整数 targetSum 求该二叉树里节点值之和等于 targetSum 路径 的数目
* 路径 不需要从根节点开始也不需要在叶子节点结束但是路径方向必须是向下的只能从父节点到子节点
*@Version: 1.0
*/
public class T90_PathSum {
@Test
public void test() {
TreeNode root = TreeUtils.structureTree(new ArrayList<>(Arrays.asList(10, 5, -3, 3, 2, null, 11, 3, -2, null, 1)), 0);
System.out.println(pathSum(root, 8));
}
/**
* 思路:可以把return的状态分为 不要之前的左 不要之前右 都不要
* 有问题:节点的始末不一定是终点
* @param root
* @param targetSum
* @return
*/
public int pathSum(TreeNode root, int targetSum) {
cur.add(0);
path(root, targetSum, 0);
return sum;
}
int sum = 0;
List<Integer> cur = new ArrayList<Integer>();
public void path(TreeNode root, int targetSum, int last) {
if (root == null) return;
int value = root.val;
for (Integer integer : cur) {
if (integer + value == targetSum) sum++;
}
int sum = value + last;
cur.add(value);
cur.add(sum);
path(root.left, targetSum, sum);
cur.remove(cur.size() - 1);
path(root.right, targetSum, sum);
cur.remove(cur.size() - 1);
}
/**
* 分别计算节点为根的和他为路径的之和
* 时间复杂度为O(N^2)
* 速度击败8.17% 内存击败46.54%
* @param root
* @param targetSum
* @return
*/
public int pathSum1(TreeNode root, int targetSum) {
if (root == null) {
return 0;
}
int ret = rootSum(root, targetSum);//计算他为根的
ret += pathSum1(root.left, targetSum);//计算不要他的 为路径的
ret += pathSum1(root.right, targetSum);
return ret;
}
public int rootSum(TreeNode root, int targetSum) {
int ret = 0;
if (root == null) {
return 0;
}
int val = root.val;
if (val == targetSum) {
ret++;
}
ret += rootSum(root.left, targetSum - val);
ret += rootSum(root.right, targetSum - val);
return ret;
}
/**
* 官方前缀和解法
* 时间复杂度O(n)
* * 前缀和的递归回溯思路
* * 从当前节点反推到根节点(反推比较好理解正向其实也只有一条)有且仅有一条路径因为这是一棵树
* * 如果此前有和为currSum-target,而当前的和又为currSum,两者的差就肯定为target了
* * 所以前缀和对于当前路径来说是唯一的当前记录的前缀和在回溯结束回到本层时去除保证其不影响其他分支的结果
* @param root
* @param targetSum
* @return
*/
public int pathSum2(TreeNode root, int targetSum) {
Map<Long, Integer> prefix = new HashMap<Long, Integer>();
prefix.put(0L, 1);
return dfs(root, prefix, 0, targetSum);
}
public int dfs(TreeNode root, Map<Long, Integer> prefix, long curr, int targetSum) {
if (root == null) {
return 0;
}
int ret = 0;
curr += root.val;
//---核心代码
//看看root到当前节点这条路上是否存在节点前缀和加target为currSum的路径
//当前节点->root节点反推有且仅有一条路径如果此前有和为currSum-target,而当前的和又为currSum,两者的差就肯定为target了
//currSum-target相当于找路径的起点起点的sum+target=currSum当前点到起点的距离就是target
ret = prefix.getOrDefault(curr - targetSum, 0);
prefix.put(curr, prefix.getOrDefault(curr, 0) + 1);
ret += dfs(root.left, prefix, curr, targetSum);
ret += dfs(root.right, prefix, curr, targetSum);
prefix.put(curr, prefix.getOrDefault(curr, 0) - 1);
return ret;
}
}