拿捏链表(二)-------反转链表

博主csdn个人主页:小小unicorn
⏩专栏分类:leetcode

代码仓库:小小unicorn的代码仓库
关注我带你学习编程知识

反转链表

  • 题目描述
  • 思路一:
    • 1.==考虑一般情况==:
    • 2.==考虑特殊情况==:
      • 2.2当链表的最后一个节点为待移除的节点时:
      • 2.2当链表的最后一个节点为待移除的节点时:
      • 2.3当传入的链表为空时:
    • 代码实现
  • 思路二:
    • 代码实现

题目描述

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

拿捏链表(二)-------反转链表_第1张图片
拿捏链表(二)-------反转链表_第2张图片
拿捏链表(二)-------反转链表_第3张图片

思路一:

反转一个单向链表,我们可以看成是将链表中的每个结点的指向反向(也就是说从后一个结点指向前一个结点)。
拿捏链表(二)-------反转链表_第4张图片
拿捏链表(二)-------反转链表_第5张图片

我们在考虑情况的时候,还是分两种情况:先考虑一般情况,再考虑极端情况。

1.考虑一般情况

思考一下:建立两个指针变量,我们如果直接让后一个结点指向前一个结点,那么后一个结点所指向的再后面一个结点的位置是不是不确定吧?
因此,我们需要定义3个指针变量:p1,p2,p3 。

p1:记录指针指向将要反转的结点反转后要指向的位置。
p2:记录指针指向将要反转的结点。
p3:记录指针指向将要反转的结点的下一个结点。
拿捏链表(二)-------反转链表_第6张图片
在反转时,我们首先让p2指向的结点指向p1指向的位置,其次让p1,p2,p3指针统一后移.
拿捏链表(二)-------反转链表_第7张图片
如此往复。

2.考虑特殊情况

特殊情况也就是对边界进行分类讨论,有三种1情况:反转第一个结点指针的指向,反转最后一个结点指针的指向,传入的链表为空时的情况。

2.2当链表的最后一个节点为待移除的节点时:

p2记录的是指针指向将要反转的结点,所以当反转第一个结点指针的指向时,p2指针便指向的就是第一个结点。
拿捏链表(二)-------反转链表_第8张图片

反转过程中是让p2指向的结点指向p1指向的位置,所以我们只需用将p1的初始值赋值为NULL即可。这样,反转后就让第一个结点指针指向NULL了(也就是说反转后的最后一个结点指向空)。

2.2当链表的最后一个节点为待移除的节点时:

当最后一个结点的指针指向被反转时,p2刚好指向了最后一个结点,在指针反转完成之后,p1,p2,p3指针统一向后移动.

拿捏链表(二)-------反转链表_第9张图片
我们可以基本上是没有问题的,而且此时也发现遍历链表时的终止条件 需要返回的新的头指针, 需要返回的新的头指针,也就是当p2指针为NULL时停止遍历,并且返回p1指针指向的位置。

我们这里需要注意,这时这3个指针统一后移动时,p3指针的后移将会失败,p3后移前指向的是NULL,因此我们后移p3指针前需判断其是否为空。

2.3当传入的链表为空时:

如果传入的链表为空,我们根本不需要对链表进行任何操作,直接返回传入的头指针就可解决。(只有一个节点时也满足)

代码实现

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指针指向的位置
}

思路二:

将原链表的结点,从头到尾遍历完,然后一个一个拿下来头插到一个新链表中。(这个新链表起始时为一个空链表)
拿捏链表(二)-------反转链表_第10张图片
直到遍历完为止。

代码实现

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;                                                //返回反转后的头指针
}

文章到这里就结束了,有更好的思路或问题的,欢迎留言评论区。

下期预告:拿捏链表(三)-----------链表的中间节点

你可能感兴趣的:(leetcode,链表,数据结构,c语言)