前言
今日文案
与其背着包袱埋头受罪,不如放下包袱享受生活;与其烦躁地抱怨生活命运不公,还不如从容淡定地笑对人生。
关于为什么我拖更了一天:
昨天是崩溃的一天(哭泣)
一、反转链表
题目描述:
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
题目来源:
https://leetcode.cn/problems/reverse-linked-list/
1、双指针写法:
如图:
这是最基本的单元,pre在前,cur中间,temp在后面,为什么要这么定义呢。如果我们要反转链表,我们就要改变节点中指针域指向的位置,让它指向前一个,cur代表我们要修改的节点,pre是前一个节点,temp,是给梯子cur过来的,保障运输的。
代码如下:
struct ListNode* reverseList(struct ListNode* head){ struct ListNode*cur=head; struct ListNode*pre=0; struct ListNode*temp=0; while(cur) //cur==0,就退出循环 { temp=cur->next; //temp进入下一个节点待命,准备运输 cur->next=pre; //cur指向上一个节点 pre=cur; //pre过来 cur=temp; //运输cur } return pre; }
关键点:pre一定要先与cur移动,不然它就找不到位置了
2、递归写法:
我们在双指针写法里面大致分为两个部分:改变节点链接方向,整体前移。那么我们的函数本身就是传值的,只需要在函数里面写出改变方向的代码,然后再利用函数递归来传值就可以实现了。
struct ListNode* reverse(struct ListNode*cur,struct ListNode*pre) { struct ListNode*temp=0; if(cur==0) //cur==0就返回 return pre; temp=cur->next; //改变链表值 cur->next=pre; return reverse(temp,cur); //赋值,把temp赋值给cur,cur赋值给pre,和双指针中类似 } struct ListNode* reverseList(struct ListNode* head){ return reverse(head,0); }
二、移除链表元素
题目来源:
这题其实并不难,就是找到要移除的节点,然后把它前一个节点链接到它后一个节点,把它架空就行,但我在这里遇到了极其多的空指针异常!!!
代码如下
struct ListNode* removeElements(struct ListNode* head, int val){ struct ListNode*temp=head; while(head&&head->val==val) //判断头节点是否是空或者是否要删除 { head=head->next; free(temp); temp=head; } struct ListNode*p=head; while(p&&(temp=p->next)) //temp去到p的下一个节点 { if(temp->val==val) //要删除temp位置 { p->next=temp->next; //p(此时在temp也就是要删节点的上一个节点),指向temp下一节点 temp=temp->next; //temp往前走 } else { p=p->next; //如果没有要删的,整体前移就好,继续寻找 } } return head; }
这里我还是想说一下空指针的问题,我深受其害!!!
借用一下小伙伴的图(反转链表的题):
一个是把temp放在while外面定义一个放while里面,为啥会报错一个正常呢!!
首先,指针可以是空的,但不能操控空指针,在外边定义的temp已经是cur->next了,在循环里面cur==0的时候,temp早已经是0,这样操作就会出现空指针异常!!希望对各位有一点帮助。
三、创建链表
题目来源:
题目分析:
这道题基本包含了链表的所有基础操作,增删改查。
typedef struct { int val; struct MyLinkedList* next; } MyLinkedList; MyLinkedList* myLinkedListCreate() { MyLinkedList*head=(MyLinkedList*)malloc(sizeof(MyLinkedList)); head->next=0; return head; } int myLinkedListGet(MyLinkedList* obj, int index) { MyLinkedList *cur = obj->next; for (int i = 0; cur != NULL; i++){ if (i == index){ return cur->val; } else{ cur = cur->next; } } return -1; } void myLinkedListAddAtHead(MyLinkedList* obj, int val) { MyLinkedList*p=(MyLinkedList*)malloc(sizeof(MyLinkedList)); p->val=val; p->next=0; p->next=obj->next; obj->next=p; } void myLinkedListAddAtTail(MyLinkedList* obj, int val) { MyLinkedList*temp=obj; MyLinkedList*p=(MyLinkedList*)malloc(sizeof(MyLinkedList)); p->val=val; p->next=0; while(temp->next) { temp=temp->next; } temp->next=p; } void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) { MyLinkedList*p=(MyLinkedList*)malloc(sizeof(MyLinkedList)); p->val=val; p->next=0; MyLinkedList*temp=obj; for(int i=0;i<index;i++) { temp=temp->next; if(temp==0) return; } p->next=temp->next; temp->next=p; } void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) { if (index == 0){ MyLinkedList *tmp = obj->next; if (tmp != NULL){ obj->next = tmp->next; free(tmp); } return; } MyLinkedList *cur = obj->next; for (int i = 1 ;cur != NULL && cur->next != NULL; i++){ if (i == index){ MyLinkedList *tmp = cur->next; if (tmp != NULL) { cur->next = tmp->next; free(tmp); } return; } else{ cur = cur->next; } } } void myLinkedListFree(MyLinkedList* obj) { while(obj != NULL){ MyLinkedList *tmp = obj; obj = obj->next; free(tmp); } }
下面就总结几个核心的点吧,实在写不动了:
1、注意空指针异常。
2、注意头节点是否需要删除。
3、注意遍历的边界。
总结
算法训练的第三天,好难了,哭泣!