01-单链表翻转
让我们思考一下,怎么让链表翻转呢,因为链表的存储方式不像数组,数组在内存中是连续的空间,因此数组只需要知道首地址+类型大小*数组下标即可访问对应元素。但链表不同,它的每一个结点内存不一定是连续的,因此给你一个结点你唯一能知道的就是当前结点的值和下一个结点的地址。
假设我们现在拥有一个结点current,那我们还可以知道下一个结点next(current->next)
那似乎问题很简单,我们只需要遍历一遍链表,每次都让当前结点的下一个的地址指向上一个结点不就完成链表翻转了吗。问题是上一个结点(也称为当前结点的前驱)地址我怎么知道呢?你想想,当前结点不就是下一个结点(也称为当前结点的后继)的前驱结点吗。那我们每次都把当前结点用一个pre保存起来不就好了,那我们每次操作当前结点就只需要让当前结点的next指向pre不就好了。
我们用图模拟一下
现在给你一个头结点,你可以知道下一个结点next,和上一个结点pre,因为头结点是链表开始的结点,所以前驱肯定是NULL。那我们怎么翻转呢。
首先需要三个结点:前驱结点,当前结点,后继结点。
//前驱结点
struct ListNode* pre=NULL;
//当前结点从头结点开始,因此把头结点地址赋给cur
struct ListNode* cur=pHead;
//后继结点
struct ListNode* next=NULL;
无论链表多长我们都只操作当前三个结点
第一步:先保存当前结点的后继结点
next=cur->next;
第二步:让当前结点指向前驱结点
cur->next=pre;
第三步:为下次翻转做准备,当前结点已经指向了前驱结点,那我们当前结点是不是就是下一个结点的前驱了
pre=cur;
第四步:为下次翻转做准备,现在操作的结点可以后移了
cur=next;
这时我们的链表的两个结点翻转就完成,那要整个链表都翻转就要加循环了
while(){
next=cur->next;
cur->next=pre;
pre=cur;
cur=next;
}
那我们的循环判断条件是什么呢
因为我们每次都是操作当前结点,那只要当前结点不为NULL我们是不是就要一直翻转下去,所以循环判断条件就是cur!=NULL
因此最终我们的整体函数是这样的
struct ListNode* ReverseList(struct ListNode* pHead ) {
struct ListNode* pre=NULL;
struct ListNode* cur=pHead;
struct ListNode* next;
while(cur){
next=cur->next;
cur->next=pre;
pre=cur;
cur=next;
}
return pre;
}