力扣203 .移除链表元素
视频讲解
法一:原链表基础上操作
主要思路:
(1)分类讨论要删除的是头节点与其他节点
易错点:
(1)删头结点时用while,防止出现target = 1,链表是 1 1 1 1 1 1 1
(2) 要删除链表中一个元素,一定要知道这个节点上一个节点
(3) 删除其他节点时cur -> next = cur -> next -> next
(4) 节点删除后必须释放,释放格式:
struct ListNode* tmp = cur -> next;
cur -> next = cur -> next -> next;
free(tmp);
代码实现:
struct ListNode* removeElements(struct ListNode* head, int val){
//删除头结点
while (head != NULL && head -> val == val) { //删除头结点不用if防止val = 1,而链表是 1 1 1 1 1 1 1
struct ListNode* tmp = head;
head = head ->next;
free(tmp);
}
struct ListNode* cur = head;
while(cur != NULL && cur -> next != NULL) {
if(cur -> next -> val == val) { //删的不是cur,是cur的下一位
struct ListNode* tmp = cur -> next;
cur -> next = cur -> next -> next;
free(tmp);
}
else {
cur = cur -> next;
}
}
return head;
}
第一次写错误
无
补充
无
法二:虚拟头结点
主要思路:
在原头结点前插入一个虚拟头结点, 这样就把原式头结点也改为普通节点,修改方式就统一了
易错点:
(1) 遍历链表时头结点是不能改的,必须另外定义一个指针cur来遍历
(2) struct ListNode* cur这样只是声明了一个类型为ListNode的结构体指针,只有写为struct ListNode* dummyHead = (struct ListNode*)malloc(sizeof(struct ListNode))这样才是创造了一个节点
(3) 返回是返回的是dummyHead->next而不是head,因为head可能已被删除
代码实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode* dummyHead = (struct ListNode*)malloc(sizeof(struct ListNode));
dummyHead -> next = head;
struct ListNode* cur = dummyHead;
while(cur != NULL && cur -> next != NULL) {
if(cur -> next -> val == val) {
struct ListNode* tmp = cur -> next;
cur -> next = cur -> next -> next;
free(tmp);
}
else {
cur = cur -> next;
}
}
//通过这种方法释放dummyHead
struct ListNode* ret = dummyHead -> next;
free(dummyHead);
return ret;
}
第一次写错误
无
力扣206.反转链表
视频讲解
法一:双指针
主要思路:
定义两个指针cur与pre,用于将链表中指向反转过来
易错点:
(1)cur先初始化为head,pre先初始化为NULL
(2)while控制循环的条件是当cur指向空指针时,这时返回的
代码实现:
struct ListNode* reverseList(struct ListNode* head){
typedef struct ListNode ListNode;
ListNode* pre, * cur;
pre = NULL, cur = head;
while(cur) {
ListNode* tmp = cur -> next;
cur -> next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
力扣707.设计链表
视频讲解
主要思路:
链表常规操作都在里面了
易错点:
(1)本题的obj就是虚拟头结点
(2)检索下标要注意index=0或超出链表长度,而且由于虚拟头节点的存在,所以cur先从obj->next开始遍历(只要不对链表操作就可以不用头节点)
(3)插入头节点时要注意是插在obj与原头节点之间
(4)注意本题要求在所给index前增加节点,所以cur停在所给index前一个节点
(5)在所给index前增加节点时要注意index<0和超限的情况
(6)增加节点时先new -> cur -> next 再 cur -> next = new
(7)删除所给index对应节点时注意index<0和index超限,其中超限的特殊情况为index恰等于链表,此时cur为尾结点,tmp为NULL,不能free,而cur -> next -> next不存在
代码实现:
//本题意思是链表里已经有虚拟头结点了
typedef struct MyLinkedList{
int val;
struct MyLinkedList* next;
} MyLinkedList;
MyLinkedList* myLinkedListCreate() {
MyLinkedList* new = (MyLinkedList* )malloc(sizeof(MyLinkedList));
new -> next = NULL;
return new;
}
//检索下标
int myLinkedListGet(MyLinkedList* obj, int index) {
int ret = -1;
MyLinkedList* cur = obj -> next; //因为obj是虚拟头结点
for(int i = 0; cur != NULL; i++) {
if(i == index) {
ret = cur -> val;
}
else {
cur = cur -> next;
}
}
return ret;
}
//增加头结点
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
MyLinkedList* dummyHead = (MyLinkedList* )malloc(sizeof(MyLinkedList));
dummyHead -> val = val;
dummyHead -> next = obj -> next;
obj -> next = dummyHead;
}
//增加尾结点
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
MyLinkedList* newTail = (MyLinkedList* )malloc(sizeof(MyLinkedList));
MyLinkedList* cur = obj;
while(cur -> next != NULL) { //尾结点插入时必须先遍历
cur = cur -> next;
}
newTail -> val = val;
cur -> next = newTail;
newTail -> next = NULL; //还要记得把新的尾结点的next赋值NULL
}
//在所给index前增加节点
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
if(index < 0) {
myLinkedListAddAtHead(obj, val);
return;
}
MyLinkedList* cur = obj;
for(int i = 0; cur != NULL; i++) {
if(i == index) {
MyLinkedList* new = (MyLinkedList* )malloc(sizeof(MyLinkedList));
new -> val = val;
new -> next = cur -> next;
cur -> next = new;
return;
}
else {
cur = cur -> next;
}
}
}
//删除对应索引的节点
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
if(index < 0) {
return;
}
MyLinkedList* cur = obj;
for(int i = 0; cur != NULL; i++) {
if(i == index) {
MyLinkedList* tmp = cur -> next;
if(tmp != NULL) {
cur -> next = cur -> next -> next;
free(tmp);
}
}
else {
cur = cur -> next;
}
}
}
void myLinkedListFree(MyLinkedList* obj) {
while(obj != NULL) {
MyLinkedList* tmp = obj;
obj = obj -> next;
free(tmp);
}
}
/**
* Your MyLinkedList struct will be instantiated and called as such:
* MyLinkedList* obj = myLinkedListCreate();
* int param_1 = myLinkedListGet(obj, index);
* myLinkedListAddAtHead(obj, val);
* myLinkedListAddAtTail(obj, val);
* myLinkedListAddAtIndex(obj, index, val);
* myLinkedListDeleteAtIndex(obj, index);
* myLinkedListFree(obj);
*/
总结:
修改链表是可以考虑虚拟头节点,有虚拟头节点是cur一般停在目标index前一个节点