203.移除链表元素
题目链接:203. 移除链表元素 - 力扣(LeetCode)
文档讲解:代码随想录 (programmercarl.com)
视频讲解:手把手带你学会操作链表 | LeetCode:203.移除链表元素_哔哩哔哩_bilibili
解题思路:
//标准解法
struct ListNode* removeElements(struct ListNode* head, int val) {
while (head != NULL && head -> val == val) {
//判断头结点是否为目标值,是则删除
struct ListNode *tmp = head;
//创建临时节点储存需要回收的节点
head = head -> next;
//返回新的头结点
free(tmp);
//释放临时节点
}
struct ListNode *cur = head;
//创建用于遍历链表的结构体指针cur
while (cur != NULL && cur -> next != NULL) {
//判断头结点以及头结点的下一个节点是否为空
if (cur -> next -> val == val) {
//判断下一个节点是否需要删除的节点
struct ListNode *tmp = cur -> next;
//创建临时节点储存需要回收的节点
cur -> next = cur -> next -> next;
//删除该节点并让cur继续遍历
free(tmp);
//释放该节点
} else {
cur = cur -> next;
//不是要删除的值则正常进行遍历
}
}
return head;
}
//虚拟头结点法
struct ListNode* removeElements(struct ListNode* head, int val) {
typedef struct ListNode* ListNode;
//自定义结构体类型,方便后续使用
ListNode dummyhead = (ListNode)malloc(sizeof(struct ListNode));
//为虚拟头结点分配内存空间
/*这里是sizeof(struct ListNode)而不是sizeof(ListNode),
因为我在上面自定义类型时是将一个结构体指针定义为ListNode,
直接sizeof(ListNode)的话会导致分配的内存不足,
这是我在写代码时遇到的错误*/
dummyhead -> next = head;
//把虚拟头结点赋值为该链表的头结点
ListNode cur = dummyhead;
//创建用于遍历链表的结构体指针cur
while (cur && cur -> next) {
//确保该链表不为空
if(cur -> next -> val == val) {
//判断判断下一个节点是否需要删除的节点
ListNode tmp = cur -> next;
//创建临时节点储存需要回收的节点
cur -> next = cur -> next -> next;
//删除该节点并让cur继续遍历
free(tmp);
} else {
cur = cur ->next;
}
}
return dummyhead -> next;
}
时间复杂度:O(n)
该题总结:这题难度不大,主要是要理解虚拟头结点思想,这个思想对之后的链表理解很重要。
707.设计链表
题目链接:707. 设计链表 - 力扣(LeetCode)
文档讲解:代码随想录 (programmercarl.com)
视频讲解:帮你把链表操作学个通透!LeetCode:707.设计链表_哔哩哔哩_bilibili
解题思路:
//定义单链表
typedef struct MyLinkedList{
int val;
struct MyLinkedList *next;
} MyLinkedList;
//创建链表
MyLinkedList* myLinkedListCreate() {
//记录链表的长度
MyLinkedList *dummyhead =(MyLinkedList*)malloc(sizeof (MyLinkedList));
//为虚拟头结点分配内存空间
dummyhead -> next = NULL;
return dummyhead;
}
int myLinkedListGet(MyLinkedList* obj, int index) {
//此时obj就是虚拟头结点
MyLinkedList *cur = obj -> next;
//创建用于遍历的链表指针,该指针指向下标为index的节点
for (int i = 0; cur; i++) {
if (i == index) {
return cur -> val;
} else {
cur = cur -> next;
}
}
return -1;
}
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
MyLinkedList *Newnode = (MyLinkedList*)malloc(sizeof (MyLinkedList));
//创建新节点并分配内存空间
Newnode -> val = val;
//为Newnode节点赋值
Newnode -> next = obj -> next;
//先让Newnode的下一个节点指向虚拟头结点的下一个节点
obj -> next = Newnode;
//再让虚拟头结点的下一个节点指向Newnode,先后顺序很重要
}
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
MyLinkedList *cur = obj;
//创建用于遍历的链表指针,该指针指向添加位置的上一个节点
while (cur -> next) {
//循环到链表的末尾
cur = cur ->next;
}
MyLinkedList *Newnode = (MyLinkedList*)malloc(sizeof (MyLinkedList));
//创建新节点
Newnode -> val = val;
Newnode -> next = NULL;
//为Newnode节点赋值
cur -> next = Newnode;
//在末尾插入新节点
}
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
MyLinkedList *cur = obj;
//创建用于遍历的链表指针,该指针指向添加位置的上一个节点
MyLinkedList *Newnode = (MyLinkedList*)malloc(sizeof (MyLinkedList));
//创建新节点
Newnode -> val = val;
//为Newnode节点赋值
for (int i = 0; cur; i++) {
if (index == i) {
Newnode -> next = cur -> next;
cur -> next = Newnode;
return;
} else {
cur = cur -> next;
}
}
//遍历到添加位置的上一个节点,并插入新节点
}
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
MyLinkedList *cur = obj;
//创建用于遍历的链表指针,该指针指向添加位置的上一个节点
for (int i = 0; cur -> next; i++) {
if (index == i) {
MyLinkedList *tmp = cur -> next;
cur -> next = cur -> next -> next;
free(tmp);
return;
} else {
cur = cur -> next;
}
//遍历到需删除节点的上一个节点,创建临时节点用于后续的释放内存
//并让需删除节点的上一个节点指向需删除节点的下一个节点
}
}
void myLinkedListFree(MyLinkedList* obj) {
while (obj) {
MyLinkedList *tmp = obj;
obj = obj -> next;
free(tmp);
}
}
时间复杂度:涉及到index的为O(n),其余的为O(1)
该题总结:这题简直了,用C语言做出来跟卡尔讲的方法还是有一定区别的,因为没办法用size,涉及到index的函数就只能用for循环去遍历来确保index合法,而且每一个函数都要去思考怎么写,只能说一题更比六题强,做出来这题大概用了三个小时。
206.反转链表
题目链接:206. 反转链表 - 力扣(LeetCode)
文档讲解:代码随想录 (programmercarl.com)
视频讲解:帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法_哔哩哔哩_bilibili
解题思路:
//双指针法
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode *cur = head;
//定义遍历链表的指针
struct ListNode *pre = NULL;
//指向cur的前一个元素的指针
while (cur) {
struct ListNode *tmp = cur -> next;
//临时储存cur的后一个元素的指针
cur -> next = pre;
//翻转cur所指的元素的next的方向
pre = cur;
//pre指针向右移动一位
cur = tmp;
//cur指针向右移动一位
}
return pre;
}
//递归解法
struct ListNode* reverse(struct ListNode *cur, struct ListNode *pre) {
if (!cur) {
return pre;
}
//设置终止条件
struct ListNode *tmp = cur -> next;
//临时储存cur的下一个元素的指针
cur -> next = pre;
return reverse(tmp, cur);
}
struct ListNode* reverseList(struct ListNode* head) {
return reverse(head, NULL);
}
时间复杂度:O(n)
该题总结:双指针法的又一种应用场景,真的很巧妙,而递归的方法我之前一直没有理解清楚什么是递归,这次通过双指针法来推导出递归法让我对递归的理解又更进了一步。
总用时:约5小时