60 KiB
title | author | date | categories | tags | draft | |
---|---|---|---|---|---|---|
leetcode everyday | Logic | 2024-02-27 | 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.
题解
读题, 题目要求寻找二叉树中任意两节点之间的最大距离. 这时这个二叉树更像是一个图的性质而不是树, 即寻找图中任意两节点之间的最大距离. 考虑任意一点到另一点的距离等于其到另一点的父节点的距离减一, 则使用一个二维数组保存每个节点的两个属性:
- 以该节点为根节点且经过该节点的最大直径
- 以该节点为根节点的子树中叶子节点到该节点的最大距离
属性1可以通过将该节点的两个子节点的属性2加和并加1来计算. 属性2取两个子节点属性2的最大值并加1来计算. 最后遍历数组求得数组中属性1的最大值即可. 有一点动态规划的思想.
题目中并没有提供二叉树的节点总数, 则可以使用动态创建的方法, 在遍历过程中每遇到新节点就在二维数组中增加一项. 这里使用递归来对树进行后序遍历, 对空节点, 设置其属性2的值为-1, 这样保证叶子节点的属性2的值为0.
解题时遇到一个小bug, 使用了一个全局切片(数组)来保存变量时, 第一个测试用例的数据会保留到测试第二个测试用例的过程中, 这大概是因为leetcode的测试是对每个用例直接调用给出的解题入口函数, 因此需要在解题函数中将使用的全局变量初始化一下, 将数组设置为空后问题得到解决.
代码
/**
* 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.
题解
找到二叉树最底层最左边的节点值, 用层序遍历遍历到最后一层找到最后一层最左边节点的值即可. 实现层序遍历可以使用一个队列, 将当前层节点的所有子节点入队后将当前层节点出队. 在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.
题解
对二叉树的奇数层, 其节点从左到右是严格递减的(意味着有两个节点的值相同是不允许的), 偶数层是严格递增的. 仍然可以使用昨天题目的层序遍历的方法, 增加一个level变量记录当前层数, 对该层内的节点值进行判断是否符合奇数层和偶数层对应的条件即可.
代码
/**
* 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之间. 保证了字符串是最大的奇数字符串.
代码
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]
题解
考虑原数组已经按照非递减排序的情况下, 找到数组中正负交界处元素, 即数组中第一个正数, 以该位置作为起始位置, 使用双指针法, 分别向前和向后遍历数组, 遍历时不断比较两个指针指向的数字的绝对值大小, 将绝对值小的数字平方后追加到结果数组的尾部, 遍历完成即可完成平方值排序. 这样只需要遍历一遍数组即可完成排序.
代码
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.
题解
给出了链表头, 要求移除从后到前的第n个节点. 如果想一遍遍历就完成这个任务. 就使用空间换时间, 使用一个数组保存所有的Next指针的值. 然后让倒数第n+1个元素的Next指针指向n个元素的Next指针即可, 注意处理链表只有一个元素的特殊情况.
代码
/**
* 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
}
}
总结
在题解中看到大部分使用的是快慢指针的解法, 快慢指针应该是本题想要的解法, 下面贴一个快慢指针的解法示例
/**
* 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即可.
通过前面的分析可以得出, 算法分为以下几步
- 将tokens数组排序
- 判断power是否大于tokens[0], 即最小的token, 若大于, 则计算当前能获得的最大score
- 将power的值增加目前tokens数组中最大值和最小值(即排好序后的最后一项和第一项)的差值, 同时将tokens数组中第一项和最后一项移除. 重复2, 3步直到power小于tokens[0]或tokens数组长度为0中止.
- 返回最大的score
代码
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:
- Pick a non-empty prefix from the string s where all the characters in the prefix are equal.
- Pick a non-empty suffix from the string s where all the characters in this suffix are equal.
- The prefix and the suffix should not intersect at any index.
- The characters from the prefix and suffix must be the same.
- 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).
题解
本题的题目略显复杂, 读起来乍一看让人不明所以, 实际上题目的目标即为从字符串的前后同时删除相同的字符, 删除时只要是相同的就全部删除, 如最前面有一个a最后面有3个a则同时将这四个a删除. 删除的字符串下标不能相交. 思路比较简单, 判断字符串的前后字符是否相同, 然后删除前后的连续相同字符即可, 注意下标不要重叠. 同时注意边界情况, 字符串只有一个字符的情况单独处理一下(直接返回1).
代码
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 forward<backward && s[forward+1] == s[forward] {
forward++
}
for backward>forward && s[backward-1] == s[backward] {
backward--
}
if forward == backward{
return 0
}
s = s[forward+1:backward]
}
}
return len(s)
}
总结
本题值得注意的地方在于for循环中条件的设置, 可能会忽略与运算符两侧条件表达式的前后顺序, 但由于短路机制的存在, 与运算符两侧表达式的前后顺序有时非常重要, 例如在本题中, 如果将forward<backward条件设置在前面, 则当forward到达数组的边界时会直接退出循环, 但若将相等判断放在前面, 会因为当forward到达数组边界时forward+1下标越界而出错.
day9 2024-03-06
141. Linked List Cycle
Given head, the head of a linked list, determine if the linked list has a cycle in it.
There is a cycle in a linked list if there is some node in the list that can be reached again by continuously following the next pointer. Internally, pos is used to denote the index of the node that tail's next pointer is connected to. Note that pos is not passed as a parameter.
Return true if there is a cycle in the linked list. Otherwise, return false.
题解
本题最简单的思路就是使用并查集, 因为go语言中的map类型在获取数据时会返回该键值在map中是否存在, 因此可以将map类型直接当作一个简单的并查集使用.
代码
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func hasCycle(head *ListNode) bool {
if head==nil{
return false
}
ptrs := make(map[*ListNode]int)
var exist bool
ptrs[head] = 1
for head.Next != nil{
_, exist = ptrs[head.Next]
if !exist{
ptrs[head.Next] = 1
head = head.Next
} else{
return true
}
}
return false
}
总结
题目中给出提示本题可以使用O(1)的空间复杂度解决, 即使用常数空间复杂度, 研究他人题解发现本题同样可以使用快慢指针,核心思想类似于加速度, 快指针的加速度比慢指针要快, 即快指针每次移动的步长比慢指针移动的步长多1. 这样若链表中有环则快指针最终总能比慢指针快整整一个环的长度从而追上慢指针. 若没有环则快指针永远不可能追上慢指针. 类似于跑步时如果一直跑, 跑得快的同学最终总能套跑得慢的同学一圈. 快慢指针在很多情景下都有很巧妙的应用. 后续可以在遇到多个相关题目后加以总结. 给出快慢指针解决本题的代码示例
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func hasCycle(head *ListNode) bool {
if head == nil {
return false
}
slow := head
fast := head
for slow != nil && fast != nil && fast.Next != nil {
slow = slow.Next
fast = fast.Next.Next
if slow == fast {
return true
}
}
return false
}
day10 2024-03-07
876. Middle of the Linked List
Given the head of a singly linked list, return the middle node of the linked list.
If there are two middle nodes, return the second middle node.
题解
本题寻找链表中间位置的元素, 是一个经典快慢指针的题目. 只需要让快指针前进的速度为2, 慢指针为1, 则快指针到达链表末尾时慢指针正好指向中间位置. 要注意链表元素个数为奇数和偶数时的处理方法的不同.
代码
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func middleNode(head *ListNode) *ListNode {
fast := head
slow := head
for fast.Next != nil && fast.Next.Next != nil{
fast = fast.Next.Next
slow = slow.Next
}
// consider list number is even
if fast.Next != nil{
slow = slow.Next
}
return slow
}
总结
本题是一道经典的快慢指针的简单题目, 进一步深化了快慢指针的应用.
day11 2024-03-08
3005. Count Elements With Maximum Frequency
You are given an array nums consisting of positive integers.
Return the total frequencies of elements in nums such that those elements all have the maximum frequency.
The frequency of an element is the number of occurrences of that element in the array.
Example 1:
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为新的最大值.
代码
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.
题解
本题使用两个指针分别指向两个数组, 然后依次移动两个指针比较大小即可, 位于数值更小的位置的数组指针向前移动直到相等或者到数组末尾为止.
代码
func getCommon(nums1 []int, nums2 []int) int {
index1, index2 := 0 , 0
for index1<len(nums1) && index2 < len(nums2){
if nums1[index1] < nums2[index2]{
index1++
}else if nums2[index2] < nums1[index1]{
index2++
} else{
return nums1[index1]
}
}
return -1
}
总结
这是一道典型的双指针问题, 思路清晰就可以很快解决.
day13 2024-03-10
349. Intersection of Two arrays
Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must be unique and you may return the result in any order.
Example 1:
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中是否有相同值.
代码
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已经被访问过, 这样可以避免在结果数组中添加重复元素.
一个示例代码如下
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.
题解
本题初始想先扫描s字符串, 使用一个map记录字符串中各个字符的数量, 再遍历order依次将字符按数量附加到末尾即可. 但考虑到字符只有26个小写英文字母, 使用一个长度为26的数组来保存对应位置的英文字母的数量. 再遍历要比map速度快.
代码
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<temp;i++{
numbers[c-a]--
result += string(c)
}
}
for key,c := range numbers{
if c!=0{
for i:=0;i<c;i++{
result += string(rune(int(a)+key))
}
}
}
return result
}
总结
注意题中条件, 遍历的类型有限的情况下直接使用数组保存, 遍历起来速度要快得多.
day15 2024-03-12
1171. Remove Zero Sum Consecutive Nodes from Linked List
Given the head of a linked list, we repeatedly delete consecutive sequences of nodes that sum to 0 until there are no such sequences.
After doing so, return the head of the final linked list. You may return any such answer.
(Note that in the examples below, all sequences are serializations of ListNode objects.)
题解
本题有一定难度, 寻找连续的和为0的连续序列要使用前缀和, 若某两个元素的前缀和相同, 则位于二者之间的序列和即为0. 删除这部分序列. 先遍历链表, 找到所有前缀和相同的元素, 然后将其中间的部分全部删除即可. 使用一个map来保存前缀和对应的Node的指针, 当找到一个前缀和相同的Node时, 从map保存的指针开始删除到该Node的所有节点, 同时删除以中间部分节点的前缀和为key的map中对应的项防止后面有和中间部分项相同的前缀和的节点存在.
代码
v/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeZeroSumSublists(head *ListNode) *ListNode {
current := head
sum := 0
summap := map[int](*ListNode){}
for current != nil{
sum += current.Val
if sum == 0{
for head != current.Next{
sum += head.Val
delete(summap, sum)
head = head.Next
}
}
ptr, exist := summap[sum]
if !exist{
summap[sum] = current
}else{
back := ptr
ptr = ptr.Next
for ptr != current{
sum += ptr.Val
delete(summap, sum)
ptr = ptr.Next
}
sum += ptr.Val
back.Next = current.Next
}
current = current.Next
}
return head
}
总结
删除中间部分节点的前缀和对应的key的项时, 考虑到中间部分的和一定为0, 因此用sum去累加中间部分节点的值并依次删除, 最后得到的sum就和删除节点开始前的sum相同. 本题一是要清楚通过前缀和来寻找连续的和为0的序列, 另一方面则是时刻记住这个序列的和为0的特性. 其实本题有一种代表元的抽象思想. 可以将一组和为0的序列看为一个, 其在加和过程中与不存在的节点具有等价性. 不影响和的变化.
day16 2024-03-13
2485. Find the Pivot Integer
Given a positive integer n, find the pivot integer x such that:
The sum of all elements between 1 and x inclusively equals the sum of all elements between x and n inclusively. Return the pivot integer x. If no such integer exists, return -1. It is guaranteed that there will be at most one pivot index for the given input.
Example 1:
Input: n = 8 Output: 6 Explanation: 6 is the pivot integer since: 1 + 2 + 3 + 4 + 5 + 6 = 6 + 7 + 8 = 21. Example 2:
Input: n = 1 Output: 1 Explanation: 1 is the pivot integer since: 1 = 1. Example 3:
Input: n = 4 Output: -1 Explanation: It can be proved that no such integer exist.
题解
本题使用数学方法进行计算
根据公式直接求出x的值即可, 注意考虑到精度问题, 要对最终得到的运算结果与取整后的数字的差的绝对值进行判断, 小于一定限度即可认为是整数值.
代码
func pivotInteger(n int) int {
n_float := float64(n)
x := n_float*math.Sqrt(n_float+1)/math.Sqrt(2*n_float)
if math.Abs(x-float64(int(x))) < 0.0000001{
return int(x)
}else {
return -1
}
}
总结
查看他人解法发现本题其实也可以使用前缀和进行求解, 将每个下标位置处的前缀和求出并保存, 倒序遍历数组, 将后面的数的和与当前位置的前缀和比较即可, 示例代码
day17 2024-03-14
930. Binary Subarrays With Sum
Given a binary array nums and an integer goal, return the number of non-empty subarrays with a sum goal.
A subarray is a contiguous part of the array.
Example 1:
Input: nums = [1,0,1,0,1], goal = 2 Output: 4 Explanation: The 4 subarrays are bolded and underlined below: [1,0,1,0,1] [1,0,1,0,1] [1,0,1,0,1] [1,0,1,0,1] Example 2:
Input: nums = [0,0,0,0,0], goal = 0 Output: 15
题解
本题可以用前缀和求解, 类似这种求数组中连续和的问题都可以用前缀和求解, 首先计算出所有位置上的前缀和, 使用滑动窗口遍历前缀和数组, 根据goal的值调整窗口的左右边界, 注意0需要特殊处理, 0是否存在不影响整个序列的和, 0所在的位置处的前缀和和前面的元素相同.
代码
func numSubarraysWithSum(nums []int, goal int) int {
sum := 0
prefixsum := []int{0}
for _,value := range nums{
sum += value
prefixsum = append(prefixsum, sum)
}
front := 1
back := 0
flag := 0
result := 0
for front < len(prefixsum){
if prefixsum[front] - prefixsum[back]<goal{
front++
}else if prefixsum[front] - prefixsum[back]>goal{
if front-1 == back{
front++
}
back++
}else{
if (front-1 == back){
result++
front++
back = flag
}else if prefixsum[back] == prefixsum[back+1]{
result++
back++
}else if (front<len(prefixsum)-1) && prefixsum[front] == prefixsum[front+1]{
result++
front++
back = flag
}else{
result++
back++
flag=back
}
}
}
return result
}
总结
本题使用滑动窗口时间复杂度上比较高, 考虑在滑动窗口的过程中, 连续的0的部分被重复遍历, 大大增加了总体的运行时间. 其实本题只需要用当前的前缀和减去goal, 并寻找前面的前缀和中是否有符合差值的前缀和存在, 存在则从该前缀和位置到当前前缀和位置直接的序列满足和为goal的条件. 已经求出了前缀和就没必要再去一个个遍历并且滑动了, 如果使用滑动窗口则没必要计算前缀和. 我的解法实际上是对前缀和理解不深刻导致的. 巧用前缀和加哈希表(存储某个前缀和出现的次数)可以快速的解决这个问题. 示例代码如下
func numSubarraysWithSum(nums []int, goal int) int {
hash := map[int]int{}
sum := 0
count := 0
hash[0] = 1
for i:=0; i < len(nums); i++ {
sum = sum + nums[i]
count = count + hash[sum - goal]
val, ok := hash[sum]
if(ok) {
hash[sum] = val + 1
} else {
hash[sum] = 1
}
}
return count
}
还有一种极其巧妙的解法, 分别求得和小于等于goal的连续子序列的数量, 再减去和小于等于goal-1的连续子序列的数量即为最终结果. 本题中求子序列等于goal是比较困难的, 要考虑很多条件, 但是求小于等于goal的子序列却是比较简单的问题. 这种方法将一个困难问题转化为两个简单的子问题求解, 得到了更高效的方法. 充分利用整体性可使问题更简单. 代码如下
func numSubarraysWithSum(nums []int, goal int) int {
return counting(nums, goal) - counting(nums, goal-1)
}
func counting(nums []int, goal int) int {
if goal < 0 {
return 0
}
left, right, sum, ans := 0, 0, 0, 0
for ; right < len(nums); right++ {
sum += nums[right]
for left <= right && sum > goal {
sum -= nums[left]
left++
}
ans += right - left + 1
}
return ans
}
day18 2024-03-15
238. Product of Array Except Self
Given an integer array nums, return an array answer such that answer[i] is equal to the product of all the elements of nums except nums[i].
The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer.
You must write an algorithm that runs in O(n) time and without using the division operation.
题解
题目中明确要求本题不能使用分治法, 且算法复杂度在O(n). 本题要求的为数组中每个元素对应的除该元素之外的其他元素的乘积. 根据给出的例子可以发现当存在0的时候是一种特殊情况. 当存在0时除0以外的其他元素对应的结果均为0. 一般情况下可以使用先求出全部元素的乘积, 再遍历数据使用总乘积除以当前元素即可求得对应位置的结果. 因为这样只需要固定遍历两次数据, 故时间复杂度为O(n), 又只需要一个变量来保存总乘积, 一个变量指示是否存在0元素, 故空间复杂度为O(1). 因为题目中明确说明了乘积保证为整数, 故在使用除法的过程中不用考虑结果为小数的问题.
代码
func productExceptSelf(nums []int) []int {
sum := 1
flag := 0
for _, value := range nums{
if value == 0{
flag++
}else{
sum *= value
}
}
if flag > 1{
return make([]int, len(nums))
}
if flag == 1{
result := []int{}
for _, value := range nums{
if value == 0{
result = append(result, sum)
}else{
result = append(result, 0)
}
}
return result
}
result := []int{}
if flag == 0{
for _, value := range nums{
result = append(result, sum/value)
}
}
return result
}
总结
查看更快的解法, 发现都使用了前缀积和后缀积, 即从前向后遍历, 计算出当前位置元素的前缀积, 然后反向遍历, 在计算出后缀积的同时就得到了最终的结果. 一个示例代码如下
func productExceptSelf(nums []int) []int {
res := make([]int, len(nums))
prefix := 1
for i, n := range nums {
res[i] = prefix
prefix *= n
}
postfix := 1
for i := len(nums) - 1; i >= 0; i-- {
res[i] *= postfix
postfix *= nums[i]
}
return res
}
其实无论是前缀和还是前缀积, 都是一个对以往的计算状态的保留, 保存了更多的信息, 避免了重复的运算, 这种思想是值得细细品味的.
day19 525. Contiguous Array
Given a binary array nums, return the maximum length of a contiguous subarray with an equal number of 0 and 1.
题解
考虑本题若想找到到某一个位置处的0和1数量相同的最长子数组长度, 只需要根据到该下标处的0和1数量的差值, 找出符合这个差值的最小下标, 用当前下标减去这个差值下标, 得到的即为0和1数量相同的子数组的长度. 关键在于想要让0和1数量相同, 需要找出能消除当前0和1数量差值的位置. 0和1数量对于寻找相同数量子数组用处不大, 二者数量的差值对于构造一个数量相同的子数组至关重要. 通过加上或减去二者的数量差即可构造出二者数量相同的子数组. 考虑清楚这一点, 思路就很清晰了.
- 遍历数组, 保存到当前位置0和1的数量
- 计算二者的差值, 在一个哈希表中寻找以这个差值为key的项是否存在, 不存在则将差值为key, 当前下标作为该key对应的值插入哈希表. 若存在则用当前下标减去哈希表中以差值作为key的对应的下标值, 即得到到当前位置0和1数量相同的最长子数组的长度. 比较这个长度和保存的最长子数组长度, 更新最长子数组长度.
关键在与将差值作为key下标作为value插入哈希表后, 后续有相同的差值作为key时不在更新哈希表, 这样保存的就是符合这个差值的位置的最小值, 也就能构造出最长的0和1数量相同的子数组.
代码
func findMaxLength(nums []int) int {
map0 := map[int]int{}
map1 := map[int]int{}
sum0 := 0
sum1 := 0
maxarray := 0
for index,value := range nums{
if value == 1{
sum1++
}else{
sum0++
}
if sum1>sum0{
i, ok := map1[sum1-sum0]
if !ok{
map1[sum1-sum0] = index
}else{
maxarray = max(maxarray, index-i)
}
}else if sum1<sum0{
i, ok := map0[sum1-sum0]
if !ok{
map0[sum1-sum0] = index
}else{
maxarray = max(maxarray, index-i)
}
}else{
maxarray = max(maxarray, index+1)
}
}
return maxarray
}
总结
本题和前几天的题目在思想上有异曲同工之妙, 也是前缀和的一种应用, 如果把0和1的数量差作为前缀和, 那么本题解题思路可简单概括为找到前缀和相等的位置的最远距离.
day20 2024-03-17
57. Insert Interval
You are given an array of non-overlapping intervals intervals where intervals[i] = [starti, endi] represent the start and the end of the ith interval and intervals is sorted in ascending order by starti. You are also given an interval newInterval = [start, end] that represents the start and end of another interval.
Insert newInterval into intervals such that intervals is still sorted in ascending order by starti and intervals still does not have any overlapping intervals (merge overlapping intervals if necessary).
Return intervals after the insertion.
Note that you don't need to modify intervals in-place. You can make a new array and return it.
题解
遍历intervals, 将interval复制到结果数组中. 判断要插入的interval的start和end是否在当前interval中
- 在范围内则将新interval的start设置为当前interval的start. 使用相同的方式判断end的范围.
- 若start或end不在当前interval范围内且小于下一个interval的start, 则将start或者end设置为新interval的start或end. 此时新interval构造完成, 将后面的interval原样复制到result中即可.
代码
func insert(intervals [][]int, newInterval []int) [][]int {
result := [][]int{}
start := newInterval[0]
end := newInterval[1]
insertInterval := []int{}
flag := 0
for _, value := range intervals{
if flag == 2{
result = append(result, value)
}else if flag == 0{
if start < value[0]{
insertInterval = append(insertInterval, start)
flag++
if end < value[0]{
insertInterval = append(insertInterval, end)
flag++
result = append(result, insertInterval)
result = append(result, value)
}else if end >= value[0] && end <= value[1]{
insertInterval = append(insertInterval, value[1])
flag++
result = append(result, insertInterval)
}
}else if start >= value[0] && start <= value[1]{
insertInterval = append(insertInterval, value[0])
flag++
if end >= value[0] && end <= value[1]{
insertInterval = append(insertInterval, value[1])
flag++
result = append(result, insertInterval)
}
}else{
result = append(result, value)
}
}else{
if end < value[0]{
insertInterval = append(insertInterval, end)
flag++
result = append(result, insertInterval)
result = append(result, value)
}else if end >= value[0] && end <= value[1]{
insertInterval = append(insertInterval, value[1])
flag++
result = append(result, insertInterval)
}
}
}
if flag == 0{
result = append(result, []int{start, end})
}else if flag == 1{
insertInterval = append(insertInterval, end)
result = append(result, insertInterval)
}
return result
}
总结
这种题思路上并没有特别之处, 但是判断逻辑比较繁琐, 需要耐心思考边界情况, 查看他人题解, 发现用原始数组与需要插入的interval做比较要比使用interval和原始数组的start和end做比较思路简单清晰得多, 这里还是对判断条件的变与不变理解的不够透彻, 需要插入的interval的start和end是不变的, 不断遍历原始数组与不变的start和end做比较要比使用不变的start和end去和不断变化的interval的start和end做比较判断起来容易得多. 找到不变的条件对于思路清楚的解决问题至关重要. 给出示例代码
func insert(intervals [][]int, newInterval []int) [][]int {
res := make([][]int, 0)
i := 0
for ; i < len(intervals) && intervals[i][1] < newInterval[0]; i++ {
res = append(res, intervals[i])
}
for ; i < len(intervals) && intervals[i][0] <= newInterval[1]; i++ {
newInterval[0] = min(intervals[i][0], newInterval[0])
newInterval[1] = max(intervals[i][1], newInterval[1])
}
res = append(res, newInterval)
for i < len(intervals) {
res = append(res, intervals[i])
i++
}
return res
}
day21 2024-03-18
452. Minimum Number of Arrows to Burst Balloons
There are some spherical balloons taped onto a flat wall that represents the XY-plane. The balloons are represented as a 2D integer array points where points[i] = [xstart, xend] denotes a balloon whose horizontal diameter stretches between xstart and xend. You do not know the exact y-coordinates of the balloons.
Arrows can be shot up directly vertically (in the positive y-direction) from different points along the x-axis. A balloon with xstart and xend is burst by an arrow shot at x if xstart <= x <= xend. There is no limit to the number of arrows that can be shot. A shot arrow keeps traveling up infinitely, bursting any balloons in its path.
Given the array points, return the minimum number of arrows that must be shot to burst all balloons.
题解
本题描述的很复杂, 其实是寻找相互交错的数组有几组, 如果将数组的范围看作集合, 范围重叠的数组可以看作一个大集合, 那么就是寻找这样的集合的数目. 本题可用贪心算法, 先将这些数组按照x_start的大小从小到大排序, 然后遍历并寻找x_start在当前重合范围内的数组,并且将重合范围设置为当前重合范围和当前遍历的数组的x_end中的较小值以缩小重合范围. 直到当前数组不满足条件, 即可认为之前的数组需要一个arrow. 继续遍历, 重复该操作, 即可找到所有需要的arrow.
代码
func findMinArrowShots(points [][]int) int {
sort.Slice(points, func (i, j int)bool{
if points[i][0]<points[j][0]{
return true
}else{
return false
}
})
arrow := 0
current := []int{points[0][0],points[0][1]}
for _, value := range points{
if value[0] <= current[1]{
if value[1] < current[1]{
current[1] = value[1]
}
current[0] = value[0]
}else{
arrow++
current[0] = value[0]
current[1] = value[1]
}
}
arrow++
return arrow
}
总结
本题的题目说明上有一些含糊, 根据题目说明, arrow只在竖直方向上移动, 也就是说, 必须要有重合的区间的数组才能用一个箭头, 假如有一个数组区间很大, 其和两个小区间重合但这两个小区间不重合, 按理来说应该使用两个arrow才能扎爆这三个气球, 但是看他人提交的代码中有如下一种代码, 似乎说明用一个arrow就可以. 究竟这种对不对有待进一步的思考. 代码如下
func findMinArrowShots(points [][]int) int {
sort.Slice(points, func(i,j int) bool {
return points[i][1] < points[j][1]
})
res := 1
arrow := points[0][1]
for i := 0; i < len(points); i++ {
if arrow >= points[i][0] {continue}
res++
arrow = points[i][1]
}
return res
}
day22 2024-03-19
621. Task Scheduler
You are given an array of CPU tasks, each represented by letters A to Z, and a cooling time, n. Each cycle or interval allows the completion of one task. Tasks can be completed in any order, but there's a constraint: identical tasks must be separated by at least n intervals due to cooling time.
Return the minimum number of intervals required to complete all tasks.
题解
拿到本题, 最直接的想法就是将每种任务的数量统计出来, 从大到小排序, 然后按照冷却时间轮流按序执行不同的任务, 不能执行任务的时间片留空. 某种任务全部执行完后, 该任务占据的时间片也留空. 知道全部任务都执行完需要的时间就是最少时间. 这是一种贪心的思想, 简单思考其正确性, 因为即使调换顺序, 在执行到某个时间时也不会比这种贪心算法执行的任务更多.
代码
func leastInterval(tasks []byte, n int) int {
tasknumber := map[byte]int{}
for _, task := range tasks{
_, exist := tasknumber[task]
if !exist{
tasknumber[task] = 1
}else{
tasknumber[task]++
}
}
tasknumber_slice := []int{}
for _, value := range tasknumber{
tasknumber_slice = append(tasknumber_slice, value)
}
sort.Ints(tasknumber_slice)
length := 0
result := 0
for {
length = len(tasknumber_slice)
for i:=1;i<=n+1;i++{
if i<=length{
if(tasknumber_slice[length-i] == 1){
if i==1{
tasknumber_slice = tasknumber_slice[:length-1]
}else{
tasknumber_slice = append(tasknumber_slice[:length-i],tasknumber_slice[length-i+1:]...)
}
}else{
tasknumber_slice[length-i]--
}
}
result++
if len(tasknumber_slice)==0{
goto Loop
}
}
sort.Ints(tasknumber_slice)
}
Loop:
return result
}
总结
看了0ms的解法, 十分惊艳, 与其将任务一个一个的安放到插槽中, 不如直接按照频率最大的任务算出必须存在的空插槽的个数, 再用剩余的任务去填这些空插槽, 最后只需要将任务总数和剩余的空插槽个数相加即可得到最终的时长. 到这里我想到, 其实频率最高的任务留出的空插槽数目是固定的, 只要将除频率最高之外的任务总数和空插槽数目相比, 若小于空插槽数目, 则最后时长就是频率最高任务完成需要的时长. 这里需要将和频率最高的任务频率相同的任务数目先减一计算算到任务总数中, 最后再加到最终的时间总数上. 若大于空插槽数目, 最终结果就是任务数目.
func leastInterval(tasks []byte, n int) int {
freq := make([]int, 26)
for _, task := range tasks {
freq[task-'A']++
}
sort.Ints(freq)
maxFreq := freq[25]
idleSlots := (maxFreq - 1) * n
for i := 24; i >= 0 && freq[i] > 0; i-- {
idleSlots -= min(maxFreq-1, freq[i])
}
idleSlots = max(0, idleSlots)
return len(tasks) + idleSlots
}
day23 2024-03-20
1669. Merge In Between Linked Lists
You are given two linked lists: list1 and list2 of sizes n and m respectively.
Remove list1's nodes from the ath node to the bth node, and put list2 in their place.
The blue edges and nodes in the following figure indicate the result:
Build the result list and return its head.
题解
本题为将链表中某段替换为指定的链表, 只需要遍历链表, 保存需要替换部分首尾两个节点的指针即可, 需要注意边界情况的处理, 即替换开头某段或者结尾某段链表时要处理空指针.
代码
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func mergeInBetween(list1 *ListNode, a int, b int, list2 *ListNode) *ListNode {
count := 0
var before *ListNode
var after *ListNode
current := list1
head := list1
for count <= b {
if count == a-1 {
before = current
}
count++
current = current.Next
}
after = current
if before == nil {
head = list2
} else {
before.Next = list2
}
current = list2
for current.Next != nil {
current = current.Next
}
current.Next = after
return head
}
总结
本题保存了首尾两个指针的地址, 是典型的用空间换时间的思路, 不过实际应用过程中可能还要根据语言注意被动态分配出去的空间回收的问题.
day24 2024-03-21
206. Reverse Linked
Given the head of a singly linked list, reverse the list, and return the reversed list.
题解
本题相当基础, 是一道简单题, 只需要将链表整个反转过来即可. 用一个变量保存当前访问的节点的前一个节点的指针, 将当前节点的next修改为指向前一个节点的指针, 遍历整个链表即可. 注意处理边界情况(头指针为0).
代码
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseList(head *ListNode) *ListNode {
var before *ListNode
current := head
for current!=nil && current.Next!=nil {
temp := current.Next
current.Next = before
before = current
current = temp
}
if head!=nil{
current.Next = before
}
return current
}
总结
这两天都是链表相关的题目, 本题是一道基础题, 在查看他人解答的过程中发现本题也可以将链表数据全部复制出来, 再遍历链表逆序赋值即可. 示例代码如下
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseList(head *ListNode) *ListNode {
arr := []int{}
node := head
for node != nil {
arr = append(arr, node.Val)
node = node.Next
}
node = head
for i := len(arr); i > 0; i-- {
node.Val = arr[i-1]
node = node.Next
}
return head
}
day25 2024-03-22
234. Palindrome Linked
Given the head of a singly linked list, return true if it is a palindrome or false otherwise.
题解
本题也是一道基础题, 使用快慢指针的方法遍历到链表的中间, 在遍历的同时将链表的前半部分的值保存到数组中, 再从中间继续向后遍历, 遍历的同时反向遍历数组, 比较遍历的节点和遍历到的数组中元素的值. 若不同则不是回文, 直到全部遍历完成为止.
代码
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func isPalindrome(head *ListNode) bool {
back := head
before:= head
if head.Next != nil{
before = head.Next
}else{
return true
}
values := []int{head.Val}
for before!= nil && before.Next != nil{
back = back.Next
values = append(values, back.Val)
before = before.Next.Next
}
if before == nil{
for i:=len(values)-2;i>=0;i--{
back = back.Next
if back.Val != values[i]{
return false
}
}
return true
}else{
for i:= len(values)-1;i>=0;i--{
back =back.Next
if back.Val != values[i]{
return false
}
}
return true
}
}
总结
查看用时较短的题解, 使用了快慢指针, 找到中间位置后将后半截链表反转, 然后从原始链表头部和反转的后半截链表头部开始依次遍历并比较即可. 这种方法时间复杂度为O(n), 空间复杂度为O(1), 代码如下
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func isPalindrome(head *ListNode) bool {
slow:=head
fast:=head.Next
for fast!=nil && fast.Next!=nil{
slow=slow.Next
fast=fast.Next.Next
}
second:=slow.Next
slow.Next=nil
second=reverse(second)
for second!=nil && head!=nil{
if second.Val!=head.Val{
return false
}
second=second.Next
head=head.Next
}
return true
}
func reverse(head *ListNode) *ListNode{
var prev *ListNode
var futr *ListNode
for head!=nil{
futr=head.Next
head.Next=prev
prev=head
head=futr
}
return prev
}