##1.《反转链表》 ——《剑指Offer》
题干如下:
题目所给接口如下:
解题:
方法一:——定义三个指针,一步步迭代将链表逆置
思路:既然想要逆置,很好的就可以想到定义两个指针来指向这个链表的头和头的下一个节点,因为这样的话,我们只需让n2 -> next = n1;即可完成这两个链表的逆置
n2->next = n1;//完成1 和 2 的逆置
但这样想了想,好像有点不太对劲。
因为我们这样做的话,我们就找不到 3 了
很自然的我们可以想到再去定义一个指针来指向 3 这样,就可以保留住 3 的地址,让我们可以迭代下去。
整理好思路后,得到下面的图:
我们让 n1 指向 NULL , n2 指向我们的head ,n3指向head->next(也即head的下一个元素的地址)
这样的话!:
让我们的n2 指向 n1 (开始为空) ,然后开始迭代起来。
让 n1 替代 上一次 n2的位置,n2替代 上一次 n3 的位置 n3替代n3的下一个位置
这样,就跑起来了
当然,数据结构嘛,某大神说过要如履薄冰~ 这里要考虑别的情况发生
如果这个链表为空怎么办?
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
struct ListNode * n1 = NULL,*n2 = head,*n3=head->next;
//这里有错误
}
那么这里的n3不就在访问空指针了?
考虑到这个问题就很好解决了,多加一个限制判断条件就行了~
struct ListNode* reverseList(struct ListNode* head){
if (head == NULL || head->next == NULL)//如果链表为空或者只有一个
return head;
struct ListNode * n1 = NULL, *n2 = head, *n3 = head->next;
}
还需要注意到一点,凡是遇到迭代,一定要考虑的是什么时候停止迭代
这里一定要慎重!慎重!(因为我第一次提交就在这栽了)
一定不是n3 == NULL 时才停止,因为在n3指向空时,n2还可以指向n1,他们两个中的val还有啊!所以,限定条件应该是 while(n2) 也即 n2不为空时 很好想,当n2为空时,就没有可以逆置的必要了。
画完图了也整理好思路了,特殊情况也考虑到位了,那么我们开始吧
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
}
if(head == NULL || head->next == NULL)//如果链表为空,或者只有一个的话
return head;
struct ListNode * n1 = NULL, * n2 = head, * n3 = head->next;
while(n2)
{
//反转
n2->next = n1;
//迭代
n1 = n2;
n2 = n3;
if(n3)
n3 = n3->next;
}
return n1;
}
运行一下~
啊这。。出现了空指针的报错。。
(还好leetcode不像牛客,给了报错提示)
出现了什么问题?其实很好想,这还是我没有考虑全面所导致的
n3已经到达了最后,指向NULL了
我这个时候再去访问它,这不是在访问空指针嘛!
改!改!改!
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
if(head == NULL || head->next == NULL)//如果链表为空,或者只有一个的话
return head;
struct ListNode * n1 = NULL, * n2 = head, * n3 = head->next;
while(n2)
{
//反转
n2->next = n1;
//迭代
n1 = n2;
n2 = n3;
if(n3)//在这判断一下,如果n3不为空的话才进行下一步迭代~
n3 = n3->next;
}
return n1;
}
方法二:——利用头插
这个方法个人很喜欢,因为头插这个方法很好就想到了
思路:举个例子,上图的例子1至5直到NULL
要想让它们逆置,将上面的数字一一拿下来头插,不就搞定了~
思路有了,代码就好写了:
首先先设置一个新的头节点 newhead 先让它指向空(NULL),然后开始头插
设置cur和next两个节点,指向第一个节点和第二个节点
至于为什么要设置next,是因为待会头插的时候,将cur拿去指向newhead后,你还得遍历到第二个节点(也即将cur移到2),但是如果没有next,你并不能得到指向2的指针。
懂得头插的同学都知道,将cur挪下来之后,要让他成为新的头节点,然后接着移动cur。
然后接着套娃,将现在的cur再挪下来当头,然后cur再继续遍历
好了好了,那么终止条件呢?
当迭代到这一步的时候,似乎可以一眼就看出来,当cur等于空时,所有的节点都进行过一次头插了。
那么限制条件就是 while(cur)
思路整理清楚了,有需要什么我们值得考虑的地方吗?
如果这个链表为空,或者只有一个节点呢?
我们先放出代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
struct ListNode * newhead = NULL;
struct ListNode * cur = head;
while(cur)
{
struct ListNode * next = cur->next;
cur->next = newhead;//头插
newhead = cur;
cur = next;//迭代
}
return newhead;
}
似乎没啥问题,cur一开始就指向我们的head,而如果链表为空,那么就是说cur指向NULL,那么循环压根进不去,直接返回newhead;
跑一下试试!
博主能力弱QAQ
如各位大佬发现错误请及时纠正和批评~