单链表的反转(非递归与递归版本)

这是Leetcode的上的一道题目:

Reverse a singly linked list.

Hint:

A linked list can be reversed either iteratively or recursively. Could you implement both?

题目要求时间复杂度为O(n),而且空间复杂度为常数级。这就必须对单链表一次遍历并且进行反序。

同时,提示有迭代方法和递归方法两种实现可以,其中对于递归方法的实现需要有较多注意点。

1.非递归版本

非递归版本的实现比较容易想到,可以申请一个空白节点指向头结点,一个保存当前遍历节点的下一个节点的指针,在每一次遍历操作中,修改当前节点的指针即可。C语言实现如下:

数据结构定义:

typedef struct ListNode{
	int val;
	struct ListNode * next;
} ListNode;


函数实现:

ListNode* reverseList(ListNode* head) {
    ListNode *q, * p = (ListNode *)malloc(sizeof(ListNode));
    p->next = NULL;
    while(head)
    {
        p->val = head->val;
        q = head->next;
        head->next = p;
        p = head;
        head = q;
    }
    q = p->next;
    free(p);
    return q;
}

经过测试后提交顺利通过。

2.递归版本

递归版本的实现起来困难一些,虽然最后的程序代码并不多,但是需要注意一些技巧。

首先,需要重新写一个递归函数,在给定的reverseList接口函数中进行调用;

其次,实现递归函数的接口,很容易想到内部的变量会在栈中保存,因此不能申请局部自动变量,否则就不是常数级的空间复杂度,因此,此处需要用到局部静态变量这个技巧(静态变量必须使用常量进行初始化)。

最后,递归调用的函数需要返回当前反转后的部分的表头和表尾,这样才能使用表尾指针修改next的值。对于表头指针,在每次递归调用时,直接传入指针变量是无法修改源指针的,需要传入指针的地址(也就是二级指针,参见另一篇文章http://blog.csdn.net/u010487568/article/details/48439313)。

总体来说,需要注意的点还是比较多的,具体实现如下(数据结构定义与非递归版本系统):

ListNode * reverseListRecursive(ListNode * head, ListNode ** nhead)
{
	static ListNode node = {0, NULL};
	static ListNode * last = &node;
	
	if (! head->next)
	{
		*nhead = head;
		return head;
	}
	last = reverseListRecursive(head->next, nhead);
	last->next = head;
	last = head;
	return last;
}

ListNode * reverseList(ListNode * head)
{
	ListNode * newhead = (ListNode * )malloc(sizeof(ListNode));
	ListNode * last = reverseListRecursive(head, &newhead);
	last->next = NULL;
	return newhead;
}
递归调用时,会一直进行栈增长,直到遇到最后一个节点,此时就会执行if语句内的代码,因为只有一个元素,因此返回值尾指针和头指针是同一个。接收到返回的尾指针后,修改尾指针的next指向当前head节点,并且修改last为当前head节点。这样递归调用完成之后,返回的last指针的next的值不为NULL,而是指向原来链表的第二个元素,因此需要在reverseList函数中进行手动修改,并最终返回新的头指针。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值