赞
踩
目录
我们通过阅读题干,可以得到的信息有:传给我们的是一个链表的头结点,需要我们将整个链表翻转(头变尾,尾变头),再将新链表的头结点返回。
题目告知我们,可以使用递归和迭代两种方法进行实现。为了便于理解,这里采用迭代的方法进行答题。我们对比一下翻转链表的两种方法:
1、先遍历当前链表,再创建新链表,从原链表的尾结点开始,从后向前取出结点,再依次放入新链表中,全部完成之后,返回新链表的头结点。
2、直接在原链表中进行翻转,只改变next指针的指向,让从前向后指,变成从后向前指。
在时间复杂度上,两种方法没有优劣之分,但是由于第一种方法需要开辟新的空间来存放新链表,在空间复杂度上,第二种方法是可以节约空间的。所以我们认为第二种方法更好。
- struct ListNode* prev = NULL;
- struct ListNode* cur = head;
- struct ListNode* tail;
在函数内创建三个指针prev(前指针)、cur(当前指针)、tail(后指针)(也可以使用head指针来代替cur指针,我们这里为了方便理解,创建三个指针,以免初学者混淆概念)。
在逻辑结构上我们想要实现的样子是:
即prev一开始不指向任何元素,只知道他在cur指针之前,而tail永远指向cur后一个位置,在改变cur->next之后起定位作用。
接下来我们就可以让三个指针依次前进,遍历整个链表,在遍历的过程当中,让cur->next从指向tail,变为指向prev。也就是从前向后指,变成从后向前指!这样我们就可以完成一次结点的翻转。
当翻转到最后一个结点的时候,我们需要考虑一个问题。此时tail已经指向了NULL,如果再次后移,tail所指向的位置就不确定了,会变成一个野指针。而cur所指向的就会成为NULL,prev变成cur的位置,那我们应当返回的是cur还是prev呢?
其实这两个问题都很好解释,当tail已经为NULL的时候,我们因当停止后移tail指针,以防tail指针指向不明内存空间。
- while(cur)//此时cur已经为NULL,tail指针不能再向后移
- {
- tail = cur->next;
- cur->next = prev;
-
- prev = cur;
- cur = tail;
- }
再来考虑返回值的问题,我们需要返回的是新链表的头结点,那么我们三个指针当中,哪个是指向头结点的指针呢?首先排除tail,因为他最后一定是遍历到链表最后的位置,而cur此时的位置指向的是原链表的NULL位置。所以只有prev指向的是原链表的最后一个元素。经过一番简单的分析,我们可以得知,return的应该是prev指针。
leetcode通过。
此题不仅考验我们对于链表next指针的理解,同时也考察了我们对于代码的掌控能力。我们要时刻控制指针的指向位置,防止发生危险访问,并且要保证指针的顺序。严格按照前中后的顺序去移动。
- //写法一
- struct ListNode* reverseList(struct ListNode* head)
- {
- struct ListNode* prev = NULL;
- struct ListNode* cur = head;
- struct ListNode* tail;
-
- while(cur)
- {
- tail = cur->next;
- cur->next = prev;
-
- prev = cur;
- cur = tail;
- }
- return prev;
- }
- //写法二
- struct ListNode* reverseList(struct ListNode* head)
- {
- if(head == NULL)//如果链表为空,则不需要翻转,返回空指针
- {
- return NULL;
- }
- struct ListNode* prev = NULL;
- struct ListNode* cur = head;
- struct ListNode* tail = cur->next;
-
- while(cur)//用来控制翻转次数
- {
- //变更指向
- cur->next = prev;
-
- //指针移动
- prev = cur;
- cur = tail;
- if(tail)
- {
- tail = cur->next;
- }
- }
- return prev;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。