代码随想录算法训练营第三天| 203.移除链表元素、707.设计链表、206.反转链表
1. 数据结构–链表
链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针)。链表在内存中不是连续分布的,而是通过指针域的指针链接在内存中各个节点。链表的增添和删除的时间复杂度为 O(1),查找的时间复杂度为 O(n)。
2. 203.移除链表元素
题目:删除链表中等于给定值 val 的所有节点。
思路:重点在于删除头节点和删除其他节点的方式不一样,因此有两种方法,一种是分别编写删除头节点和删除其他节点的代码,另一种是采用虚拟头节点的方法,统一删除头节点和其他节点,这里只展示虚拟头节点方法,如下所示(注意怎么设置指针p的值,使得删除某节点时,p指向的是该节点的上一节点):
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode *p, *dummy_head;
dummy_head = (struct ListNode*)calloc(sizeof(struct ListNode),1);
dummy_head->next = head;
p = dummy_head;
while(p->next!=NULL) {
if(p->next->val == val) {
struct ListNode *temp;
temp = p->next;
p->next = p->next->next;
free(temp);
}
else
p = p->next;
}
return dummy_head->next;
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
3. 707.设计链表
题目:涉及到链表的五个基本操作。
思路:需要理解虚拟头节点的使用方法,注意循环的终止条件以及空指针的判断。代码如下:
typedef struct MyLinkedList{
int val;
struct MyLinkedList* next;
} MyLinkedList;
MyLinkedList* myLinkedListCreate() {
MyLinkedList *head = (MyLinkedList *)calloc(sizeof(MyLinkedList),1);
head -> next = NULL;
return head;
}
int myLinkedListGet(MyLinkedList* obj, int index) {
MyLinkedList *p = obj -> next;
for(int i = 0; p != NULL; i++) {
if(i == index)
return p -> val ;
else
p = p -> next;
}
return -1;
}
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
MyLinkedList *new_head;
new_head = (MyLinkedList *)calloc(sizeof(MyLinkedList),1);
new_head -> val = val;
new_head -> next = obj -> next;
obj -> next = new_head;
}
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
MyLinkedList *new_tail, *p = obj ;
new_tail = (MyLinkedList *)calloc(sizeof(MyLinkedList),1);
new_tail -> val = val;
new_tail -> next = NULL;
while(p->next != NULL)
p = p -> next;
p->next = new_tail;
}
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
MyLinkedList *p = obj;
MyLinkedList *nnode = (MyLinkedList *)calloc(sizeof(MyLinkedList),1);
nnode -> val = val;
for(int i = 0; p != NULL; i++) {
if(i == index) {
nnode -> next = p -> next;
p -> next = nnode;
}
else
p = p -> next;
}
}
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
MyLinkedList *p = obj;
for(int i = 0; p != NULL; i++) {
if(i == index && p->next != NULL)
p -> next = p -> next -> next;
else
p = p -> next;
}
}
void myLinkedListFree(MyLinkedList* obj) {
free(obj);
}
4. 206.反转链表
题目:反转一个单链表。
示例:输入:1->2->3->4->5->NULL 输出:5->4->3->2->1->NULL
思路:双指针法,改变每个节点的指向即可,注意双指针的初始化以及反转结束的终止条件的设置。递归法,思路参考双指针法,代码更简洁(掌握得还不够熟练)。代码如下:
双指针法:
struct ListNode* reverseList(struct ListNode* head){
struct ListNode *cur = head, *pre = NULL, *temp;
while(cur) {
temp = cur -> next;
cur -> next = pre;
pre = cur;
cur = temp; //将pre和cur往后移动一位
}
return pre;
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
递归法:
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* reverse(struct ListNode* cur, struct ListNode* pre);
return reverse(head, NULL);
}
struct ListNode* reverse(struct ListNode* cur, struct ListNode* pre) {
struct ListNode *temp;
if(cur == NULL)
return pre;
temp = cur -> next;
cur -> next = pre;
return reverse(temp,cur);
}
- 时间复杂度:O(n)
- 空间复杂度:O(n)(递归调用了n层栈空间)