一、链表理论基础
链表是一种通过指针串联在一起的线性结构,每一个节点有两部分组成,一个数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null。
链表的数据结构:
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
定义构造函数,可以直接给变量赋值,采用C++默认的构造函数,则需要做以下操作。
ListNode* head = new ListNode();
head->val = 5;
(1)链表的操作
删除节点:
添加节点:
性能分析:链表和数组之间的对比
(2)链表常用的函数
- back():返回最后一个元素
- front():返回第一个元素
- 迭代器的应用:begin():返回一个元素的迭代器 end():返回最后一个元素的迭代器
- empty():判断元素是否为空
- insert():插入一个元素到list中
- pop_back():删除最后一个元素
- push_back():在list末尾添加一个元素
二、移除链表元素203
题目简述:
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
思路和想法:
对这个题目的解答,本质上是对链表的进一步了解,可以有多个指针指向同一个链表,之后按着链表的顺序进行操作(不能跳步)。
- 首先判断头结点数值是否为val,是则更换头结点,之后while循环到头结点数值不为val为止
- 后续,则判断下一个元素的数值是否为val,cur->next->val,是的话只需要cur指向下下一个元素即可。
代码实现:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
//判断头结点数值是否为val,是则更换头结点,之后while循环到头结点数值不为val为止
while (head != nullptr && head->val == val)
{
ListNode* deleteNode = head;
head = head->next;
delete deleteNode;
}
//
ListNode* cur = head;
while (cur != nullptr && cur->next != nullptr)
{
if (cur->next->val == val)
{
ListNode* deleteNode = cur->next;
cur->next = cur->next->next;
delete deleteNode;
}else
{
cur = cur->next;
}
}
return head;
}
};
三、设计链表707
题目描述:
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val
和 next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev
以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。
在链表类中实现这些功能:
- get(index):获取链表中第
index
个节点的值。如果索引无效,则返回-1
。 - addAtHead(val):在链表的第一个元素之前添加一个值为
val
的节点。插入后,新节点将成为链表的第一个节点。 - addAtTail(val):将值为
val
的节点追加到链表的最后一个元素。 - addAtIndex(index,val):在链表中的第
index
个节点之前添加值为val
的节点。如果index
等于链表的长度,则该节点将附加到链表的末尾。如果index
大于链表长度,则不会插入节点。如果index
小于0,则在头部插入节点。 - deleteAtIndex(index):如果索引
index
有效,则删除链表中的第index
个节点。
代码实现:
我这里采用虚拟头结点的方式来解决问题。这里要注意到dummyhead->next是head头结点的地址,所以访问dummyhead->next,即是访问头结点。
(1)首先设计单链表,具有两个元素,指针域 next和数值域 val
struct LinkedNode{
int val;
LinkedNode *next;
LinkedNode(int val):val(val), next(nullptr){}
};
(2)定义虚拟头结点和链表长度以及链表初始化函数
int _size; //参数长度,这个原函数应该
LinkedNode* _dummyHead; //虚拟头结点
MyLinkedList() {
_dummyHead = new LinkedNode(-1);
_size = 0;
}
(3)get(index):获取链表中第 index
个节点的值。如果索引无效,则返回-1
。
int get(int index) {
if(index < 0 || index > _size - 1) return -1;
LinkedNode* cur = _dummyHead->next; //从头结点开始遍历,遍历到第index个元素
while(index > 0){
cur = cur->next;
index--;
}
return cur->val;
}
(4)addAtHead(val):在链表的第一个元素之前添加一个值为 val
的节点。插入后,新节点将成为链表的第一个节点。
void addAtHead(int val) {
if(val < 0 || val > 1000) return;
LinkedNode* newNode = new LinkedNode(val); //新建链表结点,数值域val
//先给定头结点
newNode->next =_dummyHead->next; //newnode->next指向头结点
_dummyHead->next = newNode; //虚拟头结点指向新的头结点
_size++;
}
(5)addAtTail(val):将值为 val
的节点追加到链表的最后一个元素。
void addAtTail(int val) {
if(val < 0 || val > 1000) return ;
LinkedNode* newNode = new LinkedNode(val); //新建值为val的链表节点
LinkedNode* cur = _dummyHead; //当前节点指针变量
while(cur->next != nullptr){
cur = cur->next;
}
cur->next = newNode;
_size++;
}
(6)addAtIndex(index,val):在链表中的第 index
个节点之前添加值为 val
的节点。如果 index
等于链表的长度,则该节点将附加到链表的末尾。如果 index
大于链表长度,则不会插入节点。如果index
小于0,则在头部插入节点。----注:按照题目的逻辑实现
void addAtIndex(int index, int val) {
if(val < 0 || val > 1000 || index > _size) return; //index大于链表长度,直接返回
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
if(index < 0){
index = 0;
}
while(index--){
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
}
(7)deleteAtIndex(index):如果索引 index
有效,则删除链表中的第 index
个节点。
void deleteAtIndex(int index) {
if(index < 0 || index > _size - 1) return;
LinkedNode* cur = _dummyHead;
while(index--){
cur = cur->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
_size--;
}
四、翻转链表206
题目描述:
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
思路和想法:
如下图的动图所示,进行链表的翻转,需保存指向下一个节点的指针,之后进行cur->next = pre实现反向,最终返回头结点pre。
代码实现:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur = head;
ListNode* pre = nullptr;
ListNode* tmp;
while (cur)
{
tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
//这时pre就是头结点
return pre;
}
};