From e98d8ececc905cd25312dcdda74fbd9efc0105dd Mon Sep 17 00:00:00 2001 From: gameloader Date: Tue, 16 Jul 2024 19:16:05 +0800 Subject: [PATCH] leetcode update --- content/posts/leetcode.md | 1254 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1254 insertions(+) diff --git a/content/posts/leetcode.md b/content/posts/leetcode.md index f25d978..9f8a0da 100644 --- a/content/posts/leetcode.md +++ b/content/posts/leetcode.md @@ -8104,3 +8104,1257 @@ func build(nums []*TreeNode, leng int) *TreeNode{ ``` +## day122 2024-06-27 +### 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) + +### 题解 +本题n个节点共有n-1条边, 存在一个中心节点使得其余节点都与这个节点相连. 只需要看edges中的任意两条边, 找到这两条边共有的节点即为中心节点. + +### 代码 +```go +func findCenter(edges [][]int) int { + if edges[1][0] == edges[0][0] || edges[1][0] == edges[0][1]{ + return edges[1][0] + }else{ + return edges[1][1] + } +} +``` + +### 总结 +考虑只有一个中心节点与其余n-1个节点相连, 但其余节点也可能相连的情况, 统计所有节点的度, 度最高的即为中心节点. + +### 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. + +Consider the number of elements in nums which are not equal to val be k, to get accepted, you need to do the following things: + +Change the array nums such that the first k elements of nums contain the elements which are not equal to val. The remaining elements of nums are not important as well as the size of nums. +Return k. + +![06277nzgocBBZMpz](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/06277nzgocBBZMpz.png) + +### 题解 +本题最简单的解法当然是再创建一个相同大小的数组, 将原数组中非val的数依次复制到数组中. 另外一种方法则是双指针, 遍历数组, 头指针遇到val则与尾指针指向的非val值交换, 直到头尾指针相遇即可. + +### 代码 +```go +func removeElement(nums []int, val int) int { + length := len(nums) + if length == 0{ + return 0 + } + + if length == 1{ + if nums[0] == val{ + return 0 + }else{ + return 1 + } + } + + k := 0 + head := 0 + tail := len(nums) - 1 + for head <= tail{ + if nums[head] == val{ + for nums[tail] == val && head < tail{ + tail-- + } + if nums[tail] != val{ + nums[head],nums[tail] = nums[tail],nums[head] + k++ + } + }else{ + k++ + } + head++ + } + return k +} +``` + +## day123 2024-06-28 +### 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. + +You need to assign each city with an integer value from 1 to n, where each value can only be used once. The importance of a road is then defined as the sum of the values of the two cities it connects. + +Return the maximum total importance of all roads possible after assigning the values optimally. + +![0628laAsmqEvIGOQ](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0628laAsmqEvIGOQ.png) + +### 题解 +本题要给每个节点赋值, 使得最终边的权重和(边连接的两个节点的权重和)最大. 统计出每个节点相连的边的数量, 从大到小排序, 按序依次从最大到最小赋值并计算权重和. 这里边的权重和可以转化为节点能贡献的权重和的和, 即用节点的权重乘以和这个节点相连的边的数量. 再依次对各个节点求值并加和. + +### 题解 +```go +func maximumImportance(n int, roads [][]int) int64 { + vers := make([]int, n) + for _,value := range roads{ + vers[value[0]]++ + vers[value[1]]++ + } + sort.Ints(vers) + result := 0 + for i:=n-1;i>=0;i--{ + result += vers[i]*(i+1) + } + return int64(result) +} +``` + +## day124 2024-06-29 +### 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. + +![0629uNWG4lSJqpOB](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0629uNWG4lSJqpOB.png) + +### 题解 +本题要求出每个节点的所有祖先节点, 按照节点标号从小到大列出. 既然是有向无环图, 那么本图可以进行拓扑排序. 统计所有节点的入度, 从入度为0的节点开始, 依次遍历入度为0的所有节点, 在其全部后继节点对应的祖先数组中中加入遍历的节点标号. 删掉与这些节点相连的边, 再次查看入度为0的的节点, 重复以上操作. 直到数组中只有入度为0的节点即剩余节点都没有后继节点为止. + +但这种方式要重复向多个数组中加入相同的值. 如果在统计入度的时候将所有边反向并统计反向边对应的各个节点的入度, 则从入度为0的节点开始按入度从小到大遍历节点并将其所有后继节点(通过bfs进行遍历)放入其在结果数组中对应位置的数组内并排序即可. + +### 代码 +```go +func getAncestors(n int, edges [][]int) [][]int { + innodes := make(map[int]int) + outnodes := make([][]int, n) + maxin := 0 + for i, edge := range edges { + outnodes[edge[1]] = append(outnodes[edge[1]], edge[0]) + innodes[edge[0]]++ + maxin = max(maxin, innodes[edge[0]]) + 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 { + result[node] = bfs(node, outnodes) + delete(innodes, node) + } + } + } + + return result +} + +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 + queue = append(queue, neighbor) + ancestors = append(ancestors, neighbor) + } + } + } + + sort.Ints(ancestors) + return ancestors +} +``` + +## day125 2024-06-30 +### 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. +Type 2: Can be traversed by Bob only. +Type 3: Can be traversed by both Alice and Bob. +Given an array edges where edges[i] = [typei, ui, vi] represents a bidirectional edge of type typei between nodes ui and vi, find the maximum number of edges you can remove so that after removing the edges, the graph can still be fully traversed by both Alice and Bob. The graph is fully traversed by Alice and Bob if starting from any node, they can reach all other nodes. + +Return the maximum number of edges you can remove, or return -1 if Alice and Bob cannot fully traverse the graph. + +![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的边也执行以上操作, 最终将删除的边数目加和即可得到最终结果. + +### 代码 +```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 { + if !ufAlice.union(edge[1]-1, edge[2]-1) { + removedEdges++ + } else { + ufBob.union(edge[1]-1, edge[2]-1) + } + } + } + + // 处理类型1的边(只有Alice可以使用的边) + for _, edge := range edges { + if edge[0] == 1 { + if !ufAlice.union(edge[1]-1, edge[2]-1) { + removedEdges++ + } + } + } + + // 处理类型2的边(只有Bob可以使用的边) + for _, edge := range edges { + if edge[0] == 2 { + if !ufBob.union(edge[1]-1, edge[2]-1) { + removedEdges++ + } + } + } + + // 检查是否所有节点都连通 + if ufAlice.count != 1 || ufBob.count != 1 { + return -1 + } + + return removedEdges +} + +// 并查集结构 +type UnionFind struct { + parent []int + rank []int + count int +} + +func newUnionFind(n int) *UnionFind { + uf := &UnionFind{ + parent: make([]int, n), + rank: make([]int, n), + count: n, + } + for i := range uf.parent { + uf.parent[i] = i + } + return uf +} + +func (uf *UnionFind) find(x int) int { + if uf.parent[x] != x { + uf.parent[x] = uf.find(uf.parent[x]) + } + return uf.parent[x] +} + +func (uf *UnionFind) union(x, y int) bool { + rootX, rootY := uf.find(x), uf.find(y) + if rootX == rootY { + return false + } + if uf.rank[rootX] < uf.rank[rootY] { + uf.parent[rootX] = rootY + } else if uf.rank[rootX] > uf.rank[rootY] { + uf.parent[rootY] = rootX + } else { + uf.parent[rootY] = rootX + uf.rank[rootX]++ + } + uf.count-- + return true +} +``` + +### 总结 +要熟悉并查集的实现, 能快速根据需要实现一个简单的并查集出来. + + +## 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. + +![0701xCkXpUEp7aQv](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0701xCkXpUEp7aQv.png) + +### 题解 +本题是一道简单题, 设定一个标记位标记当前连续的奇数个数, 如果达到三个则返回true. 如果遍历到末尾还没有达到三个则返回false. + +### 代码 +```go +func threeConsecutiveOdds(arr []int) bool { + num := 0 + for _,value := range arr{ + if value % 2 == 1{ + num++ + if num >= 3{ + return true + } + }else{ + num = 0 + } + } + return false +} +``` + +## 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. + +![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次放入结果数组中. + +### 代码 +```go +func intersect(nums1 []int, nums2 []int) []int { + map1 := map[int]int{} + map2 := map[int]int{} + len1 := len(nums1) + len2 := len(nums2) + for _,value := range nums1{ + map1[value]++ + } + for _,value := range nums2{ + map2[value]++ + } + + shorter := map[int]int{} + longer := map[int]int{} + + if len1 <= len2{ + shorter = map1 + longer = map2 + }else{ + shorter = map2 + longer = map1 + } + result := []int{} + for key, value := range shorter{ + num, exist := longer[key] + if exist{ + if value <= num{ + for i:=0;i maxcover{ + maxcover = temp + finalchoice = maxchoice[index] + } + } + nums = nums[finalchoice[0]:length-finalchoice[1]] + return nums[length-4]-nums[0] +} +``` + +### 总结 +这种方法属于暴力枚举的方式, 但在题目限制只能改变3个数的条件下, 需要枚举的情况极少, 因此可以奏效. + +## day129 2024-07-04 +### 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. + +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 +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func mergeNodes(head *ListNode) *ListNode { + var startzero *ListNode + tempsum := 0 + var current *ListNode + for current=head;current.Next!=nil;current=current.Next{ + if current.Val == 0{ + if startzero == nil{ + startzero = current + continue + }else if tempsum != 0{ + startzero.Val = tempsum + startzero.Next = current + startzero = current + tempsum = 0 + } + }else{ + tempsum += current.Val + } + } + if tempsum != 0{ + startzero.Val = tempsum + startzero.Next = nil + } + return head +} +``` + +## day130 2024-07-05 +### 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. + +A node is a local minima if the current node has a value strictly smaller than the previous node and the next node. + +Note that a node can only be a local maxima/minima if there exists both a previous node and a next node. + +Given a linked list head, return an array of length 2 containing [minDistance, maxDistance] where minDistance is the minimum distance between any two distinct critical points and maxDistance is the maximum distance between any two distinct critical points. If there are fewer than two critical points, return [-1, -1]. + +![0705GyHjjK7eutFL](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0705GyHjjK7eutFL.png) + +### 题解 +本题使用双指针法, 一个指针指向前一个节点, 一个指针指向当前节点, 这样就可以判断当前节点是否是关键点(critical point). 将节点在链表中的位置也使用下标表示出来, 这样方便判断两个关键点之间的距离, 使用一个变量保存初始关键点的下标, 一个保存前一个关键点的下标, 每次遇到新的关键点计算与前一个关键点的距离并更新距离最小值. 遍历整个链表后, 用最后一个关键点下标和第一个关键点下标作差即得距离最大值. + +### 代码 +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func nodesBetweenCriticalPoints(head *ListNode) []int { + var before *ListNode + var current *ListNode + before = head + current = head.Next + beforeindex := -1 + currentindex := 1 + startindex := -1 + mindis := 1000001 + for current.Next != nil{ + if (current.Val > before.Val && current.Val > current.Next.Val) || (current.Val < before.Val && current.Val < current.Next.Val){ + if beforeindex == -1{ + beforeindex = currentindex + startindex = currentindex + }else{ + mindis = min(mindis, currentindex-beforeindex) + beforeindex = currentindex + } + } + currentindex++ + before = before.Next + current = current.Next + } + if mindis == 1000001{ + return []int{-1,-1} + } + return []int{mindis, beforeindex-startindex} +} +``` + +## day131 2024-07-06 + +### 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. +Given the two positive integers n and time, return the index of the person holding the pillow after time seconds. + +![0706R8U9WzanmoQP](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0706R8U9WzanmoQP.png) + +### 题解 + +本题是简单题, 使用time对n-1(4个人只需传承3次即可到最后一人)求模和求商, 商的奇偶可以决定传递的起点是队首还是队尾, 求模得到的余数可以决定从本次传递起点开始传递了几次. 再使用对应的传递起点计算当前传递到哪个人. +### 代码 +```go +func passThePillow(n int, time int) int { + label := time % (n-1) + dir := time / (n-1) + if dir % 2 == 1{ + return n - label + }else{ + return label + 1 + } +} +``` + +### 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. + +![0706OGiRpS80mHq2](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0706OGiRpS80mHq2.png) + +### 题解 +本题在两个链表中较长的链表上直接加上另一个链表的中对应节点的值并用标志位标记进位. 当一个链表遍历结束时将另一个链表的剩余节点直接连接到链表后面, 注意处理进位. + +### 代码 +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode { + flag := 0 + current1 := l1 + current2 := l2 + head := current1 + for current1.Next != nil && current2.Next != nil{ + current1.Val = current1.Val + current2.Val + flag + if current1.Val >= 10{ + current1.Val -= 10 + flag = 1 + }else{ + flag = 0 + } + current1 = current1.Next + current2 = current2.Next + } + if current1.Next == nil && current2.Next == nil{ + current1.Val = current1.Val + current2.Val + flag + if current1.Val >= 10{ + current1.Val -= 10 + current1.Next = &ListNode{1, nil} + } + return head + }else if current1.Next == nil{ + current1.Val = current1.Val + current2.Val + flag + if current1.Val >= 10{ + current1.Val -= 10 + flag = 1 + }else{ + flag = 0 + } + current1.Next = current2.Next + }else{ + current1.Val = current1.Val + current2.Val + flag + if current1.Val >= 10{ + current1.Val -= 10 + flag = 1 + }else{ + flag = 0 + } + } + current1 = current1.Next + for current1.Next != nil && flag == 1{ + current1.Val += flag + if current1.Val >= 10{ + current1.Val -= 10 + flag = 1 + }else{ + flag = 0 + } + current1 = current1.Next + } + if flag == 1{ + current1.Val += flag + if current1.Val >= 10{ + current1.Val -= 10 + current1.Next = &ListNode{1,nil} + } + } + return head +} +``` +## day132 2024-07-07 +### 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. + +Given the two integers numBottles and numExchange, return the maximum number of water bottles you can drink. + +### 题解 +本题是一道简单题, 和生活关联比较紧密, 只需通过循环不断判断当前手里的饮料喝完后的空瓶子数量能否换新的饮料, 将当前空瓶子个数(由余数和新换的饮料个数加和得到)除以numExchange, 商即为能换的新饮料数量, 保存余数用于下一次兑换饮料. 如此反复直至商为0. 每次将新换的饮料数量都加到最终结果中. + +### 代码 +```go +func numWaterBottles(numBottles int, numExchange int) int { + result := numBottles + current := numBottles + new := numBottles / numExchange + remain := numBottles % numExchange + for new > 0{ + result += new + current = new + remain + new = current / numExchange + remain = current % numExchange + } + return result +} +``` + + +## day133 2024-07-08 +### 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: + +Start at the 1st friend. +Count the next k friends in the clockwise direction including the friend you started at. The counting wraps around the circle and may count some friends more than once. +The last friend you counted leaves the circle and loses the game. +If there is still more than one friend in the circle, go back to step 2 starting from the friend immediately clockwise of the friend who just lost and repeat. +Else, the last friend in the circle wins the game. +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的节点值. 每次根据数到的下标直接删除数组中的这个元素. 直到数组中只有一个元素为止. + +### 代码 +```go +func findTheWinner(n int, k int) int { + length := n + current := -1 + list := []int{} + for i:=1;i<=n;i++{ + list = append(list, i) + } + for length > 1{ + current = (current + k) % length + list = append(list[0:current],list[current+1:]...) + current-- + length-- + } + return list[0] +} +``` + +### 总结 + +本题也可以采用数学方法解决, 理解数学方法的关键还是在于问题的转化, 考虑之前循环队列的解法, 当我们已知队列中有两个人并且计数为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) + +```go +func findTheWinner(n, k int) int { + winner := 1 + for i := 2; i <= n; i++ { + winner = (k+winner-1)%i + 1 + } + return winner +} + +``` + +## day134 2024-07-09 +### 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. + +![0709eXlrAsmWtCid](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0709eXlrAsmWtCid.png) + +### 题解 +本题思路比较清晰, 直接模拟顾客来和等待上菜的过程, 用一个变量保存当前顾客能吃上菜的时间, 继续遍历,判断下一个顾客的到达时间是否在前一个顾客能吃上菜时间之前, 如果在之前, 则该顾客的等待时间为上一个顾客能吃上菜的时间减去当前顾客的到达时间与当前顾客菜的等待时间之和. 否则该顾客只需等待自己的菜品制作时间即可. 此处是典型的动态规划, 只需要保存前一个顾客能吃上菜的时间(之前最后一个顾客能吃上菜的时间)就已经包含了之前所有顾客等菜和菜品制作的信息. 根据前一个顾客的时间判断当前顾客的状态并更新当前最后一个顾客吃上菜的时间. + +### 代码 +```go +func averageWaitingTime(customers [][]int) float64 { + current := 0 + waiting := 0 + for _, customer := range customers{ + if customer[0] >= current{ + waiting += customer[1] + current = customer[0]+customer[1] + }else{ + waiting += current - customer[0] + customer[1] + current = current + customer[1] + } + } + return float64(waiting)/float64(len(customers)) +} +``` +### 总结 +本题其实就是`先来先服务`场景的简单应用. + +## day135 2024-07-10 +### 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: + +"../" : Move to the parent folder of the current folder. (If you are already in the main folder, remain in the same folder). +"./" : Remain in the same folder. +"x/" : Move to the child folder named x (This folder is guaranteed to always exist). +You are given a list of strings logs where logs[i] is the operation performed by the user at the ith step. + +The file system starts in the main folder, then the operations in logs are performed. + +Return the minimum number of operations needed to go back to the main folder after the change folder operations. + +![0710BQdXlyk0sUKW](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0710BQdXlyk0sUKW.png) + +### 题解 +本题为一道简单题, 只需用一个变量记录当前目录的深度, 遍历logs数组, 根据规定的操作修改深度数值即可, `../`即为-1,`x/`即为+1, `./`不变, 注意当深度已经为0时回到父目录的操作使得深度仍然为0. + +### 代码 +```go +func minOperations(logs []string) int { + depth := 0 + for _, op := range logs{ + if op == "../"{ + if depth == 0{ + continue + } + depth-- + }else if op == "./"{ + continue + }else{ + depth++ + } + } + return depth +} +``` +### 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 +func lengthOfLongestSubstring(s string) int { + left := 0 + result := 0 + chars := make(map[byte]int) + for i, _ := range s{ + if chars[s[i]] < 1{ + chars[s[i]] = chars[s[i]]+1 + result = max(result, i-left+1) + }else{ + for left < i && chars[s[i]] >= 1{ + chars[s[left]]-- + left++ + } + chars[s[i]] = 1 + } + } + return result +} + +``` + +## day136 2024-07-11 +### 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. + +![0711Txx0hnCNHwjS](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0711Txx0hnCNHwjS.png) + +### 题解 +本题要将每个括号内的字符串都翻转, 则字符串被翻转偶数次后仍为原来的字符串本身, 因此可以用一个栈表示当前已经读入的待匹配的左括号和字符. 当匹配到右括号时, 就翻转匹配的左右括号之间的字符构成新的字符串, 并将这个字符串放入栈中, 继续向下匹配, 当再次匹配到右括号时, 重复此操作直到到字符串末尾为止, 即使用栈模拟整个字符串对应的括号内的翻转过程. + +### 代码 +```go +func reverseParentheses(s string) string { + var stack []string + length := 0 + + for _, char := range s { + if char == '(' { + stack = append(stack, "(") + length++ + } else if char == ')' { + var rev strings.Builder + for length > 0 && stack[length-1] != "(" { + fmt.Println(length) + str := stack[length-1] + stack = stack[:length-1] + length-- + rev.WriteString(reverse(str)) + } + 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 +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. +For example, when removing "ab" from "cabxbae" it becomes "cxbae". +Remove substring "ba" and gain y points. +For example, when removing "ba" from "cabxbae" it becomes "cabxe". +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值更大同理. + +### 代码 +```go +func maximumGain(s string, x int, y int) int { + cont := false + start := 0 + result := 0 + for i,_ := range s{ + if (s[i] == 'a' || s[i] == 'b'){ + if !cont{ + cont = true + start = i + } + }else{ + if cont{ + result += caculate(s[start:i],x,y) + cont = false + } + } + } + if cont{ + result += caculate(s[start:],x,y) + } + return result +} + +func caculate(s string,x int,y int)int{ + result := 0 + bignumber := 0 + smallnumber := 0 + var stacktarget byte + if x > y{ + stacktarget = 'a' + bignumber = x + smallnumber = y + }else{ + stacktarget = 'b' + bignumber = y + smallnumber = x + } + stack := 0 + front := 0 + for i,_ := range s{ + if s[i] == stacktarget{ + stack++ + }else{ + if stack > 0{ + stack-- + result += bignumber + }else{ + front++ + } + } + } + result += min(front, stack) * smallnumber + return result +} +``` + +## day138 2024-07-13 +### 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. + +All robots start moving on the line simultaneously at the same speed in their given directions. If two robots ever share the same position while moving, they will collide. + +If two robots collide, the robot with lower health is removed from the line, and the health of the other robot decreases by one. The surviving robot continues in the same direction it was going. If both robots have the same health, they are both removed from the line. + +Your task is to determine the health of the robots that survive the collisions, in the same order that the robots were given, i.e. final heath of robot 1 (if survived), final health of robot 2 (if survived), and so on. If there are no survivors, return an empty array. + +Return an array containing the health of the remaining robots (in the order they were given in the input), after no further collisions can occur. + +Note: The positions may be unsorted. + +![0713WjsqGNYwxVip](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0713WjsqGNYwxVip.png) + +### 题解 +本题是一道难题, 但核心算法只要熟悉栈操作即可, 本题机器人的位置并不是排好序的, 将机器人的编号和其位置, 方向, 血量绑定在同一个结构体后, 根据位置对结构体排序. 建立一个新栈(使用数组实现), 遍历机器人, 若机器人是向右运行的直接入栈. 向左则判断栈是否为空, 如果不为空则根据栈顶机器人的不同状态做出不同的操作, 若栈顶机器人运行方向向右且血量高于当前机器人, 则将栈顶机器人血量-1, 否则将当前机器人血量-1并弹出栈顶, 继续根据新栈顶做出不同操作. 直到栈顶机器人运行方向向左或者栈为空为止. 此时将当前机器人入栈. + +### 代码 +```go +func survivedRobotsHealths(positions []int, healths []int, directions string) []int { + type Robot struct{ + position int + health int + direction byte + index int + } + + robots := []Robot{} + + for i,value := range positions{ + newrobot := Robot{value, healths[i], directions[i],i+1} + robots = append(robots, newrobot) + } + + sort.Slice(robots, func(i, j int)bool{ + return robots[i].position < robots[j].position + }) + + stack := []Robot{} + + for _, robot := range robots{ + if robot.direction == 'R'{ + stack = append(stack, robot) + }else{ + length := len(stack) + for length > 0 && stack[length-1].direction == 'R' && robot.health > stack[length-1].health{ + robot.health-- + stack = stack[:length-1] + length-- + } + if length > 0{ + if stack[length-1].direction == 'R' && robot.health == stack[length-1].health{ + stack = stack[:length-1] + }else if stack[length-1].direction == 'R' { + stack[length-1].health-- + }else{ + stack = append(stack, robot) + } + }else{ + stack = append(stack, robot) + } + } + } + sort.Slice(stack, func(i,j int)bool{ + return stack[i].index < stack[j].index + }) + + result := []int{} + for _,value := range stack{ + result = append(result, value.health) + } + return result +} +``` + +## day139 2024-07-14 +### 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. + +One or more digits representing that element's count may follow if the count is greater than 1. If the count is 1, no digits will follow. + +For example, "H2O" and "H2O2" are possible, but "H1O2" is impossible. +Two formulas are concatenated together to produce another formula. + +For example, "H2O2He3Mg4" is also a formula. +A formula placed in parentheses, and a count (optionally added) is also a formula. + +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. + +![0714e2kIjx5sE9Oo](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0714e2kIjx5sE9Oo.png) + +### 题解 +本题是一道难题, 关键在于如何处理括号, 从括号匹配问题开始, 对于这种有括号的问题, 栈一直是解决问题的核心. 括号代表着优先级的变化, 而栈的先入后出特性其实包含了隐含的优先级, 即后入栈的先出(优先级更高), 这与括号代表的优先级恰好一致(内层括号的优先级更高, 要先算). 本题通过使用栈将全部括号展开, 遇到左括号将其入栈, 继续遍历读入原子和对应的个数, 直到遇到右括号从栈顶向下遍历并将原子个数乘以右括号后面的数字, 直到遇到左括号为止, 删掉左括号. 这不是一个标准的栈操作, 但思想上仍是栈的思想. 将全部括号展开后, 从头遍历无括号的原子, 将相同原子个数合并最后输出. + +### 代码 +```go +func countOfAtoms(formula string) string { + type atom struct { + name string + count int +} + stack := []atom{} + length := len(formula) + i := 0 + + for i < length { + if formula[i] == '(' { + stack = append(stack, atom{name: "(", count: 0}) + i++ + } else if formula[i] == ')' { + i++ + start := i + for i < length && unicode.IsDigit(rune(formula[i])) { + i++ + } + multiplier := 1 + if start < i { + multiplier, _ = strconv.Atoi(formula[start:i]) + } + j := 0 + for j = len(stack) - 1; j >= 0 && stack[j].name != "("; j-- { + stack[j].count *= multiplier + } + stack = append(stack[0:j],stack[j+1:]...) + // Remove the '(' from the stack + + } else { + start := i + i++ + for i < length && unicode.IsLower(rune(formula[i])) { + i++ + } + name := formula[start:i] + start = i + for i < length && unicode.IsDigit(rune(formula[i])) { + i++ + } + count := 1 + if start < i { + count, _ = strconv.Atoi(formula[start:i]) + } + stack = append(stack, atom{name: name, count: count}) + } + } + + atomCount := map[string]int{} + for _, a := range stack { + atomCount[a.name] += a.count + } + + keys := make([]string, 0, len(atomCount)) + for k := range atomCount { + keys = append(keys, k) + } + sort.Strings(keys) + + var result strings.Builder + for _, key := range keys { + result.WriteString(key) + if atomCount[key] > 1 { + result.WriteString(strconv.Itoa(atomCount[key])) + } + } + + return result.String() +} +``` + +## day140 2024-07-15 +### 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, + +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. + +![0715MS1tzkYMjKZ4](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0715MS1tzkYMjKZ4.png) + +### 题解 +本题先构建二叉树, 再找到二叉树的根. 构建二叉树并不困难, 可以将每个节点的指针都保存在map中, key为节点的值, value为节点的指针. 再根据描述构建每个节点. 找二叉树的根可以在构建二叉树的同时使用类似并查集的思想, 将当前描述的孩子节点的值作为下标, 父节点的值作为下标对应的值, 构造一个father数组. 若父节点作为下标之前没有指向任何值, 则将父节点的下标处的值指向父节点自身的值. 最终从任意一个节点值开始, 在father数组中查看其作为下标的值, 直到该下标处的值指向自身即为根节点的节点值. 返回根节点. + + +### 代码 +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func createBinaryTree(descriptions [][]int) *TreeNode { + trees := map[int]*TreeNode{} + father := make([]int, 100001) + for _,des := range descriptions{ + if father[des[0]] == 0{ + father[des[0]] = des[0] + } + father[des[1]] = des[0] + var child *TreeNode + if trees[des[1]] == nil{ + child = &TreeNode{des[1], nil, nil} + trees[des[1]] = child + }else{ + child = trees[des[1]] + } + if trees[des[0]] == nil{ + if des[2] == 1{ + trees[des[0]] = &TreeNode{des[0], child, nil} + }else{ + trees[des[0]] = &TreeNode{des[0], nil, child} + } + + }else{ + if des[2] == 1{ + trees[des[0]].Left = child + }else{ + trees[des[0]].Right = child + } + } + } + i := descriptions[0][0] + for father[i] != i{ + i = father[i] + } + return trees[i] +} + +``` +## day141 2024-07-16 +### 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. + +Find the shortest path starting from node s and ending at node t. Generate step-by-step directions of such path as a string consisting of only the uppercase letters 'L', 'R', and 'U'. Each letter indicates a specific direction: + +'L' means to go from a node to its left child node. +'R' means to go from a node to its right child node. +'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) + +### 题解 + +本题正常遍历二叉树, 根据遍历过程记录'L','R'直到分别找到起点和终点为止. 这里可以使用先根遍历, 如果根即为目标节点, 则设置对应的路径变量, 因为这里有两个目标节点, 需使用dfs查找两次分别找到到两个目标点的路径, 遍历过程中将之前经过的路径作为参数传递给递归函数. 这里使用字符数组要比使用字符串快得多. + +得到起点和终点的路径后, 同时从头遍历二者的路径, 直到找到路径中的第一个不同点, 此时获取到达起点剩余路径的长度, 构造相同长度的重复'U'字符串并与终点的剩余路径连接即得到从起点到终点的路径. + + +### 代码 +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func getDirections(root *TreeNode, startValue int, destValue int) string { + arrToS := dfs(root, []byte{}, startValue) + arrToD := dfs(root, []byte{}, destValue) + i := 0 + for len(arrToS) > i && len(arrToD) > i && arrToS[i] == arrToD[i] { + i++ + } + pathUp := strings.Repeat("U", len(arrToS)-i) + pathDown := string(arrToD[i:]) + return pathUp + pathDown +} + +func dfs(root *TreeNode, arr []byte, target int) []byte { + if root.Val == target { + return arr + } + if root.Left != nil { + newArr := append(arr, 'L') + if found := dfs(root.Left, newArr, target); found != nil { + return found + } + } + if root.Right != nil { + newArr := append(arr, 'R') + if found := dfs(root.Right, newArr, target); found != nil { + return found + } + } + return nil +} + +``` +