博主csdn个人主页:小小unicorn
⏩专栏分类:leetcode代码仓库:小小unicorn的代码仓库
关注我带你学习编程知识
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
反转一个单向链表,我们可以看成是将链表中的每个结点的指向反向(也就是说从后一个结点指向前一个结点)。
我们在考虑情况的时候,还是分两种情况:先考虑一般情况,再考虑极端情况。
思考一下:建立两个指针变量,我们如果直接让后一个结点指向前一个结点,那么后一个结点所指向的再后面一个结点的位置是不是不确定吧?
因此,我们需要定义3个指针变量:p1,p2,p3 。
p1:记录指针指向将要反转的结点反转后要指向的位置。
p2:记录指针指向将要反转的结点。
p3:记录指针指向将要反转的结点的下一个结点。
在反转时,我们首先让p2指向的结点指向p1指向的位置,其次让p1,p2,p3指针统一后移.
如此往复。
特殊情况也就是对边界进行分类讨论,有三种1情况:反转第一个结点指针的指向,反转最后一个结点指针的指向,传入的链表为空时的情况。
p2记录的是指针指向将要反转的结点,所以当反转第一个结点指针的指向时,p2指针便指向的就是第一个结点。
反转过程中是让p2指向的结点指向p1指向的位置,所以我们只需用将p1的初始值赋值为NULL即可。这样,反转后就让第一个结点指针指向NULL了(也就是说反转后的最后一个结点指向空)。
当最后一个结点的指针指向被反转时,p2刚好指向了最后一个结点,在指针反转完成之后,p1,p2,p3指针统一向后移动.
我们可以基本上是没有问题的,而且此时也发现遍历链表时的终止条件和 需要返回的新的头指针, 需要返回的新的头指针,也就是当p2指针为NULL时停止遍历,并且返回p1指针指向的位置。
我们这里需要注意,这时这3个指针统一后移动时,p3指针的后移将会失败,p3后移前指向的是NULL,因此我们后移p3指针前需判断其是否为空。
如果传入的链表为空,我们根本不需要对链表进行任何操作,直接返回传入的头指针就可解决。(只有一个节点时也满足)
struct ListNode
{
int val;
struct ListNode *next;
};
struct ListNode* reverseList(struct ListNode* head)
{
if (head == NULL || head->next == NULL) //当链表为空或只有一个结点时,无需操作
{
return head; //直接返回
}
struct ListNode* p1 = NULL; //记录指针指向将要反转的结点反转后要指向的位置。
struct ListNode* p2 = head; //记录指针指向将要反转的结点。
struct ListNode* p3 = head->next; //记录指针指向将要反转的结点的下一个结点。
while (p2) //p2为NULL时,停止遍历
{
p2->next = p1; //反转结点指向
p1 = p2; //指针后移
p2 = p3; //指针后移
if (p3) //判断p3是否为NULL
{
p3 = p3->next; //指针后移
}
}
return p1; //返回p1指针指向的位置
}
将原链表的结点,从头到尾遍历完,然后一个一个拿下来头插到一个新链表中。(这个新链表起始时为一个空链表)
直到遍历完为止。
struct ListNode
{
int val;
struct ListNode *next;
};
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* cur = head; //记录当前待头插的结点
struct ListNode* newhead = NULL; //新链表初始时为空
while (cur) //链表中结点头插完毕时停止循环
{
struct ListNode* next = cur->next; //记录下一个待头插的结点
cur->next = newhead; //将结点头插至新链表
newhead = cur; //新链表头指针后移
cur = next; //指向下一个待头插的结点
}
return newhead; //返回反转后的头指针
}
文章到这里就结束了,有更好的思路或问题的,欢迎留言评论区。
下期预告:拿捏链表(三)-----------链表的中间节点