什么是链表:链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
链表的头结点为head。
如图所示:
链表分为:单链表、双链表、循环链表。
链表在存储时是不连续分布的,其查找的时间复杂度为O(n),插入/删除的时间复杂度为O(n)。
一个典型的单链表定义如下:
// 单链表
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
题目链接:203. 移除链表元素
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
文章讲解/视频讲解:https://programmercarl.com/0203.%E7%A7%BB%E9%99%A4%E9%93%BE%E8%A1%A8%E5%85%83%E7%B4%A0.html
新建一个空节点,作为virtual head,连接到链表的head前面,然后遍历该链表,找到满足条件的节点,删除。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* virtual_head = new ListNode(0);
virtual_head->next = head;
ListNode* curr = head, *pre = virtual_head;
while(curr != nullptr){
if(curr->val == val){
ListNode* tmp = curr;
pre->next = curr->next;
curr = curr->next;
delete tmp;
}
else{
curr = curr->next;
pre = pre->next;
}
}
return virtual_head->next;
}
};
题目链接:707. 设计链表
你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val
和 next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev
以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList
类:
MyLinkedList()
初始化 MyLinkedList
对象。int get(int index)
获取链表中下标为 index
的节点的值。如果下标无效,则返回 -1
。void addAtHead(int val)
将一个值为 val
的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。void addAtTail(int val)
将一个值为 val
的节点追加到链表中作为链表的最后一个元素。void addAtIndex(int index, int val)
将一个值为 val
的节点插入到链表中下标为 index
的节点之前。如果 index
等于链表的长度,那么该节点会被追加到链表的末尾。如果 index
比长度更大,该节点将 不会插入 到链表中。void deleteAtIndex(int index)
如果下标有效,则删除链表中下标为 index
的节点。文章讲解/视频讲解:https://programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html
用单链表来实现,如上题一样设置一个虚拟头节点,方便插入和删除,其他部分都是模拟,多在细节上进行把握即可。
注意deleteAtIndex函数的说明,如果下标有效,则删除下标为index的节点,我没有加判断,get函数和addAtIndex函数也是类似,需要加个判断。
// 利用了leetcode为我们写好的ListNode
class MyLinkedList {
private:
ListNode* dummyHead;
public:
MyLinkedList() {
dummyHead = new ListNode(0);
}
int get(int index) {
ListNode* curr = dummyHead->next;
int curr_index = 0;
while(curr_index < index){
curr = curr->next;
curr_index++;
if(curr == nullptr) break;
}
return curr == nullptr ? -1 : curr->val;
}
void addAtHead(int val) {
ListNode* newNode = new ListNode(val);
newNode->next = dummyHead->next;
dummyHead->next = newNode;
}
void addAtTail(int val) {
ListNode* curr = dummyHead;
while(curr->next != nullptr) curr = curr->next;
ListNode* newNode = new ListNode(val);
curr->next = newNode;
}
void addAtIndex(int index, int val) {
ListNode* curr = dummyHead;
int curr_index = 0;
while(curr_index < index){
curr = curr->next;
curr_index++;
if(curr == nullptr) break;
}
if(curr == nullptr) return;
ListNode* newNode = new ListNode(val);
newNode->next = curr->next;
curr->next = newNode;
}
void deleteAtIndex(int index) {
ListNode* curr = dummyHead;
int curr_index = 0;
while(curr_index < index){
curr = curr->next;
curr_index++;
if(curr == nullptr) break;
}
if(curr == nullptr || curr->next == nullptr) return;
ListNode* tmp = curr->next;
curr->next = curr->next->next;
delete tmp;
}
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
题目链接:206. 反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
文章讲解/视频讲解:https://programmercarl.com/0206.%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8.html
设置两个节点pre和curr,分别代表前一个节点和当前节点,初始时分别指向null和头节点,然后循环进行反转操作:
每一次循环,将curr->next传递给tmp节点暂存(因为接下来curr会和后续节点截断),然后令curr->next指向pre,之后pre指向curr当前节点,curr指向tmp,开启下一次循环,直到curr指向的节点为空,此时pre指向的就是反转后的链表的头节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr, *curr = head;
while(curr != nullptr){
ListNode* tmp = curr->next;
curr->next = pre;
pre = curr;
curr = tmp;
}
return pre;
}
};