- 翻转链表
翻转一个链表
样例 1:
输入: 1->2->3->null
输出: 3->2->1->null
样例 2:
输入: 1->2->3->4->null
输出: 4->3->2->1->null
挑战
在原地一次翻转完成
按照题目原地反转的要求,当前链表A1的next->A2,A2的next->A3…我们要做的是直接将A2的next变为A1,A3的next变为A2…链表的重要特性就是顺序访问,而非数组的随机访问。这里的转换涉及3个数据,需要处理的节点CurNode,我们需要讲其next更换为前一个节点PreNode,这一步更改了后链表就断了,我们还需提前保留CurNode中的原来NextNode。
根据CurNode的位置,该反转操作需要分为链表头,中间,链表尾。
1.链表头: 该过程为CurNode从A1移动到A2的过程的操作
步骤1:PreNode = CurNode #PreNode指向了A1
步骤2:CurNode = NextNode #CurNode指向了A2
步骤3:NextNode = CurNode.next #NextNode指向了A3
然后就可以进行替换操作了:
步骤4:CurNode.next = PreNode #将A2中的next指向了A1
这个算法要充分考虑操作顺序,一不留神就乱,算法肯定不是最优,后面再优化吧。然后就进链表中间段。
2.中间
这里中间的操作和之前没有差别,不赘述,但是并不是说不需要考虑,通常,序列的头尾都是容易出问题的点。后面我们可以看到。
3.链表尾
链表尾的判断为CurNode.next == None.此时我们要做什么呢?和之前的处理就不一样了。
PreNode = CurNode #PreNode指向了A1 无需移动,就放到倒数第2位置好了
CurNode = CurNode.next #CurNode指向了A2 无需移动,就放到倒数第1位置好了
NextNode = CurNode.next #NextNode指向了A3
然后就可以进行替换操作了:
CurNode.next = PreNode #将A2中的next指向了A1
CurNode = NextNode #往下走,注意这里不能按照传统的链表遍历:CurNode=CurNode.next。因为内容已变。
def reverse(self, head):
tail = head #未来的尾巴要处理一下
CurNode = head
NextNode = CurNode.next
if not NextNode or not CurNode: #如果是空链表或只有一个节点返回。
return head
while CurNode:
PreNode = CurNode #步骤1
CurNode = NextNode #步骤2
if not CurNode.next:
CurNode.next = PreNode #步骤4
break #链表尾部了,结束了
else:
NextNode = CurNode.next #步骤3
CurNode.next = PreNode #步骤4
head = CurNode
tail.next = None #不这样的话成循环链表了。A《=》A2间循环
return head
当我们遇到下一题的时候,发现和上一题差不多,**增加了2个节点的处理,开始处理点start,和结束处理点end。**那我们需要根据这几点的不同位置进行分析。
36. 翻转链表 II
翻转链表中第m个节点到第n个节点的部分
样例
输入: 1->2->3->4->5->NULL, m = 2 and n = 4,
输出: 1->4->3->2->5->NULL.
挑战
在原地一次翻转完成
注意事项
m,n满足1 ≤ m ≤ n ≤ 链表长度
假如我们要反转A3~A7之间的节点(m=3,n=7),那么我们必须记住A2和A8,分别标记为tail_of_pre,和head_of_last.中间的反转过程类似上面的做法,但我们需要在反转后将~start之前这一段,start~end反转后这一段,end~后这3段链表链接起来。
tail_of_pre.next = end
start.next = head_of_last
这样通过图形的方式展示数据动线就比较清晰了。结合这两个链表反转的问题,解答的过程基本上可分为布局,处理,善后三个阶段。
布局阶段类似与电影《天煞》中外星人飞船飞到全球主要攻击目标上空,等待计时器同步后再发起攻击的过程。而我们解题之前首先要制定策略,获取相关的信息。 而这个策略通常是基于我们已有的知识经验,我们已经知道X问题的解法,而问题Y可以转化成X,但需要额外的信息和处理。这样问题Y的思路基本上清楚了。而布局阶段就要为此做准备。
我们需要获取tail_of_pre和start的位置。题目要求m>=1.当m=1时,其实就是从头开始反转了。
if m == 1:
tail_of_pre = None
CurNode = head
NextNode = CurNode.next
tail/start = head #同问题1,未来的尾巴要处理一下
else:
for i in range(m-2): #找到第一段尾部,这里m=2,A1就是tail_of_pre.A2就是curNode,链表的访问就是这么麻烦。range(0)里面是没有东西的。所以下面这句不会执行,如果m=3,那么range(1)是0,下面这句会执行一次。tail_of_pre来到A2的位置上。
tail_of_pre = tail_of_pre.next
tail/start = tail_of_pre.next #start位置
CurNode = tail_of_pre.next #curNode位置
NextNode = CurNode.next #保留下个节点的信息
**处理阶段:**该阶段和第一阶段类似。不过这里的循环用了一个计数器.该步骤结束后,我们得到了end/curNode和head_of_last的节点位置。curNode就是end位置。
count = 0
while count < n-m:
PreNode = CurNode #步骤1
CurNode = NextNode #步骤2
if count == n-m:
head_of_last = CurNode.next #找到head_of_last位置
CurNode.next = PreNode #步骤4
break
else:
NextNode = CurNode.next #步骤3
head_of_last = NextNode
CurNode.next = PreNode #步骤4
count+=1
**善后阶段:**将3段串起来。这里有个从头开始反转和不从头开始反转的区别。
start.next = head_of_last
if tail_of_pre:
tail_of_pre.next = CurNode
return head
if not tail_of_pre:
return CurNode
完整的代码段如下,反正感觉不好,多个指针一起move,ganjuejiuxiangchishi,
def reverseBetween(self, head, m, n):
# write your code here
tail_of_pre = head #第一段的尾部
head_of_last = head #第三段的头部
start = head #第二段未来的尾部
CurNode = head #第二段,也就是待处理段的游标
if not head.next:
return head
if m == 1:
tail_of_pre = None
CurNode = head
NextNode = CurNode.next
start = head
else:
for i in range(m-2):
tail_of_pre = tail_of_pre.next
start = tail_of_pre.next #当前的起始节点,未来的尾巴
CurNode = tail_of_pre.next #开始处理
NextNode = CurNode.next #保留下个节点的信息
if not NextNode or not CurNode: #如果是空链表或只有一个节点返回。
return head
count = 0
while count < n-m:
PreNode = CurNode #步骤1
CurNode = NextNode #步骤2开始
if count == n-m:
head_of_last = CurNode.next
CurNode.next = PreNode
break
else:
NextNode = CurNode.next #步骤2结束
head_of_last = NextNode
CurNode.next = PreNode #步骤3
count+=1
start.next = head_of_last
if tail_of_pre:
tail_of_pre.next = CurNode
return head
if not tail_of_pre:
return CurNode