反转链表的循环方式和递归方式

出自《剑指Offer》第16题。

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

链表的结点定义:

struct ListNode
{
int m_nKey;
ListNode *m_pNext;

};


反转的过程主要是先把pNode所指向的结点用临时指针保存一下,然后你转pNode,使得pNode指向它的前一个结点。最后再更新pNode,pPre。

循环法:

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;
}

递归法:

ListNode *reverse(ListNode *pPrev,ListNode *pNode)
{
	if(pNode==NULL)
		return pPrev;
	ListNode *pNext=pNode->m_pNext;
	pNode->m_pNext=pPrev;
	pPrev=pNode;
	pNode=pNext;
	return reverse(pPrev,pNode);
}
ListNode * ReverseList_recursion(ListNode *pHead)
{
	ListNode *pNode=pHead;
	ListNode *pPrev=NULL;
	return reverse(pPrev,pNode);
}
定义链表的结构、初始化链表、打印链表中数据。

#include<iostream>
using namespace std;
struct ListNode
{
	int m_nKey;
	ListNode *m_pNext;
};
ListNode * initialList(int arr[],int len)
{
	ListNode *head=new ListNode;
	ListNode *p=head;
	ListNode * node=NULL;
	for(int ii=0;ii<len;ii++)
	{
		node=new ListNode;
		node->m_nKey=arr[ii];
		p->m_pNext=node;
		p=node;
	}
	p->m_pNext=NULL;
	ListNode *temp=head;
	head=head->m_pNext;
	delete temp;
	return head;
}
void printList(ListNode *head)
{
	if(head==NULL)
		cout<<"Empty List!"<<endl;
	else
	{
		while(head!=NULL)
		{
			cout<<head->m_nKey<<"\t";
			head=head->m_pNext;
		}
		cout<<endl;
	}
}

清空链表:

void clearList(ListNode * &head)
{
	if(head==NULL)
		return;
	while(head!=NULL)
	{
		ListNode * temp=head;
		head=head->m_pNext;
		delete temp;
		temp=NULL;
	}
	
}

测试程序:

int main()
{
	int arr[]={1,2,3,4,5,6,7,8,9,10};
	int len=sizeof(arr)/sizeof(arr[0]);

	cout<<"循环法反转链表:"<<endl;
	
	ListNode *head=initialList(arr,len);
	cout<<"反转前:"<<endl;
	printList(head);
	head=ReverseList(head);
	cout<<"反转后:"<<endl;
	printList(head);
	clearList(head);//清空链表

	cout<<"递归法反转链表"<<endl;
	head=initialList(arr,len);
	cout<<"反转前:"<<endl;
	printList(head);
	head=ReverseList_recursion(head);//递归形式
	cout<<"反转后:"<<endl;
	printList(head);
	clearList(head);

	cout<<"循环法输入为空链表时"<<endl;
	head=initialList(arr,0);
	cout<<"反转前:"<<endl;
	printList(head);
	head=ReverseList(head);
	cout<<"反转后:"<<endl;
	printList(head);
	clearList(head);

	cout<<"递归法输入为空链表时"<<endl;
	head=initialList(arr,0);
	cout<<"反转前:"<<endl;
	printList(head);
	head=ReverseList(head);
	cout<<"反转后:"<<endl;
	printList(head);


	return 0;
}

测试结果:




<think>嗯,用户现在想了解链表反转递归方法。让我先回忆一下链表反转的基本概念。链表反转是一个常见的算法问题,通常可以用迭代或者递归两种方法来解决。用户可能已经了解迭代的方法,现在想深入理解递归的实现。 首先,我需要确认链表的结构。通常单链表节点包含一个值和一个指向下一个节点的指针递归的关键在于找到基准情况和递归步骤。反转链表递归方法可能需要从链表的末尾开始,逐步反转每个节点的指针方向。 接下来,我得想清楚递归的基准情况。当链表为空或者只有一个节点时,反转后的链表就是它本身,所以基准情况应该是当节点为空或者节点的下一个节点为空时返回当前节点。这时候递归开始回溯,逐个调整指针。 然后,递归步骤应该如何处理呢?假设从第n个节点开始递归,当递归到最后一个节点时,返回该节点作为新的头节点。然后每一层递归需要将当前节点的下一个节点的next指针指向自己,同时将自己的next指针置为null,避免形成环。这一步可能需要画图来辅助理解,确保指针的正确调整。 用户可能对递归的过程感到困惑,特别是如何通过递归调用来改变指针的指向。需要详细解释每一层递归的作用,以及如何将问题分解为更小的子问题。例如,假设链表为1->2->3->4->null,递归到最后一个节点4时,返回4作为新头节点。然后回到3的递归层,将4的next指向3,同时3的next原本指向4,现在需要置为null,避免循环。依次类推,直到处理完整个链表。 还需要注意边界条件的处理,比如空链表或者只有一个节点的链表,这时候直接返回即可。同时,要确保在反转后,原来的头节点变成了尾节点,其next指针为null。 可能用户在实际编写代码时会遇到问题,比如忘记将当前节点的next置为null,导致链表出现环。这时候需要强调每一步指针调整的重要性。此外,递归的栈深度问题也需要提及,如果链表过长可能导致栈溢出,这时候迭代方法可能更优。 总结来说,递归反转链表的关键在于基准条件的确定和递归步骤中指针的调整。通过将大问题分解为小问题,逐步从后往前调整指针方向,最终实现整个链表反转。需要确保用户理解递归的调用顺序和指针变化的每一步,这样才能正确实现算法。</think>链表反转递归实现可通过分解问题与反向链接实现,以下是逐步解析: --- ### **一、基础定义** - **链表节点结构** ```java class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } ``` - **递归核心思想** 将链表分为 **头节点** 与 **剩余部分**,递归反转剩余链表后,调整指针方向。 --- ### **二、递归步骤拆解** 1. **基准条件** ```java if (head == null || head.next == null) return head; ``` - 空链表或单节点链表无需反转 2. **递归调用** ```java ListNode newHead = reverseList(head.next); // 假设后续链表反转 ``` 3. **反向链接** ```java head.next.next = head; // 将下一节点的next指向当前节点 head.next = null; // 断开原正向链接 ``` --- ### **三、执行过程演示(以1->2->3->4为例)** | 递归层级 | 操作分解 | 链表状态变化 | |----------|-------------------------------|-----------------------| | 初始状态 | `1 -> 2 -> 3 -> 4 -> null` | | | 递归至末节点 | `newHead`指向4 | `4 -> null` | | 回溯到节点3 | 设置`3.next.next = 3` | `4 -> 3`,`3.next=null` | | 回溯到节点2 | 设置`2.next.next = 2` | `4 -> 3 -> 2`,`2.next=null` | | 回溯到节点1 | 设置`1.next.next = 1` | `4 -> 3 -> 2 -> 1`,`1.next=null` | --- ### **四、代码实现** ```java public ListNode reverseList(ListNode head) { if (head == null || head.next == null) return head; ListNode newHead = reverseList(head.next); head.next.next = head; // 关键反向操作 head.next = null; // 防止循环 return newHead; } ``` --- ### **五、复杂度分析** - **时间复杂度**:$O(n)$,每个节点访问一次 - **空间复杂度**:$O(n)$,递归栈深度与链表长度成正比 --- ### **六、递归 vs 迭代对比** | | 递归实现 | 迭代实现 | |----------|------------------------|------------------------| | 代码简洁性 | ⭐⭐⭐⭐ | ⭐⭐⭐ | | 空间复杂度 | $O(n)$(栈空间) | $O(1)$ | | 适用场景 | 短链表/教学演示 | 长链表/生产环境 | --- 通过递归的逐层回溯完成指针反向,需重点理解 **“当前节点后续链表反转”** 的假设前提。建议结合图示逐步推演指针变化过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值