反转链表 | 单链表的逆置

面试题16:反转链表

一.题目描述

定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。

链表结点定义如下:

struct ListNode
{
    int m_nKey;
    ListNode* m_pNext;
};

二.分析问题

1.举例分析

2.解题思路

解法1

为了正确地反转一个链表,需要调整链表中指针的方向。为了将调整指针这个复杂的过程分析清楚,我们可以借助图形来直观地分析。如图所示的链表中,h、i和j是3个相邻的结点。假设经过若干操作,我们已经把结点h之前的指针调整完毕,这些结点的m_pNext都指向前面一个结点。接下来我们把i的m_pNext指向h,此时的链表结构如图所示。

注:

(a)一个链表。

(b)把i之前所有的结点的m_pNext都指向前一个结点,导致链表在结点i、j之间断裂。

不难注意到,由于结点i的m_pNext指向了它的前一个结点,导致我们无法在链表中遍历到结点j。为了避免链表在结点i处断开,我们需要在调整结点i的m_pNext之前,把结点j保存下来。

也就是说我们在调整结点i的m_pNext指针时,除了需要知道结点i本身之外,还需要i的前一个结点h,因为我们需要把结点i的m_pNext指向结点h。同时,我们还事先需要保存i的一个结点j,以防止链表断开。因此相应地我们需要定义3个指针,分别指向当前遍历到的结点、它的前一个结点及后一个结点。

最后我们试着找到反转后链表的头结点。不难分析出反转后链表的头结点是原始链表的尾结点。什么结点是尾结点?自然是m_pNext为NULL的结点。

需要注意的问题

  • 输入的链表头指针为NULL或者整个链表只有一个结点时,程序立即崩溃。
  • 反转后的链表出现断裂。
  • 返回的反转之后的头结点不是原始链表的尾结点。

解法2

解法2和解法1的唯一差别

  • 解法1反转后链表的头结点是原始链表的尾结点
  • 解法2反转后链表的头结点并没有改变

三.代码

解法1【就地逆置】

ListNode* ReverseList(ListNode* pHead)
{
	ListNode* pReverseHead = NULL;
	ListNode* pNode = pHead;
	ListNode* pPrev = NULL;
	while (pNode != NULL)
	{
		ListNode* pNext = pNode->m_pNext;

		if (pNext == NULL)
		{
			pReverseHead = pNode;
		}
		pNode->m_pNext = pPrev;
		pPrev = pNode;
		pNode = pNext;
	}
	return pReverseHead;
}

 

void ReverseList(ListNode* pHead)
{
	if (pHead == NULL)		reuturn NULL;

	ListNode* p = pHead->m_pNext;
	if (p == NULL)		return NULL;

	ListNode* q = NULL;
	ListNode* s = NULL;

	while (p != NULL)
	{
		q = p->m_pNext;
		p->m_pNext = s;
		s = p;
		p = q;
	}
	pHead->m_pNext = s;
}

解法2【头插法】

void ReverseList(ListNode* pHead)
{
	if (pHead == NULL)		reuturn NULL;

	ListNode* q = NULL;
	ListNode* p = pHead->m_pNext;
	pHead->m_pNext = NULL;
	
	if (p == NULL)		return NULL;

	while (p != NULL)
	{
		q = p;
		p = p->m_pNext;
		q->m_pNext = pHead->m_pNext;/*第一次执行这条语句时,q->next=NULL,此时q变为尾节点*/
		pHead->m_pNext = q;
	}
}

解法3【递归】

Node* reverse(Node* list) {
	if (list == nullptr || list->next == nullptr) {
		return list;
	}
	Node* last = reverse(list->next);
	list->next->next = list;
	list->next = nullptr;
	return last;
}

示例: 

现有如下一单链表,使用递归的方法进行逆置。 

 当执行到最后一个

Node* last = reverse(list->next);

后,last和list的位置如下图 

接着执行

list->next->next = list;
list->next = nullptr;

 

此时 return last,返回到上一个递归调用点,此时last和list的位置如下 

接着再执行

list->next->next = list;
list->next = nullptr;

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== 

此时return last,last就是逆置后链表的新头结点。 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值