目录
(一)链表理论基础
链表的定义:
// 单链表
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
ListNode* head = new ListNode(5); // 初始化
(二)移除链表元素
1.题目描述
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
2. 思路
设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了。最后return 头结点的时候,别忘了 return dummyNode->next;
, 这才是新的头结点。
最好使用虚拟头结点来使操作简洁,记得删除之后要释放空间(
delete
)
3. 解题过程
难易程度:简单
标签:递归、链表
/**
* 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* result = new ListNode(0, head);
ListNode* temp = result;
while(temp->next){
if(temp->next->val == val){
temp->next = temp->next->next;
}
else{
temp = temp->next;
}
}
return result->next;
}
};
(三)设计链表
1. 题目描述
你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性: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
的节点。
2. 思路
可能需要注意点
:delete
命令指示释放了tmp
指针原本所指的那部分内存,被delete
后的指针tmp
的值(地址)并非就是NULL
,而是随机值。也就是被delete
后,如果不再加上一句tmp=nullptr
,tmp
会成的为乱指的野指针,如果之后的程序不小心使用了tmp
,会指向难以预想的内存空间。
3. 解题过程
难易程度:中等
标签:设计、链表
看到题目其实一头雾水,虽然之前甚至有过用C来写链表的操作,但是对他给定一个框架来完成链表设计感觉不知道从何下手,后来还是大概看了一点代码随想录的内容写的,这个题目之后可能还要多看看,只会用不会建了...
很多细节问题,通过打印链表一点一点找问题在哪里,比如index和size的关系,什么时候插入什么时候删除,最后要把调试的打印链表的函数删掉,否则会超时。
class MyLinkedList {
public:
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) {};
};
MyLinkedList() {
dummyHead = new ListNode(0);
size = 0;
}
int get(int index) {
if(index >= size || index < 0){
return -1;
}
ListNode* temp = dummyHead->next;
while(index--){
temp = temp->next;
}
return temp->val;
}
void addAtHead(int val) {
ListNode* temp = new ListNode(val, dummyHead->next);
dummyHead->next = temp;
size++;
}
void addAtTail(int val) {
ListNode* temp = new ListNode(val);
ListNode* p = dummyHead;
while(p->next){
p = p->next;
}
p->next = temp;
size++;
}
void addAtIndex(int index, int val) {
if(index > size || index < 0){
return;
}
ListNode* p = new ListNode(val);
ListNode* temp = dummyHead;
while(index--){
temp = temp->next;
}
p->next = temp->next;
temp->next = p;
size++;
}
void deleteAtIndex(int index) {
if(index >= size || index < 0){
return;
}
ListNode* temp = dummyHead;
while(index--){
temp = temp->next;
}
temp->next = temp->next->next;
size--;
}
private:
ListNode* dummyHead;
int size; // 存一下链表的长度,便于get和addAtIndex的实现
};
/**
* 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);
*/
(四)反转链表
1. 题目描述
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
2. 思路
首先定义一个
cur
指针,指向头结点,再定义一个pre
指针,初始化为null
。然后就要开始反转了,首先要把
cur->next
节点指针保存一下,将cur->next
指向pre ,此时已经反转了第一个节点了。接下来,就是循环代码逻辑,继续移动
pre
和cur
指针。最后,
cur
指针已经指向了null
,循环结束,链表反转完毕。 此时return pre
指针就可以了,pre
指针就指向了新的头结点。
3. 解题过程
难易程度:简单
标签:递归、链表
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur = head;
ListNode* pre = nullptr;
ListNode* q;
while(cur){
q = cur->next;
cur->next = pre;
pre = cur;
cur = q;
}
return pre;
}
};