206. 反转链表
题目
代码
func reverseList(head *ListNode) *ListNode {
var pre, next *ListNode
for head != nil {
next = head.Next
head.Next = pre
pre = head
head = next
}
return pre
}
题解
性能
160. 相交链表
题目
代码
func getIntersectionNode(headA, headB *ListNode) *ListNode {
pa, pb := headA, headB
for pa != pb {
if pa==nil {
pa = headB
} else {
pa = pa.Next
}
if pb==nil {
pb = headA
} else {
pb = pb.Next
}
}
return pa
}
题解
双指针,
从两个头节点开始,走一遍自己的,再走一遍对方的,
相等则返回。
能解决相同节点数不相交的问题,并且可以保证只走一次,不会走多次。
用例
性能
21. 合并两个有序链表
题目
代码
func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode {
head := &ListNode{}
cur := head
for list1!=nil || list2!=nil {
if list1==nil {
cur.Next = list2
list2 = list2.Next
} else if list2==nil {
cur.Next = list1
list1 = list1.Next
} else if list1.Val < list2.Val {
cur.Next = list1
list1 = list1.Next
} else {
cur.Next = list2
list2 = list2.Next
}
cur = cur.Next
}
cur.Next = nil
return head.Next
}
题解
比较左右两个链表,把小的放进去
性能
86. 分隔链表
题目
代码
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func partition(head *ListNode, x int) *ListNode {
small := &ListNode{}
big := &ListNode{}
cur1 := small
cur2 := big
for head != nil {
if head.Val<x {
cur1.Next = head
cur1 = head
} else {
cur2.Next = head
cur2 = head
}
head = head.Next
}
cur2.Next = nil
cur1.Next = big.Next
return small.Next
}
题解
用两个链表存储,small存储比给定值小的,big存储大于等于给定值的。
最后记得链上,将next设置为nil,即可。
性能
非常有趣...这个代码的性能是这样的
但是我本来写的代码是这样的
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func partition(head *ListNode, x int) *ListNode {
small := &ListNode{}
big := &ListNode{}
cur1 := small
cur2 := big
for head != nil {
if head.Val<x {
cur1.Next = head
cur1 = cur1.Next
} else {
cur2.Next = head
cur2 = cur2.Next
}
head = head.Next
}
cur2.Next = nil
cur1.Next = big.Next
return small.Next
}
性能是这样的:
可以看到,区别只有
cur1 = cur1.Next
cur2 = cur2.Next
和
cur1 = head
cur2 = head
性能会有4ms的差异,65%的人能避免......还是挺神奇的
142. 环形链表 II
题目
代码
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func detectCycle(head *ListNode) *ListNode {
slow, fast := head, head
for {
if fast==nil || fast.Next==nil {
return nil
} else {
fast = fast.Next.Next
}
slow = slow.Next
if slow==fast {
break
}
}
fast = head
for slow != fast {
slow=slow.Next
fast=fast.Next
}
return slow
}
思路
双指针
- 首先确定是否有环。
- 慢指针slow走一步,快指针fast走两步。
- 如果快指针到了nil,那就证明无环。
- 如果两者相等,那就证明有环。
- 然后找到环入口,这是一个数学问题。
- 先上结论:让其中一个指针从head开始一步一步走,两者重新相遇的地方就是环的入口。
- 假设,
- head到环入口的距离是x,
- 环入口到接触点的距离是y,
- 接触点到环入口的距离是z。
- 即:
- 环的长度是y+z
- 慢指针走了x+y的长度(慢指针一定最多只走了一圈,可以自己推导)
- 快指针走了x + n*(y+z) + y,(n是走的圈数,不重要),也等于2 * (x+y)
- 我们想找到环的入口,也就是x。
- 证明结论的方法:
- x+n(y+z)+y=2x+2y
- x+y=n(y+z)
- x+y=(n-1)(y+z)+y+z
- x=(n-1)(y+z)+y
- 忽略n-1圈,x就等于y。
- 也就是说,fast指针重新指向head(只是为了复用变量,实际走的一步,已经不是快指针了)。然后走了x步,slow指针在圈内走了n-1圈,额外走了y步,两者恰好相遇。
- 编码
- 第一个for循环没有写条件,fast!=slow,是因为两者最开始都等于head,不会执行for循环。所以放在里边
- 第一个for循环先走fast,是因为需要校验fast的nil,省的校验slow的nil了
性能
92. 反转链表 II
题目
代码
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func reverseBetween(head *ListNode, left int, right int) *ListNode {
if head==nil || left==right {
return head
}
// 假设输入为:12.345.678,3,5
// 期望输出为:12.543.678
// 需要保存2,3,5,6
// leftEnd, reverseBegin, reverseEnd, rightBegin
HEAD := &ListNode{Next: head}
tmp := HEAD
i := 1
for ; i<left; i++ {
tmp = tmp.Next
// fmt.Println("i:", i, ", tmp:", tmp.Val)
}
leftEnd := tmp
reverseBegin := tmp.Next
// fmt.Println()
var pre, next *ListNode
for i--; i<=right; i++ {
next = tmp.Next
tmp.Next = pre
pre = tmp
tmp = next
// fmt.Println("i:", i, ", tmp:", tmp.Val)
}
reverseEnd := pre
rightBegin := next
leftEnd.Next = reverseEnd
reverseBegin.Next = rightBegin
// fmt.Println()
// fmt.Println(leftEnd.Val)
// fmt.Println(reverseBegin.Val)
// fmt.Println(reverseEnd.Val)
// fmt.Println(rightBegin.Val)
return HEAD.Next
}
思路
- 遍历一次
- 先举个例子:
- 假设输入为:12.345.678,3,5
- 期望输出为:12.543.678
- 需要保存2,3,5,6
- 先找到入口
- 需要记录四个内容:
- leftEnd:待翻转内容,之前的最后一个指针
- reverseBegin:待翻转的第一个指针
- reverseEnd:待翻转的最后一个指针
- rightBegin:待翻转内容,之后的第一个指针
- 然后将leftEnd.Next指向reverseEnd,也就是2指向5
- 然后将reverseBegin.Next指向rightBegin,也就是3指向6
- 先举个例子:
- 边界条件
- 自己写case试出来的,注意i--
性能
很奇怪,我的代码加了注释跑到双百,不加注释内存66.4%
138. 复制带随机指针的链表
题目
代码
/**
* Definition for a Node.
* type Node struct {
* Val int
* Next *Node
* Random *Node
* }
*/
func copyRandomList(head *Node) *Node {
m := make(map[*Node]*Node, 1)
cur := &Node{Next: head}
ans := &Node{}
tmp := ans
for cur.Next!=nil {
if m[cur.Next]==nil {
tmp.Next = &Node{Val: cur.Next.Val}
m[cur.Next] = tmp.Next
} else {
tmp.Next = m[cur.Next]
}
if cur.Random!=nil {
if m[cur.Random]==nil {
tmp.Random = &Node{Val: cur.Random.Val}
m[cur.Random] = tmp.Random
} else {
tmp.Random = m[cur.Random]
}
}
cur = cur.Next
tmp = tmp.Next
}
tmp.Random = m[cur.Random]
return ans.Next
}
思路
用一个哈希表,k为原始链表中的每一个节点,v为新链表中的每个节点。
cur为原始链表指针,tmp为新链表指针。
判断cur的next是否在map中,判断cur的random是否在map中。
性能
20. 有效的括号
题目
代码
func isValid(s string) bool {
stack := make([]rune, 0, len(s)/4)
l := 0
for _, b := range s {
if b=='(' || b=='{' || b=='[' {
stack = append(stack, b)
} else {
l = len(stack)
if l == 0 {
return false
}
if b==')' && stack[l-1]=='(' {
stack = stack[:l-1]
} else if b==']' && stack[l-1]=='[' {
stack = stack[:l-1]
} else if b=='}' && stack[l-1]=='{' {
stack = stack[:l-1]
} else {
return false
}
}
}
if len(stack)==0 {
return true
}
return false
}
思路
用栈,在出栈的时候判断是否匹配。
// 本来想这样写的
// 但是:[是91,]是93,92是\,人麻了
// if int(b) == int(stack[l-1])+1 {
// stack = stack[:l-1]
// } else {
// return false
// }
性能
155. 最小栈
题目
代码
type MinStack struct {
stack []int
minStack []int
len int
}
func Constructor() MinStack {
return MinStack{
stack : []int{},
minStack : []int{},
len : 0,
}
}
func (this *MinStack) Push(val int) {
if this.len == 0 {
this.minStack = append(this.minStack, val)
} else {
this.minStack = append(this.minStack, min(val, this.minStack[this.len-1]))
}
this.stack = append(this.stack, val)
this.len++
}
func (this *MinStack) Pop() {
this.stack = this.stack[:this.len-1]
this.minStack = this.minStack[:this.len-1]
this.len--
}
func (this *MinStack) Top() int {
return this.stack[this.len-1]
}
func (this *MinStack) GetMin() int {
return this.minStack[this.len-1]
}
func min(x int, y int) int {
if x<y {
return x
}
return y
}
思路
维护一个最小栈,这个辅助栈的大小和主栈一致。
在插入一个元素时,除了入栈操作,还要判断当前值和最小栈的栈顶值谁小,
如果栈顶小,就再塞入一个栈顶,
如果栈顶大,就塞入一个新值。
性能
946. 验证栈序列
题目
代码
func validateStackSequences(pushed []int, popped []int) bool {
n := len(pushed)
i, j := 0, 0
tmp := make([]int, 0, n/2)
now := 0
for i!=n || j!=n {
now = len(tmp)
if j<n && now>0 && tmp[now-1]==popped[j] {
// 先判断能否出栈
tmp = tmp[:now-1]
j++
} else if i<n {
// 判断能否入栈
tmp = append(tmp, pushed[i])
i++
} else {
return false
}
}
return true
}
思路
- 先判断能否出栈
- 出栈条件:栈不为空,且,等于出栈表的这一项
- 然后判断能否入栈
- 入栈条件:还有剩余的入栈表
性能
739. 每日温度
题目
代码
type StackStruct struct {
index int
value int
}
func dailyTemperatures(temperatures []int) []int {
n := len(temperatures)
stack := make([]StackStruct, 0)
ans := make([]int, n)
i:=0
l := 0
for i<n {
l = len(stack)
if l>0 && stack[l-1].value<temperatures[i] {
// 判断出栈条件
ans[stack[l-1].index] = i - stack[l-1].index
stack = stack[:l-1]
} else {
// 入栈
stack = append(stack, StackStruct{
index: i,
value: temperatures[i],
})
i++
}
}
return ans
}
思路
用栈来存储结构体,结构体里有入栈的下标,和入栈的值的内容
- 每次遍历到一个值,先判断是否出栈
- 出栈条件:当前值比栈顶的值要大,则将栈顶弹出
- 出栈操作:
ans[栈顶元素.index] = i - 栈顶元素.index
- 不出栈则入栈
- 塞进当前index和value
- 结束条件:表走到最后一步。这个时候栈里的内容不用管,都会是0,因为这个栈一定是从小到大的,栈顶是最小的。
性能
相同代码每次跑出来的耗时都不一样,每太想到哪里的内存还可以优化
224. 基本计算器
题目
解法1
通过从前往后的顺序遍历,击穿括号,让符号正确
代码1
func calculate(s string) int {
stack := []int{1}
ans := 0
now := 0
op := 1
for _, b := range s {
if b==' ' {
continue
} else if isNum(b) {
now = now*10 + int(b-'0')
} else {
ans += op*now
now = 0
if b=='+' {
op = stack[len(stack)-1]
} else if b=='-' {
op = -stack[len(stack)-1]
} else if b=='(' {
stack = append(stack, op)
} else if b==')' {
stack = stack[:len(stack)-1]
op = stack[len(stack)-1]
}
}
}
ans += op*now
return ans
}
func isNum(r rune) bool {
if r-'0'>=0 && r-'0'<=9 {
return true
}
return false
}
思路1
func calculate(s string) int {
// 通过从前往后的顺序遍历,击穿括号,让符号正确
stack := []int{1} // 只存储值的正负号,并且只有在有括号时才操作
ans := 0 // 最终结果
now := 0 // 当前这个数的真实值
op := 1 // 因为不是逆波兰,所以这里存储的op是上一个符号
for _, b := range s {
if b==' ' {
continue
} else if isNum(b) {
now = now*10 + int(b-'0')
} else {
// 因为不是逆波兰,所以需要在确认一个数结束之后,计算上一个符号的结果,例如:
/*
ans+34-
这个式子的时候,在检查到第二个符号(减号)的时候。
可以确认34这个数结束了,所以可以计算前一个值(即ans+34的结果)。
存储的op是减号之前的上一个符号,即加号
*/
ans += op*now
now = 0
if b=='+' {
// 如果遇到加号,那真实的符号应该是上一个符号
op = stack[len(stack)-1]
} else if b=='-' {
// 如果遇到减号,那真实的符号应该是上一个符号取反
op = -stack[len(stack)-1]
} else if b=='(' {
// 如果遇到括号,需要把上一个遇到的符号放进stack中,击穿的效果就在如此,每次塞进去的都是真实的符号,相当于是去掉了括号
stack = append(stack, op)
} else if b==')' {
// 如果遇到括号,代表之前括号前的符号影响消失了,应该出栈
stack = stack[:len(stack)-1]
op = stack[len(stack)-1]
}
}
}
ans += op*now
return ans
}
func isNum(r rune) bool {
if r-'0'>=0 && r-'0'<=9 {
return true
}
return false
}
性能1
解法2
两个栈,分别存数和符号,每次将当前能处理的处理掉
代码2
import (
"fmt"
"strings"
)
func calculate(s string) int {
s = strings.Replace(s, " ", "", -1)
n := len(s)
i := 0
ops := []byte{}
nums := []int{0}
tmp := 0
for i=0; i<n; i++ {
if s[i]=='(' {
ops = append(ops, '(')
} else if s[i] == ')' {
for len(ops)>=0 {
if ops[len(ops)-1]=='(' {
ops = ops[:len(ops)-1]
break
} else {
calcu(&nums, &ops)
}
}
} else {
if isNum(s[i]) {
tmp=0
for i<n && isNum(s[i]) {
tmp*=10
tmp += int(s[i]-'0')
i++
}
nums = append(nums, tmp)
i--
} else {
// 解决1-(-2)这种case
if i>0 && s[i-1]=='(' {
nums = append(nums, 0)
}
// 必须在拿到符号的时候算,不能在拿到数字的时候算
for len(ops)!=0 && ops[len(ops)-1]!='(' {
calcu(&nums, &ops)
}
ops = append(ops, s[i])
}
}
}
for len(ops)!=0 {
calcu(&nums, &ops)
}
return nums[len(nums)-1]
}
func calcu(nums *[]int, ops *[]byte) {
// 注意取地址,不然的话是新
n1 := len(*nums)
n2 := len(*ops)
if (*ops)[n2-1] == '+' {
(*nums)[n1-2] += (*nums)[n1-1]
} else {
(*nums)[n1-2] -= (*nums)[n1-1]
}
*nums = (*nums)[:n1-1]
*ops = (*ops)[:n2-1]
}
func isNum(b byte) bool {
if b-'0'>=0 && b-'0'<=9 {
return true
}
return false
}
思路2
注意:必须在拿到符号的时候算,不能在拿到数字的时候算
性能2
解法3
自己写出来的蠢笨方法,只将当前能处理的处理掉。
比如无括号的,一直往前算,如果是括号内的值,一直算到遇到左括号。
代码3
import "strings"
func calculate(s string) int {
s = strings.Replace(s, " ", "", -1)
n := len(s)
nums := []int{}
ops := []int{}
if s[0]=='-' {
nums = append(nums, 0)
}
tmpNum := 0
i := 0
lenNums := 0
lenOps := 0
for i<n {
if !isNum(s[i]) {
if s[i]=='+' {
ops = append(ops, 1)
i++
} else if s[i]=='-' {
ops = append(ops, 2)
i++
} else if s[i] == '(' {
ops = append(ops, 3)
i++
if s[i]=='-' {
ops = append(ops, 2)
nums = append(nums, 0)
i++
}
} else if s[i]==')' {
if ops[len(ops)-1] == 3 {
ops = ops[:len(ops)-1]
for lenOps = len(ops); lenOps>0; lenOps = len(ops) {
lenNums = len(nums)
if ops[lenOps-1] == 1 {
ops = ops[:lenOps-1]
nums[lenNums-2] += nums[lenNums-1]
nums = nums[:lenNums-1]
} else if ops[lenOps-1] == 2 {
ops = ops[:lenOps-1]
nums[lenNums-2] = nums[lenNums-2]-nums[lenNums-1]
nums = nums[:lenNums-1]
} else {
break
}
}
}
i++
}
continue
} else {
tmpNum = 0
for i<n && isNum(s[i]) {
tmpNum*=10
tmpNum += int(s[i]-'0')
i++
}
nums = append(nums, tmpNum)
}
for lenOps = len(ops); lenOps>0; lenOps = len(ops) {
lenNums = len(nums)
if ops[lenOps-1] == 1 {
ops = ops[:lenOps-1]
nums[lenNums-2] += nums[lenNums-1]
nums = nums[:lenNums-1]
} else if ops[lenOps-1] == 2 {
ops = ops[:lenOps-1]
nums[lenNums-2] = nums[lenNums-2]-nums[lenNums-1]
nums = nums[:lenNums-1]
} else {
break
}
}
}
for lenOps = len(ops); lenOps>0; lenOps = len(ops) {
lenNums = len(nums)
if ops[lenOps-1] == 1 {
ops = ops[:lenOps-1]
nums[lenNums-2] += nums[lenNums-1]
nums = nums[:lenNums-1]
} else if ops[lenOps-1] == 2 {
ops = ops[:lenOps-1]
nums[lenNums-2] = nums[lenNums-2]-nums[lenNums-1]
nums = nums[:lenNums-1]
} else {
ops = ops[:len(ops)-1]
}
}
return nums[0]
}
func isNum(r byte) bool {
if r-'0'>=0 && r-'0'<=9 {
return true
}
return false
}
思路3
冗余度很高,但是没有想到很好的解法,因为遇到左括号的时候的处理方式不一样,所以没法封装成方法。
性能3
42. 接雨水
题目
解法1
代码1
func trap(height []int) int {
n := len(height)
rightHigh := make([]int, n)
ans := 0
rightHigh[n-1]=height[n-1]
i := n-2
for ; i>=0; i-- {
if height[i]>rightHigh[i+1] {
rightHigh[i]=height[i]
} else {
rightHigh[i]=rightHigh[i+1]
}
}
leftHighTmp := height[0]
for i=1; i<n; i++ {
if height[i]<leftHighTmp && height[i]<rightHigh[i] {
if leftHighTmp<rightHigh[i] {
ans += leftHighTmp-height[i]
} else {
ans += rightHigh[i]-height[i]
}
}
if height[i]>leftHighTmp {
leftHighTmp = height[i]
}
}
return ans
}
思路1
当前位置能存多少水=MIN(MAX(左边的每一个),MAX(右边的每一个高度))-当前高度 注意左右都不能为0,卡不住水
性能1
之前时间是99.54%
解法2
栈 // TODO
232. 用栈实现队列
题目
代码
type MyQueue struct {
queue []int
}
func Constructor() MyQueue {
return MyQueue {
queue : []int{},
}
}
func (this *MyQueue) Push(x int) {
tmp := make([]int, 0, len(this.queue))
for len(this.queue)!=0 {
tmp = append(tmp, this.queue[len(this.queue)-1])
this.queue = this.queue[:len(this.queue)-1]
}
tmp = append(tmp, x)
for len(tmp)!=0 {
this.queue = append(this.queue, tmp[len(tmp)-1])
tmp = tmp[:len(tmp)-1]
}
}
func (this *MyQueue) Pop() int {
tmp := this.queue[len(this.queue)-1]
this.queue = this.queue[:len(this.queue)-1]
return tmp
}
func (this *MyQueue) Peek() int {
return this.queue[len(this.queue)-1]
}
func (this *MyQueue) Empty() bool {
return len(this.queue)==0
}
/**
* Your MyQueue object will be instantiated and called as such:
* obj := Constructor();
* obj.Push(x);
* param_2 := obj.Pop();
* param_3 := obj.Peek();
* param_4 := obj.Empty();
*/
思路
用go写确实很蠢。
思路和普通的一样,两个栈来回倒实现队列。
我选择了在push的时候用临时栈来倒,原因是因为peek和pop是两个,push是一个。
性能
239. 滑动窗口最大值
代码
func maxSlidingWindow(nums []int, k int) []int {
n := len(nums)
ans := make([]int, 0, n-k+1)
deque := make([]int, 0, k)
var pushAndGet func(i int)int
pushAndGet = func(i int)int {
for len(deque)>0 && nums[deque[len(deque)-1]]<nums[i] {
deque = deque[:len(deque)-1]
}
deque = append(deque, i)
for len(deque)>0 && deque[0]<i-k+1 {
deque = deque[1:]
}
return deque[0]
}
i:=0
for ; i<k-1; i++ {
pushAndGet(i)
}
for ; i<n; i++ {
ans = append(ans, nums[pushAndGet(i)])
}
return ans
}
思路
- 原因
- 如果要进来一个又晚(向右滑动,index一定增加,一定更晚弹出滑动窗口),又相对大的数(可能比一些数大) 。
- 那么一定不用考虑之前,又小,又相对早的数。
- 做法
- 构建一个递减队列
- 从后往前看,如果当前的数比队尾的数大,把队尾数扔掉
- 扔掉的这些数,就是又小,又早的数。它们未来绝对不会是需要返回的数。
- 将当前的数放在队尾
- 如果接下来的所有数都比这个数小,随着窗口右滑,前边大的数被弹出,就会获取到这个数。
- 检验队首的值是否合法(窗口范围内),将不合法的值删除后,返回队首的值
- 只需要最大的数,已经满足它是一个递减队列,所以从队首获取第一个合法的值即可。
- 只需要让队首的第一个数合法,不需要考虑后边的值是否合法。因为需要返回的是最大的值,等到队首这个数非法的时候,再去判断其他的数是否合法,找合法里的最大的值即可。
性能
性能不准的,相同的代码每次跑结果都不一样
234. 回文链表
用O(n)时间复杂度和O(1)空间复杂度
代码
func isPalindrome(head *ListNode) bool {
fast, slow := head, head
for {
if fast.Next==nil {
break
}
if fast.Next.Next==nil {
break
}
fast=fast.Next.Next
slow=slow.Next
}
right := slow.Next
var pre,next *ListNode
for right!=nil {
next=right.Next
right.Next=pre
pre=right
right=next
}
for pre!=nil && head!=nil {
if pre.Val!=head.Val {
return false
}
pre=pre.Next
head=head.Next
}
return true
}
思路
- 首先,这种链表的,还说能O(1)空间复杂度解决的,都考虑一下是否快慢指针好使
- 当快指针不能再走的时候,从慢指针截断,将后续链表翻转,然后一个个比较即可。
- 因为截断是快指针到末尾时发生的,所以两个链表最多相差一个,不用考虑最后一个(回文特性)
性能
同样,性能不准,每次跑出来的结果都不一致
剑指 Offer 22. 链表中倒数第k个节点
代码
func getKthFromEnd(head *ListNode, k int) *ListNode {
fast, slow := head, head
i := 0
for fast != nil && i < k {
fast=fast.Next
i++
}
for fast != nil {
fast=fast.Next
slow=slow.Next
}
return slow
}
思路
双指针,fast指针先走k个,然后慢指针开始走,当fast到nil的时候,slow指针指向的就是倒数第k个。
以示例为例:链表: 1->2->3->4->5,k=2,期望返回4->5
fast,slow都指向1。
fast走k步,也就是2步。第一步,fast指向2,第二步,fast指向3。
现在fast指向3,slow指向1,两者一起走,直到fast指向nil。
第一步,fast指向4,slow指向2。
第二步,fast指向5,slow指向3.
第三部,fast指向nil,slow指向4.
直接返回slow即可
性能
25. K 个一组翻转链表
代码
func reverseKGroup(head *ListNode, k int) *ListNode {
HEAD := ListNode{Next:head}
now:=&HEAD
var realPre, futureLast, pre, next *ListNode
i:=0
for now!=nil {
realPre=now
i=0
for i<k && now.Next!=nil {
now=now.Next
i++
}
if i<k {
break
}
futureLast=realPre.Next
pre=now.Next
now=realPre.Next
i=0
for i<k {
next=now.Next
now.Next=pre
pre=now
now=next
i++
}
realPre.Next=pre
now=futureLast
}
return HEAD.Next
}
思路
- 链表问题,该画图一定要画图。链表问题,可以新建一个头结点解决很多问题。
- 以题目中的1,2,3,4,5。k=2为例。
新建一个头结点HEAD为0,next指向1,用now指向HEAD这个节点。初始状态是0,1,2,3,4,5
我们期望第一次的翻转的结果是这样的:0,2,1,3,4,5
以第一次翻转为例,在翻转前希望保留几个节点,用于翻转后的连接。
0:用于指向翻转后的头节点,我起名realPre,是真正的前节点。
2:翻转后的头节点,用于被前节点连接,不用提前存储,因为反转链表后的pre会指向这里。
1:翻转后的最后一个节点,我起名futureLast。有两个作用,第一个作用,用于连接下一个待翻转的头,第二个作用,由于是从0开始的,所以下一个循环反转链表也应该从这里开始。
3:下一个待翻转的头,用于被上一个反转的最后一个节点连接,不用提前存储,因为反转链表前的next指向这里可以直接处理好。
最开始now指向0。
先保存now:0到realPre。
然后保存now.Next:1为futureLast,它是反转前的头结点,是翻转后的尾节点。
now向后走k步,也就是指向2。
(如果没有走到,通过走的步数不够会直接break,不会翻转)
2不需要提前存储,因为最后的pre会指向这里。
3不需要提前存储,通过给pre赋初始值指向这里。
pre指向now.Next:3,now指向翻转的起点,也就是1。
开始翻转,翻转k次。
翻转后,pre指向2。
将整个链表连接。
前一个节点realPre:0指向新的头结点pre:2.
(已经连接好)翻转前的头节点/翻转后的尾节点,futureLast:1指向后续节点3
刚刚的开头是0,也就是开始翻转的前一个节点,所以现在的now也需要指向下一个翻转的前节点,也就是futureLast:1。
这一次翻转的内容是1,2变成2,1
下一次翻转的内容是3,4变成4,3
性能
同样,性能的值没有意义,以下是完全相同的代码。