先看题:要求把一个链表比如1-2-3-4-5的顺序倒过来;
迭代法:
我们同样面对这样的题可以想到和移除链表中的元素一样设置两个指针来标记结点,通过不断地移动向后遍历整个链表,就可以实现链表的重排序。
代码段:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* pre=NULL;
struct ListNode* cur=head;
while(cur!=NULL){
//这个题的关键点就是我们要想到一个指针无法瞻前顾后,,所以我们要设置两个指针,一前一后。但我们在改变前面的指针域的指向后,原本的链就会断,所以我们要提前把前面的结点的后一个结点存起来。这样就可以移动两个指针。
struct ListNode* node=cur->next;
cur->next=pre;
pre=cur;
cur=node;
}
return pre;
}
为什么说这种简单粗暴法很容易理解,迭代就很好解释。首先我们管观察上面那个链表其实链表的最后一个结点5的后继结点是有一个null的。原本的顺序是1到5,5再指向null,反过来我们要把这个链表反转我们是不是也可以在原本的头结点也就是1的前面也设置一个null呢。同时我们可以设置两个指针,一个prev指针并把null赋值给prev,一个curr指针,接着我们再把头结点head赋值给curr。基本思路就是把prev赋值给curr.next,再让指针不断向后遍历把链表中的结点的指向对象变成前面那个而不是原本的后面那个就行了。但各位有没有发现其实代码中在更换结点指针域指向对象的操作之前还有一个操作就是创建了一个next的变量来存储原本curr.next的位置。上面的基本思路看上去么问题,但你仔细想想。如果没有next存放curr.next,我们又如何才能向后移动指针嘞。所以要提前把那个地址存下来,移动的时候要用再用。
最后就是简单的移动指针,重复上面的操作了。要注意的就是那个返回值不再是head了而是prev。curr指针已经到了null的上面了。新链表的头指针就是prev所指的。所以返回值是prev
递归法:
首先为什么之前说一般链表题一般都可以用递归的方法来解决,因为链表具有天然的递归性。一个递的过程一个归的过程。什么叫递的过程呢,就是简单来说为遍历链表的过程,归的意思就是遍历完,再从最后一个结点向前归的意思。
一般递归方法都是把一个大问题拆分成两个子问题,如果子问题的求解方式和大问题的求解方式一样的话,就可以不断的重复这个函数就可以不断拆解不断解决。并且最后存在最小子问题。
代码段:
/**
* 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* NewHead=reverseList(head->next);
//比如归的第二次我们来到倒数第二个结点,此时主函数的head值就是倒数第二个结点,我们要做的就是把倒数第二个结点挂到倒数第一个结点的后面,如何做呢。我们就可以利用原本的指针先head->next这个时候就是指向倒数第一个结点,再next就是倒数第一个结点的指向,所以我们写出head->next->next=head;
head->next->next=head;
head->next=NULL;
return NewHead;
}