青铜挑战-手写链表反转
链表翻转是一个出现率特别高的算法题,链表翻转是学习链表最重要的问题,没有之一
下面是来自LeetCode206:
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
解本题有两种方法,带头结点和不带头节点,重中之重,务必要搞懂
解法1. 建立虚拟头结点辅助反转链表
我们先建立一个虚拟头结点ans,这种方法其实就是对这个虚拟节点进行头插法把链表中的元素一个一个插进去,由于是头插法,所以先插进去的元素反而在后面,进而实现链表翻转,这种方法简单易懂,非常好理解,直接上代码:
ListNode* reverseList(ListNode* head) {
// 建立虚拟头结点
ListNode* ans = (ListNode*)malloc(sizeof(ListNode));
ans->next = NULL;
// 定义一个工作指针
ListNode* p = head;
// 遍历链表
while(p != NULL) {
ListNode* q = p->next;
// 把当前元素用头插法插入ans
p->next = ans->next;
ans->next = p;
// 更新工作指针
p = q;
}
return ans->next;
}
这种方式好理解应用也广,但是万一遇到这种方法被禁止的情况怎么办?比如面试官想进一步考察你的能力,这个时候就要用到不带头节点的方法了
解法2. 直接操作链表实现翻转
先看一下下面这张链表翻转原理图
我们发现,链表翻转实质上就是翻转每个节点之间的指向,如果用p,q两个指针,我们可以做到让后一个指向前一个,如让2指向1:q->next = p
,但是这样一来,2和3之间的链接就断了,导致我们永远都找不到3这个结点以及其后面的所有节点,所以我们再加入一个pre指针,使他成为p的前向结点,下面是执行示意图:
图中的prev对应pre,cur对应p,next对应q
ListNode* reverseList(ListNode* head) {
// 前向指针初始时为空
ListNode* pre = NULL;
ListNode* p = head;
// 遍历
while(p != NULL) {
ListNode* q = p->next;
// 翻转两个节点的指向,让后一个结点指向前一个节点
p->next = pre;
// 更新指针,同时向后移
pre = p;
p = q;
}
return pre;
}