如何实现单链表反转
方法一:就地逆序
在遍历链表的时候,修改当前结点的指针域的指向,让其指向它的前驱结点。为此需要用一个指针变量保存前驱结点的地址。此外,为了在调整当前结点指针域的指向后还能找到后继结点,还需要另外一个指针变量来保存后继结点的地址。除此之外,还需要特别注意对链表首尾结点的特殊处理
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
void Reverse(ListNode* head)
{
if (head == NULL || head->next == NULL)
{
return;
}
ListNode* pre = NULL; //前驱结点
ListNode* cur = NULL; //当前结点
ListNode* next = NULL; //后继结点
//把链表首结点变为尾结点
cur = head->next;
next = cur->next;
cur->next = NULL;
pre = cur;
cur = next;
//使当前遍历到的结点cur指向其前驱结点
while (cur->next != NULL)
{
next = cur->next;
cur->next = pre;
pre = cur;
cur = cur->next;
cur = next;
}
//最后一个结点指向倒数第二个结点
cur->next = pre;
//链表头结点指向原来链表的尾结点
head->next = cur;
}
这种方法只需要对链表进行一次遍历,因此时间复杂度为O(n),空间复杂度为O(1)
方法二:递归法
对不带头结点的单链表进行逆序
//first是指向链表第一个结点的指针的指针
void RecursiveReverse(ListNode** first)
{
if (first == NULL || *first == NULL)
{
return;
}
ListNode* cur;
ListNode* rest;
cur = *first;
rest = cur->next;
if (rest == NULL)
{
return;
}
//逆序rest
RecursiveReverse(&rest);
//把第一个结点添加到尾结点
cur->next->next = cur;
cur->next = NULL;
//更新逆序后链表第一个结点的指向
*first = rest;
}
对带头结点的单链表进行逆序
//head是指向链表头结点指针的指针
void Reverse(ListNode** head)
{
if(*head == NULL || (*head)->next == NULL)
{
return;
}
//获取第一个结点
ListNode* firstNode = (*head)->next;
//对链表进行逆序
RecursiveReverse(&firstNode);
//头结点指向逆序后链表的第一个结点
(*head)->next = firstNode;
}
递归算法也只需要对链表进行一次遍历,因此算法的时间复杂度为O(n),但需要额外的压栈和弹栈操作
方法三:插入法
从链表第二个结点开始,把遍历到的结点插入到头结点的后面,直到遍历结束
void Reverse(ListNode* head)
{
if (head == NULL || head->next == NULL)
{
return;
}
ListNode* cur = NULL; //当前结点
ListNode* next = NULL; //后继结点
cur = head->next->next;
//设置链表第一个结点为尾结点
head->next->next = NULL;
//把遍历到结点插入到头结点后面
while (cur != NULL)
{
next = cur->next;
cur->next = head->next;
head->next = cur;
cur = next;
}
}