反转链表这题真的是面试很是喜欢考的了,这题看起来简单,可是能用两种方法一遍 bug free 也是不容易的,面试的时候能够筛下来一大批人,不管是对 junior 仍是 senior 面试都很爱考。java
今天齐姐就带你梳理清楚思路,思路清楚了才能写码若有神。node
这是从力扣中文站上截下来的,可是这个输出不太形象。git
对链表的反转,并非要把它实际翻个个,只是动一动 next 指针就行了。github
咱们先看对数组进行反转。面试
数组是一个物理上连续存储的数据结构,反转以后原来放 1 的位置就变成了放 5.数组
可是链表并非,由于链表在物理上是不连续的,它的每一个单元 ListNode
是经过 next
指针链接在一块儿的,而每一个 ListNode 之间在内存里并不必定是挨着的。数据结构
因此反转链表,就不是非要把 1 的位置放 5,由于它们想在哪在哪。学习
那么怎么保证这个顺序呢?spa
沿着 next 指针的方向走下去,就是链表的顺序。这也就保证了,只要咱们拿到了头节点,就掌控了整个 LinkedList.3d
那么题目中的例子,形象点是这个样子滴:
也就是元素本身不用动,只须要动动小指针,就是反转了。
递归的三步骤你们还记得吗?
Base case + 拆解 + 组合
不记得的赶忙在公众号内回复「递归」二字,获取递归的入门篇详解。
那么咱们来看这个题的:
当只有一个 node,或者没有 node 了呗,也就是
if(node == null || node.next == null) { return node; }
其实呢,只剩一个 node 的时候严格来说并非 base case,而是 corner case,
由于它本能够再 break down 到 node == null 的,但由于后面有对 node.next 的 dereference 操做,因此不能省略。
递归的拆解就是把大问题,分解成小一点点的问题,直到 base case 能够返回,进行第三步的组合。
那么这里就是
组合的意思是,假设咱们可以拿到小问题的解,那么用小问题的解去构造大问题的解。
那么这个问题里如何构造呢?
这里很明显,在 2 后面接上 1 就好了,可是怎么拿到 2 呢?
别忘了,原问题里,此时还有 1 指向 2 呢~
也就是 node1.next = node2
,
而后把 2 指向 1:node2.next = node1
合起来就是:node1.next.next = node1
思路清楚就不绕,看着以为绕的就是没想清楚哼~
递归的代码写起来都很简洁:
class Solution { public ListNode reverseList(ListNode head) { if(head == null || head.next == null) { return head; } ListNode newHead = reverseList(head.next); head.next.next = head; head.next = null; return newHead; } }
咱们在「递归」这篇文章里说过,递归的时间复杂度分析方法就是把递归树画出来,每一个节点的时间加起来就好了。
这个递归树是一个很简单的单项链表,每一个节点上作的就是那三行代码,也就是「组合」作的事,即 O(1) 的时间,总共有 n 个节点,那么总的时间就是 O(n).
那看递归树也很明显了,每一层也就用了点小变量,是 O(1),因此总的空间共是 O(n).
(谁能告诉我这个中文的专业说法。。
Iterative 的思路就是:
过一遍这个 Linked List,边过边把这个 node 的 next 指针指向前面那个 node,直到过彻底部。
这样说太抽象,面试时也是,直接过例子。
那也就是把 1 的 next 指针翻过来指向 NULL;
把 2 的 next 指针翻过来指向 1;
把 3 的 next 指针翻过来指向 2;
...
因此咱们还须要一个变量来记录当前 node 的前一个 node,不妨设为 prev.
同时呢,一旦咱们把指针翻转过去,后面的那个 node 就丢了有木有!因此还须要有个额外的变量事先记录下后面的 node,设为 nxt,这样才不至于走丢~
翻转箭头:把 1 的 next 指针指向 NULL;
这样子,同时咱们也明确了,prev 的初始化应该是 NULL.
而后把这仨变量都移动到下一个位置:
翻转箭头:把 2 的 next 指针指向 1,
而后三人行:
翻转箭头:把 3 的 next 指针指向 2,
再齐步走:
再把 4 的反过来:
再日后走:
再把 5 的 next 反过来:
可是由于咱们的 while 循环包含了
「翻转箭头」+「三人行」
两个步骤,因此还须要走完最后一个三人行,变成:
不少同窗搞不清楚这个 while 循环的结束条件,其实很简单,你就走个例子画画图嘛!
那结束条件就是 curr = null
的时候,
最后返回的是 prev
.
好了,看代码吧:
class Solution { public ListNode reverseList(ListNode head) { ListNode prev = null; ListNode curr = head; while(curr != null) { ListNode nxt = curr.next; curr.next = prev; // 翻转箭头 prev = curr; //三人行 curr = nxt; //三人行 } return prev; } }
这里的时间复杂度很明显了,就是过了一遍这个链表,因此是 O(n).
空间是 O(1).
若是你喜欢这篇文章,记得给我点赞留言哦~大家的支持和承认,就是我创做的最大动力,咱们下篇文章见!
我是小齐,纽约程序媛,终生学习者,天天晚上 9 点,云自习室里不见不散!
更多干货文章见个人 Github: https://github.com/xiaoqi6666...