--- title: leetcode everyday author: Logic date: 2024-02-27 categories: [""] tags: [] draft: false --- ## day 1 2024-02-27 ### 543 Diameter of Binary Tree Given the root of a binary tree, return the length of the diameter of the tree. The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root. The length of a path between two nodes is represented by the number of edges between them. ![0227W27EaKxHpjd2](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0227W27EaKxHpjd2.png) ### 题解 读题, 题目要求寻找二叉树中任意两节点之间的最大距离. 这时这个二叉树更像是一个图的性质而不是树, 即寻找图中任意两节点之间的最大距离. 考虑任意一点到另一点的距离等于其到另一点的父节点的距离减一, 则使用一个二维数组保存每个节点的两个属性: 1. 以该节点为根节点且经过该节点的最大直径 2. 以该节点为根节点的子树中叶子节点到该节点的最大距离 属性1可以通过将该节点的两个子节点的属性2加和并加1来计算. 属性2取两个子节点属性2的最大值并加1来计算. 最后遍历数组求得数组中属性1的最大值即可. 有一点动态规划的思想. 题目中并没有提供二叉树的节点总数, 则可以使用动态创建的方法, 在遍历过程中每遇到新节点就在二维数组中增加一项. 这里使用递归来对树进行后序遍历, 对空节点, 设置其属性2的值为-1, 这样保证叶子节点的属性2的值为0. 解题时遇到一个小bug, 使用了一个全局切片(数组)来保存变量时, 第一个测试用例的数据会保留到测试第二个测试用例的过程中, 这大概是因为leetcode的测试是对每个用例直接调用给出的解题入口函数, 因此需要在解题函数中将使用的全局变量初始化一下, 将数组设置为空后问题得到解决. ### 代码 ```go /** * Definition for a binary tree node. * type TreeNode struct { * Val int * Left *TreeNode * Right *TreeNode * } */ var length [][]int func diameterOfBinaryTree(root *TreeNode) int { length = nil _ = postorder(root) max := -1 for _, value := range length{ if value[0] > max{ max = value[0] } } return max } func postorder(father *TreeNode) int { if father != nil{ len1 := postorder(father.Left) len2 := postorder(father.Right) len := make([]int,2) // find the max diameter pass through current node from the tree rooted current node len[0] = len1 + len2 + 2 len[1] = max(len1, len2) + 1 length = append(length, len) return len[1] } else { return -1 } } ``` ## day2 2024-02-28 ### 513. Find Bottom Left Tree Value Given the root of a binary tree, return the leftmost value in the last row of the tree. ![0228z98bIXZ3aQeQ](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0228z98bIXZ3aQeQ.png) ### 题解 找到二叉树最底层最左边的节点值, 用层序遍历遍历到最后一层找到最后一层最左边节点的值即可. 实现层序遍历可以使用一个队列, 将当前层节点的所有子节点入队后将当前层节点出队. 在go中可以使用切片实现一个队列. 使用一个标记变量记录当前层所有节点是否有子节点, 若无子节点则当前层为最低层, 返回当前层最左侧节点的值(此时队列中第一个节点的值). ### 代码 ```go /** * Definition for a binary tree node. * type TreeNode struct { * Val int * Left *TreeNode * Right *TreeNode * } */ func findBottomLeftValue(root *TreeNode) int { queue := []*TreeNode{root} lowest := false key := -1 var father *TreeNode for !lowest{ queue = queue[key+1:] lowest = true for key, father = range queue{ if(father.Left != nil || father.Right != nil){ lowest = false if(father.Left != nil){ queue = append(queue, father.Left) } if(father.Right != nil){ queue = append(queue, father.Right) } } } } return queue[0].Val } ``` ### 总结 在题解中看到了一个使用深度优先搜索的方法, 记录当前搜索到的层级, 始终保存最大层级的第一个被搜索到的值, 因为使用的是后序遍历, 则每次遇到的当前层大于保存的最大层级时, 该节点就为新的最大层级的第一个节点, 即题目中要求的最左值(leftmost). 算法时间复杂度为O(n)------只遍历一次所有节点. ## day3 2024-02-29 ### 1609. Even Odd Tree A binary tree is named Even-Odd if it meets the following conditions: The root of the binary tree is at level index 0, its children are at level index 1, their children are at level index 2, etc. For every even-indexed level, all nodes at the level have odd integer values in strictly increasing order (from left to right). For every odd-indexed level, all nodes at the level have even integer values in strictly decreasing order (from left to right). Given the root of a binary tree, return true if the binary tree is Even-Odd, otherwise return false. ![0229uX1ZoOdkFEtk](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0229uX1ZoOdkFEtk.png) ### 题解 对二叉树的奇数层, 其节点从左到右是严格递减的(意味着有两个节点的值相同是不允许的), 偶数层是严格递增的. 仍然可以使用昨天题目的层序遍历的方法, 增加一个level变量记录当前层数, 对该层内的节点值进行判断是否符合奇数层和偶数层对应的条件即可. ### 代码 ```go /** * Definition for a binary tree node. * type TreeNode struct { * Val int * Left *TreeNode * Right *TreeNode * } */ func isEvenOddTree(root *TreeNode) bool { level := 0 index := -1 var value *TreeNode queue := []*TreeNode{root} saved := 0 for len(queue) != 0{ // remove visited nodes queue = queue[index+1:] index = 0 if(level%2 == 0){ for index, value = range queue{ if(index == 0){ saved = value.Val if(saved %2 != 1){return false} } else{ if(value.Val <= saved || (value.Val%2) != 1){ return false } else{ saved = value.Val } } if(value.Left != nil){queue = append(queue, value.Left)} if(value.Right != nil){queue = append(queue, value.Right)} } level++ } else{ for index, value = range queue{ if(index == 0){ saved = value.Val if(saved %2 != 0){return false} } else{ if(value.Val >= saved || value.Val %2 != 0){ return false } else{ saved = value.Val } } if(value.Left != nil){queue = append(queue, value.Left)} if(value.Right != nil){queue = append(queue, value.Right)} } level++ } } return true } ``` ### 总结 go语言中的for range循环, 如果使用类似`for key, value := range list` 的形式, 那么key, value这两个变量都会在当前作用域下新建, 意味着即使在前面定义了key, key的值在循环结束后也不会被修改. 若想修改之前定义的key值, 需要将value也提前定义好并使用`=`而不是`:=`. go语言中的for range循环时如果使用`:=`会新建两个变量, 然后将slice中的值复制给value变量, 将对应的index值赋值给key变量, 这意味着value变量不会指向数组中对应位置的地址, 而是一个不变的单独地址. ## day4 2024-03-01 ### 2864. Maximum Odd Binary number You are given a binary string s that contains at least one '1'. You have to rearrange the bits in such a way that the resulting binary number is the maximum odd binary number that can be created from this combination. Return a string representing the maximum odd binary number that can be created from the given combination. Note that the resulting string can have leading zeros. Example 1: > Input: s = "010" > Output: "001" > Explanation: Because there is just one '1', it must be in the last position. So the answer is "001". Example 2: > Input: s = "0101" > Output: "1001" > Explanation: One of the '1's must be in the last position. The maximum number that can be made with the remaining digits is "100". So the answer is "1001". ### 题解 题目中说明了给出的字符串中至少有一个1, 因此可以复制一个字符串, 然后遍历原字符串, 遇到第一个1放在最后一位, 0永远插入到倒数第二位, 不是第一个1放在字符串最前面. 由此除保证字符串是奇数的最后一个1以外, 其余的1都在字符串最前面, 其余的0都插入在最前面的一串1和最后的1之间. 保证了字符串是最大的奇数字符串. ### 代码 ```go func maximumOddBinaryNumber(s string) string { s_copy := "" flag := 0 for _, value := range s{ if value == '1'{ if flag != 0{ s_copy = "1" + s_copy } else{ s_copy = s_copy + "1" flag = 1 } } else{ if(len(s_copy) >=2){ s_copy = string(s_copy[:len(s_copy)-1]) + "0" + string(s_copy[len(s_copy)-1]) } else { s_copy = "0" + s_copy } } } return s_copy } ``` ### 总结 在处理字符串的时候像中间某个位置插入字符也要使用双引号, 如插入字符0要用`+"0"`而不是`+'0'`, 此外在截取切片的时候go的切片是左闭右开的. 如[0:3]截取的是0,1,2三个数 ## day5 2024-03-02 ### 977. Squares of a Sorted Array Given an integer array nums sorted in non-decreasing order, return an array of the squares of each number sorted in non-decreasing order. Example 1: Input: nums = [-4,-1,0,3,10] Output: [0,1,9,16,100] Explanation: After squaring, the array becomes [16,1,0,9,100]. After sorting, it becomes [0,1,9,16,100]. Example 2: Input: nums = [-7,-3,2,3,11] Output: [4,9,9,49,121] ### 题解 考虑原数组已经按照非递减排序的情况下, 找到数组中正负交界处元素, 即数组中第一个正数, 以该位置作为起始位置, 使用双指针法, 分别向前和向后遍历数组, 遍历时不断比较两个指针指向的数字的绝对值大小, 将绝对值小的数字平方后追加到结果数组的尾部, 遍历完成即可完成平方值排序. 这样只需要遍历一遍数组即可完成排序. ### 代码 ```go func sortedSquares(nums []int) []int { index := 0 value := nums[0] var result []int for index, value = range nums{ if(value >= 0){ break } } backward := index - 1 forward := index if index != 0{ for _,_ = range nums{ if backward<0{ result = append(result, nums[forward]*nums[forward]) forward++ } else if forward>= len(nums){ result = append(result, nums[backward] * nums[backward]) backward-- } else{ if(abs(nums[forward]) < abs(nums[backward])){ result = append(result, nums[forward]*nums[forward]) forward++ } else{ result = append(result, nums[backward] * nums[backward]) backward-- } } } }else{ for _,_ = range nums{ result = append(result, nums[forward]*nums[forward]) forward++ } } return result } func abs(val int) int { if(val < 0){ return -val }else{ return val } } ``` ### 总结 注意一个go的语法问题, 如果在for range循环中两个变量都使用匿名变量, 则应该使用赋值运算符而不是创建并赋值运算符, 即`for _,_ = range slice` 而不是`for _,_ := range slice`. 这很可能是因为匿名变量默认为已经创建好的变量, 不需要再创建匿名变量本身了. ## day6 2024-03-03 ### 19. Remove Nth Node From End of List Given the head of a linked list, remove the nth node from the end of the list and return its head. ![0303SQETJiUaYzaQ](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0303SQETJiUaYzaQ.png) ### 题解 给出了链表头, 要求移除从后到前的第n个节点. 如果想一遍遍历就完成这个任务. 就使用空间换时间, 使用一个数组保存所有的Next指针的值. 然后让倒数第n+1个元素的Next指针指向n个元素的Next指针即可, 注意处理链表只有一个元素的特殊情况. ### 代码 ```go /** * Definition for singly-linked list. * type ListNode struct { * Val int * Next *ListNode * } */ func removeNthFromEnd(head *ListNode, n int) *ListNode { var ptr []*ListNode current := head for current.Next != nil{ ptr = append(ptr, current) current = current.Next } ptr = append(ptr, current) if(len(ptr) == 1){ return nil }else if len(ptr) == n{ return ptr[1] }else{ ptr[len(ptr)-n-1].Next = ptr[len(ptr)-n].Next return head } } ``` ### 总结 在题解中看到大部分使用的是快慢指针的解法, 快慢指针应该是本题想要的解法, 下面贴一个快慢指针的解法示例 ```go /** * Definition for singly-linked list. * type ListNode struct { * Val int * Next *ListNode * } */ func removeNthFromEnd(head *ListNode, n int) *ListNode { dummyHead := &ListNode{-1, head} cur, prevOfRemoval := dummyHead, dummyHead for cur.Next != nil{ // n step delay for prevOfRemoval if n <= 0 { prevOfRemoval = prevOfRemoval.Next } cur = cur.Next n -= 1 } // Remove the N-th node from end of list nthNode := prevOfRemoval.Next prevOfRemoval.Next = nthNode.Next return dummyHead.Next } ``` ## day7 2024-03-04 ### 948. Bag of Tokens You start with an initial power of power, an initial score of 0, and a bag of tokens given as an integer array tokens, where each tokens[i] donates the value of tokeni. Your goal is to maximize the total score by strategically playing these tokens. In one move, you can play an unplayed token in one of the two ways (but not both for the same token): **Face-up**: If your current power is at least tokens[i], you may play tokeni, losing tokens[i] power and gaining 1 score. **Face-down**: If your current score is at least 1, you may play tokeni, gaining tokens[i] power and losing 1 score. Return the maximum possible score you can achieve after playing any number of tokens. Example 1: Input: tokens = [100], power = 50 Output: 0 Explanation: Since your score is 0 initially, you cannot play the token face-down. You also cannot play it face-up since your power (50) is less than tokens[0] (100). Example 2: Input: tokens = [200,100], power = 150 Output: 1 Explanation: Play token1 (100) face-up, reducing your power to 50 and increasing your score to 1. There is no need to play token0, since you cannot play it face-up to add to your score. The maximum score achievable is 1. Example 3: Input: tokens = [100,200,300,400], power = 200 Output: 2 Explanation: Play the tokens in this order to get a score of 2: Play token0 (100) face-up, reducing power to 100 and increasing score to 1. Play token3 (400) face-down, increasing power to 500 and reducing score to 0. Play token1 (200) face-up, reducing power to 300 and increasing score to 1. Play token2 (300) face-up, reducing power to 0 and increasing score to 2. The maximum score achievable is 2. ### 题解 本题的目的是最大化score. 一个基本的策略就是通过小的power换取score, 通过score换取大的power, 利用换到的大power赚取中间水平的token的score. 关键在于, 如何找到最大能换取的score. 首先考虑每次进行一次Face-up和Face-down, score没有变化, 只有power增大了, 那么每次都将score置0, 并判断当前能获得的最大score即可. 通过前面的分析可以得出, 算法分为以下几步 1. 将tokens数组排序 2. 判断power是否大于tokens[0], 即最小的token, 若大于, 则计算当前能获得的最大score 3. 将power的值增加目前tokens数组中最大值和最小值(即排好序后的最后一项和第一项)的差值, 同时将tokens数组中第一项和最后一项移除. 重复2, 3步直到power小于tokens[0]或tokens数组长度为0中止. 4. 返回最大的score ### 代码 ```go func bagOfTokensScore(tokens []int, power int) int { sort.Ints(tokens) var powernormal []int remain := power score := 0 powernormal = append(powernormal, score) for len(tokens) != 0 && power > tokens[0]{ remain = power for _, value := range tokens{ if power >= value{ score++ power -= value } else{ break } } powernormal = append(powernormal, score) score = 0 remain += tokens[len(tokens)-1] - tokens[0] if len(tokens) <= 1{ break } tokens = tokens[1:len(tokens)-1] power = remain } sort.Ints(powernormal) return powernormal[len(powernormal)-1] } ``` ### 总结 在实现过程中, 起初使用一个数组来保存每次的score值, 这样空间复杂度略大, 后来查看他人代码, 发现只需要一个max变量来保存当前最大的score值, 并在每次循环计算当前轮次的score值时与当前的最大值比较并根据二者大小更新max变量的值即可, 这样只需要O(1)的空间复杂度. ## day8 2024-03-05 ### 1750. Minimum Length of String After Deleting Similar Ends Given a string s consisting only of characters 'a', 'b', and 'c'. You are asked to apply the following algorithm on the string any number of times: 1. Pick a non-empty prefix from the string s where all the characters in the prefix are equal. 2. Pick a non-empty suffix from the string s where all the characters in this suffix are equal. 3. The prefix and the suffix should not intersect at any index. 4. The characters from the prefix and suffix must be the same. 5. Delete both the prefix and the suffix. Return the minimum length of s after performing the above operation any number of times (possibly zero times). ![0305Qc2qNiDBOKRk](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0305Qc2qNiDBOKRk.png) ### 题解 本题的题目略显复杂, 读起来乍一看让人不明所以, 实际上题目的目标即为从字符串的前后同时删除相同的字符, 删除时只要是相同的就全部删除, 如最前面有一个a最后面有3个a则同时将这四个a删除. 删除的字符串下标不能相交. 思路比较简单, 判断字符串的前后字符是否相同, 然后删除前后的连续相同字符即可, 注意下标不要重叠. 同时注意边界情况, 字符串只有一个字符的情况单独处理一下(直接返回1). ### 代码 ```go func minimumLength(s string) int { forward := 0 backward := 0 for s[0] == s[len(s)-1]{ if len(s) == 1{ return 1 } else { forward = 0 backward = len(s)-1 for forwardforward && s[backward-1] == s[backward] { backward-- } if forward == backward{ return 0 } s = s[forward+1:backward] } } return len(s) } ``` ### 总结 本题值得注意的地方在于for循环中条件的设置, 可能会忽略与运算符两侧条件表达式的前后顺序, 但由于短路机制的存在, 与运算符两侧表达式的前后顺序有时非常重要, 例如在本题中, 如果将forward Input: nums = [1,2,2,3,1,4] > Output: 4 > Explanation: The elements 1 and 2 have a frequency of 2 which is the maximum frequency in the array. > So the number of elements in the array with maximum frequency is 4. Example 2: > Input: nums = [1,2,3,4,5] > Output: 5 > Explanation: All elements of the array have a frequency of 1 which is the maximum. > So the number of elements in the array with maximum frequency is 5. ### 题解 因为题目给出了正整数的范围为1-100, 因此本题可以用简单的数组来解决, 数组下标表示对应的整数, 0不做任何表示. 然后遍历数组将频率最多的元素相加即可. 可以设置一个max标志位来表示当前的最大频率, 相等则增加和, 比max大则将和重置并设max为新的最大值. ### 代码 ```go func maxFrequencyElements(nums []int) int { frequency := make([]int, 101) for _,value := range nums{ frequency[value]++ } max := 0 sum := 0 for _,value := range frequency{ if value > max{ max = value sum = max } else if value == max{ sum += value }else{ continue } } return sum } ``` ### 总结 本题注意考查数据范围, 在数据范围有限的情况下直接使用数组要比哈希表快得多. ## day12 2024-03-09 ### 2540. Minimum Common Value Given two integer arrays nums1 and nums2, sorted in non-decreasing order, return the minimum integer common to both arrays. If there is no common integer amongst nums1 and nums2, return -1. Note that an integer is said to be common to nums1 and nums2 if both arrays have at least one occurrence of that integer. Example 1: > Input: nums1 = [1,2,3], nums2 = [2,4] > Output: 2 > Explanation: The smallest element common to both arrays is 2, so we return 2. Example 2: > Input: nums1 = [1,2,3,6], nums2 = [2,3,4,5] > Output: 2 > Explanation: There are two common elements in the array 2 and 3 out of which 2 is the smallest, so 2 is returned. ### 题解 本题使用两个指针分别指向两个数组, 然后依次移动两个指针比较大小即可, 位于数值更小的位置的数组指针向前移动直到相等或者到数组末尾为止. ### 代码 ```go func getCommon(nums1 []int, nums2 []int) int { index1, index2 := 0 , 0 for index1 Input: nums1 = [1,2,2,1], nums2 = [2,2] > Output: [2] Example 2: > Input: nums1 = [4,9,5], nums2 = [9,4,9,8,4] > Output: [9,4] > Explanation: [4,9] is also accepted. ### 题解 因为数组是无序数组, 寻找二者的相同元素比较困难, 故可先对两数组排序, 然后双指针遍历两个数组找到数组中的相同值. 将值作为key, key对应的value为true放入map中. 这里不需要多余判断map中是否已经存在这个key了, 因为再次给相同的key赋值不会增加新的条目, 而只是覆盖之前的key的值, 我们只需要key来判断map中是否有相同值. ### 代码 ```go func intersection(nums1 []int, nums2 []int) []int { intersection := make(map[int]bool) sort.Ints(nums1) sort.Ints(nums2) index1, index2 := 0,0 for index1 < len(nums1) && index2 < len(nums2){ if nums1[index1] < nums2[index2]{ index1++ }else if nums1[index1] > nums2[index2]{ index2++ }else{ _, ok := intersection[nums1[index1]] if !ok{ intersection[nums1[index1]] = true } index1++ index2++ } } var result []int for key, _ := range intersection{ result = append(result, key) } return result } ``` ### 总结 在查看他人题解过程中, 发现排序其实是没有必要的, 可以直接将一个数组中的值全部作为key, 对应的value为true放入map中. 然后遍历另外一个数组, 同时判断当前遍历的元素在不在map中, 若存在则将其放入结果数组中, 同时将map中key对应的value置为false, 表示该key已经被访问过, 这样可以避免在结果数组中添加重复元素. 一个示例代码如下 ```go func intersection(nums1 []int, nums2 []int) []int { res := make([]int, 0) m := make(map[int]bool, len(nums1)) for _, v := range nums1 { if _, exists := m[v]; exists { continue } m[v] = false } for _, v := range nums2 { used, exists := m[v] if exists && !used { res = append(res, v) m[v] = true } } return res } ``` ## day14 2024-03-11 ### 791. Custom Sort String You are given two strings order and s. All the characters of order are unique and were sorted in some custom order previously. Permute the characters of s so that they match the order that order was sorted. More specifically, if a character x occurs before a character y in order, then x should occur before y in the permuted string. Return any permutation of s that satisfies this property. ![0311fLXVhVrYfXgA](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0311fLXVhVrYfXgA.png) ### 题解 本题初始想先扫描s字符串, 使用一个map记录字符串中各个字符的数量, 再遍历order依次将字符按数量附加到末尾即可. 但考虑到字符只有26个小写英文字母, 使用一个长度为26的数组来保存对应位置的英文字母的数量. 再遍历要比map速度快. ### 代码 ```go func customSortString(order string, s string) string { a := 'a' result := "" numbers := make([]int, 26) for _, character := range s{ numbers[character-a]++ } for _,c := range order{ temp := numbers[c-a] for i:=0;i