diff --git a/content/posts/leetcode.md b/content/posts/leetcode.md index 1a05c99..e0931ba 100644 --- a/content/posts/leetcode.md +++ b/content/posts/leetcode.md @@ -9910,3 +9910,809 @@ func sortArray(nums []int) []int { return sortedArray } ``` + +## day151 2024-07-26 +### 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. + +![0726o56eEoMULQLn](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0726o56eEoMULQLn.png) + +### 题解 +本题思路是比较明确的, 使用dijistra算法找到每个点到其他点的最短距离并保留在阈值距离以内的点, 比较每个点在阈值距离以内的点的数量, 找到点的数量最少的点并返回值最大的即可, 此处也可以使用Floyd算法来计算最短路径. + +### 代码 +```go +type Edge struct { + to int + weight int +} + +type MinHeap struct { + edges []Edge +} + +func (h *MinHeap) Len() int { return len(h.edges) } +func (h *MinHeap) Less(i, j int) bool { return h.edges[i].weight < h.edges[j].weight } +func (h *MinHeap) Swap(i, j int) { h.edges[i], h.edges[j] = h.edges[j], h.edges[i] } + +func (h *MinHeap) Push(x interface{}) { + h.edges = append(h.edges, x.(Edge)) +} + +func (h *MinHeap) Pop() interface{} { + old := h.edges + n := len(old) + x := old[n-1] + h.edges = old[0 : n-1] + return x +} + +func dijkstra(n int, edges [][]int, start int, distanceThreshold int) []int { + graph := make([][]Edge, n) + for _, edge := range edges { + graph[edge[0]] = append(graph[edge[0]], Edge{edge[1], edge[2]}) + graph[edge[1]] = append(graph[edge[1]], Edge{edge[0], edge[2]}) + } + + dist := make([]int, n) + for i := range dist { + dist[i] = math.MaxInt32 + } + dist[start] = 0 + + h := &MinHeap{} + heap.Push(h, Edge{start, 0}) + + for h.Len() > 0 { + current := heap.Pop(h).(Edge) + currentCity := current.to + currentDistance := current.weight + + if currentDistance > distanceThreshold { + break + } + + for _, edge := range graph[currentCity] { + if newDist := currentDistance + edge.weight; newDist < dist[edge.to] { + dist[edge.to] = newDist + heap.Push(h, Edge{edge.to, newDist}) + } + } + } + + return dist +} + +func findTheCity(n int, edges [][]int, distanceThreshold int) int { + minReachable := n + 1 + cityWithMinReachable := -1 + + for i := 0; i < n; i++ { + distance := dijkstra(n, edges, i, distanceThreshold) + reachableCount := 0 + + for _, d := range distance { + if d <= distanceThreshold { + reachableCount++ + } + } + + if reachableCount < minReachable || (reachableCount == minReachable && i > cityWithMinReachable) { + minReachable = reachableCount + cityWithMinReachable = i + } + } + + return cityWithMinReachable +} +``` + +## day152 2024-07-27 +### 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]. + +![0727giQdhU1gMved](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0727giQdhU1gMved.png) + +### 题解 +本题和昨天的题目核心思路上基本相同, 只需要遍历原始字母和其映射字母以及相应的花费, 将字母对应的映射字母和其花费保存起来, 这里可以使用一个map, 也可以把字母当成下标, 用一个26*26的数组来保存(只有小写字母). 关键还是当原始字母和映射字母均相同时如果有多个不同的花费, 要将最小的花费保存下来. 因为这里我们只关心最小花费, 每个映射都是最小花费最终得到的即为最小花费, 是一个比较清晰的贪心法. 需要注意的是可能存在c->e, e->b这样的转换的花费比c->b小得多, 是不是感觉很熟悉, 实际上这个问题完全可以把字母当成节点, 花费当成边的花费, 这样问题就转换成了和昨天完全一样的问题. 使用同样的方法求解即可. 本题中求解最短路径我们使用Floyd-Warshall算法, 该算法的原理为动态规划. + +### 代码 +```go +func minimumCost(source string, target string, original []byte, changed []byte, cost []int) int64 { + costs := make([][]int, 26) + for i,_ := range costs{ + costs[i] = make([]int, 26) + for j:= range costs[i]{ + costs[i][j] = math.MaxInt32 + } + costs[i][i] = 0 + } + for i,_ := range original{ + costs[original[i]-'a'][changed[i]-'a'] = min(cost[i],costs[original[i]-'a'][changed[i]-'a']) + } + for k:=0;k<26;k++{ + for i:=0;i<26;i++{ + for j:=0;j<26;j++{ + if costs[i][j] > costs[i][k]+costs[k][j] { + costs[i][j] = costs[i][k] + costs[k][j] + } + } + } + } + result := 0 + for i := range source{ + if costs[source[i]-'a'][target[i]-'a'] == math.MaxInt32{ + return -1 + }else{ + result += costs[source[i]-'a'][target[i]-'a'] + } + } + return int64(result) +} +``` + +### 总结 +如果点特别多并且图为稀疏图, 用map来保存边的权重并执行Floyd算法效率会更高, 避免了不连通的边的遍历. + +## day153 2024-07-28 +### 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. + +The second minimum value is defined as the smallest value strictly larger than the minimum value. + +For example the second minimum value of [2, 3, 4] is 3, and the second minimum value of [2, 2, 4] is 4. +Given n, edges, time, and change, return the second minimum time it will take to go from vertex 1 to vertex n. + +Notes: + +You can go through any vertex any number of times, including 1 and n. +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时不断填充这两个数组, 当某个节点在这两个数组中都存在值时, 说明该节点已经找到了最短路径和次短路径, 其余路径我们并不关心, 直接跳过该节点的其余处理即可. + +### 代码 +```go +func secondMinimum(n int, edges [][]int, time int, change int) int { + g := make([][]int, n+1) + for _, edge := range edges { + u, v := edge[0], edge[1] + g[u] = append(g[u], v) + g[v] = append(g[v], u) + } + + dist1 := make([]int, n+1) + dist2 := make([]int, n+1) + for i := 1; i <= n; i++ { + dist1[i] = -1 + dist2[i] = -1 + } + dist1[1] = 0 + + q := list.New() + q.PushBack([]interface{}{1, 1}) + + for q.Len() > 0 { + front := q.Remove(q.Front()).([]interface{}) + x := front[0].(int) + freq := front[1].(int) + + t := dist1[x] + if freq == 2 { + t = dist2[x] + } + + if (t/change)%2 == 1 { + t = (t/change + 1)*change + time + } else { + t += time + } + + for _, y := range g[x] { + if dist1[y] == -1 { + dist1[y] = t + q.PushBack([]interface{}{y, 1}) + } else if dist2[y] == -1 && dist1[y] != t { + if y == n { + return t + } + dist2[y] = t + q.PushBack([]interface{}{y, 2}) + } + } + } + + return 0 +} +``` + +## day154 2024-07-29 +### 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: + +Choose 3 soldiers with index (i, j, k) with rating (rating[i], rating[j], rating[k]). +A team is valid if: (rating[i] < rating[j] < rating[k]) or (rating[i] > rating[j] > rating[k]) where (0 <= i < j < k < n). +Return the number of teams you can form given the conditions. (soldiers can be part of multiple teams). + +![0729PmaRN33Zh8l6](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0729PmaRN33Zh8l6.png) + +### 题解 +这种类型的问题核心在于找到通过保存什么样的状态, 可以简化枚举的过程, 避免重复枚举. 思考一下可知只要知道某个数前面和后面比该数大和比该数小的数字的个数, 即可得到以这个数为中间数的符合要求的三个数字的组合. 如某个数前面比它小(大)的数字有n个, 后面比它大(小)的数字有m个, 则以该数为中间数字满足条件的三数组合有m*n个. 依次遍历每个数字, 将数字作为三个数字中的中间数字来找到满足条件的组合, 这样寻找组合不会出现重复计数的问题. 因为每次用于固定的中间数字都不相同. 可以使用两个数组来保存所有位置前后比其大或者小的数字个数. 这里每个数字都要遍历一遍数组来找到前后比这个数字大或者小的数字个数, 总体时间复杂度为n^2. + +### 代码 +```go +func numTeams(rating []int) int { + length := len(rating) + type frontback struct{ + front int + back int + } + small := make([]frontback, length) + big := make([]frontback, length) + for i:=1;i rating[i]{ + countbig++ + } + } + small[i].front = countsmall + big[i].front = countbig + countsmall = 0 + countbig = 0 + for j:=i+1;j rating[i]{ + countbig++ + } + } + small[i].back = countsmall + big[i].back = countbig + } + result := 0 + for i:=1;i= 0; lastBook-- { + currentBookThickness := books[lastBook][0] + currentBookHeight := books[lastBook][1] + + if currentShelfWidth+currentBookThickness > maxShelfWidth { + break + } + + currentShelfWidth += currentBookThickness + currentShelfHeight = max(currentShelfHeight, currentBookHeight) + + currentArrangementHeight := minHeights[lastBook] + currentShelfHeight + minHeights[bookIndex] = min(minHeights[bookIndex], currentArrangementHeight) + } + } + + return minHeights[len(books)] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +## day157 2024-08-01 +### 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. +The next character denotes the gender of the person. +The following two characters are used to indicate the age of the person. +The last two characters determine the seat allotted to that person. +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比较即可. + +### 代码 +```go +func countSeniors(details []string) int { + result := 0 + for _, detail := range details{ + if (detail[11]-'0')*10+(detail[12]-'0') > 60{ + result++ + } + } + return result +} +``` +### 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 +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. + +![0801yrz4KdPhaYyL](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0801yrz4KdPhaYyL.png) + +### 题解 +本题主要也是读懂题意, 读懂题目分别获取数字千分位, 百分位, 十分位, 个位的数字, 并根据题目内容进行转换即可. 本题的巧妙之处在于可以将对应的字母不同的数字都列出来并从数字中按从大到小减去这些数字并加上对应的罗马字母, 这很像组合硬币的问题. + +### 代码 +```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++ { + for num >= values[i] { + builder.WriteString(symbols[i]) + num -= values[i] + } + } + + return builder.String() +} +``` + +## day158 2024-08-02 +### 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. + +Given a binary circular array nums, return the minimum number of swaps required to group all 1's present in the array together at any location. + +![0802KouymBwKOk9y](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0802KouymBwKOk9y.png) + +### 题解 +本题中每个数组中1的数量是固定的, 我们先计数数组中1的个数后只要找到包含1数量最多的长度为数组全部1的数量的子数组, 则此子数组中0的个数即为最少的交换次数. 如何找到这样的子数组呢, 对于一个给定的数组, 因为1的数量固定, 则要找到满足条件的长度固定的子数组, 可以使用滑动窗口, 窗口长度设置为1的数量, 随着窗口滑动不断更新窗口中0的个数的最小值. 从数组开头开始滑动直到数组结尾. 最终得到的最小值即为最少交换次数. + +### 代码 +```go +func minSwaps(nums []int) int { + countone := 0 + for _,num := range nums{ + if num == 1{ + countone++ + } + } + length := len(nums) + min0 := 0 + current0 := 0 + left := 0 + right := countone-1 + for _,num := range nums[0:countone]{ + if num == 0{ + min0++ + } + } + current0 = min0 + for left < length{ + if nums[(right+1)%length] == 0{ + current0++ + } + if nums[left] == 0{ + current0-- + } + min0 = min(current0, min0) + left++ + right++ + } + return min0 +} +``` + +## day159 2024-08-03 +### 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. + +![0803cdry9pcOd9hx](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0803cdry9pcOd9hx.png) + +### 题解 +本题要抓住问题的核心, arr数组中可以对任意子数组进行翻转, 则实际上通过不同长度的数组翻转的组合可以实现交换数组中任意两个数的位置, 因为题目只要求判断arr能否通过子数组翻转变成target, 则只要两个数组中有相同数量的相同字母, 根据之前的结论对arr数组中任意两个数都可以交换的结论可知一定可以通过不断交换位置使得arr和target相同. 先遍历target数组, 保存数组中的字母及其对应的个数, 再遍历arr数组, 看是否字母及其个数均与target相同即得结果. + +### 代码 +```go +func canBeEqual(target []int, arr []int) bool { + targets := make([]int, 1001) + for _, num := range target{ + targets[num]++ + } + for _,num := range arr{ + targets[num]-- + if targets[num] < 0{ + return false + } + } + return true +} +``` + +### 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 +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. +C can be placed before D (500) and M (1000) to make 400 and 900. +Given a roman numeral, convert it to an integer. + +![0803G9q8SYYzSAm5](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0803G9q8SYYzSAm5.png) + +### 题解 +本题是之前整数转换成罗马数字的题目的反向问题, 将罗马数字转换成整数, 则采取类似的思路, 只不过这次从后向前遍历罗马字符串并将对应的数字加到最终结果中, 用一个变量保存前一次加的值, 如果当前数字比前一次加上的值小则应减去当前数字, 最终得到结果. + +### 代码 +```go +func romanToInt(s string) int { + romanMap := map[rune]int{ + 'I': 1, + 'V': 5, + 'X': 10, + 'L': 50, + 'C': 100, + 'D': 500, + 'M': 1000, + } + + total := 0 + prevValue := 0 + + for i := len(s) - 1; i >= 0; i-- { + currentValue := romanMap[rune(s[i])] + + if currentValue < prevValue { + total -= currentValue + } else { + total += currentValue + } + + prevValue = currentValue + } + + return total +} +``` + +## 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. + +![0804D4KV3I3rptIz](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0804D4KV3I3rptIz.png) + +### 题解 +本题先考虑计算子数组的和时, 如果固定子数组的开始下标, 不断扩展子数组的长度, 则这些子数组的和具有天然的递增顺序. 即可以通过固定子数组的开始下标得到以某下标开始的子数组和的递增序列. 遍历到以下一个数组下标开始的子数组并计算子数组和时, 相当于合并两个递增的数组, 将和不断插入到之前已经有序的数组中, 为了快速插入到之前的有序数组并且保持数组仍为有序数组, 可以使用优先级队列. + +### 代码 +```go +type Pq []int +func (pq Pq)Len() int{ + return len(pq) + } + +func (pq Pq) Less(i, j int)bool{ + return pq[i] < pq[j] + } +func (pq Pq) Swap(i, j int){ + pq[i],pq[j] = pq[j],pq[i] + } +func (pq *Pq) Pop() interface{} { + n := len(*pq) + item := (*pq)[n-1] + (*pq) = (*pq)[0:n-1] + return item + } +func (pq *Pq) Push(x interface{}) { + *pq = append(*pq, x.(int)) + } + +func rangeSum(nums []int, n int, left int, right int) int { + pq := Pq{} + sum := 0 + for j:=0;j