From 242a7ef404e62c2ad5edac5884826cd0a95bb607 Mon Sep 17 00:00:00 2001 From: gameloader Date: Thu, 12 Sep 2024 15:40:05 +0800 Subject: [PATCH] leetcode update --- content/posts/leetcode.md | 1488 +++++++++++++++++++++++++++---------- 1 file changed, 1079 insertions(+), 409 deletions(-) diff --git a/content/posts/leetcode.md b/content/posts/leetcode.md index 5a10e05..b6f22c9 100644 --- a/content/posts/leetcode.md +++ b/content/posts/leetcode.md @@ -7984,7 +7984,8 @@ func minKBitFlips(nums []int, k int) int { ``` ## day120 2024-06-25 -### 1038. Binary Search Tree to Greater Sum Tree + +### 1038. Binary Search Tree to Greater Sum Tree Given the root of a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original BST is changed to the original key plus the sum of all keys greater than the original key in BST. @@ -7994,14 +7995,15 @@ The left subtree of a node contains only nodes with keys less than the node's ke The right subtree of a node contains only nodes with keys greater than the node's key. Both the left and right subtrees must also be binary search trees. -![0625JppqmJoxVzA1](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0625JppqmJoxVzA1.png) +![0625JppqmJoxVzA1](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0625JppqmJoxVzA1.png) ### 题解 -考虑到树为一棵二叉搜索树, 因此对于任何一个节点, 比它大的节点有其右子树的节点, 若其父节点为祖父节点的左子节点, 则还有其祖父节点以及祖父节点的右子树的节点. 以此类推. 考虑最简单的仅有三个节点的情况: 一个父节点和左右两个子节点. 对于左子节点而言, 需要加父节点和右子节点的值. 对于父节点要加右子节点的值. 右子节点不用处理. 因此通过对树遍历将问题转化为类似三个节点的情况. 左右子树均可视为一个节点. 从根节点开始, 对节点进行如下操作, 递归遍历右子树, 计算右子树所有节点的和返回. 将当前节点的值加上右子树的和, 将和传递给左子树并递归遍历左子树, 返回左子树所有节点的和. 最终返回根节点和左右两个子树的和. 注意更新节点值和计算子树和是分开的, 更新节点值要将传递的值和当前值以及右子树的和相加. 但返回时返回的是原来的节点值对应的子树和. +考虑到树为一棵二叉搜索树, 因此对于任何一个节点, 比它大的节点有其右子树的节点, 若其父节点为祖父节点的左子节点, 则还有其祖父节点以及祖父节点的右子树的节点. 以此类推. 考虑最简单的仅有三个节点的情况: 一个父节点和左右两个子节点. 对于左子节点而言, 需要加父节点和右子节点的值. 对于父节点要加右子节点的值. 右子节点不用处理. 因此通过对树遍历将问题转化为类似三个节点的情况. 左右子树均可视为一个节点. 从根节点开始, 对节点进行如下操作, 递归遍历右子树, 计算右子树所有节点的和返回. 将当前节点的值加上右子树的和, 将和传递给左子树并递归遍历左子树, 返回左子树所有节点的和. 最终返回根节点和左右两个子树的和. 注意更新节点值和计算子树和是分开的, 更新节点值要将传递的值和当前值以及右子树的和相加. 但返回时返回的是原来的节点值对应的子树和. ### 代码 -```go + +```go /** * Definition for a binary tree node. * type TreeNode struct { @@ -8036,7 +8038,9 @@ func rightFirst(root *TreeNode, pass int)int{ ``` ## day121 2024-06-26 -### 1382. Balance a Binary Search Tree + +### 1382. Balance a Binary Search Tree + Given the root of a binary search tree, return a balanced binary search tree with the same node values. If there is more than one answer, return any of them. A binary search tree is balanced if the depth of the two subtrees of every node never differs by more than 1. @@ -8044,12 +8048,14 @@ A binary search tree is balanced if the depth of the two subtrees of every node ![0626MDuY3BC9YtnG](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0626MDuY3BC9YtnG.png) ### 题解 -本题要求将二叉搜索树转换为二叉平衡搜索树, 这就是在二叉平衡树新插入节点时常用的平衡操作. 但在本题中不需要使用常规的二叉平衡树中常用的左旋, 右旋等操作, 原因是二叉平衡树中常用的平衡操作是在插入一个新节点时用于调整树的平衡性的, 所以最多只需两次旋转即可调整平衡. 但在本题中, 树的不平衡性可能非常大, 且我们只需要在原来的树的基础上获得一棵新树. 只需考虑构造出一棵平衡树即可, 不需要考虑多次调整的情况. -因此遍历当前整棵二叉搜索树, 将树中的值从小到大保存到数组中, 二分法构造一棵新的平衡树就能得到最终结果. +本题要求将二叉搜索树转换为二叉平衡搜索树, 这就是在二叉平衡树新插入节点时常用的平衡操作. 但在本题中不需要使用常规的二叉平衡树中常用的左旋, 右旋等操作, 原因是二叉平衡树中常用的平衡操作是在插入一个新节点时用于调整树的平衡性的, 所以最多只需两次旋转即可调整平衡. 但在本题中, 树的不平衡性可能非常大, 且我们只需要在原来的树的基础上获得一棵新树. 只需考虑构造出一棵平衡树即可, 不需要考虑多次调整的情况. + +因此遍历当前整棵二叉搜索树, 将树中的值从小到大保存到数组中, 二分法构造一棵新的平衡树就能得到最终结果. ### 代码 -```go + +```go /** * Definition for a binary tree node. * type TreeNode struct { @@ -8105,19 +8111,22 @@ func build(nums []*TreeNode, leng int) *TreeNode{ ``` ## day122 2024-06-27 -### 1791. Find Center of Star Graph + +### 1791. Find Center of Star Graph There is an undirected star graph consisting of n nodes labeled from 1 to n. A star graph is a graph where there is one center node and exactly n - 1 edges that connect the center node with every other node. You are given a 2D integer array edges where each edges[i] = [ui, vi] indicates that there is an edge between the nodes ui and vi. Return the center of the given star graph. -![0627D3O8tjqrKhmI](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0627D3O8tjqrKhmI.png) +![0627D3O8tjqrKhmI](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0627D3O8tjqrKhmI.png) ### 题解 -本题n个节点共有n-1条边, 存在一个中心节点使得其余节点都与这个节点相连. 只需要看edges中的任意两条边, 找到这两条边共有的节点即为中心节点. + +本题n个节点共有n-1条边, 存在一个中心节点使得其余节点都与这个节点相连. 只需要看edges中的任意两条边, 找到这两条边共有的节点即为中心节点. ### 代码 -```go + +```go func findCenter(edges [][]int) int { if edges[1][0] == edges[0][0] || edges[1][0] == edges[0][1]{ return edges[1][0] @@ -8128,9 +8137,10 @@ func findCenter(edges [][]int) int { ``` ### 总结 + 考虑只有一个中心节点与其余n-1个节点相连, 但其余节点也可能相连的情况, 统计所有节点的度, 度最高的即为中心节点. -### 27. Remove Element +### 27. Remove Element Given an integer array nums and an integer val, remove all occurrences of val in nums in-place. The order of the elements may be changed. Then return the number of elements in nums which are not equal to val. @@ -8142,10 +8152,12 @@ Return k. ![06277nzgocBBZMpz](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/06277nzgocBBZMpz.png) ### 题解 -本题最简单的解法当然是再创建一个相同大小的数组, 将原数组中非val的数依次复制到数组中. 另外一种方法则是双指针, 遍历数组, 头指针遇到val则与尾指针指向的非val值交换, 直到头尾指针相遇即可. + +本题最简单的解法当然是再创建一个相同大小的数组, 将原数组中非val的数依次复制到数组中. 另外一种方法则是双指针, 遍历数组, 头指针遇到val则与尾指针指向的非val值交换, 直到头尾指针相遇即可. ### 代码 -```go + +```go func removeElement(nums []int, val int) int { length := len(nums) if length == 0{ @@ -8182,7 +8194,9 @@ func removeElement(nums []int, val int) int { ``` ## day123 2024-06-28 -### 2285. Maximum Total Importance of Roads + +### 2285. Maximum Total Importance of Roads + You are given an integer n denoting the number of cities in a country. The cities are numbered from 0 to n - 1. You are also given a 2D integer array roads where roads[i] = [ai, bi] denotes that there exists a bidirectional road connecting cities ai and bi. @@ -8194,10 +8208,12 @@ Return the maximum total importance of all roads possible after assigning the va ![0628laAsmqEvIGOQ](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0628laAsmqEvIGOQ.png) ### 题解 -本题要给每个节点赋值, 使得最终边的权重和(边连接的两个节点的权重和)最大. 统计出每个节点相连的边的数量, 从大到小排序, 按序依次从最大到最小赋值并计算权重和. 这里边的权重和可以转化为节点能贡献的权重和的和, 即用节点的权重乘以和这个节点相连的边的数量. 再依次对各个节点求值并加和. + +本题要给每个节点赋值, 使得最终边的权重和(边连接的两个节点的权重和)最大. 统计出每个节点相连的边的数量, 从大到小排序, 按序依次从最大到最小赋值并计算权重和. 这里边的权重和可以转化为节点能贡献的权重和的和, 即用节点的权重乘以和这个节点相连的边的数量. 再依次对各个节点求值并加和. ### 题解 -```go + +```go func maximumImportance(n int, roads [][]int) int64 { vers := make([]int, n) for _,value := range roads{ @@ -8214,24 +8230,28 @@ func maximumImportance(n int, roads [][]int) int64 { ``` ## day124 2024-06-29 -### 2192. All Ancestors of a Node in a Directed Acyclic Graph + +### 2192. All Ancestors of a Node in a Directed Acyclic Graph + You are given a positive integer n representing the number of nodes of a Directed Acyclic Graph (DAG). The nodes are numbered from 0 to n - 1 (inclusive). You are also given a 2D integer array edges, where edges[i] = [fromi, toi] denotes that there is a unidirectional edge from fromi to toi in the graph. Return a list answer, where answer[i] is the list of ancestors of the ith node, sorted in ascending order. -A node u is an ancestor of another node v if u can reach v via a set of edges. +A node u is an ancestor of another node v if u can reach v via a set of edges. ![0629uNWG4lSJqpOB](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0629uNWG4lSJqpOB.png) ### 题解 -本题要求出每个节点的所有祖先节点, 按照节点标号从小到大列出. 既然是有向无环图, 那么本图可以进行拓扑排序. 统计所有节点的入度, 从入度为0的节点开始, 依次遍历入度为0的所有节点, 在其全部后继节点对应的祖先数组中中加入遍历的节点标号. 删掉与这些节点相连的边, 再次查看入度为0的的节点, 重复以上操作. 直到数组中只有入度为0的节点即剩余节点都没有后继节点为止. -但这种方式要重复向多个数组中加入相同的值. 如果在统计入度的时候将所有边反向并统计反向边对应的各个节点的入度, 则从入度为0的节点开始按入度从小到大遍历节点并将其所有后继节点(通过bfs进行遍历)放入其在结果数组中对应位置的数组内并排序即可. +本题要求出每个节点的所有祖先节点, 按照节点标号从小到大列出. 既然是有向无环图, 那么本图可以进行拓扑排序. 统计所有节点的入度, 从入度为0的节点开始, 依次遍历入度为0的所有节点, 在其全部后继节点对应的祖先数组中中加入遍历的节点标号. 删掉与这些节点相连的边, 再次查看入度为0的的节点, 重复以上操作. 直到数组中只有入度为0的节点即剩余节点都没有后继节点为止. + +但这种方式要重复向多个数组中加入相同的值. 如果在统计入度的时候将所有边反向并统计反向边对应的各个节点的入度, 则从入度为0的节点开始按入度从小到大遍历节点并将其所有后继节点(通过bfs进行遍历)放入其在结果数组中对应位置的数组内并排序即可. ### 代码 -```go + +```go func getAncestors(n int, edges [][]int) [][]int { innodes := make(map[int]int) outnodes := make([][]int, n) @@ -8243,14 +8263,14 @@ func getAncestors(n int, edges [][]int) [][]int { edges[i][0], edges[i][1] = edges[i][1], edges[i][0] } result := make([][]int, n) - + // 处理入度为0的节点 for i := 0; i < n; i++ { if _, exist := innodes[i]; !exist { result[i] = bfs(i, outnodes) } } - + for inDegree := 1; inDegree <= maxin; inDegree++ { for node, degree := range innodes { if degree == inDegree { @@ -8259,7 +8279,7 @@ func getAncestors(n int, edges [][]int) [][]int { } } } - + return result } @@ -8267,11 +8287,11 @@ func bfs(start int, outnodes [][]int) []int { queue := []int{start} visited := make(map[int]bool) ancestors := make([]int, 0) - + for len(queue) > 0 { node := queue[0] queue = queue[1:] - + for _, neighbor := range outnodes[node] { if !visited[neighbor] { visited[neighbor] = true @@ -8280,14 +8300,16 @@ func bfs(start int, outnodes [][]int) []int { } } } - + sort.Ints(ancestors) return ancestors } ``` ## day125 2024-06-30 -### 1579. Remove Max Number of Edges to Keep Graph Fully Traversable + +### 1579. Remove Max Number of Edges to Keep Graph Fully Traversable + Alice and Bob have an undirected graph of n nodes and three types of edges: Type 1: Can be traversed by Alice only. @@ -8300,17 +8322,19 @@ Return the maximum number of edges you can remove, or return -1 if Alice and Bob ![06308ceBjTn41NK5](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/06308ceBjTn41NK5.png) ### 题解 -本题是一道难题, type3的路径是Alice和Bob都可以使用的, 如果想让每个人都能遍历整个图并且使图中的边最少, 无疑首先要尽量使用type3的路径. 通过type3的路径能连通的点Alice和Bob不需要再使用type1和type2连通, 在type3的连通图中的type1和type2的边都可以删去. 不能删除的边是连通type3边构成的连通图之间连通的type1和type2的边. 使用并查集将type3的边构成的连通图中同一个连通图的节点用一个代表节点表示出来. 遍历type1的边, 将那些能将type3构成的不同连通图连通起来的边保留, 其余的均删除并计数, 若最后所有节点均在同一个集合中, 则Alice能遍历所有节点. 同理对type2的边也执行以上操作, 最终将删除的边数目加和即可得到最终结果. + +本题是一道难题, type3的路径是Alice和Bob都可以使用的, 如果想让每个人都能遍历整个图并且使图中的边最少, 无疑首先要尽量使用type3的路径. 通过type3的路径能连通的点Alice和Bob不需要再使用type1和type2连通, 在type3的连通图中的type1和type2的边都可以删去. 不能删除的边是连通type3边构成的连通图之间连通的type1和type2的边. 使用并查集将type3的边构成的连通图中同一个连通图的节点用一个代表节点表示出来. 遍历type1的边, 将那些能将type3构成的不同连通图连通起来的边保留, 其余的均删除并计数, 若最后所有节点均在同一个集合中, 则Alice能遍历所有节点. 同理对type2的边也执行以上操作, 最终将删除的边数目加和即可得到最终结果. ### 代码 -```go + +```go func maxNumEdgesToRemove(n int, edges [][]int) int { // 初始化两个并查集,一个用于Alice,一个用于Bob ufAlice := newUnionFind(n) ufBob := newUnionFind(n) - + removedEdges := 0 - + // 首先处理类型3的边(Alice和Bob都可以使用的边) for _, edge := range edges { if edge[0] == 3 { @@ -8321,7 +8345,7 @@ func maxNumEdgesToRemove(n int, edges [][]int) int { } } } - + // 处理类型1的边(只有Alice可以使用的边) for _, edge := range edges { if edge[0] == 1 { @@ -8330,7 +8354,7 @@ func maxNumEdgesToRemove(n int, edges [][]int) int { } } } - + // 处理类型2的边(只有Bob可以使用的边) for _, edge := range edges { if edge[0] == 2 { @@ -8339,12 +8363,12 @@ func maxNumEdgesToRemove(n int, edges [][]int) int { } } } - + // 检查是否所有节点都连通 if ufAlice.count != 1 || ufBob.count != 1 { return -1 } - + return removedEdges } @@ -8393,20 +8417,24 @@ func (uf *UnionFind) union(x, y int) bool { ``` ### 总结 -要熟悉并查集的实现, 能快速根据需要实现一个简单的并查集出来. +要熟悉并查集的实现, 能快速根据需要实现一个简单的并查集出来. ## day126 2024-07-01 -### 1550. Three Consecutive Odds -Given an integer array arr, return true if there are three consecutive odd numbers in the array. Otherwise, return false. + +### 1550. Three Consecutive Odds + +Given an integer array arr, return true if there are three consecutive odd numbers in the array. Otherwise, return false. ![0701xCkXpUEp7aQv](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0701xCkXpUEp7aQv.png) ### 题解 -本题是一道简单题, 设定一个标记位标记当前连续的奇数个数, 如果达到三个则返回true. 如果遍历到末尾还没有达到三个则返回false. + +本题是一道简单题, 设定一个标记位标记当前连续的奇数个数, 如果达到三个则返回true. 如果遍历到末尾还没有达到三个则返回false. ### 代码 -```go + +```go func threeConsecutiveOdds(arr []int) bool { num := 0 for _,value := range arr{ @@ -8424,17 +8452,20 @@ func threeConsecutiveOdds(arr []int) bool { ``` ## day127 2024-07-02 -### 350. Intersection of Two Arrays II -Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must appear as many times as it shows in both arrays and you may return the result in any order. +### 350. Intersection of Two Arrays II + +Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must appear as many times as it shows in both arrays and you may return the result in any order. ![070274XGKa0XOzSu](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/070274XGKa0XOzSu.png) ### 题解 -本题是一道简单题, 要获取在nums1和nums2中都出现的数组元素和其个数. 则可以用map记录nums1和nums2中出现的数字和出现次数. 遍历其中一个map, 在另一个map中查找当前map中的key出现的次数, 取比较小的次数n将对应key重复n次放入结果数组中. + +本题是一道简单题, 要获取在nums1和nums2中都出现的数组元素和其个数. 则可以用map记录nums1和nums2中出现的数字和出现次数. 遍历其中一个map, 在另一个map中查找当前map中的key出现的次数, 取比较小的次数n将对应key重复n次放入结果数组中. ### 代码 -```go + +```go func intersect(nums1 []int, nums2 []int) []int { map1 := map[int]int{} map2 := map[int]int{} @@ -8476,22 +8507,25 @@ func intersect(nums1 []int, nums2 []int) []int { } ``` - ## day128 2024-07-03 -### 1509. Minimum Difference Between Largest and Smallest Value in Three Moves + +### 1509. Minimum Difference Between Largest and Smallest Value in Three Moves + You are given an integer array nums. In one move, you can choose one element of nums and change it to any value. -Return the minimum difference between the largest and smallest value of nums after performing at most three moves. +Return the minimum difference between the largest and smallest value of nums after performing at most three moves. ![0703KBCausAlS1vQ](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0703KBCausAlS1vQ.png) ### 题解 -本题可以改变三个数字的值和可以删掉三个数字效果上差不多, 问题变成了应该删掉哪三个数字, 显然为了让删掉后剩余数组中数字的极差最小, 应该从数组的最小值或者最大值中删除, 问题在于应该删掉的三个数字中哪些从最大值方向删除, 哪些从最小值方向删除. 考虑最终的目标为让极差最小, 从最小值方向删除和从最大值方向删除三个数字一共只有四种组合, 小3大0, 小1大2, 小2大1, 小0大3. 直接计算四种组合对应的减少极差的值, 比较大小选择能减少极差最大的组合并删除对应元素, 最后计算删除后数组的极差. + +本题可以改变三个数字的值和可以删掉三个数字效果上差不多, 问题变成了应该删掉哪三个数字, 显然为了让删掉后剩余数组中数字的极差最小, 应该从数组的最小值或者最大值中删除, 问题在于应该删掉的三个数字中哪些从最大值方向删除, 哪些从最小值方向删除. 考虑最终的目标为让极差最小, 从最小值方向删除和从最大值方向删除三个数字一共只有四种组合, 小3大0, 小1大2, 小2大1, 小0大3. 直接计算四种组合对应的减少极差的值, 比较大小选择能减少极差最大的组合并删除对应元素, 最后计算删除后数组的极差. ### 代码 -```go + +```go func minDifference(nums []int) int { length := len(nums) if length <= 4{ @@ -8514,10 +8548,13 @@ func minDifference(nums []int) int { ``` ### 总结 -这种方法属于暴力枚举的方式, 但在题目限制只能改变3个数的条件下, 需要枚举的情况极少, 因此可以奏效. + +这种方法属于暴力枚举的方式, 但在题目限制只能改变3个数的条件下, 需要枚举的情况极少, 因此可以奏效. ## day129 2024-07-04 -### 2181. Merge Nodes in Between Zeros + +### 2181. Merge Nodes in Between Zeros + You are given the head of a linked list, which contains a series of integers separated by 0's. The beginning and end of the linked list will have Node.val == 0. For every two consecutive 0's, merge all the nodes lying in between them into a single node whose value is the sum of all the merged nodes. The modified list should not contain any 0's. @@ -8527,10 +8564,12 @@ Return the head of the modified linked list. ![0704FKp6L7iDJHNv](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0704FKp6L7iDJHNv.png) ### 题解 + 本题要将0分隔的链表中两个0之间的数字求和合并为一个节点, 并删掉0节点, 则可以遍历链表, 遇到0记录起始0的节点指针, 对后面的非0数字求和, 再次遇到0则用和替换起始0节点的值, 并将这个节点的Next指针指向新遇到的0, 将新遇到的0设为起始0, 如此反复. 注意当出现连续0时, 直接忽视, 按照前述方法正常求和即可. ### 代码 -```go + +```go /** * Definition for singly-linked list. * type ListNode struct { @@ -8566,7 +8605,9 @@ func mergeNodes(head *ListNode) *ListNode { ``` ## day130 2024-07-05 -### 2058. Find the Minimum and Maximum Number of Nodes Between Critical Points + +### 2058. Find the Minimum and Maximum Number of Nodes Between Critical Points + A critical point in a linked list is defined as either a local maxima or a local minima. A node is a local maxima if the current node has a value strictly greater than the previous node and the next node. @@ -8580,10 +8621,12 @@ Given a linked list head, return an array of length 2 containing [minDistance, m ![0705GyHjjK7eutFL](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0705GyHjjK7eutFL.png) ### 题解 -本题使用双指针法, 一个指针指向前一个节点, 一个指针指向当前节点, 这样就可以判断当前节点是否是关键点(critical point). 将节点在链表中的位置也使用下标表示出来, 这样方便判断两个关键点之间的距离, 使用一个变量保存初始关键点的下标, 一个保存前一个关键点的下标, 每次遇到新的关键点计算与前一个关键点的距离并更新距离最小值. 遍历整个链表后, 用最后一个关键点下标和第一个关键点下标作差即得距离最大值. + +本题使用双指针法, 一个指针指向前一个节点, 一个指针指向当前节点, 这样就可以判断当前节点是否是关键点(critical point). 将节点在链表中的位置也使用下标表示出来, 这样方便判断两个关键点之间的距离, 使用一个变量保存初始关键点的下标, 一个保存前一个关键点的下标, 每次遇到新的关键点计算与前一个关键点的距离并更新距离最小值. 遍历整个链表后, 用最后一个关键点下标和第一个关键点下标作差即得距离最大值. ### 代码 -```go + +```go /** * Definition for singly-linked list. * type ListNode struct { @@ -8623,7 +8666,8 @@ func nodesBetweenCriticalPoints(head *ListNode) []int { ## day131 2024-07-06 -### 2582. Pass the Pillow +### 2582. Pass the Pillow + There are n people standing in a line labeled from 1 to n. The first person in the line is holding a pillow initially. Every second, the person holding the pillow passes it to the next person standing in the line. Once the pillow reaches the end of the line, the direction changes, and people continue passing the pillow in the opposite direction. For example, once the pillow reaches the nth person they pass it to the n - 1th person, then to the n - 2th person and so on. @@ -8633,9 +8677,11 @@ Given the two positive integers n and time, return the index of the person holdi ### 题解 -本题是简单题, 使用time对n-1(4个人只需传承3次即可到最后一人)求模和求商, 商的奇偶可以决定传递的起点是队首还是队尾, 求模得到的余数可以决定从本次传递起点开始传递了几次. 再使用对应的传递起点计算当前传递到哪个人. +本题是简单题, 使用time对n-1(4个人只需传承3次即可到最后一人)求模和求商, 商的奇偶可以决定传递的起点是队首还是队尾, 求模得到的余数可以决定从本次传递起点开始传递了几次. 再使用对应的传递起点计算当前传递到哪个人. + ### 代码 -```go + +```go func passThePillow(n int, time int) int { label := time % (n-1) dir := time / (n-1) @@ -8648,17 +8694,20 @@ func passThePillow(n int, time int) int { ``` ### 2. Add Two Numbers + You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list. -You may assume the two numbers do not contain any leading zero, except the number 0 itself. +You may assume the two numbers do not contain any leading zero, except the number 0 itself. ![0706OGiRpS80mHq2](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0706OGiRpS80mHq2.png) ### 题解 + 本题在两个链表中较长的链表上直接加上另一个链表的中对应节点的值并用标志位标记进位. 当一个链表遍历结束时将另一个链表的剩余节点直接连接到链表后面, 注意处理进位. ### 代码 -```go + +```go /** * Definition for singly-linked list. * type ListNode struct { @@ -8728,8 +8777,11 @@ func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode { return head } ``` + ## day132 2024-07-07 -### 1518. Water Bottles + +### 1518. Water Bottles + There are numBottles water bottles that are initially full of water. You can exchange numExchange empty water bottles from the market with one full water bottle. The operation of drinking a full water bottle turns it into an empty bottle. @@ -8737,10 +8789,12 @@ The operation of drinking a full water bottle turns it into an empty bottle. Given the two integers numBottles and numExchange, return the maximum number of water bottles you can drink. ### 题解 -本题是一道简单题, 和生活关联比较紧密, 只需通过循环不断判断当前手里的饮料喝完后的空瓶子数量能否换新的饮料, 将当前空瓶子个数(由余数和新换的饮料个数加和得到)除以numExchange, 商即为能换的新饮料数量, 保存余数用于下一次兑换饮料. 如此反复直至商为0. 每次将新换的饮料数量都加到最终结果中. + +本题是一道简单题, 和生活关联比较紧密, 只需通过循环不断判断当前手里的饮料喝完后的空瓶子数量能否换新的饮料, 将当前空瓶子个数(由余数和新换的饮料个数加和得到)除以numExchange, 商即为能换的新饮料数量, 保存余数用于下一次兑换饮料. 如此反复直至商为0. 每次将新换的饮料数量都加到最终结果中. ### 代码 -```go + +```go func numWaterBottles(numBottles int, numExchange int) int { result := numBottles current := numBottles @@ -8756,9 +8810,10 @@ func numWaterBottles(numBottles int, numExchange int) int { } ``` - ## day133 2024-07-08 -### 1823. Find the Winner of the Circular Game + +### 1823. Find the Winner of the Circular Game + There are n friends that are playing a game. The friends are sitting in a circle and are numbered from 1 to n in clockwise order. More formally, moving clockwise from the ith friend brings you to the (i+1)th friend for 1 <= i < n, and moving clockwise from the nth friend brings you to the 1st friend. The rules of the game are as follows: @@ -8773,10 +8828,12 @@ Given the number of friends, n, and an integer k, return the winner of the game. ![0708SPpMsBmYFF3W](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0708SPpMsBmYFF3W.png) ### 题解 -本题直接模拟这一游戏过程即可, 直观思路是使用一个循环队列模拟圆桌上的所有人, 模拟游戏过程, 每次数到的人就将其从循环队列中删除, 循环队列可以使用一个数组来构建, 每次超出队列长度从头开始遍历即可. 数组初始化为1-n的节点值. 每次根据数到的下标直接删除数组中的这个元素. 直到数组中只有一个元素为止. + +本题直接模拟这一游戏过程即可, 直观思路是使用一个循环队列模拟圆桌上的所有人, 模拟游戏过程, 每次数到的人就将其从循环队列中删除, 循环队列可以使用一个数组来构建, 每次超出队列长度从头开始遍历即可. 数组初始化为1-n的节点值. 每次根据数到的下标直接删除数组中的这个元素. 直到数组中只有一个元素为止. ### 代码 -```go + +```go func findTheWinner(n int, k int) int { length := n current := -1 @@ -8792,17 +8849,17 @@ func findTheWinner(n int, k int) int { } return list[0] } -``` +``` ### 总结 -本题也可以采用数学方法解决, 理解数学方法的关键还是在于问题的转化, 考虑之前循环队列的解法, 当我们已知队列中有两个人并且计数为k时的获胜者编号, 则当队列中有三个人时, 计数为k, 通过计数将某个人删除后问题又变为两个人并且计数为k时的获胜者编号, 只需要将原来的编号加上三个人时删掉的人的编号即可得到最终的编号. 这种解决方法将直观的从整体的队列根据计数挨个从队列中删除对应计数编号的人直到只剩一个人从而得到获胜者编号反向转换为从只有一个人时找到最终的获胜者编号到n-1时的获胜者编号最终到n个人时的获胜者编号. 是经典的递归解决问题的思想. 而在想明白这个思路后, 将递归部分转换为迭代, 即可得到最终的简单解法. +本题也可以采用数学方法解决, 理解数学方法的关键还是在于问题的转化, 考虑之前循环队列的解法, 当我们已知队列中有两个人并且计数为k时的获胜者编号, 则当队列中有三个人时, 计数为k, 通过计数将某个人删除后问题又变为两个人并且计数为k时的获胜者编号, 只需要将原来的编号加上三个人时删掉的人的编号即可得到最终的编号. 这种解决方法将直观的从整体的队列根据计数挨个从队列中删除对应计数编号的人直到只剩一个人从而得到获胜者编号反向转换为从只有一个人时找到最终的获胜者编号到n-1时的获胜者编号最终到n个人时的获胜者编号. 是经典的递归解决问题的思想. 而在想明白这个思路后, 将递归部分转换为迭代, 即可得到最终的简单解法. 具体数学证明可见: -[找出游戏的获胜者](https://leetcode.cn/problems/find-the-winner-of-the-circular-game/solutions/1463524/zhao-chu-you-xi-de-huo-sheng-zhe-by-leet-w2jd) +[找出游戏的获胜者](https://leetcode.cn/problems/find-the-winner-of-the-circular-game/solutions/1463524/zhao-chu-you-xi-de-huo-sheng-zhe-by-leet-w2jd) -```go +```go func findTheWinner(n, k int) int { winner := 1 for i := 2; i <= n; i++ { @@ -8814,22 +8871,26 @@ func findTheWinner(n, k int) int { ``` ## day134 2024-07-09 -### 1701. Average Waiting Time + +### 1701. Average Waiting Time + There is a restaurant with a single chef. You are given an array customers, where customers[i] = [arrivali, timei]: arrivali is the arrival time of the ith customer. The arrival times are sorted in non-decreasing order. timei is the time needed to prepare the order of the ith customer. When a customer arrives, he gives the chef his order, and the chef starts preparing it once he is idle. The customer waits till the chef finishes preparing his order. The chef does not prepare food for more than one customer at a time. The chef prepares food for customers in the order they were given in the input. -Return the average waiting time of all customers. Solutions within 10-5 from the actual answer are considered accepted. +Return the average waiting time of all customers. Solutions within 10-5 from the actual answer are considered accepted. ![0709eXlrAsmWtCid](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0709eXlrAsmWtCid.png) ### 题解 -本题思路比较清晰, 直接模拟顾客来和等待上菜的过程, 用一个变量保存当前顾客能吃上菜的时间, 继续遍历,判断下一个顾客的到达时间是否在前一个顾客能吃上菜时间之前, 如果在之前, 则该顾客的等待时间为上一个顾客能吃上菜的时间减去当前顾客的到达时间与当前顾客菜的等待时间之和. 否则该顾客只需等待自己的菜品制作时间即可. 此处是典型的动态规划, 只需要保存前一个顾客能吃上菜的时间(之前最后一个顾客能吃上菜的时间)就已经包含了之前所有顾客等菜和菜品制作的信息. 根据前一个顾客的时间判断当前顾客的状态并更新当前最后一个顾客吃上菜的时间. + +本题思路比较清晰, 直接模拟顾客来和等待上菜的过程, 用一个变量保存当前顾客能吃上菜的时间, 继续遍历,判断下一个顾客的到达时间是否在前一个顾客能吃上菜时间之前, 如果在之前, 则该顾客的等待时间为上一个顾客能吃上菜的时间减去当前顾客的到达时间与当前顾客菜的等待时间之和. 否则该顾客只需等待自己的菜品制作时间即可. 此处是典型的动态规划, 只需要保存前一个顾客能吃上菜的时间(之前最后一个顾客能吃上菜的时间)就已经包含了之前所有顾客等菜和菜品制作的信息. 根据前一个顾客的时间判断当前顾客的状态并更新当前最后一个顾客吃上菜的时间. ### 代码 -```go + +```go func averageWaitingTime(customers [][]int) float64 { current := 0 waiting := 0 @@ -8845,11 +8906,15 @@ func averageWaitingTime(customers [][]int) float64 { return float64(waiting)/float64(len(customers)) } ``` + ### 总结 + 本题其实就是`先来先服务`场景的简单应用. ## day135 2024-07-10 -### 1598. Crawler Log Folder + +### 1598. Crawler Log Folder + The Leetcode file system keeps a log each time some user performs a change folder operation. The operations are described below: @@ -8866,10 +8931,12 @@ Return the minimum number of operations needed to go back to the main folder aft ![0710BQdXlyk0sUKW](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0710BQdXlyk0sUKW.png) ### 题解 -本题为一道简单题, 只需用一个变量记录当前目录的深度, 遍历logs数组, 根据规定的操作修改深度数值即可, `../`即为-1,`x/`即为+1, `./`不变, 注意当深度已经为0时回到父目录的操作使得深度仍然为0. + +本题为一道简单题, 只需用一个变量记录当前目录的深度, 遍历logs数组, 根据规定的操作修改深度数值即可, `../`即为-1,`x/`即为+1, `./`不变, 注意当深度已经为0时回到父目录的操作使得深度仍然为0. ### 代码 -```go + +```go func minOperations(logs []string) int { depth := 0 for _, op := range logs{ @@ -8887,16 +8954,20 @@ func minOperations(logs []string) int { return depth } ``` -### 3. Longest Substring Without Repeating Characters + +### 3. Longest Substring Without Repeating Characters + Given a string s, find the length of the longest substring without repeating characters. ![0707bo6XVX54rJMj](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0707bo6XVX54rJMj.png) ### 题解 + 本题用经典的滑动窗口求解, 当遇到重复字符时, 缩小窗口直到字符不重复. 继续扩大窗口. 更新窗口最大长度, 直到遍历完整个字符串为止. 要注意的是本题是不能有重复字符, 包括空格和特殊字符等, 因此用map是最方便的. ### 代码 -```go + +```go func lengthOfLongestSubstring(s string) int { left := 0 result := 0 @@ -8919,24 +8990,28 @@ func lengthOfLongestSubstring(s string) int { ``` ## day136 2024-07-11 -### 1190. Reverse Substrings Between Each Pair of Parentheses + +### 1190. Reverse Substrings Between Each Pair of Parentheses + You are given a string s that consists of lower case English letters and brackets. Reverse the strings in each pair of matching parentheses, starting from the innermost one. -Your result should not contain any brackets. +Your result should not contain any brackets. -![0711Txx0hnCNHwjS](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0711Txx0hnCNHwjS.png) +![0711Txx0hnCNHwjS](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0711Txx0hnCNHwjS.png) ### 题解 -本题要将每个括号内的字符串都翻转, 则字符串被翻转偶数次后仍为原来的字符串本身, 因此可以用一个栈表示当前已经读入的待匹配的左括号和字符. 当匹配到右括号时, 就翻转匹配的左右括号之间的字符构成新的字符串, 并将这个字符串放入栈中, 继续向下匹配, 当再次匹配到右括号时, 重复此操作直到到字符串末尾为止, 即使用栈模拟整个字符串对应的括号内的翻转过程. + +本题要将每个括号内的字符串都翻转, 则字符串被翻转偶数次后仍为原来的字符串本身, 因此可以用一个栈表示当前已经读入的待匹配的左括号和字符. 当匹配到右括号时, 就翻转匹配的左右括号之间的字符构成新的字符串, 并将这个字符串放入栈中, 继续向下匹配, 当再次匹配到右括号时, 重复此操作直到到字符串末尾为止, 即使用栈模拟整个字符串对应的括号内的翻转过程. ### 代码 -```go + +```go func reverseParentheses(s string) string { var stack []string length := 0 - + for _, char := range s { if char == '(' { stack = append(stack, "(") @@ -8950,37 +9025,39 @@ func reverseParentheses(s string) string { length-- rev.WriteString(reverse(str)) } - stack = stack[:length-1] + stack = stack[:length-1] stack = append(stack, rev.String()) } else { stack = append(stack, string(char)) length++ } } - + var result strings.Builder for _, str := range stack { result.WriteString(str) } - + return result.String() } func reverse(s string) string { runes := []rune(s) length := len(runes) - + for i := 0; i < length/2; i++ { runes[i], runes[length-1-i] = runes[length-1-i], runes[i] } - + return string(runes) } ``` ## day137 2024-07-12 -### 1717. Maximum Score From Removing Substrings + +### 1717. Maximum Score From Removing Substrings + You are given a string s and two integers x and y. You can perform two types of operations any number of times. Remove substring "ab" and gain x points. @@ -8992,10 +9069,12 @@ Return the maximum points you can gain after applying the above operations on s. ![0712VRksdxp5LeQU](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0712VRksdxp5LeQU.png) ### 题解 -本题可以注意到由于x,y一个值比另一个值更大, 则ab,ba两个组合中只要优先选择组合成价值更大的组合就能得到最大值. 即通过贪心即可得到连续的ab串的最大值, 而优先组合可以通过栈来实现, 如果ab组合值更大, 遍历ab串并将读取的字符入栈, 当栈顶为a并且当前字符为b时将a出栈, 为a则继续入栈, 直到遍历到ab串末尾, 再将栈中剩余的a和b组合为ba. 这里可以不将只能和a组合成ba的b入栈, 而是用变量保存其个数, 最后直接通过个数计算结果即可. ba值更大同理. + +本题可以注意到由于x,y一个值比另一个值更大, 则ab,ba两个组合中只要优先选择组合成价值更大的组合就能得到最大值. 即通过贪心即可得到连续的ab串的最大值, 而优先组合可以通过栈来实现, 如果ab组合值更大, 遍历ab串并将读取的字符入栈, 当栈顶为a并且当前字符为b时将a出栈, 为a则继续入栈, 直到遍历到ab串末尾, 再将栈中剩余的a和b组合为ba. 这里可以不将只能和a组合成ba的b入栈, 而是用变量保存其个数, 最后直接通过个数计算结果即可. ba值更大同理. ### 代码 -```go + +```go func maximumGain(s string, x int, y int) int { cont := false start := 0 @@ -9053,7 +9132,9 @@ func caculate(s string,x int,y int)int{ ``` ## day138 2024-07-13 -### 2751. Robot Collisions + +### 2751. Robot Collisions + There are n 1-indexed robots, each having a position on a line, health, and movement direction. You are given 0-indexed integer arrays positions, healths, and a string directions (directions[i] is either 'L' for left or 'R' for right). All integers in positions are unique. @@ -9071,10 +9152,12 @@ Note: The positions may be unsorted. ![0713WjsqGNYwxVip](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0713WjsqGNYwxVip.png) ### 题解 -本题是一道难题, 但核心算法只要熟悉栈操作即可, 本题机器人的位置并不是排好序的, 将机器人的编号和其位置, 方向, 血量绑定在同一个结构体后, 根据位置对结构体排序. 建立一个新栈(使用数组实现), 遍历机器人, 若机器人是向右运行的直接入栈. 向左则判断栈是否为空, 如果不为空则根据栈顶机器人的不同状态做出不同的操作, 若栈顶机器人运行方向向右且血量高于当前机器人, 则将栈顶机器人血量-1, 否则将当前机器人血量-1并弹出栈顶, 继续根据新栈顶做出不同操作. 直到栈顶机器人运行方向向左或者栈为空为止. 此时将当前机器人入栈. + +本题是一道难题, 但核心算法只要熟悉栈操作即可, 本题机器人的位置并不是排好序的, 将机器人的编号和其位置, 方向, 血量绑定在同一个结构体后, 根据位置对结构体排序. 建立一个新栈(使用数组实现), 遍历机器人, 若机器人是向右运行的直接入栈. 向左则判断栈是否为空, 如果不为空则根据栈顶机器人的不同状态做出不同的操作, 若栈顶机器人运行方向向右且血量高于当前机器人, 则将栈顶机器人血量-1, 否则将当前机器人血量-1并弹出栈顶, 继续根据新栈顶做出不同操作. 直到栈顶机器人运行方向向左或者栈为空为止. 此时将当前机器人入栈. ### 代码 -```go + +```go func survivedRobotsHealths(positions []int, healths []int, directions string) []int { type Robot struct{ position int @@ -9116,8 +9199,8 @@ func survivedRobotsHealths(positions []int, healths []int, directions string) [] } }else{ stack = append(stack, robot) - } - } + } + } } sort.Slice(stack, func(i,j int)bool{ return stack[i].index < stack[j].index @@ -9132,7 +9215,9 @@ func survivedRobotsHealths(positions []int, healths []int, directions string) [] ``` ## day139 2024-07-14 -### 726. Number of Atoms + +### 726. Number of Atoms + Given a string formula representing a chemical formula, return the count of each atom. The atomic element always starts with an uppercase character, then zero or more lowercase letters, representing the name. @@ -9148,15 +9233,17 @@ A formula placed in parentheses, and a count (optionally added) is also a formul For example, "(H2O2)" and "(H2O2)3" are formulas. Return the count of all elements as a string in the following form: the first name (in sorted order), followed by its count (if that count is more than 1), followed by the second name (in sorted order), followed by its count (if that count is more than 1), and so on. -The test cases are generated so that all the values in the output fit in a 32-bit integer. +The test cases are generated so that all the values in the output fit in a 32-bit integer. -![0714e2kIjx5sE9Oo](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0714e2kIjx5sE9Oo.png) +![0714e2kIjx5sE9Oo](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0714e2kIjx5sE9Oo.png) ### 题解 -本题是一道难题, 关键在于如何处理括号, 从括号匹配问题开始, 对于这种有括号的问题, 栈一直是解决问题的核心. 括号代表着优先级的变化, 而栈的先入后出特性其实包含了隐含的优先级, 即后入栈的先出(优先级更高), 这与括号代表的优先级恰好一致(内层括号的优先级更高, 要先算). 本题通过使用栈将全部括号展开, 遇到左括号将其入栈, 继续遍历读入原子和对应的个数, 直到遇到右括号从栈顶向下遍历并将原子个数乘以右括号后面的数字, 直到遇到左括号为止, 删掉左括号. 这不是一个标准的栈操作, 但思想上仍是栈的思想. 将全部括号展开后, 从头遍历无括号的原子, 将相同原子个数合并最后输出. + +本题是一道难题, 关键在于如何处理括号, 从括号匹配问题开始, 对于这种有括号的问题, 栈一直是解决问题的核心. 括号代表着优先级的变化, 而栈的先入后出特性其实包含了隐含的优先级, 即后入栈的先出(优先级更高), 这与括号代表的优先级恰好一致(内层括号的优先级更高, 要先算). 本题通过使用栈将全部括号展开, 遇到左括号将其入栈, 继续遍历读入原子和对应的个数, 直到遇到右括号从栈顶向下遍历并将原子个数乘以右括号后面的数字, 直到遇到左括号为止, 删掉左括号. 这不是一个标准的栈操作, 但思想上仍是栈的思想. 将全部括号展开后, 从头遍历无括号的原子, 将相同原子个数合并最后输出. ### 代码 -```go + +```go func countOfAtoms(formula string) string { type atom struct { name string @@ -9186,7 +9273,7 @@ func countOfAtoms(formula string) string { } stack = append(stack[0:j],stack[j+1:]...) // Remove the '(' from the stack - + } else { start := i i++ @@ -9230,7 +9317,8 @@ func countOfAtoms(formula string) string { ``` ## day140 2024-07-15 -### 2196. Create Binary Tree From Descriptions + +### 2196. Create Binary Tree From Descriptions You are given a 2D integer array descriptions where descriptions[i] = [parenti, childi, isLefti] indicates that parenti is the parent of childi in a binary tree of unique values. Furthermore, @@ -9238,16 +9326,17 @@ If isLefti == 1, then childi is the left child of parenti. If isLefti == 0, then childi is the right child of parenti. Construct the binary tree described by descriptions and return its root. -The test cases will be generated such that the binary tree is valid. +The test cases will be generated such that the binary tree is valid. ![0715MS1tzkYMjKZ4](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0715MS1tzkYMjKZ4.png) ### 题解 -本题先构建二叉树, 再找到二叉树的根. 构建二叉树并不困难, 可以将每个节点的指针都保存在map中, key为节点的值, value为节点的指针. 再根据描述构建每个节点. 找二叉树的根可以在构建二叉树的同时使用类似并查集的思想, 将当前描述的孩子节点的值作为下标, 父节点的值作为下标对应的值, 构造一个father数组. 若父节点作为下标之前没有指向任何值, 则将父节点的下标处的值指向父节点自身的值. 最终从任意一个节点值开始, 在father数组中查看其作为下标的值, 直到该下标处的值指向自身即为根节点的节点值. 返回根节点. +本题先构建二叉树, 再找到二叉树的根. 构建二叉树并不困难, 可以将每个节点的指针都保存在map中, key为节点的值, value为节点的指针. 再根据描述构建每个节点. 找二叉树的根可以在构建二叉树的同时使用类似并查集的思想, 将当前描述的孩子节点的值作为下标, 父节点的值作为下标对应的值, 构造一个father数组. 若父节点作为下标之前没有指向任何值, 则将父节点的下标处的值指向父节点自身的值. 最终从任意一个节点值开始, 在father数组中查看其作为下标的值, 直到该下标处的值指向自身即为根节点的节点值. 返回根节点. ### 代码 -```go + +```go /** * Definition for a binary tree node. * type TreeNode struct { @@ -9277,7 +9366,7 @@ func createBinaryTree(descriptions [][]int) *TreeNode { }else{ trees[des[0]] = &TreeNode{des[0], nil, child} } - + }else{ if des[2] == 1{ trees[des[0]].Left = child @@ -9294,8 +9383,10 @@ func createBinaryTree(descriptions [][]int) *TreeNode { } ``` + ## day141 2024-07-16 -### 2096. Step-By-Step Directions From a Binary Tree Node to Another + +### 2096. Step-By-Step Directions From a Binary Tree Node to Another You are given the root of a binary tree with n nodes. Each node is uniquely assigned a value from 1 to n. You are also given an integer startValue representing the value of the start node s, and a different integer destValue representing the value of the destination node t. @@ -9306,7 +9397,7 @@ Find the shortest path starting from node s and ending at node t. Generate step- 'U' means to go from a node to its parent node. Return the step-by-step directions of the shortest path from node s to node t. -![0716cNylqTGph1XZ](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0716cNylqTGph1XZ.png) +![0716cNylqTGph1XZ](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0716cNylqTGph1XZ.png) ### 题解 @@ -9314,9 +9405,9 @@ Return the step-by-step directions of the shortest path from node s to node t. 得到起点和终点的路径后, 同时从头遍历二者的路径, 直到找到路径中的第一个不同点, 此时获取到达起点剩余路径的长度, 构造相同长度的重复'U'字符串并与终点的剩余路径连接即得到从起点到终点的路径. - ### 代码 -```go + +```go /** * Definition for a binary tree node. * type TreeNode struct { @@ -9327,7 +9418,7 @@ Return the step-by-step directions of the shortest path from node s to node t. */ func getDirections(root *TreeNode, startValue int, destValue int) string { arrToS := dfs(root, []byte{}, startValue) - arrToD := dfs(root, []byte{}, destValue) + arrToD := dfs(root, []byte{}, destValue) i := 0 for len(arrToS) > i && len(arrToD) > i && arrToS[i] == arrToD[i] { i++ @@ -9359,7 +9450,9 @@ func dfs(root *TreeNode, arr []byte, target int) []byte { ``` ## day142 2024-07-17 -### 1110. Delete Nodes And Return Forest + +### 1110. Delete Nodes And Return Forest + Given the root of a binary tree, each node in the tree has a distinct value. After deleting all nodes with a value in to_delete, we are left with a forest (a disjoint union of trees). @@ -9369,10 +9462,12 @@ Return the roots of the trees in the remaining forest. You may return the result ![0717NX9V70B0DCGG](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0717NX9V70B0DCGG.png) ### 题解 -本题关键在于了解什么样的节点会成为森林中树的根节点, 根据描述可以得出结论: 如果父节点是一个要被删除的节点且子节点不被删除, 那么这个子节点将成为森林中一棵树的根节点. 使用dfs遍历树并根据这一结论将根节点加入最终结果中, 注意处理当父节点不被删除但其子节点被删除时将父节点的这个子节点设置为空. 根据我们的结论, 每次只要对父节点和其左右子节点做出判断即可, 这样就转换为了仅包含父节点和左右子节点的小问题, 就可以使用递归来遍历整棵树得出最终结果. + +本题关键在于了解什么样的节点会成为森林中树的根节点, 根据描述可以得出结论: 如果父节点是一个要被删除的节点且子节点不被删除, 那么这个子节点将成为森林中一棵树的根节点. 使用dfs遍历树并根据这一结论将根节点加入最终结果中, 注意处理当父节点不被删除但其子节点被删除时将父节点的这个子节点设置为空. 根据我们的结论, 每次只要对父节点和其左右子节点做出判断即可, 这样就转换为了仅包含父节点和左右子节点的小问题, 就可以使用递归来遍历整棵树得出最终结果. ### 代码 -```go + +```go /** * Definition for a binary tree node. * type TreeNode struct { @@ -9418,7 +9513,7 @@ func delNodes(root *TreeNode, to_delete []int) []*TreeNode { root.Right = nil } } - } + } } dfs(root) return result @@ -9426,7 +9521,9 @@ func delNodes(root *TreeNode, to_delete []int) []*TreeNode { ``` ## day143 2024-07-18 -### 1530. Number of Good Leaf Nodes Pairs + +### 1530. Number of Good Leaf Nodes Pairs + You are given the root of a binary tree and an integer distance. A pair of two different leaf nodes of a binary tree is said to be good if the length of the shortest path between them is less than or equal to distance. Return the number of good leaf node pairs in the tree. @@ -9434,10 +9531,12 @@ Return the number of good leaf node pairs in the tree. ![0718DCKotaG6SnQm](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0718DCKotaG6SnQm.png) ### 题解 + 考虑对于一个节点来说, 只要知道了到其左子树的所有叶子节点的距离, 和到其右子树的所有叶子节点的距离, 就可以将左子树的所有叶子节点距离和右子树所有叶子节点距离依次加和并与distance比较. 则通过后序遍历, 先遍历左右子树最后将左右子树的叶子节点的距离(数组)返回, 按照上面的思路与distance做比较, 直到遍历完整棵树为止. 这里可以将到叶子节点的最短距离记录下来用于优化, 当左右子树到叶子节点的最小值和已经大于distance时, 其余节点没必要继续进行比较. 直接返回即可. ### 代码 -```go + +```go /** * Definition for a binary tree node. * type TreeNode struct { @@ -9489,22 +9588,28 @@ func countPairs(root *TreeNode, distance int) int { return result } ``` + ### 总结 + 该种解法的时间复杂度与叶子节点的数量有关(约为节点总数乘以叶子节点数量的平方), 考虑节点总数最多为2的10次方, 则叶子节点最多为2的9次方即512个, 即便是平方的复杂度也在可以接受的范围内. ## day144 2024-07-19 -### 1380. Lucky Numbers in a Matrix + +### 1380. Lucky Numbers in a Matrix + Given an m x n matrix of distinct numbers, return all lucky numbers in the matrix in any order. -A lucky number is an element of the matrix such that it is the minimum element in its row and maximum in its column. +A lucky number is an element of the matrix such that it is the minimum element in its row and maximum in its column. ![0719ZtW5ztQktBOJ](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0719ZtW5ztQktBOJ.png) ### 题解 + 本题遍历两遍, 将每行最小的值的下标和每列最大的值的下标分别放入两个数组, 数组的下标代表对应的行数和列数, 遍历最小值数组, 根据对应的下标在最大值数组中找到对应的值看是否与最小值数组中的下标相同. 相同则表名找到了一个lucky number. 通过提前构建最大值数组, 可以仅遍历一遍数组即同时找到每行的最小值和每列的最大值, 只需在遍历行的时候将每个数与当前该列的最大值比较并更新即可通过一遍遍历完成任务. ### 代码 -```go + +```go func luckyNumbers (matrix [][]int) []int { rowlen := len(matrix) colen := len(matrix[0]) @@ -9535,7 +9640,9 @@ func luckyNumbers (matrix [][]int) []int { ``` ## day145 2024-07-20 -### 1605. Find Valid Matrix Given Row and Column Sums + +### 1605. Find Valid Matrix Given Row and Column Sums + You are given two arrays rowSum and colSum of non-negative integers where rowSum[i] is the sum of the elements in the ith row and colSum[j] is the sum of the elements of the jth column of a 2D matrix. In other words, you do not know the elements of the matrix, but you do know the sums of each row and column. Find any matrix of non-negative integers of size rowSum.length x colSum.length that satisfies the rowSum and colSum requirements. @@ -9545,10 +9652,12 @@ Return a 2D array representing any matrix that fulfills the requirements. It's g ![0720Jr3skSqkPy0Y](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0720Jr3skSqkPy0Y.png) ### 题解 -本题使用贪心算法, 在填充每一行的数字时, 每次都取当前位置行和和列和的较小值. 再分别从行和和列和中减去当前取的值. 继续填充下一个位置的数字. 这样可以保证填充的每个数字都不会使当前行和列超过和的限制(实际按这个思路只要取小于等于二者较小值即可), 直接取二者中的较小值可以保证二者中的一个变为0. 避免了后续继续填充时可能会因行或列的和的限制而无法取得合适的数的问题(0对和没有影响). 而当和为0时只需将该位置填充为0即可. + +本题使用贪心算法, 在填充每一行的数字时, 每次都取当前位置行和和列和的较小值. 再分别从行和和列和中减去当前取的值. 继续填充下一个位置的数字. 这样可以保证填充的每个数字都不会使当前行和列超过和的限制(实际按这个思路只要取小于等于二者较小值即可), 直接取二者中的较小值可以保证二者中的一个变为0. 避免了后续继续填充时可能会因行或列的和的限制而无法取得合适的数的问题(0对和没有影响). 而当和为0时只需将该位置填充为0即可. ### 代码 -```go + +```go func restoreMatrix(rowSum []int, colSum []int) [][]int { result := [][]int{} collen := len(colSum) @@ -9568,7 +9677,8 @@ func restoreMatrix(rowSum []int, colSum []int) [][]int { ``` ## day146 2024-07-21 -### 2392. Build a Matrix With Conditions + +### 2392. Build a Matrix With Conditions You are given a positive integer k. You are also given: @@ -9587,10 +9697,12 @@ Return any matrix that satisfies the conditions. If no answer exists, return an ![0721PAEBPtA5CUTD](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0721PAEBPtA5CUTD.png) ### 题解 + 本题的条件实际上隐含了一种先后顺序, above在below的上面可以理解为above在below之前, 拓扑排序就是解决这种广义上的"顺序关系", 从a->b存在一种后继关系. 可以将above和below的关系视为有向图中的一条边, 从above指向below. 但本题中可能会出现环, 出现环即产生了冲突, 无法进行拓扑排序. 此时无解, 因此先判断由两个条件数组构建的有向图是否包含环, 包含环则无解, 不包含环继续进行拓扑排序, 得到拓扑排序后的数组, 按照数组中数对应的下标将其放入对应的行或者列. 注意对行进行拓扑排序得到的结果和对列进行拓扑排序得到的结果二者并不冲突. 所以分别排序并按顺序填入数组就能得到正确答案. ### 代码 -```go + +```go func buildMatrix(k int, rowConditions [][]int, colConditions [][]int) [][]int { // 定义检查环和拓扑排序的辅助函数 hasCycle := func(graph [][]int, k int) bool { @@ -9706,7 +9818,9 @@ func buildMatrix(k int, rowConditions [][]int, colConditions [][]int) [][]int { ``` ## day147 2024-07-22 -### 2418. Sort the People + +### 2418. Sort the People + You are given an array of strings names, and an array heights that consists of distinct positive integers. Both arrays are of length n. For each index i, names[i] and heights[i] denote the name and height of the ith person. @@ -9716,10 +9830,12 @@ Return names sorted in descending order by the people's heights. ![0722oZxFqSgIHFvB](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0722oZxFqSgIHFvB.png) ### 题解 -本题先将人名和身高绑定成一个结构体, 再根据身高排序, 思路是比较简单, 因此手动实现快排用于身高排序. + +本题先将人名和身高绑定成一个结构体, 再根据身高排序, 思路是比较简单, 因此手动实现快排用于身高排序. ### 代码 -```go + +```go func sortPeople(names []string, heights []int) []string { type people struct{ name string @@ -9732,7 +9848,7 @@ func sortPeople(names []string, heights []int) []string { var quicksort func(sortpeople []people) quicksort = func(sortpeople []people){ if len(sortpeople) <= 1{ - return + return } flag := sortpeople[0] i := 1 @@ -9763,18 +9879,22 @@ func sortPeople(names []string, heights []int) []string { ``` ## day148 2024-07-23 -### 1636. Sort Array by Increasing Frequency + +### 1636. Sort Array by Increasing Frequency + Given an array of integers nums, sort the array in increasing order based on the frequency of the values. If multiple values have the same frequency, sort them in decreasing order. Return the sorted array. -![0723bKEQAXFqTMve](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0723bKEQAXFqTMve.png) +![0723bKEQAXFqTMve](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0723bKEQAXFqTMve.png) ### 题解 + 本题可使用计数排序, 先使用map统计各个数字出现的次数, 再直接对原始数组进行排序, 首先根据出现的次数排序, 如果两个数字出现次数相同则根据数字大小排序, 将数字更大的放在前面, 这里只需根据上面的条件写好go内置的sort函数即可完成排序. 最后返回结果. ### 代码 -```go + +```go func frequencySort(nums []int) []int { mapp:= make(map[int]int) n:= len(nums) @@ -9795,7 +9915,9 @@ func frequencySort(nums []int) []int { ``` ## day149 2024-07-24 -### 2191. Sort the Jumbled Numbers + +### 2191. Sort the Jumbled Numbers + You are given a 0-indexed integer array mapping which represents the mapping rule of a shuffled decimal system. mapping[i] = j means digit i should be mapped to digit j in this system. The mapped value of an integer is the new integer obtained by replacing each occurrence of digit i in the integer with mapping[i] for all 0 <= i <= 9. @@ -9805,15 +9927,17 @@ You are also given another integer array nums. Return the array nums sorted in n Notes: Elements with the same mapped values should appear in the same relative order as in the input. -The elements of nums should only be sorted based on their mapped values and not be replaced by them. +The elements of nums should only be sorted based on their mapped values and not be replaced by them. ![0724RmELEDU4yGW7](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0724RmELEDU4yGW7.png) ### 题解 -本题要解决两个问题, 一是将原始数字映射为其对应的映射数字, 二是将映射数字排序并按序排列对应的原始数字并返回. 将原始数字映射为对应数字只需每次除以10, 按位处理即可, 注意单独处理0的情况. 对映射后数字排序并反映到原来的数组上需要注意体重明确说明了对于映射数字相同的数字需保持原始数组的顺序, 因此需要稳定排序, 在go中使用sort.SliceStable即可进行稳定排序. + +本题要解决两个问题, 一是将原始数字映射为其对应的映射数字, 二是将映射数字排序并按序排列对应的原始数字并返回. 将原始数字映射为对应数字只需每次除以10, 按位处理即可, 注意单独处理0的情况. 对映射后数字排序并反映到原来的数组上需要注意体重明确说明了对于映射数字相同的数字需保持原始数组的顺序, 因此需要稳定排序, 在go中使用sort.SliceStable即可进行稳定排序. ### 代码 -```go + +```go func sortJumbled(mapping []int, nums []int) []int { type mapNum struct{ raw int @@ -9855,18 +9979,22 @@ func toMap(raw int, mapping []int)int{ ``` ## day150 2024-07-25 -### 912. Sort an Array + +### 912. Sort an Array + Given an array of integers nums, sort the array in ascending order and return it. -You must solve the problem without using any built-in functions in O(nlog(n)) time complexity and with the smallest space complexity possible. +You must solve the problem without using any built-in functions in O(nlog(n)) time complexity and with the smallest space complexity possible. ![0725AQCvbUrAA3cY](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0725AQCvbUrAA3cY.png) ### 题解 -本题是一道基础题, 要求手动实现一种排序方式进行排序, 之前已经实现过归并排序, 快排, 计数排序, 因此本次实现堆排序, 实现堆排序的关键在于构建一个最小堆. + +本题是一道基础题, 要求手动实现一种排序方式进行排序, 之前已经实现过归并排序, 快排, 计数排序, 因此本次实现堆排序, 实现堆排序的关键在于构建一个最小堆. ### 代码 -```go + +```go type IntHeap []int @@ -9889,8 +10017,8 @@ func (h *IntHeap) Push(x interface{}) { func (h *IntHeap) Pop() interface{} { old := *h n := len(old) - x := old[n-1] - *h = old[0 : n-1] + x := old[n-1] + *h = old[0 : n-1] return x } @@ -9912,20 +10040,24 @@ func sortArray(nums []int) []int { ``` ## day151 2024-07-26 -### 1334. Find the City With the Smallest Number of Neighbors at a Threshold Distance + +### 1334. Find the City With the Smallest Number of Neighbors at a Threshold Distance + There are n cities numbered from 0 to n-1. Given the array edges where edges[i] = [fromi, toi, weighti] represents a bidirectional and weighted edge between cities fromi and toi, and given the integer distanceThreshold. Return the city with the smallest number of cities that are reachable through some path and whose distance is at most distanceThreshold, If there are multiple such cities, return the city with the greatest number. -Notice that the distance of a path connecting cities i and j is equal to the sum of the edges' weights along that path. +Notice that the distance of a path connecting cities i and j is equal to the sum of the edges' weights along that path. -![0726o56eEoMULQLn](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0726o56eEoMULQLn.png) +![0726o56eEoMULQLn](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0726o56eEoMULQLn.png) ### 题解 -本题思路是比较明确的, 使用dijistra算法找到每个点到其他点的最短距离并保留在阈值距离以内的点, 比较每个点在阈值距离以内的点的数量, 找到点的数量最少的点并返回值最大的即可, 此处也可以使用Floyd算法来计算最短路径. + +本题思路是比较明确的, 使用dijistra算法找到每个点到其他点的最短距离并保留在阈值距离以内的点, 比较每个点在阈值距离以内的点的数量, 找到点的数量最少的点并返回值最大的即可, 此处也可以使用Floyd算法来计算最短路径. ### 代码 -```go + +```go type Edge struct { to int weight int @@ -10012,22 +10144,26 @@ func findTheCity(n int, edges [][]int, distanceThreshold int) int { ``` ## day152 2024-07-27 -### 2976. Minimum Cost to Convert String I + +### 2976. Minimum Cost to Convert String I + You are given two 0-indexed strings source and target, both of length n and consisting of lowercase English letters. You are also given two 0-indexed character arrays original and changed, and an integer array cost, where cost[i] represents the cost of changing the character original[i] to the character changed[i]. You start with the string source. In one operation, you can pick a character x from the string and change it to the character y at a cost of z if there exists any index j such that cost[j] == z, original[j] == x, and changed[j] == y. Return the minimum cost to convert the string source to the string target using any number of operations. If it is impossible to convert source to target, return -1. -Note that there may exist indices i, j such that original[j] == original[i] and changed[j] == changed[i]. +Note that there may exist indices i, j such that original[j] == original[i] and changed[j] == changed[i]. ![0727giQdhU1gMved](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0727giQdhU1gMved.png) ### 题解 -本题和昨天的题目核心思路上基本相同, 只需要遍历原始字母和其映射字母以及相应的花费, 将字母对应的映射字母和其花费保存起来, 这里可以使用一个map, 也可以把字母当成下标, 用一个26*26的数组来保存(只有小写字母). 关键还是当原始字母和映射字母均相同时如果有多个不同的花费, 要将最小的花费保存下来. 因为这里我们只关心最小花费, 每个映射都是最小花费最终得到的即为最小花费, 是一个比较清晰的贪心法. 需要注意的是可能存在c->e, e->b这样的转换的花费比c->b小得多, 是不是感觉很熟悉, 实际上这个问题完全可以把字母当成节点, 花费当成边的花费, 这样问题就转换成了和昨天完全一样的问题. 使用同样的方法求解即可. 本题中求解最短路径我们使用Floyd-Warshall算法, 该算法的原理为动态规划. + +本题和昨天的题目核心思路上基本相同, 只需要遍历原始字母和其映射字母以及相应的花费, 将字母对应的映射字母和其花费保存起来, 这里可以使用一个map, 也可以把字母当成下标, 用一个26\*26的数组来保存(只有小写字母). 关键还是当原始字母和映射字母均相同时如果有多个不同的花费, 要将最小的花费保存下来. 因为这里我们只关心最小花费, 每个映射都是最小花费最终得到的即为最小花费, 是一个比较清晰的贪心法. 需要注意的是可能存在c->e, e->b这样的转换的花费比c->b小得多, 是不是感觉很熟悉, 实际上这个问题完全可以把字母当成节点, 花费当成边的花费, 这样问题就转换成了和昨天完全一样的问题. 使用同样的方法求解即可. 本题中求解最短路径我们使用Floyd-Warshall算法, 该算法的原理为动态规划. ### 代码 -```go + +```go func minimumCost(source string, target string, original []byte, changed []byte, cost []int) int64 { costs := make([][]int, 26) for i,_ := range costs{ @@ -10062,10 +10198,13 @@ func minimumCost(source string, target string, original []byte, changed []byte, ``` ### 总结 + 如果点特别多并且图为稀疏图, 用map来保存边的权重并执行Floyd算法效率会更高, 避免了不连通的边的遍历. ## day153 2024-07-28 -### 2045. Second Minimum Time to Reach Destination + +### 2045. Second Minimum Time to Reach Destination + A city is represented as a bi-directional connected graph with n vertices where each vertex is labeled from 1 to n (inclusive). The edges in the graph are represented as a 2D integer array edges, where each edges[i] = [ui, vi] denotes a bi-directional edge between vertex ui and vertex vi. Every vertex pair is connected by at most one edge, and no vertex has an edge to itself. The time taken to traverse any edge is time minutes. Each vertex has a traffic signal which changes its color from green to red and vice versa every change minutes. All signals change at the same time. You can enter a vertex at any time, but can leave a vertex only when the signal is green. You cannot wait at a vertex if the signal is green. @@ -10083,10 +10222,12 @@ You can assume that when the journey starts, all signals have just turned green. ![0728Jbh6QjYRGlCE](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0728Jbh6QjYRGlCE.png) ### 题解 -本题是一道难度, 解决本题要将问题分解开. 首先考虑找到第二小时长到达目的地的路的问题, 因为经过每条路需要的时间都相同, 因此经过的节点数量与最终需要的时长成正比, 因此只需要找到经过节点第二多的路径即可. 注意当到达目的地的路径长度有多个不同值时, 直接存在第二长的路径, 而到达目的地的路径长度均相同时, 需要回退一步, 这种情况只需将最短路径经过的节点+2即得到第二长的路径. 另一个需要解决的问题是如何找到所有能到达目的地的路径, 这里可以使用bfs来解决, 使用bfs的好处在于不需要考虑只有一个长度的路径和不同长度的路径时不同的处理方式, 设置两个距离数组表示第一次访问到某节点需要的时间和第二次访问到某节点需要的时间, 只需要在bfs时不断填充这两个数组, 当某个节点在这两个数组中都存在值时, 说明该节点已经找到了最短路径和次短路径, 其余路径我们并不关心, 直接跳过该节点的其余处理即可. + +本题是一道难度, 解决本题要将问题分解开. 首先考虑找到第二小时长到达目的地的路的问题, 因为经过每条路需要的时间都相同, 因此经过的节点数量与最终需要的时长成正比, 因此只需要找到经过节点第二多的路径即可. 注意当到达目的地的路径长度有多个不同值时, 直接存在第二长的路径, 而到达目的地的路径长度均相同时, 需要回退一步, 这种情况只需将最短路径经过的节点+2即得到第二长的路径. 另一个需要解决的问题是如何找到所有能到达目的地的路径, 这里可以使用bfs来解决, 使用bfs的好处在于不需要考虑只有一个长度的路径和不同长度的路径时不同的处理方式, 设置两个距离数组表示第一次访问到某节点需要的时间和第二次访问到某节点需要的时间, 只需要在bfs时不断填充这两个数组, 当某个节点在这两个数组中都存在值时, 说明该节点已经找到了最短路径和次短路径, 其余路径我们并不关心, 直接跳过该节点的其余处理即可. ### 代码 -```go + +```go func secondMinimum(n int, edges [][]int, time int, change int) int { g := make([][]int, n+1) for _, edge := range edges { @@ -10101,15 +10242,15 @@ func secondMinimum(n int, edges [][]int, time int, change int) int { dist1[i] = -1 dist2[i] = -1 } - dist1[1] = 0 + dist1[1] = 0 q := list.New() - q.PushBack([]interface{}{1, 1}) + q.PushBack([]interface{}{1, 1}) for q.Len() > 0 { front := q.Remove(q.Front()).([]interface{}) - x := front[0].(int) - freq := front[1].(int) + x := front[0].(int) + freq := front[1].(int) t := dist1[x] if freq == 2 { @@ -10117,31 +10258,33 @@ func secondMinimum(n int, edges [][]int, time int, change int) int { } if (t/change)%2 == 1 { - t = (t/change + 1)*change + time + t = (t/change + 1)*change + time } else { - t += time + t += time } for _, y := range g[x] { - if dist1[y] == -1 { + if dist1[y] == -1 { dist1[y] = t - q.PushBack([]interface{}{y, 1}) - } else if dist2[y] == -1 && dist1[y] != t { + q.PushBack([]interface{}{y, 1}) + } else if dist2[y] == -1 && dist1[y] != t { if y == n { - return t + return t } dist2[y] = t - q.PushBack([]interface{}{y, 2}) + q.PushBack([]interface{}{y, 2}) } } } - return 0 + return 0 } ``` ## day154 2024-07-29 -### 1395. Count Number of Teams + +### 1395. Count Number of Teams + There are n soldiers standing in a line. Each soldier is assigned a unique rating value. You have to form a team of 3 soldiers amongst them under the following rules: @@ -10153,10 +10296,12 @@ Return the number of teams you can form given the conditions. (soldiers can be p ![0729PmaRN33Zh8l6](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0729PmaRN33Zh8l6.png) ### 题解 -这种类型的问题核心在于找到通过保存什么样的状态, 可以简化枚举的过程, 避免重复枚举. 思考一下可知只要知道某个数前面和后面比该数大和比该数小的数字的个数, 即可得到以这个数为中间数的符合要求的三个数字的组合. 如某个数前面比它小(大)的数字有n个, 后面比它大(小)的数字有m个, 则以该数为中间数字满足条件的三数组合有m*n个. 依次遍历每个数字, 将数字作为三个数字中的中间数字来找到满足条件的组合, 这样寻找组合不会出现重复计数的问题. 因为每次用于固定的中间数字都不相同. 可以使用两个数组来保存所有位置前后比其大或者小的数字个数. 这里每个数字都要遍历一遍数组来找到前后比这个数字大或者小的数字个数, 总体时间复杂度为n^2. + +这种类型的问题核心在于找到通过保存什么样的状态, 可以简化枚举的过程, 避免重复枚举. 思考一下可知只要知道某个数前面和后面比该数大和比该数小的数字的个数, 即可得到以这个数为中间数的符合要求的三个数字的组合. 如某个数前面比它小(大)的数字有n个, 后面比它大(小)的数字有m个, 则以该数为中间数字满足条件的三数组合有m\*n个. 依次遍历每个数字, 将数字作为三个数字中的中间数字来找到满足条件的组合, 这样寻找组合不会出现重复计数的问题. 因为每次用于固定的中间数字都不相同. 可以使用两个数组来保存所有位置前后比其大或者小的数字个数. 这里每个数字都要遍历一遍数组来找到前后比这个数字大或者小的数字个数, 总体时间复杂度为n^2. ### 代码 -```go + +```go func numTeams(rating []int) int { length := len(rating) type frontback struct{ @@ -10199,20 +10344,24 @@ func numTeams(rating []int) int { ``` ## day155 2024-07-30 -### 1653. Minimum Deletions to Make String Balanced + +### 1653. Minimum Deletions to Make String Balanced + You are given a string s consisting only of characters 'a' and 'b'​​​​. You can delete any number of characters in s to make s balanced. s is balanced if there is no pair of indices (i,j) such that i < j and s[i] = 'b' and s[j]= 'a'. -Return the minimum number of deletions needed to make s balanced. +Return the minimum number of deletions needed to make s balanced. ![0730MZPahToxI3xk](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0730MZPahToxI3xk.png) ### 题解 -本题想实现题中所述的平衡字符串, 需要在前面删掉一定数量的b, 后面删掉一定数量的a以满足题目要求的b都在a的后面的条件, 则先统计字符串中a和b的数量, 再进行计算, 每次遇到b时计算该b前面需要删除的b的个数和该b后面的a的个数并加和作为需要删除的字符总数. 极端情况为当第一次遇到b且b后存在a时, 相当于删掉所有b后的a. 对于ababa这样的字符串, 第一次遇到b我们计算的是删掉后面的两个a, 第二次遇到b我们计算的是第一个b和第二个b后面的a. 通过这种方式遍历数组并计算需要删掉的字符个数即可. + +本题想实现题中所述的平衡字符串, 需要在前面删掉一定数量的b, 后面删掉一定数量的a以满足题目要求的b都在a的后面的条件, 则先统计字符串中a和b的数量, 再进行计算, 每次遇到b时计算该b前面需要删除的b的个数和该b后面的a的个数并加和作为需要删除的字符总数. 极端情况为当第一次遇到b且b后存在a时, 相当于删掉所有b后的a. 对于ababa这样的字符串, 第一次遇到b我们计算的是删掉后面的两个a, 第二次遇到b我们计算的是第一个b和第二个b后面的a. 通过这种方式遍历数组并计算需要删掉的字符个数即可. ### 代码 -```go + +```go func minimumDeletions(s string) int { counta := 0 prea := 0 @@ -10244,11 +10393,12 @@ func minimumDeletions(s string) int { return result } ``` + ### 总结 -这个思路还是有些复杂, 实际上只要不断保证局部最优最终就能得到全局最优, 而局部最优可以通过如下方式得到, 对于一个同时包含a和b的字符串, 如果a前面出现了b, 那么可以删掉b后面的a, 也可以删掉a前面的b, 但当b后面没有出现a时, b不需要被删除. 因此可以先将出现的b的次数保存, 当遇到a时再判断在之前已经删除的字符基础上再多删掉一个a和删掉前面的全部b哪种方式需要删除的字符个数更少. +这个思路还是有些复杂, 实际上只要不断保证局部最优最终就能得到全局最优, 而局部最优可以通过如下方式得到, 对于一个同时包含a和b的字符串, 如果a前面出现了b, 那么可以删掉b后面的a, 也可以删掉a前面的b, 但当b后面没有出现a时, b不需要被删除. 因此可以先将出现的b的次数保存, 当遇到a时再判断在之前已经删除的字符基础上再多删掉一个a和删掉前面的全部b哪种方式需要删除的字符个数更少. -```go +```go func minimumDeletions(s string) int { var res, bCount int @@ -10264,7 +10414,9 @@ func minimumDeletions(s string) int { ``` ## day156 2024-07-31 -### 1105. Filling Bookcase Shelves + +### 1105. Filling Bookcase Shelves + You are given an array books where books[i] = [thicknessi, heighti] indicates the thickness and height of the ith book. You are also given an integer shelfWidth. We want to place these books in order onto bookcase shelves that have a total width shelfWidth. @@ -10279,10 +10431,12 @@ Return the minimum possible height that the total bookshelf can be after placing ![0731wJnxtMCrafvs](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0731wJnxtMCrafvs.png) ### 题解 -本题是典型的动态规划的题目, 要求必须按顺序放置书, 则需要放置新的一本书时我们有两个选择. 1. 在之前放过书的最后一排继续放书 2. 开始在新的一排放书 这两种选择最终哪种会得到更好的结果是未知的. 假设f(i)为前i本书能得到的最小高度. 对于放置第j本书而言, 可以将其单独作为一排, 则总高度为第j本书的高度j+f(j-1). 也可以将其与前面的书一块放在同一排, 如将j和j-1本书放在同一排, 此时总高度为max(j,j-1)+f(j-2). 以此类推(max(j,j-1,j-2...)+f(j-4))直到多本书不能够放在同一排为止. 将已经计算过的f(i)保存起来可以减少重复运算, f(0)=0,且f(1)=books[0][1]. + +本题是典型的动态规划的题目, 要求必须按顺序放置书, 则需要放置新的一本书时我们有两个选择. 1. 在之前放过书的最后一排继续放书 2. 开始在新的一排放书 这两种选择最终哪种会得到更好的结果是未知的. 假设f(i)为前i本书能得到的最小高度. 对于放置第j本书而言, 可以将其单独作为一排, 则总高度为第j本书的高度j+f(j-1). 也可以将其与前面的书一块放在同一排, 如将j和j-1本书放在同一排, 此时总高度为max(j,j-1)+f(j-2). 以此类推(max(j,j-1,j-2...)+f(j-4))直到多本书不能够放在同一排为止. 将已经计算过的f(i)保存起来可以减少重复运算, f(0)=0,且f(1)=books[0][1]. ### 代码 -```go + +```go func minHeightShelves(books [][]int, shelfWidth int) int { return arrangeBooks(books, shelfWidth) } @@ -10333,7 +10487,9 @@ func min(a, b int) int { ``` ## day157 2024-08-01 -### 2678. Number of Senior Citizens + +### 2678. Number of Senior Citizens + You are given a 0-indexed array of strings details. Each element of details provides information about a given passenger compressed into a string of length 15. The system is such that: The first ten characters consist of the phone number of passengers. @@ -10345,10 +10501,12 @@ Return the number of passengers who are strictly more than 60 years old. ![0801B5jDYOOc70ze](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0801B5jDYOOc70ze.png) ### 题解 -本题是一道简单题, 最基本的字符串操作, 一道只需要能把题读懂并且懂如何通过数组下标访问数组就能解决的问题, 通过下标找到年龄对应的两个字符并且将其转换为数字和60比较即可. + +本题是一道简单题, 最基本的字符串操作, 一道只需要能把题读懂并且懂如何通过数组下标访问数组就能解决的问题, 通过下标找到年龄对应的两个字符并且将其转换为数字和60比较即可. ### 代码 -```go + +```go func countSeniors(details []string) int { result := 0 for _, detail := range details{ @@ -10359,37 +10517,41 @@ func countSeniors(details []string) int { return result } ``` -### 12. Integer to Roman + +### 12. Integer to Roman + Seven different symbols represent Roman numerals with the following values: -Symbol Value -I 1 -V 5 -X 10 -L 50 -C 100 -D 500 -M 1000 +Symbol Value +I 1 +V 5 +X 10 +L 50 +C 100 +D 500 +M 1000 Roman numerals are formed by appending the conversions of decimal place values from highest to lowest. Converting a decimal place value into a Roman numeral has the following rules: If the value does not start with 4 or 9, select the symbol of the maximal value that can be subtracted from the input, append that symbol to the result, subtract its value, and convert the remainder to a Roman numeral. If the value starts with 4 or 9 use the subtractive form representing one symbol subtracted from the following symbol, for example, 4 is 1 (I) less than 5 (V): IV and 9 is 1 (I) less than 10 (X): IX. Only the following subtractive forms are used: 4 (IV), 9 (IX), 40 (XL), 90 (XC), 400 (CD) and 900 (CM). Only powers of 10 (I, X, C, M) can be appended consecutively at most 3 times to represent multiples of 10. You cannot append 5 (V), 50 (L), or 500 (D) multiple times. If you need to append a symbol 4 times use the subtractive form. -Given an integer, convert it to a Roman numeral. +Given an integer, convert it to a Roman numeral. ![0801yrz4KdPhaYyL](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0801yrz4KdPhaYyL.png) ### 题解 -本题主要也是读懂题意, 读懂题目分别获取数字千分位, 百分位, 十分位, 个位的数字, 并根据题目内容进行转换即可. 本题的巧妙之处在于可以将对应的字母不同的数字都列出来并从数字中按从大到小减去这些数字并加上对应的罗马字母, 这很像组合硬币的问题. + +本题主要也是读懂题意, 读懂题目分别获取数字千分位, 百分位, 十分位, 个位的数字, 并根据题目内容进行转换即可. 本题的巧妙之处在于可以将对应的字母不同的数字都列出来并从数字中按从大到小减去这些数字并加上对应的罗马字母, 这很像组合硬币的问题. ### 代码 -```go + +```go import "strings" func intToRoman(num int) string { values := []int{1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1} symbols := []string{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"} - + var builder strings.Builder for i := 0; num > 0; i++ { @@ -10404,7 +10566,9 @@ func intToRoman(num int) string { ``` ## day158 2024-08-02 -### 2134. Minimum Swaps to Group All 1's Together II + +### 2134. Minimum Swaps to Group All 1's Together II + A swap is defined as taking two distinct positions in an array and swapping the values in them. A circular array is defined as an array where we consider the first element and the last element to be adjacent. @@ -10414,10 +10578,12 @@ Given a binary circular array nums, return the minimum number of swaps required ![0802KouymBwKOk9y](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0802KouymBwKOk9y.png) ### 题解 -本题中每个数组中1的数量是固定的, 我们先计数数组中1的个数后只要找到包含1数量最多的长度为数组全部1的数量的子数组, 则此子数组中0的个数即为最少的交换次数. 如何找到这样的子数组呢, 对于一个给定的数组, 因为1的数量固定, 则要找到满足条件的长度固定的子数组, 可以使用滑动窗口, 窗口长度设置为1的数量, 随着窗口滑动不断更新窗口中0的个数的最小值. 从数组开头开始滑动直到数组结尾. 最终得到的最小值即为最少交换次数. + +本题中每个数组中1的数量是固定的, 我们先计数数组中1的个数后只要找到包含1数量最多的长度为数组全部1的数量的子数组, 则此子数组中0的个数即为最少的交换次数. 如何找到这样的子数组呢, 对于一个给定的数组, 因为1的数量固定, 则要找到满足条件的长度固定的子数组, 可以使用滑动窗口, 窗口长度设置为1的数量, 随着窗口滑动不断更新窗口中0的个数的最小值. 从数组开头开始滑动直到数组结尾. 最终得到的最小值即为最少交换次数. ### 代码 -```go + +```go func minSwaps(nums []int) int { countone := 0 for _,num := range nums{ @@ -10452,18 +10618,22 @@ func minSwaps(nums []int) int { ``` ## day159 2024-08-03 -### 1460. Make Two Arrays Equal by Reversing Subarrays + +### 1460. Make Two Arrays Equal by Reversing Subarrays + You are given two integer arrays of equal length target and arr. In one step, you can select any non-empty subarray of arr and reverse it. You are allowed to make any number of steps. -Return true if you can make arr equal to target or false otherwise. +Return true if you can make arr equal to target or false otherwise. ![0803cdry9pcOd9hx](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0803cdry9pcOd9hx.png) ### 题解 -本题要抓住问题的核心, arr数组中可以对任意子数组进行翻转, 则实际上通过不同长度的数组翻转的组合可以实现交换数组中任意两个数的位置, 因为题目只要求判断arr能否通过子数组翻转变成target, 则只要两个数组中有相同数量的相同字母, 根据之前的结论对arr数组中任意两个数都可以交换的结论可知一定可以通过不断交换位置使得arr和target相同. 先遍历target数组, 保存数组中的字母及其对应的个数, 再遍历arr数组, 看是否字母及其个数均与target相同即得结果. + +本题要抓住问题的核心, arr数组中可以对任意子数组进行翻转, 则实际上通过不同长度的数组翻转的组合可以实现交换数组中任意两个数的位置, 因为题目只要求判断arr能否通过子数组翻转变成target, 则只要两个数组中有相同数量的相同字母, 根据之前的结论对arr数组中任意两个数都可以交换的结论可知一定可以通过不断交换位置使得arr和target相同. 先遍历target数组, 保存数组中的字母及其对应的个数, 再遍历arr数组, 看是否字母及其个数均与target相同即得结果. ### 代码 -```go + +```go func canBeEqual(target []int, arr []int) bool { targets := make([]int, 1001) for _, num := range target{ @@ -10479,33 +10649,36 @@ func canBeEqual(target []int, arr []int) bool { } ``` -### 13. Roman to Integer +### 13. Roman to Integer + Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. -Symbol Value -I 1 -V 5 -X 10 -L 50 -C 100 -D 500 -M 1000 +Symbol Value +I 1 +V 5 +X 10 +L 50 +C 100 +D 500 +M 1000 For example, 2 is written as II in Roman numeral, just two ones added together. 12 is written as XII, which is simply X + II. The number 27 is written as XXVII, which is XX + V + II. Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: -I can be placed before V (5) and X (10) to make 4 and 9. -X can be placed before L (50) and C (100) to make 40 and 90. +I can be placed before V (5) and X (10) to make 4 and 9. +X can be placed before L (50) and C (100) to make 40 and 90. C can be placed before D (500) and M (1000) to make 400 and 900. -Given a roman numeral, convert it to an integer. +Given a roman numeral, convert it to an integer. ![0803G9q8SYYzSAm5](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0803G9q8SYYzSAm5.png) ### 题解 -本题是之前整数转换成罗马数字的题目的反向问题, 将罗马数字转换成整数, 则采取类似的思路, 只不过这次从后向前遍历罗马字符串并将对应的数字加到最终结果中, 用一个变量保存前一次加的值, 如果当前数字比前一次加上的值小则应减去当前数字, 最终得到结果. + +本题是之前整数转换成罗马数字的题目的反向问题, 将罗马数字转换成整数, 则采取类似的思路, 只不过这次从后向前遍历罗马字符串并将对应的数字加到最终结果中, 用一个变量保存前一次加的值, 如果当前数字比前一次加上的值小则应减去当前数字, 最终得到结果. ### 代码 -```go + +```go func romanToInt(s string) int { romanMap := map[rune]int{ 'I': 1, @@ -10537,18 +10710,22 @@ func romanToInt(s string) int { ``` ## day160 2024-08-04 -### 1508. Range Sum of Sorted Subarray Sums -You are given the array nums consisting of n positive integers. You computed the sum of all non-empty continuous subarrays from the array and then sorted them in non-decreasing order, creating a new array of n * (n + 1) / 2 numbers. -Return the sum of the numbers from index left to index right (indexed from 1), inclusive, in the new array. Since the answer can be a huge number return it modulo 109 + 7. +### 1508. Range Sum of Sorted Subarray Sums + +You are given the array nums consisting of n positive integers. You computed the sum of all non-empty continuous subarrays from the array and then sorted them in non-decreasing order, creating a new array of n \* (n + 1) / 2 numbers. + +Return the sum of the numbers from index left to index right (indexed from 1), inclusive, in the new array. Since the answer can be a huge number return it modulo 109 + 7. ![0804D4KV3I3rptIz](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0804D4KV3I3rptIz.png) ### 题解 -本题先考虑计算子数组的和时, 如果固定子数组的开始下标, 不断扩展子数组的长度, 则这些子数组的和具有天然的递增顺序. 即可以通过固定子数组的开始下标得到以某下标开始的子数组和的递增序列. 遍历到以下一个数组下标开始的子数组并计算子数组和时, 相当于合并两个递增的数组, 将和不断插入到之前已经有序的数组中, 为了快速插入到之前的有序数组并且保持数组仍为有序数组, 可以使用优先级队列. + +本题先考虑计算子数组的和时, 如果固定子数组的开始下标, 不断扩展子数组的长度, 则这些子数组的和具有天然的递增顺序. 即可以通过固定子数组的开始下标得到以某下标开始的子数组和的递增序列. 遍历到以下一个数组下标开始的子数组并计算子数组和时, 相当于合并两个递增的数组, 将和不断插入到之前已经有序的数组中, 为了快速插入到之前的有序数组并且保持数组仍为有序数组, 可以使用优先级队列. ### 代码 -```go + +```go type Pq []int func (pq Pq)Len() int{ return len(pq) @@ -10564,7 +10741,7 @@ func (pq *Pq) Pop() interface{} { n := len(*pq) item := (*pq)[n-1] (*pq) = (*pq)[0:n-1] - return item + return item } func (pq *Pq) Push(x interface{}) { *pq = append(*pq, x.(int)) @@ -10598,25 +10775,30 @@ func rangeSum(nums []int, n int, left int, right int) int { ``` ### 总结 + 这种解法时间复杂度很高, 可以使用前缀和的前缀和求解, 这种思路比较复杂, 推荐看官方题解 -[官方题解](https://leetcode.cn/problems/range-sum-of-sorted-subarray-sums/solutions/371273/zi-shu-zu-he-pai-xu-hou-de-qu-jian-he-by-leetcode-/) +[官方题解](https://leetcode.cn/problems/range-sum-of-sorted-subarray-sums/solutions/371273/zi-shu-zu-he-pai-xu-hou-de-qu-jian-he-by-leetcode-/) ## day161 2024-08-05 -### 2053. Kth Distinct String in an Array + +### 2053. Kth Distinct String in an Array + A distinct string is a string that is present only once in an array. Given an array of strings arr, and an integer k, return the kth distinct string present in arr. If there are fewer than k distinct strings, return an empty string "". -Note that the strings are considered in the order in which they appear in the array. +Note that the strings are considered in the order in which they appear in the array. ![0805UlLbJTx7tG9M](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0805UlLbJTx7tG9M.png) ### 题解 -本题是简单题, 先遍历数组并使用map保存字符串及其对应的出现次数, 再遍历数组找到只出现过一次的第k个字符串即可, 如没有这样的字符串返回空字符串. + +本题是简单题, 先遍历数组并使用map保存字符串及其对应的出现次数, 再遍历数组找到只出现过一次的第k个字符串即可, 如没有这样的字符串返回空字符串. ### 代码 -```go + +```go func kthDistinct(arr []string, k int) string { maps := map[string]int{} for _, str := range arr{ @@ -10634,25 +10816,29 @@ func kthDistinct(arr []string, k int) string { return "" } ``` + ### 6. Zigzag Conversion + The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility) -P A H N +P A H N A P L S I I G -Y I R +Y I R And then read line by line: "PAHNAPLSIIGYIR" Write the code that will take a string and make this conversion given a number of rows: string convert(string s, int numRows); - + ![0805OnbsL4hII6Vy](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0805OnbsL4hII6Vy.png) ### 题解 -本题可以直接模拟z形排列的过程, 用一个二维数组来保存z形排列过程中每一行的字符串, 最后依次将每行字符串拼接起来即可. 模拟排列过程可以用两个变量, 一个控制排列的方向(从上到下或者从下到上), 一个控制当前的行数. 用strings.Builder可以快速拼接字符串. + +本题可以直接模拟z形排列的过程, 用一个二维数组来保存z形排列过程中每一行的字符串, 最后依次将每行字符串拼接起来即可. 模拟排列过程可以用两个变量, 一个控制排列的方向(从上到下或者从下到上), 一个控制当前的行数. 用strings.Builder可以快速拼接字符串. ### 代码 -```go + +```go func convert(s string, numRows int) string { if numRows == 1{ return s @@ -10684,7 +10870,8 @@ func convert(s string, numRows int) string { ``` ## day162 2024-08-06 -### 3016. Minimum Number of Pushes to Type Word II + +### 3016. Minimum Number of Pushes to Type Word II You are given a string word containing lowercase English letters. @@ -10694,15 +10881,17 @@ It is allowed to remap the keys numbered 2 to 9 to distinct collections of lette Return the minimum number of pushes needed to type word after remapping the keys. -An example mapping of letters to keys on a telephone keypad is given below. Note that 1, *, #, and 0 do not map to any letters. +An example mapping of letters to keys on a telephone keypad is given below. Note that 1, \*, #, and 0 do not map to any letters. ![0806QKsLD0fydkoK](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0806QKsLD0fydkoK.png) ### 题解 -对于这种问题关键是把问题核心抽象出来, 本题中任意数量的字母可以映射在任意的电话按键上, 则采用类似哈夫曼编码的思想, 将出现频率最高的使其编码长度最短就能使总长度最短. 对应在本题中就是尽量让出现频率高的字母映射到按键的前面位置. 这样总的按键次数就会变少. 可用的按键总共有8个, 则先统计字符串中所有字母出现的频率并从大到小排序, 随后8个为一组进行映射, 如将频率前8的字母映射到8个按键的第一位上, 以此类推. 设组数为k, 将当前组中字母出现的次数与k相乘, 并将这些相加, 得到最终结果. 题目只要求最终的按键次数总和, 因此可以直接用一个数组, 下标表示26个小写字母, 对应的项表示出现频数, 将该数组排序后, 按照之前的思路每8个一组与1,2,3,4相乘即得最终结果. + +对于这种问题关键是把问题核心抽象出来, 本题中任意数量的字母可以映射在任意的电话按键上, 则采用类似哈夫曼编码的思想, 将出现频率最高的使其编码长度最短就能使总长度最短. 对应在本题中就是尽量让出现频率高的字母映射到按键的前面位置. 这样总的按键次数就会变少. 可用的按键总共有8个, 则先统计字符串中所有字母出现的频率并从大到小排序, 随后8个为一组进行映射, 如将频率前8的字母映射到8个按键的第一位上, 以此类推. 设组数为k, 将当前组中字母出现的次数与k相乘, 并将这些相加, 得到最终结果. 题目只要求最终的按键次数总和, 因此可以直接用一个数组, 下标表示26个小写字母, 对应的项表示出现频数, 将该数组排序后, 按照之前的思路每8个一组与1,2,3,4相乘即得最终结果. ### 代码 -```go + +```go func minimumPushes(word string) int { chars := make([]int, 26) for i,_ := range word{ @@ -10718,16 +10907,20 @@ func minimumPushes(word string) int { ``` ## day163 2024-08-07 -### 273. Integer to English words -Convert a non-negative integer num to its English words representation. + +### 273. Integer to English words + +Convert a non-negative integer num to its English words representation. ![0807FLw6W1m2ZZzo](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0807FLw6W1m2ZZzo.png) ### 题解 + 本题虽然是一道难题, 但思路也是比较清晰的, 首先理解英文的计数是以千为一组进行计数的, 如千(10^3), 百万(10^6), 十亿(10^9). 则通过一个函数单独处理每个三位一组的数据中的三位数最终解析的英文, 再补充上该三位数对应的单位即可. 注意处理0这种特殊情况 ### 代码 -```go + +```go func numberToWords(num int) string { if num == 0{ return "Zero" @@ -10800,7 +10993,7 @@ func comma(input int,data map[int]string)string{ result.WriteString(data[input/100]) result.WriteString(" ") result.WriteString("Hundred") - + } if input % 100 < 20 && (input % 100) != 0{ if input / 100 > 0{ @@ -10822,20 +11015,24 @@ func comma(input int,data map[int]string)string{ ``` ## day164 2024-08-08 -### 885. Spiral Matrix III + +### 885. Spiral Matrix III + You start at the cell (rStart, cStart) of an rows x cols grid facing east. The northwest corner is at the first row and column in the grid, and the southeast corner is at the last row and column. -You will walk in a clockwise spiral shape to visit every position in this grid. Whenever you move outside the grid's boundary, we continue our walk outside the grid (but may return to the grid boundary later.). Eventually, we reach all rows * cols spaces of the grid. +You will walk in a clockwise spiral shape to visit every position in this grid. Whenever you move outside the grid's boundary, we continue our walk outside the grid (but may return to the grid boundary later.). Eventually, we reach all rows \* cols spaces of the grid. -Return an array of coordinates representing the positions of the grid in the order you visited them. +Return an array of coordinates representing the positions of the grid in the order you visited them. ![0808KR49y4uewtPI](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0808KR49y4uewtPI.png) ### 题解 -本题按照题目要求进行顺时针遍历, 对于超出边界的位置只需不将其输出到结果数组中即能完成题目的要求. 进行顺时针遍历的过程为每走完两个方向就将前进的步数加1, 如一开始的几次移动分别为右1, 下1, 左2, 上2, 右3, 下3... 因此按照这个规律遍历整个矩阵并将不超出边界的位置坐标放入结果数组中. 移动过程中使用一个变量表示方向, 并记录已经走过的在矩阵内部的位置的个数, 直到走过的个数与矩阵内元素的个数相同结束遍历. + +本题按照题目要求进行顺时针遍历, 对于超出边界的位置只需不将其输出到结果数组中即能完成题目的要求. 进行顺时针遍历的过程为每走完两个方向就将前进的步数加1, 如一开始的几次移动分别为右1, 下1, 左2, 上2, 右3, 下3... 因此按照这个规律遍历整个矩阵并将不超出边界的位置坐标放入结果数组中. 移动过程中使用一个变量表示方向, 并记录已经走过的在矩阵内部的位置的个数, 直到走过的个数与矩阵内元素的个数相同结束遍历. ### 代码 -```go + +```go func spiralMatrixIII(rows int, cols int, rStart int, cStart int) [][]int { position := make([]int, 2) position[0] = rStart @@ -10874,20 +11071,24 @@ func spiralMatrixIII(rows int, cols int, rStart int, cStart int) [][]int { ``` ## day165 2024-08-09 -### 840. Magic Squares In Grid + +### 840. Magic Squares In Grid + A 3 x 3 magic square is a 3 x 3 grid filled with distinct numbers from 1 to 9 such that each row, column, and both diagonals all have the same sum. Given a row x col grid of integers, how many 3 x 3 contiguous magic square subgrids are there? -Note: while a magic square can only contain numbers from 1 to 9, grid may contain numbers up to 15. +Note: while a magic square can only contain numbers from 1 to 9, grid may contain numbers up to 15. ![0809LUp4RVEAKUlN](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0809LUp4RVEAKUlN.png) ### 题解 -本题要求找到所有满足行列及斜的和都相同且只由1-9组成的的3\*3矩阵的个数, 则将1-9填入3\*3矩阵中且满足如上要求必须每行每列的和均为1-9的和的1/3即15, 因此只需从头开始依次判断矩阵中的每个3\*3矩阵是否满足每行每列及斜行和都为15即可, 任意条件不满足即可继续判断下一个矩阵是否满足条件. 因为矩阵的行列数都不超过10, 因此需要判断的次数并不多. 这里判断全部行列是否满足条件只需将3\*3矩阵遍历一遍即可, 用一个sums数组保存列和, 在按行遍历的同时将每列的和可以同时求出(sums[0],[1],[2]分别表示三列, 每次将对应列上的元素加到对应的列和中). + +本题要求找到所有满足行列及斜的和都相同且只由1-9组成的的3\*3矩阵的个数, 则将1-9填入3\*3矩阵中且满足如上要求必须每行每列的和均为1-9的和的1/3即15, 因此只需从头开始依次判断矩阵中的每个3\*3矩阵是否满足每行每列及斜行和都为15即可, 任意条件不满足即可继续判断下一个矩阵是否满足条件. 因为矩阵的行列数都不超过10, 因此需要判断的次数并不多. 这里判断全部行列是否满足条件只需将3\*3矩阵遍历一遍即可, 用一个sums数组保存列和, 在按行遍历的同时将每列的和可以同时求出(sums[0],[1],[2]分别表示三列, 每次将对应列上的元素加到对应的列和中). ### 代码 -```go + +```go func numMagicSquaresInside(grid [][]int) int { rows := len(grid) cols := len(grid[0]) @@ -10931,27 +11132,31 @@ func judge(rowi int, coli int, grid [][]int)bool{ for _,visit := range visited{ if !visit{ return false - } + } } - return true + return true } ``` ## day166 2024-08-10 -### 959. Regions Cut By Slashes + +### 959. Regions Cut By Slashes + An n x n grid is composed of 1 x 1 squares where each 1 x 1 square consists of a '/', '\', or blank space ' '. These characters divide the square into contiguous regions. Given the grid grid represented as a string array, return the number of regions. -Note that backslash characters are escaped, so a '\' is represented as '\\'. +Note that backslash characters are escaped, so a '\' is represented as '\\'. ![081082pd6GdnNDxA](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/081082pd6GdnNDxA.png) ### 题解 + 像这样将一些小的区域连接在一起构成一个大区域, 最终计数组合成的大区域有几个的问题常常使用并查集解决, 让每个大区域派出一个代表元, 最终统计代表元的个数即可知道大区域的个数. 问题在于, 如何合并, 合并什么. 我们考虑'/',和'\'这两种情况, 可以发现如果将方块分割成四个小三角形, 右边的三角形都要和右侧方块的左边三角形合并在一起, 下面的三角形要和下面方块的上三角形合并在一起. 那么我们可以将所有的方块都分割成四个小三角形, 再根据题目中提供的斜线情况来合并这些小三角形(相当于给斜线做减法). ### 代码 -```go + +```go func regionsBySlashes(grid []string) int { rmap := make(map[int]int, 0) ll := len(grid) @@ -11022,24 +11227,28 @@ func (s *Set) Find(x int) int { ``` ## day167 2024-08-11 -### 1568. Minimum Number of Days to Disconnect Island + +### 1568. Minimum Number of Days to Disconnect Island + You are given an m x n binary grid grid where 1 represents land and 0 represents water. An island is a maximal 4-directionally (horizontal or vertical) connected group of 1's. The grid is said to be connected if we have exactly one island, otherwise is said disconnected. In one day, we are allowed to change any single land cell (1) into a water cell (0). -Return the minimum number of days to disconnect the grid. +Return the minimum number of days to disconnect the grid. -![0811KjAjKyWx5Ah1](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0811KjAjKyWx5Ah1.png) +![0811KjAjKyWx5Ah1](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0811KjAjKyWx5Ah1.png) ### 题解 + 本题是一道难题, 但整体思路还是比较清晰, 对于任意一个岛屿矩阵, 如果有岛屿之间不相连, 则直接返回0, 如果只有一个岛屿且存在一个点使得去掉这个点后这一个岛屿会被分割为两个岛屿, 则返回1, 如果不存在这样的点则直接返回2. 对于任意形状的岛屿, 将其中最多两个土地变为水就能使一个岛屿分割为两个(考虑111,111,111. 只需将任意一个角分割出来即可, 即分割后为101,110,111, 此时已经将原来的一个岛屿分割为两个). 判断是否只有一个岛屿是之前已经解决过的问题, 使用dfs即可解决. 但判断是否存在一个点使得去掉这个点后一个岛屿会被分割为两个则是一个难解决的问题, 直观上可能会想到使用并查集来解决, 这种方案是可行的, 只是时间复杂度会比较高, 因为要遍历每一个土地来判断删掉这个土地后并查集是否会变成两个. 这里要引入图论中的一些概念, 这样的点在无向图中被称为割点. 要求割点就要引入一个求强连通分量的算法Tarjan算法. 有趣的是, 这个算法的发明者也是并查集的作者Robert E. Tarjan. 该作者在图算法上有着极为杰出的贡献. 对此算法的讲解推荐下面的youtube视频 -[tarjan算法](https://www.youtube.com/watch?v=TyWtx7q2D7Y) +[tarjan算法](https://www.youtube.com/watch?v=TyWtx7q2D7Y) ### 代码 -```go + +```go func minDays(grid [][]int) int { m := len(grid) @@ -11073,13 +11282,13 @@ func minDays(grid [][]int) int { } isCut := make([]bool, m*n) - dfn := make([]int, m*n) - low := make([]int, m*n) + dfn := make([]int, m*n) + low := make([]int, m*n) dfsClock := 0 cnt := 0 var tarjan func(int, int) int - tarjan = func(v, fa int) int { + tarjan = func(v, fa int) int { dfsClock++ dfn[v] = dfsClock low[v] = dfsClock @@ -11124,27 +11333,33 @@ func minDays(grid [][]int) int { return 2 } ``` -### 总结 -图算法往往比较复杂, 在理解抽象的图算法基础上还需要我们将问题抽象出来, 对应到具体的图的问题中, 如是无向图还是有向图, 求解的是图中的什么特殊点或者特殊边. 这些都需要长期的积累. -[洛谷--割点](https://www.luogu.com.cn/problem/P3388) +### 总结 + +图算法往往比较复杂, 在理解抽象的图算法基础上还需要我们将问题抽象出来, 对应到具体的图的问题中, 如是无向图还是有向图, 求解的是图中的什么特殊点或者特殊边. 这些都需要长期的积累. + +[洛谷--割点](https://www.luogu.com.cn/problem/P3388) ## day168 2024-08-12 -### 703. Kth Largest Element in a Stream + +### 703. Kth Largest Element in a Stream + Design a class to find the kth largest element in a stream. Note that it is the kth largest element in the sorted order, not the kth distinct element. Implement KthLargest class: KthLargest(int k, int[] nums) Initializes the object with the integer k and the stream of integers nums. -int add(int val) Appends the integer val to the stream and returns the element representing the kth largest element in the stream. +int add(int val) Appends the integer val to the stream and returns the element representing the kth largest element in the stream. ![0812bV1KZSBD8bVl](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0812bV1KZSBD8bVl.png) ### 题解 + 题目很好理解, 即不断向数组中添加数字但始终返回当前数组中所有数中第K大的数字(可以重复,这里的第K大是排序后的第K个). 这里可以使用一个最小堆, 因为我们只需要第K大的数字, 其余数字并不关心, 因此只需保证最大堆中永远只有k个数字则此时堆顶数字即为当前stream中全部数字中第K大的数字, 能够使用这种方法的另一个原因在于这里的stream只有添加操作而没有删除操作. 如果有删除操作的话, 因为只保留了全部stream中到目前为止第K大及以下的数字, 如果删掉堆顶, 则无法确定之前被舍弃的数字中哪个应该重新成为堆顶(如1,2,3,4 若堆只保留三个数字, 则当到4时堆内为4,3,2三个数, 堆顶为2如果有删除操作将3删掉, 则1应该成为堆顶, 但1已经被舍弃). 因此在有删除操作的情况下这种方法并不适用. ### 代码 -```go + +```go import ( "container/heap" ) @@ -11226,29 +11441,31 @@ func (this *KthLargest) Add(val int) int { ``` ## day169 2024-08-13 -### 40. Combination Sum II + +### 40. Combination Sum II Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sum to target. Each number in candidates may only be used once in the combination. -Note: The solution set must not contain duplicate combinations. +Note: The solution set must not contain duplicate combinations. ![0813KLpTnkmkLkAv](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0813KLpTnkmkLkAv.png) ### 题解 + 这种题目第一反应是需要遍历所有的数字组合来找到符合条件的数字, 但显然可以根据各种条件进行剪枝, 那么总体思路就是在遍历所有组合的基础上进行剪枝操作. 首先想清楚遍历所有组合可以使用回溯算法. 其次是可以从哪些方面进行剪枝. 目标是找到所有和等于目标数字的数字组合, 则显然当当前数字和已经大于目标数字时, 就不需要再去和后面的数字进行组合了, 这是最基本的剪枝. 再次考虑题目要求, 要求找到所有的组合且组合不能重复, 那么这里我们可以对相同的数字进行剪枝, 因为如果有多个相同数字, 在数组已有数字相同的情况下(前缀一样)选取任意一个得到的后续的组合都是相同的. 举例来说, 对于目标和为3的情况, 如果我们有1,2,2,2四个数字, 则选取1和任意一个2对应的组合都是相同的. 这里的重要前提是已有数字相同, 即选择的层级在同一层, 仍然举例来说, 如果目标和为8, 且有1,2,2,2,3,5这几个数字. 则当已有数字为1时, 选取三个2中的任意一个2后, 再与2后面的数字组合, 得到的数组都是一样的, 但如果已有数字是1,2 则此时选取后面两个2中的任意一个最终得到的组合都是一样的. 可能有人会在此处困惑, 这里在1时从三个2中选一个得到的不也是1,2吗, 和在已有数字是1,2的情况有什么区别呢, 这里的关键在回溯的层级, 如果选择1位于第一层, 则接下来回溯需要我们先选择第一个2继续向下搜索, 结束后再选择第二个2继续搜索, 最后选择第三个2继续搜索, 此时这三个2都位于第二层. 而已有1,2的情况相当于已经选择了1和第一个2后的继续向下搜索的过程, 此时继续选择第二个2或者第三个2, 这两个2位于第三层. 我画了一个简单的示意图, 可以很好的理解这个问题(不同颜色表示不同的2, 将整个搜索树展开可以清楚的看到位于不同层级的2, 这些2在不同层级的作用不同). 由此可知, 对于这个问题, 位于同一层的相同数字起到的作用是一样的. 可以对其进行剪枝. ![0813INopp8IMG_1272B111D280-1](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0813INopp8IMG_1272B111D280-1.jpeg) - ### 代码 -```go + +```go func combinationSum2(candidates []int, target int) [][]int { sort.Ints(candidates) // Sort the array to manage duplicates result := [][]int{} current := []int{} - + var backtrack func(target, start int) backtrack = func(target, start int) { if target == 0 { // Base case: if we hit the target, add the current combination to the result @@ -11263,20 +11480,24 @@ func combinationSum2(candidates []int, target int) [][]int { break // 单个数字已经大于目标直接退出 } current = append(current, candidates[i]) - backtrack(target-candidates[i], i+1) - current = current[:len(current)-1] + backtrack(target-candidates[i], i+1) + current = current[:len(current)-1] } } - + backtrack(target, 0) return result } ``` + ### 总结 -看评论区有人提到这个问题与子集和问题相同, 子集和是个经典的np完全问题. 因此这个问题只能遍历所有组合找到所有符合的答案, 剪枝不过是局部优化, 在最坏情况下可能所有组合都满足条件, 此时相当于不存在剪枝. + +看评论区有人提到这个问题与子集和问题相同, 子集和是个经典的np完全问题. 因此这个问题只能遍历所有组合找到所有符合的答案, 剪枝不过是局部优化, 在最坏情况下可能所有组合都满足条件, 此时相当于不存在剪枝. ## day170 2024-08-14 -### 719. Find K-th Smallest Pair Distance + +### 719. Find K-th Smallest Pair Distance + The distance of a pair of integers a and b is defined as the absolute difference between a and b. Given an integer array nums and an integer k, return the kth smallest distance among all the pairs nums[i] and nums[j] where 0 <= i < j < nums.length. @@ -11284,11 +11505,12 @@ Given an integer array nums and an integer k, return the kth smallest distance a ![0814nDv1GexjGquG](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0814nDv1GexjGquG.png) ### 题解 + 本题是一道难题, 因为要找的是数对差中第k小的数对, 因此重要的是数字与数字的差, 暴力方法即为将所有的数对差都求出来后排序即可直接得到第k位的差值. 但显然可以对此进行剪枝, 这时可以想到如果数组有序的话, 当数字a和数字b的差已经大于目标值时, 就没必要继续计算a和大于b的数字的差值. 问题在于最终要求的就是这里的目标值, 在没有目标值的情况下, 如何确定目标值, 这个问题就变成了一个查找问题. 在数组排序后, 我们可以知道整个数组中两数差的最大值(数组第一个数字和最后一个数字的差), 有了最大值和最小值(0), 就可以使用二分法来假设目标值, 假设好目标值后计算小于等于目标值的差值个数, 如果大于等于k个则更新右边界, 否则更新左边界. 这里要注意为什么等于k的时候还要继续更新右边界, 因为通过二分法假设目标值可能会出现目标值并不存在于数组的差值中但仍然满足第k个这个条件, 如差值为1,2,3,5. 而假设当前的假设目标值为4, 则小于4和小于5的差值都为3个, 但4其实并不存在数组的差值中, 要继续更新边界直到找到准确的在数组差值中的目标值. - ### 代码 -```go + +```go func smallestDistancePair(nums []int, k int) int { var enough = func(x int) bool { var i, j, count int @@ -11314,29 +11536,35 @@ func smallestDistancePair(nums []int, k int) int { } else { hi = cur } - + } return lo } ``` + ### 总结 + 像二分法这种为人熟知的方法一定要深刻理解其背后的思想和目标场景, 才能在复杂的背景下准确应用. ## day171 2024-08-15 -### 860. Lemonade Change + +### 860. Lemonade Change + At a lemonade stand, each lemonade costs $5. Customers are standing in a queue to buy from you and order one at a time (in the order specified by bills). Each customer will only buy one lemonade and pay with either a $5, $10, or $20 bill. You must provide the correct change to each customer so that the net transaction is that the customer pays $5. Note that you do not have any change in hand at first. -Given an integer array bills where bills[i] is the bill the ith customer pays, return true if you can provide every customer with the correct change, or false otherwise. +Given an integer array bills where bills[i] is the bill the ith customer pays, return true if you can provide every customer with the correct change, or false otherwise. ![0815blTU4df5vcg6](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0815blTU4df5vcg6.png) ### 题解 -本题是简单题, 比较直接的做法可以通过贪心来解决该题, 每次找钱时都先尝试找较大的金额, 再找较小的金额. 但实际上因为题目中只有三种额度的钱, 则可以直接枚举所有情况(一共三种), 即对10直接找一张5, 对20找一张10一张5或者三张5. 对20要先枚举找一张10的情况, 因为找10一定要和5搭配, 而找三张5则不受10的影响. 这种直接枚举速度比较快. + +本题是简单题, 比较直接的做法可以通过贪心来解决该题, 每次找钱时都先尝试找较大的金额, 再找较小的金额. 但实际上因为题目中只有三种额度的钱, 则可以直接枚举所有情况(一共三种), 即对10直接找一张5, 对20找一张10一张5或者三张5. 对20要先枚举找一张10的情况, 因为找10一定要和5搭配, 而找三张5则不受10的影响. 这种直接枚举速度比较快. ### 代码 -```go + +```go func lemonadeChange(bills []int) bool { money := make([]int, 2) for _,bill := range bills{ @@ -11362,7 +11590,9 @@ func lemonadeChange(bills []int) bool { ``` ## day172 2024-08-16 -### 624. Maximum Distance in Arrays + +### 624. Maximum Distance in Arrays + You are given m arrays, where each array is sorted in ascending order. You can pick up two integers from two different arrays (each array picks one) and calculate the distance. We define the distance between two integers a and b to be their absolute difference |a - b|. @@ -11372,10 +11602,12 @@ Return the maximum distance. ![0816Eud0miJaDw23](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0816Eud0miJaDw23.png) ### 题解 -考虑到本题中各个数组已经从小到大排好序, 则直接使用每个数组的首位数字(最小数字)分别与其余数组的末位数字(最大数字)做差并取绝对值, 这些值中的最大值即为所求的值. 来考虑只有两个数组的情况, 如果数组1的最小值为a,最大值为b, 数组2的最小值为c, 最大值为d. 若a= 0. --x for x < 0. +-x for x < 0. ![0817XUKrbszCuxge](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0817XUKrbszCuxge.png) ### 题解 -本题每一行中各个位置能得到的最大点数仅与上一行有关, 因此只要解决两行矩阵第二行如何根据第一行求得每个位置能得到的最大点数这一子问题即可得解. 最直接的想法当然是暴力, 对第二行每个数字都遍历第一行的全部数字, 每个数字都通过点数-距离得到其对于第二行该数字的价值. 最后取价值最大的数加在该数字上, 这种方式如果每行数字个数为n个, 需要n^2的复杂度. 则若共有m行, 整体需要m*n^2的复杂度. 因此我们需要思考一些方式使得每行只需遍历常数次即能让下一行每个位置找到上一行中的最大值. 这个问题的思路在某些地方与"接雨水"这到经典题有相似之处. 虽然我们在从左向右遍历的时候对于任意位置不能得到全局最大值, 但在遍历的过程中其实已经获得了当前下标左侧的全部值的信息, 因此我们可以记录到某一下标的左侧数组中的全部数字到这一下标的最大价值, 只需定义一个变量leftmax, 并在遍历过程中不断将leftmax-1和当前下标的数字比较并更新leftmax. 举例来说, 如1,3,2,6,4这几个数字, 遍历1时leftmax为1, 遍历3时将leftmax-1(1-1)和3比较得到新的leftmax为3, 遍历2时将leftmax-1(3-1)和2比较得到新的leftmax为2. 将每个位置的leftmax都保存下来, 这样相当于将求每个位置的最大价值这一问题拆为两个子问题, 即求解每个位置左侧的最大价值和每个位置右侧的最大价值. 最后再将这两个问题的解比较即得最终解. 这样的好处在于这两个子问题都是可以"并行求解"的, 即对行r遍历一遍行r-1即可求出所有位置的一个子问题的解, 求得两个子问题的解也只需两次. 而原问题则需要每个位置都遍历一遍行r-1, 在遍历的过程中虽然访问了行r-1的全部数字, 但大部分信息都被抛弃了, 在下一个位置又要重新获取这些信息. 这是非常低效的, 在之前题解中多次强调, 能越充分的利用信息就有越高的求解效率. -因此本题求解过程为对行r, 遍历两次行r-1获得全部位置的leftmax和rightmax, 最后遍历一遍行r并更新每个位置的最大价值即可. +本题每一行中各个位置能得到的最大点数仅与上一行有关, 因此只要解决两行矩阵第二行如何根据第一行求得每个位置能得到的最大点数这一子问题即可得解. 最直接的想法当然是暴力, 对第二行每个数字都遍历第一行的全部数字, 每个数字都通过点数-距离得到其对于第二行该数字的价值. 最后取价值最大的数加在该数字上, 这种方式如果每行数字个数为n个, 需要n^2的复杂度. 则若共有m行, 整体需要m\*n^2的复杂度. 因此我们需要思考一些方式使得每行只需遍历常数次即能让下一行每个位置找到上一行中的最大值. 这个问题的思路在某些地方与"接雨水"这到经典题有相似之处. 虽然我们在从左向右遍历的时候对于任意位置不能得到全局最大值, 但在遍历的过程中其实已经获得了当前下标左侧的全部值的信息, 因此我们可以记录到某一下标的左侧数组中的全部数字到这一下标的最大价值, 只需定义一个变量leftmax, 并在遍历过程中不断将leftmax-1和当前下标的数字比较并更新leftmax. 举例来说, 如1,3,2,6,4这几个数字, 遍历1时leftmax为1, 遍历3时将leftmax-1(1-1)和3比较得到新的leftmax为3, 遍历2时将leftmax-1(3-1)和2比较得到新的leftmax为2. 将每个位置的leftmax都保存下来, 这样相当于将求每个位置的最大价值这一问题拆为两个子问题, 即求解每个位置左侧的最大价值和每个位置右侧的最大价值. 最后再将这两个问题的解比较即得最终解. 这样的好处在于这两个子问题都是可以"并行求解"的, 即对行r遍历一遍行r-1即可求出所有位置的一个子问题的解, 求得两个子问题的解也只需两次. 而原问题则需要每个位置都遍历一遍行r-1, 在遍历的过程中虽然访问了行r-1的全部数字, 但大部分信息都被抛弃了, 在下一个位置又要重新获取这些信息. 这是非常低效的, 在之前题解中多次强调, 能越充分的利用信息就有越高的求解效率. + +因此本题求解过程为对行r, 遍历两次行r-1获得全部位置的leftmax和rightmax, 最后遍历一遍行r并更新每个位置的最大价值即可. ### 代码 -```go + +```go func maxPoints(points [][]int) int64 { rows := len(points) if rows == 1{ @@ -11443,7 +11679,7 @@ func maxPoints(points [][]int) int64 { for col:=0;col 0时 -U = 3 * (2^a * 3^(b-1) * 5^c) 当b > 0时 -U = 5 * (2^a * 3^b * 5^(c-1)) 当c > 0时 -c) 注意到括号中的表达式(2^(a-1) * 3^b * 5^c)、(2^a * 3^(b-1) * 5^c)和(2^a * 3^b * 5^(c-1))都是更小的丑数,因为它们的指数和比U小。 +U = 2 _ (2^(a-1) _ 3^b _ 5^c) 当a > 0时 +U = 3 _ (2^a _ 3^(b-1) _ 5^c) 当b > 0时 +U = 5 _ (2^a _ 3^b _ 5^(c-1)) 当c > 0时 +c) 注意到括号中的表达式(2^(a-1) _ 3^b _ 5^c)、(2^a _ 3^(b-1) _ 5^c)和(2^a _ 3^b \* 5^(c-1))都是更小的丑数,因为它们的指数和比U小。 d) 根据归纳假设,这些更小的丑数已经在我们生成的n个丑数中。 @@ -11488,7 +11727,8 @@ e) 因此,U必定可以通过将某个已知的丑数乘以2、3或5得到。 则由此, 生成丑数可以通过之前的丑数分别乘以2,3,5得到, 为了保证得到的丑数是有序的, 则可以用三个指针分别表示2,3,5三个数当前已经乘过的丑数是哪个. 每次取三个指针位置的数乘以对应的被乘数中最小的.如初始丑数都为1, 设指针分别为i2,i3,i5, 则先将2乘1得到2, 同时指针i2指向1表示下标为0的数字(1)已经乘过2了, 继续这个操作不断计数, 直到得到目标丑数. ### 代码 -```go + +```go func nthUglyNumber(n int) int { ugly := make([]int, n) ugly[0] = 1 @@ -11519,7 +11759,9 @@ func nthUglyNumber(n int) int { ``` ## day174 2024-08-19 -### 650. 2 Keys Keyboard + +### 650. 2 Keys Keyboard + There is only one character 'A' on the screen of a notepad. You can perform one of two operations on this notepad for each step: Copy All: You can copy all the characters present on the screen (a partial copy is not allowed). @@ -11529,13 +11771,14 @@ Given an integer n, return the minimum number of operations to get the character ![0819quqWn30l1biR](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0819quqWn30l1biR.png) ### 题解 + 本题题目有点幽默, 一个经典的程序员梗, 程序员键盘上只需要两个键就够了, 一个复制, 一个粘贴. 本题正是一个这样的键盘. -回归题目本身, 第一步可以发现如果n能被一个正整数a整除, 则可以通过将n/a个数的'A'粘贴a-1次得到n个'A', 加上复制操作总共需要的操作次数为a次. 则可以发现只要将n分解成几个因子相乘, 将这几个因子相加就是一个得到n的操作总数. 接下来的问题就是如何分解能让这个操作总数最小, 这里很容易想到将n分解为质因数, 因为质因数是比较特殊的因数, 这是我们的猜测, 可以通过简单的证明说明分解成质因数一定不大于分解成合数因数的操作次数, 只需证明如果a,b均大于1则a+b(质因数的操作总数)<=a*b(合数因数的操作总数). +回归题目本身, 第一步可以发现如果n能被一个正整数a整除, 则可以通过将n/a个数的'A'粘贴a-1次得到n个'A', 加上复制操作总共需要的操作次数为a次. 则可以发现只要将n分解成几个因子相乘, 将这几个因子相加就是一个得到n的操作总数. 接下来的问题就是如何分解能让这个操作总数最小, 这里很容易想到将n分解为质因数, 因为质因数是比较特殊的因数, 这是我们的猜测, 可以通过简单的证明说明分解成质因数一定不大于分解成合数因数的操作次数, 只需证明如果a,b均大于1则a+b(质因数的操作总数)<=a\*b(合数因数的操作总数). 首先,我们可以考虑 ab - (a+b),如果能证明这个差值非负,那么就证明了 ab ≥ a+b。 -a*b - (a+b) = ab - a - b +a\*b - (a+b) = ab - a - b = ab - a - b + 1 - 1 = (a-1)(b-1) - 1 @@ -11550,7 +11793,8 @@ a*b - (a+b) = ab - a - b 由此我们得到只要将n分解成质因数相乘再将这些因子相加就能得到最少的操作总数. 如何将n分解成质因数相乘呢, 只需从2开始不断用n试除质因子, 当无法整除时将质因子加一(不用担心可以被合数整除, 如如果一个数能被6整除, 则从2递增到3时一定已经将这个数除尽了), 直到因子大于等于n的平方根即可. 每次分解出一个质因子都将其加和到操作总数上即得最终的答案. ### 代码 -```go + +```go func minSteps(n int) int { if n == 1{ return 0 @@ -11576,26 +11820,30 @@ func minSteps(n int) int { ``` ## day 175 2024-08-20 -### 1140. Stone Game II -Alice and Bob continue their games with piles of stones. There are a number of piles arranged in a row, and each pile has a positive integer number of stones piles[i]. The objective of the game is to end with the most stones. -Alice and Bob take turns, with Alice starting first. Initially, M = 1. +### 1140. Stone Game II -On each player's turn, that player can take all the stones in the first X remaining piles, where 1 <= X <= 2M. Then, we set M = max(M, X). +Alice and Bob continue their games with piles of stones. There are a number of piles arranged in a row, and each pile has a positive integer number of stones piles[i]. The objective of the game is to end with the most stones. + +Alice and Bob take turns, with Alice starting first. Initially, M = 1. + +On each player's turn, that player can take all the stones in the first X remaining piles, where 1 <= X <= 2M. Then, we set M = max(M, X). The game continues until all the stones have been taken. -Assuming Alice and Bob play optimally, return the maximum number of stones Alice can get. +Assuming Alice and Bob play optimally, return the maximum number of stones Alice can get. ![0820rbyn5xa0NtDv](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0820rbyn5xa0NtDv.png) ### 题解 -本题思考对于Alice取石子的场景, 如何知道Alice最终最多能取到多少石子, 对于最简单的场景, 如当前Alice只能取一堆或者两堆石子, 我们只需知道当Alice取一堆石子后后续最多能取到多少石子, 并与当前一堆石子加和, 即可知道如果选择只取一堆石子Allice最终最多能得到多少石子, 取两堆石子同理. 则问题变为当Alice已经取完一次石子后, 后续Alice最多能取到多少石子, 对于Alice来说, 目标为取得最多的石子, 而对于Bob来说, 目标则是让Alice取到最少的石子. 则可以设定一个flag表示当前是Alice的回合还是Bob的回合, 如果是Alice的回合, 则尝试所有可行的取石子方法并加上取石子后剩余的石子堆中Alice能取到的最大石子数再取一个最大值. 如果是Bob的回合, 则也尝试所有可行的取石子方法并返回Alice能取石子的最小值. 如石子总共有3堆, Alice先取可以取一堆或者两堆, 则Alice取一堆加上剩余两堆Alice还能取得的最大值. 剩余两堆中Bob同样可以取一堆或者取两堆, 如果Bob选择取一堆则Alice还能取到第三堆, 如果Bob选择取两堆, 则Alice只能取到0, 对Bob来说, 当然是最小化Alice能取的石子个数即让Alice取0, 此时剩余两堆中Alice能取得的最大值就是0. 用状态来表示可以表示为取0加上A(2,1,0). 其中2表示下一个人从第2堆开始取, 1表示上一个人取完后M的值, 即下一个人可以取的堆上限的1/2, 0表示下一个取的人该是Bob, 1表示该是Alice. 将状态记忆化, 遇上重复的状态不需要再次递归计算. -这类问题的关键是把大问题抽象成一个统一的结构, 分解成小问题解决, 在本题中即为当Alice可取的堆数从1到2M时, 只需考虑用Alice取的堆数加上剩余的堆中Alice能取得的最大值即可. 至于剩余的堆中Alice能取得最大值怎么计算, 因为此问题的结构和前一个问题完全相同, 则我们一定可以通过不断递归将问题规模不断变小最终到达一个终止状态得到解决, 因此不用过于考虑计算细节, 只需确定好终止状态, 其余部分通过结构相同问题递归处理就能得到最终答案. +本题思考对于Alice取石子的场景, 如何知道Alice最终最多能取到多少石子, 对于最简单的场景, 如当前Alice只能取一堆或者两堆石子, 我们只需知道当Alice取一堆石子后后续最多能取到多少石子, 并与当前一堆石子加和, 即可知道如果选择只取一堆石子Allice最终最多能得到多少石子, 取两堆石子同理. 则问题变为当Alice已经取完一次石子后, 后续Alice最多能取到多少石子, 对于Alice来说, 目标为取得最多的石子, 而对于Bob来说, 目标则是让Alice取到最少的石子. 则可以设定一个flag表示当前是Alice的回合还是Bob的回合, 如果是Alice的回合, 则尝试所有可行的取石子方法并加上取石子后剩余的石子堆中Alice能取到的最大石子数再取一个最大值. 如果是Bob的回合, 则也尝试所有可行的取石子方法并返回Alice能取石子的最小值. 如石子总共有3堆, Alice先取可以取一堆或者两堆, 则Alice取一堆加上剩余两堆Alice还能取得的最大值. 剩余两堆中Bob同样可以取一堆或者取两堆, 如果Bob选择取一堆则Alice还能取到第三堆, 如果Bob选择取两堆, 则Alice只能取到0, 对Bob来说, 当然是最小化Alice能取的石子个数即让Alice取0, 此时剩余两堆中Alice能取得的最大值就是0. 用状态来表示可以表示为取0加上A(2,1,0). 其中2表示下一个人从第2堆开始取, 1表示上一个人取完后M的值, 即下一个人可以取的堆上限的1/2, 0表示下一个取的人该是Bob, 1表示该是Alice. 将状态记忆化, 遇上重复的状态不需要再次递归计算. + +这类问题的关键是把大问题抽象成一个统一的结构, 分解成小问题解决, 在本题中即为当Alice可取的堆数从1到2M时, 只需考虑用Alice取的堆数加上剩余的堆中Alice能取得的最大值即可. 至于剩余的堆中Alice能取得最大值怎么计算, 因为此问题的结构和前一个问题完全相同, 则我们一定可以通过不断递归将问题规模不断变小最终到达一个终止状态得到解决, 因此不用过于考虑计算细节, 只需确定好终止状态, 其余部分通过结构相同问题递归处理就能得到最终答案. ### 代码 -```go + +```go func stoneGameII(piles []int) int { memo := make(map[string]int) length := len(piles) @@ -11637,20 +11885,24 @@ func stoneGameII(piles []int) int { ``` ## day 176 2024-08-21 -### 664. Strange Printer + +### 664. Strange Printer + There is a strange printer with the following two special properties: The printer can only print a sequence of the same character each time. At each turn, the printer can print new characters starting from and ending at any place and will cover the original existing characters. -Given a string s, return the minimum number of turns the printer needed to print it. +Given a string s, return the minimum number of turns the printer needed to print it. ![08213P3Mjb62YhEb](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/08213P3Mjb62YhEb.png) ### 题解 + 本题是一道难题, 难在如何把问题转换为相同结构的子问题, 本题打印字符串过程的区间是非常重要的, 根据题目可以发现, 如果字符串两端是相同的, 如abba, 则在打印一串a的时候可以同时打印出两端的两个a. 则abba需要打印的次数和abb需要打印的次数相同, abb打印的次数由abb的两个子问题决定, 分别是ab和b,a和bb两个问题的打印次数的最小值决定. 由此可知, 本题可将一个区间内打印次数的问题转换为区间内子区间的打印次数的问题, 区间两端值相同时直接等于子区间的次数, 不同则需遍历所有可能取最小值. 由此不断减小问题规模直到区间长度为1, 直接返回1次即可. 如昨天所言, 只需考虑问题如何由子问题组合得到答案, 不需考虑子问题具体如何求解, 只需将子问题整个当作可以求解并求解好的结果直接使用. 子问题再通过递归自行求解. ### 代码 -```go + +```go func strangePrinter(s string) (result int) { newstr := string(s[0]) @@ -11658,7 +11910,7 @@ func strangePrinter(s string) (result int) { for i:= 1 ;i < len(s); i++ { if s[i] != s[i-1] { newstr += string(s[i]) - } + } } m := len(newstr) @@ -11682,7 +11934,7 @@ func strangePrinter(s string) (result int) { for k:=j;k 1001 candidates = append(candidates, int(math.Pow10(length)) + 1) - + // 情况2:1000 -> 999 candidates = append(candidates, int(math.Pow10(length-1)) - 1) - + // 获取左半部分 leftHalf := n[:(length+1)/2] leftNum, _ := strconv.Atoi(leftHalf) - + // 情况3:直接镜像 candidates = append(candidates, createPalindrome(leftNum, length%2 == 0)) - + // 情况4:左半部分+1 candidates = append(candidates, createPalindrome(leftNum+1, length%2 == 0)) - + // 情况5:左半部分-1 candidates = append(candidates, createPalindrome(leftNum-1, length%2 == 0)) - + closest := candidates[0] minDiff := abs(num - closest) - + for _, candidate := range candidates[1:] { diff := abs(num - candidate) if (diff < minDiff && diff > 0) || (diff == minDiff && candidate < closest) { @@ -11849,7 +12114,7 @@ func nearestPalindromic(n string) string { minDiff = diff } } - + return strconv.Itoa(closest) } @@ -11875,16 +12140,20 @@ func abs(x int) int { ``` ## day180 2024-08-25 -### 145. Binary Tree Postorder Traversal -Given the root of a binary tree, return the postorder traversal of its nodes' values. + +### 145. Binary Tree Postorder Traversal + +Given the root of a binary tree, return the postorder traversal of its nodes' values. ![0825UT0EkQ59RcnE](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0825UT0EkQ59RcnE.png) ### 题解 -本题为基础题, 考查最简单的二叉树遍历方法, 使用递归遍历二叉树, 先遍历左子节点, 再遍历右子节点, 最后将根节点的值加入到结果数组当中. + +本题为基础题, 考查最简单的二叉树遍历方法, 使用递归遍历二叉树, 先遍历左子节点, 再遍历右子节点, 最后将根节点的值加入到结果数组当中. ### 代码 -```go + +```go /** * Definition for a binary tree node. * type TreeNode struct { @@ -11912,8 +12181,11 @@ func postorderTraversal(root *TreeNode) []int { return result } ``` + ## day181 2024-08-26 -### 590. N-ary Tree Postorder Traversal + +### 590. N-ary Tree Postorder Traversal + Given the root of an n-ary tree, return the postorder traversal of its nodes' values. Nary-Tree input serialization is represented in their level order traversal. Each group of children is separated by the null value (See examples) @@ -11921,10 +12193,12 @@ Nary-Tree input serialization is represented in their level order traversal. Eac ![0826V88pNkYMVEH1](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0826V88pNkYMVEH1.png) ### 题解 + 本题和昨天的二叉树遍历思路上一致, 只需要将二叉树遍历的左右子节点改为遍历父节点的孩子节点数组即可. ### 代码 -```go + +```go /** * Definition for a Node. * type Node struct { @@ -11953,22 +12227,26 @@ func postorder(root *Node) []int { ``` ## day182 2024-08-27 -### 1514. Path with Maximum Probability + +### 1514. Path with Maximum Probability + You are given an undirected weighted graph of n nodes (0-indexed), represented by an edge list where edges[i] = [a, b] is an undirected edge connecting the nodes a and b with a probability of success of traversing that edge succProb[i]. Given two nodes start and end, find the path with the maximum probability of success to go from start to end and return its success probability. -If there is no path from start to end, return 0. Your answer will be accepted if it differs from the correct answer by at most 1e-5. +If there is no path from start to end, return 0. Your answer will be accepted if it differs from the correct answer by at most 1e-5. -![0827fiB1rHFThy9y](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0827fiB1rHFThy9y.png) +![0827fiB1rHFThy9y](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0827fiB1rHFThy9y.png) ### 题解 -本题寻找从一个点到另外一个点的最大可能性, 其实与寻找从一个点到另外一个点的最小距离异曲同工, 而这个图是一个无向图且边的权重均为正, 这能让我们想到dijistra算法. 只是需要对dijistra算法进行一些改动, 将计算两点之间的最短距离改为计算两点之间路径的总的可能性, 将每次从队列中取出距离原点距离最近的点改为取出距离原点可能性最大的点, 其余部分不变. + +本题寻找从一个点到另外一个点的最大可能性, 其实与寻找从一个点到另外一个点的最小距离异曲同工, 而这个图是一个无向图且边的权重均为正, 这能让我们想到dijistra算法. 只是需要对dijistra算法进行一些改动, 将计算两点之间的最短距离改为计算两点之间路径的总的可能性, 将每次从队列中取出距离原点距离最近的点改为取出距离原点可能性最大的点, 其余部分不变. 实现dijistra算法在寻找当前队列中到原点距离最短的节点时,如果使用数组则需遍历整个数组, 但使用优先级队列则可以直接得到有最短距离的节点(本题中为最大可能性) ### 代码 -```go + +```go type Edge struct { to int probability float64 @@ -12022,7 +12300,7 @@ func (pq *PriorityQueue) Pop() interface{} { func maxProbability(n int, edges [][]int, succProb []float64, start_node int, end_node int) float64 { graph := buildGraph(n, edges, succProb) - + probs := make([]float64, n) probs[start_node] = 1.0 @@ -12057,22 +12335,26 @@ func maxProbability(n int, edges [][]int, succProb []float64, start_node int, en ``` ## day183 2024-08-28 -### 1905. Count Sub Islands + +### 1905. Count Sub Islands + You are given two m x n binary matrices grid1 and grid2 containing only 0's (representing water) and 1's (representing land). An island is a group of 1's connected 4-directionally (horizontal or vertical). Any cells outside of the grid are considered water cells. An island in grid2 is considered a sub-island if there is an island in grid1 that contains all the cells that make up this island in grid2. -Return the number of islands in grid2 that are considered sub-islands. +Return the number of islands in grid2 that are considered sub-islands. ![0828Lc8ouMrz7EDc](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0828Lc8ouMrz7EDc.png) ### 题解 + 本题在数grid2岛屿个数的基础上增加判断岛屿中所有陆地块是否都包含在grid1中即可, 如果有任意的陆地块不包含在grid1的陆地中则这个岛屿不算sub island. 判断岛屿中的陆地个数则已经之前多次处理过, 使用dfs即可, 每次都向四个方向进行dfs, dfs过程中判断当前陆地块是否包含在grid1的陆地块中, 不包含则直接返回, 包含继续dfs直到四周均无法继续dfs为止. 需要注意的是, 在dfs的过程中不能因为grid2中的岛屿中某块陆地已经不在grid1中就直接返回, 因为需要将整块岛屿都遍历完, 如果不将整块岛屿都遍历完并置0的话会出现一块岛屿被分割遍历多次的情况. 在返回的过程中, 如果用true和false表示遍历的岛屿中的陆地在不在grid1中, 则需要设置一个变量并和各个方向dfs的返回结果做并, 而不是直接返回四个方向dfs的并(如dfs1&&dfs2&&dfs3&&dfs4), 因为并运算的短路效应, 如果前面的dfs有为false的情况则后面的dfs不会执行, 而我们需要四个dfs都被执行. 故需要设置单独的变量来保存四个dfs的结果. ### 代码 -```go + +```go func countSubIslands(grid1 [][]int, grid2 [][]int) int { rows := len(grid2) cols := len(grid2[0]) @@ -12084,7 +12366,7 @@ func countSubIslands(grid1 [][]int, grid2 [][]int) int { cond := true if g1[row][col] == 0{ - cond = false + cond = false } g2[row][col] = 0 cond = dfs(g1, g2, row-1, col) && cond @@ -12109,23 +12391,28 @@ func countSubIslands(grid1 [][]int, grid2 [][]int) int { ``` ## day184 2024-08-29 -### 947. Most Stones Removed with Same Row or Column + +### 947. Most Stones Removed with Same Row or Column + On a 2D plane, we place n stones at some integer coordinate points. Each coordinate point may have at most one stone. A stone can be removed if it shares either the same row or the same column as another stone that has not been removed. -Given an array stones of length n where stones[i] = [xi, yi] represents the location of the ith stone, return the largest possible number of stones that can be removed. +Given an array stones of length n where stones[i] = [xi, yi] represents the location of the ith stone, return the largest possible number of stones that can be removed. ![0829acaVmeQ5MUz7](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0829acaVmeQ5MUz7.png) ### 题解 -本题通过观察可以发现只要彼此之间有关联(共行或共列)的一系列坐标, 最后仅保留一个, 其余均可通过某种顺序被全部删除. 如(0,0), (0,2), (2,0), (2,2), 四个点之前每个都和另外一个有关联, 则最终四个点仅保留一个. 也就意味着, 如果a与b有关联, 则a b可视为在同一个集合中, 若c和a有关联, 则c b a可视为在同一个集合中, 以此类推, 直到没有新的数字加入这个集合, 这里可以使用并查集, 最后只需用全部坐标点的个数减掉集合的个数即得到可以被删除点的最多个数. + +本题通过观察可以发现只要彼此之间有关联(共行或共列)的一系列坐标, 最后仅保留一个, 其余均可通过某种顺序被全部删除. 如(0,0), (0,2), (2,0), (2,2), 四个点之前每个都和另外一个有关联, 则最终四个点仅保留一个. 也就意味着, 如果a与b有关联, 则a b可视为在同一个集合中, 若c和a有关联, 则c b a可视为在同一个集合中, 以此类推, 直到没有新的数字加入这个集合, 这里可以使用并查集, 最后只需用全部坐标点的个数减掉集合的个数即得到可以被删除点的最多个数. 问题在于, 如何构造并查集, 由题目可知, 处在同一行或者同一列的点均可以视为在同一个集合中, 则可以将同一行的点进行union合并,同一列的也合并, 但点是二维的, 而行或列单独是一维的, 将点按照行列合并并不方便, 因此需要想办法将维度降低, 考虑行列的个数最多均为10^4, 则最简单的方法就是将列数加上10^4+1, 从而将其和行数放在同一个轴上, 这样就将每个点的二维行列转换成了一维的行来表示, 列放在全部行的后面来表示. 则基于此对每个点的列转化和和行进行合并操作即可构造需要的并查集, 最后用点的个数减去并查集的集合个数即得需要删除的点数. 这里很有意思的一个思路在于维度压缩, 因为题目中点的坐标有范围, 则x,y两个轴不会被完全使用, 仅仅使用了x,y两个轴上的一个片段, 则可以将y轴的这个片段投射到x轴的某个原本片段之外的区域上, 这样就将原本在两个轴上的问题转换成了一个轴上的问题, 方便解决. + ### 代码 -```go + +```go func removeStones(stones [][]int) int { fa := make([]int,20005) for i := range fa{ @@ -12147,7 +12434,7 @@ func removeStones(stones [][]int) int { } mp := make(map[int]int) - + for _,stone := range stones{ mp[find(stone[0])]++ } @@ -12156,7 +12443,9 @@ func removeStones(stones [][]int) int { ``` ## day185 2024-08-30 -### 2699. Modify Graph Edge Weights + +### 2699. Modify Graph Edge Weights + You are given an undirected weighted connected graph containing n nodes labeled from 0 to n - 1, and an integer array edges where edges[i] = [ai, bi, wi] indicates that there is an edge between nodes ai and bi with weight wi. Some edges have a weight of -1 (wi = -1), while others have a positive weight (wi > 0). @@ -12165,18 +12454,19 @@ Your task is to modify all edges with a weight of -1 by assigning them positive Return an array containing all edges (even unmodified ones) in any order if it is possible to make the shortest distance from source to destination equal to target, or an empty array if it's impossible. -Note: You are not allowed to modify the weights of edges with initial positive weights. +Note: You are not allowed to modify the weights of edges with initial positive weights. ![08302Qfrs1K0b16t](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/08302Qfrs1K0b16t.png) ### 题解 -本题首先注意到目标是要求最短路, 而本题中边权重除了-1外均为正值, 而负值也只有-1一个. 则可以先忽略所有边权重为-1的边, 对其他边可以使用dijistra算法(使用优先级队列优化算法效率). 得到的结果有四种情况, 比target大, 比target小, 和target相等, 以及从源点到目标点之间没有可行路径. 若和target相等, 则将其余-1边均置为一个任意大的数, 不影响最终结果即可. 考虑到target最大为10^9, 可以置为10^9+1. 若比target小, 则不可能通过修改-1边的值得到一条最短路径和target相等(因为当前最短路径已经比target小, 若得到新的最短路只能比当前最短路更小, 只会距离target越来越远), 这种情况返回空集即可. 若比target大或没有可行路径, 则需考虑如何处理-1边. 修改-1边相当于将之前忽略的边加入到图中, 加入到图后要考虑两个问题, 加入忽略的边后是否能得到一条新的从源点到目标点的路径, 以及这条路径的权重和与target的大小关系. 在处理-1边时需要先解决第一个问题, 能否得到新的路径, -1权重被修改后的范围为1-2*10^9, 因此我们将边-1的权重先修改为1, 再执行dijistra算法, 看是否能得到从源点到目标点的小于等于target的路径. 不能则证明这条-1边权重修改对目标没有影响, 继续修改下一条-1边权重为1, 再次执行dijistra算法直到dijistra算法的结果小于等于target为止. 此时若小于target则将此时被修改权重的-1边权重调整一下, 增加target和当前路径权重和的差值即得到了等于target的路径. 剩余的-1边一样赋予一个足够大的不影响结果的值即可. -注意此处需要每次修改一条-1边的权重就执行一次dijistra, 因为每一条边能产生的影响都是独立的, 无法预知任意一条-1边对结果的影响, 修改-1的权重相当于给图增加一条新的边, 单独处理每条边的影响方便我们调整这条边的权重得到路径和为target的路径, 如果同时增加两条边, 则还需要判断应该调整哪条边才能得到目标权重, 这一过程进一步增加了复杂性. +本题首先注意到目标是要求最短路, 而本题中边权重除了-1外均为正值, 而负值也只有-1一个. 则可以先忽略所有边权重为-1的边, 对其他边可以使用dijistra算法(使用优先级队列优化算法效率). 得到的结果有四种情况, 比target大, 比target小, 和target相等, 以及从源点到目标点之间没有可行路径. 若和target相等, 则将其余-1边均置为一个任意大的数, 不影响最终结果即可. 考虑到target最大为10^9, 可以置为10^9+1. 若比target小, 则不可能通过修改-1边的值得到一条最短路径和target相等(因为当前最短路径已经比target小, 若得到新的最短路只能比当前最短路更小, 只会距离target越来越远), 这种情况返回空集即可. 若比target大或没有可行路径, 则需考虑如何处理-1边. 修改-1边相当于将之前忽略的边加入到图中, 加入到图后要考虑两个问题, 加入忽略的边后是否能得到一条新的从源点到目标点的路径, 以及这条路径的权重和与target的大小关系. 在处理-1边时需要先解决第一个问题, 能否得到新的路径, -1权重被修改后的范围为1-2\*10^9, 因此我们将边-1的权重先修改为1, 再执行dijistra算法, 看是否能得到从源点到目标点的小于等于target的路径. 不能则证明这条-1边权重修改对目标没有影响, 继续修改下一条-1边权重为1, 再次执行dijistra算法直到dijistra算法的结果小于等于target为止. 此时若小于target则将此时被修改权重的-1边权重调整一下, 增加target和当前路径权重和的差值即得到了等于target的路径. 剩余的-1边一样赋予一个足够大的不影响结果的值即可. +注意此处需要每次修改一条-1边的权重就执行一次dijistra, 因为每一条边能产生的影响都是独立的, 无法预知任意一条-1边对结果的影响, 修改-1的权重相当于给图增加一条新的边, 单独处理每条边的影响方便我们调整这条边的权重得到路径和为target的路径, 如果同时增加两条边, 则还需要判断应该调整哪条边才能得到目标权重, 这一过程进一步增加了复杂性. ### 代码 -```go + +```go var INF int type Edge struct { @@ -12284,20 +12574,24 @@ func modifiedGraphEdges(n int, edges [][]int, source int, destination int, targe ``` ## day186 2024-08-31 -### 1. Two Sum + +### 1. Two Sum + Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target. You may assume that each input would have exactly one solution, and you may not use the same element twice. -You can return the answer in any order. +You can return the answer in any order. ![0831COEbBIu5rdYe](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0831COEbBIu5rdYe.png) ### 题解 + leetcode每日一题系统貌似出问题了(英文版leetcode), 今天的每日一题重复了前几天的1514题, 看了看那天的题目不知什么时候被换成了这道两数和问题, 作为leetcode第一题其地位不亚于abandon. 本题是一道简单题, 只需先扫描一遍数组将数字作为key将其在原数组中的下标作为value存放在一个哈希表中. 再扫描数组, 查询target和当前数字的差值是否存在于哈希表中, 如果存在则返回当前数字和差值作为key的value组成的数组, 注意处理一些特殊情况, 如当差值和数字自身相同时, 因为数组中可能出现重复数字, 如果有多个重复数字, 则返回这个数字的两个不同下标, 如果不重复, 因为不能返回两个相同的下标作为结果, 则跳过这个数字继续向下处理. ### 代码 -```go + +```go func twoSum(nums []int, target int) []int { find := map[int][]int{} for i,num := range nums{ @@ -12317,20 +12611,24 @@ func twoSum(nums []int, target int) []int { ``` ## day187 2024-09-01 -### 2022. Convert 1D Array Into 2D Array -You are given a 0-indexed 1-dimensional (1D) integer array original, and two integers, m and n. You are tasked with creating a 2-dimensional (2D) array with m rows and n columns using all the elements from original. -The elements from indices 0 to n - 1 (inclusive) of original should form the first row of the constructed 2D array, the elements from indices n to 2 * n - 1 (inclusive) should form the second row of the constructed 2D array, and so on. +### 2022. Convert 1D Array Into 2D Array -Return an m x n 2D array constructed according to the above procedure, or an empty 2D array if it is impossible. +You are given a 0-indexed 1-dimensional (1D) integer array original, and two integers, m and n. You are tasked with creating a 2-dimensional (2D) array with m rows and n columns using all the elements from original. + +The elements from indices 0 to n - 1 (inclusive) of original should form the first row of the constructed 2D array, the elements from indices n to 2 \* n - 1 (inclusive) should form the second row of the constructed 2D array, and so on. + +Return an m x n 2D array constructed according to the above procedure, or an empty 2D array if it is impossible. ![09011WbuX5ihPxx0](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/09011WbuX5ihPxx0.png) ### 题解 -本题是一道简单题, 先判断能否转换成要求的形状的二维数组, 再构造这个二维数组即可, 判断也很直接, 获取一维数组的长度和要求的二维数组的行列数的乘积比较, 不相等就不能转换. 本题如果出现在面试肯定是道送分题. + +本题是一道简单题, 先判断能否转换成要求的形状的二维数组, 再构造这个二维数组即可, 判断也很直接, 获取一维数组的长度和要求的二维数组的行列数的乘积比较, 不相等就不能转换. 本题如果出现在面试肯定是道送分题. ### 代码 -```go + +```go func construct2DArray(original []int, m int, n int) [][]int { if len(original)!=m*n{ return [][]int{} @@ -12348,20 +12646,24 @@ func construct2DArray(original []int, m int, n int) [][]int { ``` ## day188 2024-09-02 -### 1894. Find the Student that Will Replace the Chalk + +### 1894. Find the Student that Will Replace the Chalk + There are n students in a class numbered from 0 to n - 1. The teacher will give each student a problem starting with the student number 0, then the student number 1, and so on until the teacher reaches the student number n - 1. After that, the teacher will restart the process, starting with the student number 0 again. You are given a 0-indexed integer array chalk and an integer k. There are initially k pieces of chalk. When the student number i is given a problem to solve, they will use chalk[i] pieces of chalk to solve that problem. However, if the current number of chalk pieces is strictly less than chalk[i], then the student number i will be asked to replace the chalk. Return the index of the student that will replace the chalk pieces. -![09025Z393o8xY4f7](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/09025Z393o8xY4f7.png) +![09025Z393o8xY4f7](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/09025Z393o8xY4f7.png) ### 题解 -本题是一道中等难度题, 但解题思路很直观, 对于这种可能需要经过多轮最后返回剩下粉笔不足那个同学的编号的问题, 可以直接跳过中间大家都能拿到足够粉笔的轮次, 即将几个同学需要的粉笔个数打包看成一个整体, 从总数中先去掉这个整体的倍数直到剩下的粉笔不够拿完一整个轮次, 再去判断具体的哪个同学粉笔不足. 对于此类只有最后一轮次有用中间的都不影响的情况, 直接通过取余保留最后一轮再判断最终结果. + +本题是一道中等难度题, 但解题思路很直观, 对于这种可能需要经过多轮最后返回剩下粉笔不足那个同学的编号的问题, 可以直接跳过中间大家都能拿到足够粉笔的轮次, 即将几个同学需要的粉笔个数打包看成一个整体, 从总数中先去掉这个整体的倍数直到剩下的粉笔不够拿完一整个轮次, 再去判断具体的哪个同学粉笔不足. 对于此类只有最后一轮次有用中间的都不影响的情况, 直接通过取余保留最后一轮再判断最终结果. ### 代码 -```go + +```go func chalkReplacer(chalk []int, k int) int { chalksum := 0 for _, num := range chalk{ @@ -12379,7 +12681,9 @@ func chalkReplacer(chalk []int, k int) int { ``` ## day189 2024-09-03 -### 1945. Sum of Digits of String After Convert + +### 1945. Sum of Digits of String After Convert + You are given a string s consisting of lowercase English letters, and an integer k. First, convert s into an integer by replacing each letter with its position in the alphabet (i.e., replace 'a' with 1, 'b' with 2, ..., 'z' with 26). Then, transform the integer by replacing it with the sum of its digits. Repeat the transform operation k times in total. @@ -12389,14 +12693,16 @@ For example, if s = "zbax" and k = 2, then the resulting integer would be 8 by t Convert: "zbax" ➝ "(26)(2)(1)(24)" ➝ "262124" ➝ 262124 Transform #1: 262124 ➝ 2 + 6 + 2 + 1 + 2 + 4 ➝ 17 Transform #2: 17 ➝ 1 + 7 ➝ 8 -Return the resulting integer after performing the operations described above. +Return the resulting integer after performing the operations described above. ![09033rbokxnELzJO](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/09033rbokxnELzJO.png) ### 题解 -本题是一道简单题, 只需按照题目所述过程对字符串进行转换, 在转换字符串过程中可以直接将各个字母得到的数字进行按位加和, 无需将字符串转换为整个数字后再进行处理, 因为转换成数字后也是对数字进行按位加和避免溢出可以在处理字符串的过程中直接处理. 再不断对得到的新数字重复按位加和, 此处只要加和到和小于10即可停止直接返回结果, 因为只有一位数字的情况下怎么加和都是这位数字. + +本题是一道简单题, 只需按照题目所述过程对字符串进行转换, 在转换字符串过程中可以直接将各个字母得到的数字进行按位加和, 无需将字符串转换为整个数字后再进行处理, 因为转换成数字后也是对数字进行按位加和避免溢出可以在处理字符串的过程中直接处理. 再不断对得到的新数字重复按位加和, 此处只要加和到和小于10即可停止直接返回结果, 因为只有一位数字的情况下怎么加和都是这位数字. ### 代码 -```go + +```go func getLucky(s string, k int) int { rawnum := 0 convertbyte := 0 @@ -12425,7 +12731,9 @@ func getLucky(s string, k int) int { ``` ## day190 2024-09-04 -### 874. Walking Robot Simulation + +### 874. Walking Robot Simulation + A robot on an infinite XY-plane starts at point (0, 0) facing north. The robot can receive a sequence of these three possible types of commands: -2: Turn left 90 degrees. @@ -12441,59 +12749,64 @@ North means +Y direction. East means +X direction. South means -Y direction. West means -X direction. -There can be obstacle in [0,0]. +There can be obstacle in [0,0]. ![0904tWTRM0Is4AwD](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0904tWTRM0Is4AwD.png) ### 题解 -本题按照题目中的要求遍历数组并对机器人的行动进行模拟, 每当一次行动结束后计算其到原点的欧氏距离, 并更新最大值.在执行行动过程中如何判断是否碰到障碍物, 对于一个前进k步的行动, 每次前进1步都判断一下当前坐标是否碰到障碍物. 在go中可以使用map作为哈希表, 将障碍物的坐标, 一个固定的长度为2的数组作为键, 布尔值作为值存储. 则在前进过程中同样将当前位置的坐标作为键, 判断是否为障碍物. 如果是障碍物, 直接停止前进. + +本题按照题目中的要求遍历数组并对机器人的行动进行模拟, 每当一次行动结束后计算其到原点的欧氏距离, 并更新最大值.在执行行动过程中如何判断是否碰到障碍物, 对于一个前进k步的行动, 每次前进1步都判断一下当前坐标是否碰到障碍物. 在go中可以使用map作为哈希表, 将障碍物的坐标, 一个固定的长度为2的数组作为键, 布尔值作为值存储. 则在前进过程中同样将当前位置的坐标作为键, 判断是否为障碍物. 如果是障碍物, 直接停止前进. ### 代码 -```go + +```go func robotSim(commands []int, obstacles [][]int) int { dx := []int{0, 1, 0, -1} // 北、东、南、西 - dy := []int{1, 0, -1, 0} + dy := []int{1, 0, -1, 0} x, y := 0, 0 direction := 0 maxDistSquared := 0 - + // 创建障碍物集合 obstacleSet := make(map[[2]int]bool) for _, obstacle := range obstacles { obstacleSet[[2]int{obstacle[0], obstacle[1]}] = true } - + for _, cmd := range commands { - if cmd == -2 { + if cmd == -2 { direction = (direction - 1 + 4) % 4 - } else if cmd == -1 { + } else if cmd == -1 { direction = (direction + 1) % 4 - } else { + } else { for step := 0; step < cmd; step++ { nextX, nextY := x + dx[direction], y + dy[direction] if obstacleSet[[2]int{nextX, nextY}] { break } x, y = nextX, nextY - + } distSquared := x*x + y*y if distSquared > maxDistSquared { maxDistSquared = distSquared - } + } } } - + return maxDistSquared } ``` ### 总结 -本题除了用固定长度的数组作为哈希表的键值, 也可以将x,y坐标通过某种方式哈希后映射到一个唯一的哈希值, 从而将这个哈希值作为键值. 比较简单的哈希方法为将y乘以一个倍数再与x相加, 只要让得到的和不重复(哈希不碰撞)即可. 倍数可以取x,y坐标的最大值. 核心在于让得到的哈希值唯一且对于同样的输入能得到相同的哈希值. + +本题除了用固定长度的数组作为哈希表的键值, 也可以将x,y坐标通过某种方式哈希后映射到一个唯一的哈希值, 从而将这个哈希值作为键值. 比较简单的哈希方法为将y乘以一个倍数再与x相加, 只要让得到的和不重复(哈希不碰撞)即可. 倍数可以取x,y坐标的最大值. 核心在于让得到的哈希值唯一且对于同样的输入能得到相同的哈希值. ## day191 2024-09-05 + ### 2028. Find Missing Observations + You have observations of n + m 6-sided dice rolls with each face numbered from 1 to 6. n of the observations went missing, and you only have the observations of m rolls. Fortunately, you have also calculated the average value of the n + m rolls. You are given an integer array rolls of length m where rolls[i] is the value of the ith observation. You are also given the two integers mean and n. @@ -12502,15 +12815,17 @@ Return an array of length n containing the missing observations such that the av The average value of a set of k numbers is the sum of the numbers divided by k. -Note that mean is an integer, so the sum of the n + m rolls should be divisible by n + m. +Note that mean is an integer, so the sum of the n + m rolls should be divisible by n + m. ![0905t879DaTShf7B](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0905t879DaTShf7B.png) ### 题解 -本题首先判断什么情况下不存在可行解, 用平均数计算数字和再减去已知的数字后的余数如果比6n大或比n小, 则无可行解, 6n表示未知的n个骰子都是6, n表示未知的n个骰子都是1. 若有可行解, 则先将这n个骰子均赋值为1, 随后使用贪心赋值, 直到将余数用完. 如3个骰子, 先赋值1,1,1. 若余数为7, 则再赋值6,1,1, 再赋值6,3,1. 每次都尽可能将一个骰子赋予最大值. + +本题首先判断什么情况下不存在可行解, 用平均数计算数字和再减去已知的数字后的余数如果比6n大或比n小, 则无可行解, 6n表示未知的n个骰子都是6, n表示未知的n个骰子都是1. 若有可行解, 则先将这n个骰子均赋值为1, 随后使用贪心赋值, 直到将余数用完. 如3个骰子, 先赋值1,1,1. 若余数为7, 则再赋值6,1,1, 再赋值6,3,1. 每次都尽可能将一个骰子赋予最大值. ### 代码 -```go + +```go func missingRolls(rolls []int, mean int, n int) []int { rollsum := 0 for _,roll := range rolls{ @@ -12538,16 +12853,20 @@ func missingRolls(rolls []int, mean int, n int) []int { ``` ## day192 2024-09-06 -### 3217. Delete Nodes From Linked List Present in Array -You are given an array of integers nums and the head of a linked list. Return the head of the modified linked list after removing all nodes from the linked list that have a value that exists in nums. + +### 3217. Delete Nodes From Linked List Present in Array + +You are given an array of integers nums and the head of a linked list. Return the head of the modified linked list after removing all nodes from the linked list that have a value that exists in nums. ![0906o1q2BrIlssFL](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0906o1q2BrIlssFL.png) ### 题解 + 本题先保存nums中都有哪些数字, 本题的关键在于如何快速判断某个数是否在nums中, 考虑到nums范围到10^5. 因此可以直接用10^5的布尔数组, 下标表示对应的数字, true表示存在, false表示不存在. 当然用哈希表也是可以的. 随后用双指针法对链表中的节点进行遍历并删掉包含在nums中的节点即可. ### 代码 -```go + +```go /** * Definition for singly-linked list. * type ListNode struct { @@ -12578,3 +12897,354 @@ func modifiedList(nums []int, head *ListNode) *ListNode { return head } ``` + +## day193 2024-09-07 + +### 1367. Linked List in Binary Tree + +Given a binary tree root and a linked list with head as the first node. + +Return True if all the elements in the linked list starting from the head correspond to some downward path connected in the binary tree otherwise return False. + +In this context downward path means a path that starts at some node and goes downwards. + +![0907UocXVtnbLaMZ](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0907UocXVtnbLaMZ.png) + +### 题解 + +本题首先要遍历二叉树找到节点值和链表的head值相同的节点, 再尝试判断是否有一个向下的路径, 路径上各个节点的值和链表完全一致. 判断是否有路径满足时可以使用dfs, 如果孩子节点值满足链表中下一个节点的值继续遍历直到遍历完整个链表后返回true, 否则返回false. + +总体思路为, 遍历二叉树(我使用了bfs), 遍历到节点的值和链表的head值相同则调用函数使用dfs判断是否有路径满足链表, 满足则直接终止遍历. 不满足继续遍历二叉树. + +判断是否有路径满足链表的函数设计是在一般的dfs函数上增加了当前节点和链表当前节点值是否相同的判断, 不同直接返回false. 若相同且链表已经遍历到末尾则返回true(递归函数的终止状态). 设计递归函数一定记得设计终止状态. 终止状态清晰了那么中间部分只需完成重复的局部子问题(本题中是遍历节点的左右节点). + +### 代码 + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func isSubPath(head *ListNode, root *TreeNode) bool { + bfs := []*TreeNode{} + bfs = append(bfs, root) + result := false + for len(bfs) > 0{ + for _,node := range bfs{ + if node.Val == head.Val{ + if findPath(head, node){ + result = true + bfs = []*TreeNode{} + break + } + } + if node.Left != nil{ + bfs = append(bfs, node.Left) + } + if node.Right != nil{ + bfs = append(bfs, node.Right) + } + bfs = bfs[1:] + } + } + return result +} + +func findPath(head *ListNode, root *TreeNode)bool{ + if head.Val != root.Val{ + return false + }else if head.Next == nil{ + return true + } + result := false + if root.Left != nil{ + result = result || findPath(head.Next, root.Left) + } + if root.Right != nil{ + result = result || findPath(head.Next, root.Right) + } + return result +} +``` + +## day194 2024-09-08 + +### 725. Split Linked List in Parts + +Given the head of a singly linked list and an integer k, split the linked list into k consecutive linked list parts. + +The length of each part should be as equal as possible: no two parts should have a size differing by more than one. This may lead to some parts being null. + +The parts should be in the order of occurrence in the input list, and parts occurring earlier should always have a size greater than or equal to parts occurring later. + +Return an array of the k parts. + +![0908vAgxRg5Bk0yS](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0908vAgxRg5Bk0yS.png) + +### 题解 + +本题就采用最直接的思路, 先遍历一遍数组, 统计所有节点的个数, 再计算得到分割成k部分后每部分的节点个数. 若总节点个数为n, 则n/k得到的是分割后每部分的个数, n%k得到的是分割成k个后剩余的个数(不足k), 则可将这剩余的n%k分配到n%k个部分中, 每部分一个, 即满足题目中的大小差不超过1这一条件. + +本题还可以采用快慢指针, 只不过要根据题目设置k个不同的快慢指针, 实现起来似乎更麻烦. + +### 代码 + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func splitListToParts(head *ListNode, k int) []*ListNode { + num := 0 + current := head + for current != nil{ + num++ + current = current.Next + } + current = head + ave := num / k + remain := num % k + count := ave + if remain > 0{ + count++ + remain-- + } + result := []*ListNode{head} + for current != nil{ + count-- + if count == 0{ + temp := current + current = current.Next + temp.Next = nil + result = append(result, current) + count += ave + if remain > 0{ + count++ + remain-- + } + }else{ + current = current.Next + } + } + if num < k{ + for i:=0;i<(k-num-1);i++{ + result = append(result, nil) + } + }else{ + result = result[0:k] + } + return result + +} + +``` + +## day195 2024-09-09 + +### 2326. Spiral Matrix IV + +You are given two integers m and n, which represent the dimensions of a matrix. + +You are also given the head of a linked list of integers. + +Generate an m x n matrix that contains the integers in the linked list presented in spiral order (clockwise), starting from the top-left of the matrix. If there are remaining empty spaces, fill them with -1. + +Return the generated matrix. + +![0909bHxqUbda73OX](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0909bHxqUbda73OX.png) + +### 题解 + +本题按照题目要求将链表中的数字依次顺时针序填入指定大小的数组中即可. 如何实现顺时针填入, 为了代码的简洁性, 可以用方向数组来表示向上下左右四个方向前进时时行列坐标的变化, 用一个变量指示方向, 这样就可以将四个方向前进的代码统一起来, 行列分别与前进的方向数组当前方向的坐标相加即表示前进. 注意起点位于0,0的左面. 分别记录当前行和列应该前进的步数, 按行移动一次则将行步数减一, 按列则将列步数减一. 直到遍历矩阵所有格子则结束填充. + +### 代码 + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func spiralMatrix(m int, n int, head *ListNode) [][]int { + matrix := [][]int{} + for i:=0;i 0{ + for i:=0;i y{ + big = x + small = y + }else{ + big = y + small = x + } + remain = big % small + for remain > 0{ + big = small + small = remain + remain = big % small + } + return small +} + + +``` + +## day197 2024-09-11 + +### 2220. Minimum Bit Flips to Convert Number + +A bit flip of a number x is choosing a bit in the binary representation of x and flipping it from either 0 to 1 or 1 to 0. + +For example, for x = 7, the binary representation is 111 and we may choose any bit (including any leading zeros not shown) and flip it. We can flip the first bit from the right to get 110, flip the second bit from the right to get 101, flip the fifth bit from the right (a leading zero) to get 10111, etc. +Given two integers start and goal, return the minimum number of bit flips to convert start to goal. + +![0911Bp83tVuclXiK](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0911Bp83tVuclXiK.png) + +### 题解 + +本题是一道简单题, 对两个数进行异或操作会使得两个数二进制位上数字相同的位变为0, 数字不同的位变为1, 则进行异或后统计有多少个1即可, 本题大概是让人了解一下异或操作. + +### 代码 + +```go +func minBitFlips(start int, goal int) int { + // XOR the two numbers to get the differing bits + diff := start ^ goal + count := 0 + + // Count the number of 1's in the XOR result + for diff > 0 { + count += diff & 1 + diff >>= 1 + } + + return count +} +``` + +## day198 2024-09-12 + +### 1684. Count the Number of Consistent Strings + +You are given a string allowed consisting of distinct characters and an array of strings words. A string is consistent if all characters in the string appear in the string allowed. + +Return the number of consistent strings in the array words. + +![0912Wvb3dm8bZMGM](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0912Wvb3dm8bZMGM.png) + +### 题解 + +本题是一道简单题, allowed中的字母均为不同字母, 则可用长度为26的布尔数组保存allowed中哪个字母出现过, 再遍历words数组, 对每个字符串进行遍历, 一旦出现过未在allowed中出现的字母则直接跳过, 遍历下一个字符串, 对符合要求的字符串进行计数. + +### 代码 + +```go +func countConsistentStrings(allowed string, words []string) int { + allow := make([]bool, 26) + for i,_ := range allowed{ + allow[allowed[i]-'a'] = true + } + flag := true + result := 0 + for _,word := range words{ + flag = true + for i,_ := range word{ + if !allow[word[i]-'a']{ + flag = false + break + } + } + if flag{ + result++ + } + } + return result +} +```