看动画,拿 Offer:大厂算法面试真题全解析( 二 )


再通过代码= head , 将指向新链表的头结点 。
最后通过代码 head = next , 将 head 指向当前原链表的头结点 。
然后完成了第 2 个节点的逆置 。调用打印出原链表与新链表 , head 对应的原链表为 3、4、5 , 对应的新链表为 1、2 。
对于第 3、4、5 个节点的逆置 , 我们开发相同的 3 组代码 , 在逆置过程中 , 可以通过打印节点变化 , 原链表从 3、4、5 变为 4、5 , 再到 5 , 最终为 NULL;新链表从 2、1 变为 3、2、1 , 再到 4、3、2、1 , 最终为 5、4、3、2、1 。
我们可以发现 , 每次逆置结点时的指针操作代码是完全一样的 , 所以可以通过循环来代替这些重复操作 。
整体代码如下 , 首先构造 5 个结点 a、b、c、d、e , 使用 next 指针将它们连接在一起 。
将 head 指向链表的第一个结点 a , 设置与 next 指针 , 并赋值为空 , 然后打印原链表与新链表 , 原始链表为 1、2、3、4、5 , 新链表为空 。
利用 for 循环重复 5 次逆置操作的代码 , 每次操作与逆置某一个结点的代码一样 , 即 4 个步骤:
代码 next = head->next , 备份 head 指针的下一个结点;head->next =  , 将 head 指向的结点与新链表的头结点连接; = head , 将指向新链表头结点 , 赋值为 head;head = next , 将 head 指向 next 指向的结点 , 即当前原链表的头结点 。
然后打印两个链表 , 我们看到 head 指向的原始链表从 1、2、3、4、5 变为 2、3、4、5 , 然后是 3、4、5 等等 , 最后变为 NULL 。指向的新链表从 NULL , 变为 1 , 然后是 2、1 等等 , 最后变为 5、4、3、2、1 。
我们刚刚通过 for 循环逆置了一个五个结点的链表 , 而在本题中我们并不知道链表结点的个数 , 题目需要开发的接口只传入了链表的头指针 head 。虽然我们可以通过循环来统计链表的结点个数 , 然后再通过 for 循环逆置链表 , 但这样做未免显得有些笨拙 。
所以我们将 for 循环修改为 while 循环 , while 循环的条件是判断 head 指针是否为空 , 当 head 为空时 , 原链表即完成了全部结点的逆置 , 而循环内的代码是完全一样的 , 这就是更通用的链表逆置方法 , 直接逆置法 。
至此 , 方法 1 就讲完了 。
除了直接逆置法 , 我们来看另一种方法 , 头插法 。
头插法需要设置一个额外的临时头结点 temp , 这里要特别注意 , temp 不是一个指针 , 而是一个结点 。
头插法利用 head 指针遍历原链表 , 每遍历一个结点 , 即将 head 访问的结点插入 temp 结点后 。当所有的结点都插入到 temp 结点后时 , head 指向空 , temp 结点的 next 指针即指向逆置后的新链表 。
我们来看头插法完整的运行过程 , 初始状态 head 指向的链表为 1、2、3、4、5 , temp 头结点后面没有结点 , 将此时 head 指向的结点插入到 temp 结点后 , 原始链表变为 2、3、4、5 , temp 结点后的链表为 1;然后将结点 2 插入到 temp 的后面 , 原始链表变为 3、4、5 , 新链表变为 2、1;随着 head 指针遍历原始链表 , 原始链表上的所有结点都被插入到结点的后面 , 最终 head 指针指向空 , temp 结点后的链表为 5、4、3、2、1 。而 temp.next 指向的地址即为新链表的第一个结点 , 我们将它返回 。