链表是数据结构中比较重要的一段内容,面试中也很多关于链表的问题。今天学习了链表的反转,总结一下,大致有5个方法。
结构体声明如下
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
1.将旧链表内容存进数组,再倒序的方式存入链表
比较浪费空间,需要多次遍历,但是比较容易。这里不再赘述。
2.从p->next开始遍历,不断把节点插到head的后面
head
↓
比如 1——2——3——4——5
1——3——2——4——5
1——4——3——2——5
1——5——4——3——2
最后通过首尾相连,再断链的方法就完成了。 如果你的链表是有空链表头的,那么则可以省去最后的连接步骤。
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* p = pHead->next;
ListNode* q;
while(p->next != NULL){ //把P的后面一个节点插入头部后面
q = p->next;
p->next = q->next;
q->next = pHead->next;
pHead->next = q;
}
p->next = pHead; //成环
pHead = p->next->next; //头部向后移一个
p->next->next = NULL; //断链
return pHead;
}
};
3.创建一个新表,遍历旧表,把节点依次插入新表的头部
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* oldList = pHead;
ListNode* newList = NULL;
ListNode* tmp;
while(oldList != NULL){
tmp = oldList; // 得到要插入的节点
oldList = tmp->next; // oldList 向后移
tmp->next = newList; // 把新表插入 tmp 后面
newList = tmp; // 新表此时 head 为tmp
}
return newList;
}
};
4.用三个指针,遍历一次链表,完成每个节点都指向前面节点的工作
即 从 A > B > C > D > E > NULL
到 NULL < A < B < C < D < E 的过程
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(pHead == NULL)
return NULL;
ListNode* pre = NULL;
ListNode* next = NULL;
ListNode* head = pHead;
while(head != NULL){
next = head->next;
head->next = pre;
pre = head;
head = next;
}
return pre;
}
};
5.递归方法。
递归的方法相对来说就很巧妙了,用递归一直移动到链表的最后,返回一个newHead,这是我们的新头节点
从递归结束的时候开始分析,过程大概是这样的:节点从最后(newHead)遍历到pHead,除了newHead,其他任何节点都作为后一个节点的后续节点,并指向NULL
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(pHead == NULL) // 鲁棒性
return NULL;
if(pHead->next == NULL){ //当找到最后一个节点,返回这个节点作为newHead
return pHead;
}
ListNode* newHead = ReverseList(pHead->next); //保存newHead
pHead->next->next = pHead; //作为后一个节点的后续节点
pHead->next = NULL;
return newHead;
}
};
总结:
1.对链表的操作要非常仔细,有些操作调换一下上下位置,含义可能是完全不一样的。要想清楚逻辑以后再动手
2.什么问题其实都不妨尝试想一想递归操作能否实现。
留个问题:这是最基本的单链表的反转,那么其他链表呢?