前言
最近一直在重温算法,该看的都看了一遍,今天开始准备刷题了每天一道,并不会把所有的题目都记录下来,会挑一些有趣的题目放在上面,欢迎一起讨论(第一天略微会简单点)。
二叉树的所有路径
LeetCode第257题,难度简单,该题需要输出二叉树从根节点到叶子节点的所有路径。
该题使用的是迭代的方式进行解答的,执行用时1ms,使用String进行根节点的一个叠加,这里还有一种想法:String在不断的叠加时,会耗费大量空间,可以使用Stack加StringBuilder的方式来节省空间。不断的迭代非叶子节点的左右节点,进行叠加,直到叶子节点,将其加入集合。
public class Solution1 {
public static void main(String[] args) {
Node root = new Node(1);
Node node1 = new Node(2);
Node node2 = new Node(3);
Node node3 = new Node(4);
Node node4 = new Node(5);
Node node5 = new Node(6);
Node node6 = new Node(7);
Node node7 = new Node(8);
Node node8 = new Node(9);
root.left = node1;
node1.left = node2;
node1.right = node3;
root.right = node4;
node2.left = node5;
node2.right = node6;
node5.left = node7;
node7.left = node8;
//结果:[1->2->3->6->8->9, 1->2->3->7, 1->2->4, 1->5]
System.out.println(binaryTreePaths(root));
}
public static List<String> binaryTreePaths(Node root) {
List<String> result = new ArrayList<>();
if (root == null) {
return result;
}
findPath(root, root.val + "", result);
return result;
}
private static void findPath(Node root, String string, List<String> result) {
if (root.left == null && root.right == null) {
result.add(string);
return;
}else{
if (root.left != null) {
findPath(root.left, string + "->" + root.left.val, result);
}
if (root.right != null) {
findPath(root.right, string + "->" + root.right.val, result);
}
}
}
}
class Node {
int val;
Node left;
Node right;
Node(int x) {
val = x;
}
}
IPO
LeetCode第502题,难度困难,从给定项目中选择最多 k 个不同项目的列表,以最大化最终资本,并输出最终可获得的最多资本。
该题思路首先需要将利润和成本组成一个对象,然后判断该项目中,那些是大于初始成本的,那些是小于初始成本的,小于初始成本的项目放入利润降序排列的队列,大于初始成本的项目放入成本升序排列的队列中,之后遍历利润队列,取出的第一个利润,肯定是利润最大的项目,当该项目的利润加到初始成本中后,再次判断成本队列中,是否有满足初始成本的项目,将其加入利润队列中,循环遍历得出最大利润。
/**
* 返回最大的资本
*
* @param k
* @param W
* @param Profits
* @param Capital
* @return
*/
public static int findMaximizedCapital(int k, int W, int[] Profits, int[] Capital) {
//创建两个优先队列
PriorityQueue<Project> profitsQueue = new PriorityQueue<>(new ProfitsComparator());
PriorityQueue<Project> capitalQueue = new PriorityQueue<>(new CapitalComparator());
for (int i = 0; i < Profits.length; i++) {
//将利润和所需资本绑定在一个对象中
Project project = new Project(Profits[i], Capital[i]);
//如果该项目所需的成本大于初始成本
if (project.capital > W) {
capitalQueue.add(project);
} else {
profitsQueue.add(project);
}
}
while (k > 0 && !profitsQueue.isEmpty()) {
//profitsQueue头部的是利润最大且满足初始成本的项目
W += profitsQueue.poll().profits;
//增加了初始成本后,判断capitalQueue的队头元素是否可以满足当前资本了
while (!capitalQueue.isEmpty() && W >= capitalQueue.peek().capital) {
//将其加入profitsQueue
profitsQueue.add(capitalQueue.poll());
}
k--;
}
return W;
}
public static class Project {
public int profits;
public int capital;
public Project(int profits, int capital) {
this.profits = profits;
this.capital = capital;
}
}
/**
* 以利润倒序
*/
public static class ProfitsComparator implements Comparator<Project> {
@Override
public int compare(Project o1, Project o2) {
return o2.profits - o1.profits;
}
}
/**
* 以成本升序
*/
public static class CapitalComparator implements Comparator<Project> {
@Override
public int compare(Project o1, Project o2) {
return o1.capital - o2.capital;
}
}
图像重叠
LeetCode835题,转换其中一个图像,向左,右,上,或下滑动任何数量的单位,并把它放在另一个图像的上面,怎样移动元素,可以得到最大的重叠数。
思路分析:其实该题的描述的过于复杂,转换一下,其实是求数组A中的元素1和数组B中的元素1,怎样的移动可以求出最大的重叠数,即将数组A中的元素1到数组B中的元素1的下标差求出来,并且不断的累加,得出结果。
public static int largestOverlap(int[][] A, int[][] B) {
int max = 0;
//存储A中元素1到B中元素1的重叠数
int[][] nums = new int[2 * B.length][2 * B.length];
for (int i = 0; i < A.length; i++) {
for (int j = 0; j < A[i].length; j++) {
//找到数组A中元素为1的坐标
if (A[i][j] == 1) {
for (int k = 0; k < B.length; k++) {
for (int l = 0; l < B[i].length; l++) {
//寻找数组B中元素1的坐标
if (B[k][l] == 1) {
//使用固定的算法计算A、B元素的距离
nums[B.length - 1 + (k - i)][B.length - 1 + (l - j)]++;
}
}
}
}
}
}
//得出nums数组中最大的值,即为重叠数的最大值
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < nums[i].length; j++) {
if (nums[i][j] > max) {
max = nums[i][j];
}
}
}
return max;
}
递增的三元子序列
LeetCode334题,给定一个未排序的数组,判断这个数组中是否存在长度为 3 的递增子序列,可以不用连续,只是需要递增的3个数即可。
思路分析:该题只判断数组中的元素,是否存在三个递增的元素即可,并不要求连续,我们只需要记录第一个值和第二个值即可,第三个值如果同时大于前两个值,则说明存在三个递增的元素,返回true即可,同理遍历完数组后,未找到的话,则返回false。
public static boolean increasingTriplet(int[] nums) {
int one = Integer.MAX_VALUE;
int two = Integer.MAX_VALUE;
for (int i = 0; i < nums.length; i++) {
//如果小于第一个数,则将其记录下来
if (one >= nums[i]) {
one = nums[i];
} else if (two >= nums[i]) {
//不满足上面的判断,则说明是大于one的,且小于two的
two = nums[i];
} else {
//如果都不满足,则说明该数是大于one和two的
return true;
}
}
return false;
}
找出井字棋的获胜者
LeetCode1275题,黑白棋子谁先连成三个棋子谁先获胜,五子棋的简略版三子棋。
思路分析:该题只需判断moves数组中的最后一步即可,因为一旦获胜游戏就结束,所以只需围绕moves中的最后一步来展开判断即可。
//步数小于5,不会有获胜者
if (moves.length < 5) {
return "Pending";
}
//记录对角线、行、列相同棋子出现的次数
int rowCount = 0;
int colCount = 0;
int subCount = 0;
int addCount = 0;
//最后一步棋下标为moves.length-1,只需从上一步判断即可,循环也只需循环获胜者的步法
for (int i = moves.length - 3; i >= 0; i-=2) {
if (moves[moves.length - 1][0] == moves[i][0]) {
colCount++;
if (colCount == 2) {
return moves.length % 2 == 0 ? "B" : "A";
}
}
if (moves[moves.length - 1][1] == moves[i][1]) {
rowCount++;
if (rowCount == 2) {
return moves.length % 2 == 0 ? "B" : "A";
}
}
if (moves[moves.length - 1][0] + moves[moves.length - 1][1] == moves[i][0] + moves[i][1]) {
addCount++;
if (addCount == 2) {
return moves.length % 2 == 0 ? "B" : "A";
}
}
if (moves[moves.length - 1][0] - moves[moves.length - 1][1] == moves[i][0] - moves[i][1]) {
subCount++;
if (subCount == 2) {
return moves.length % 2 == 0 ? "B" : "A";
}
}
}
if (moves.length == 9) {
return "Draw";
}
return "Pending";
任务调度器
LeetCode621题,该题需要完成指定任务数组中的所有任务,并且有对应的规则,需要注意的是获取完成任务的最小时间。
思路分析:引用LeetCode官方和评论区一些见解,首先统计出现次数最多的一个种类,并且计算完成该种类需要的一个时间,在执行该种类任务的时候,产生的空闲时间对于其余任务来说,会有两种情况,一是足够完成剩余的所有任务,二是不够完成剩余的任务,如果是第一种情况,则返回完成种类任务次数最多的时间即可。如果是第二种情况的话,则说明Figure2图片中的格子个数不够用了,而需要增加的格子个数就是剩余任务的个数,则返回任务的长度即可。
// 假设数组 ["A","A","A","B","B","C"],n = 2,A的频率最高,记为count = 3,所以两个A之间必须间隔2个任务,才能满足题意并且是最短
// 时间(两个A的间隔大于2的总时间必然不是最短),因此执行顺序为: A->X->X->A->X->X->A,这里的X表示除了A以外其他字母,或者是待命,
// 不用关心具体是什么,反正用来填充两个A的间隔的。上面执行顺序的规律是: 有count - 1个A,其中每个A需要搭配n个X,再加上最后一个A,
// 所以总时间为 (count - 1) * (n + 1) + 1要注意可能会出现多个频率相同且都是最高的任务,比如 ["A","A","A","B","B","B","C","C"],
// 所以最后会剩下一个A和一个B,因此最后要加上频率最高的不同任务的个数 maxCount公式算出的值可能会比数组的长度小,
// 如["A","A","B","B"],n = 0,此时要取数组的长度
if (tasks.length <= 1 || n < 1) return tasks.length;
//步骤1
int[] counts = new int[26];
for (int i = 0; i < tasks.length; i++) {
counts[tasks[i] - 'A']++;
}
//步骤2
Arrays.sort(counts);
int maxCount = counts[25];
int retCount = (maxCount - 1) * (n + 1) + 1;
int i = 24;
//步骤3
while (i >= 0 && counts[i] == maxCount) {
retCount++;
i--;
}
//步骤4
return Math.max(retCount, tasks.length);