链表理论基础
链表的类型
单链表
链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思).链表的入口节点称为链表的头结点也就是head.
双链表
每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。
双链表 既可以向前查询也可以向后查询。
循环链表
链表首尾相连.
链表的存储方式
在内存中不是连续分布的
使用过指针域的指针链接在内存中的各个节点分布在内存的某地址上
链表的定义
// 单链表
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
203 移除链表元素 Remove Linked List Elements
首先去判断这个链表的头是否是空的或者和val的值相等. 如果是创建一个tmp 来充当一个临时的链表头来连接下一个链表. 在C++中需要删除链表来释放内存
接下来就是判断后面的链表是否和val的值相等,并进行判断删除.
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
//删除头结点
while (head != NULL && head->val == val){
ListNode* tmp = head;
head = head->next;
delete tmp;
}
//删除非头结点
ListNode* cur = head;
while (cur != NULL && cur->next != NULL){
if (cur->next->val == val){
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
} else {
cur = cur->next;
}
}
return head;
}
};
707 设计链表 Design Linked List
需要实现的功能
get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
设计链表的五个接口
获取链表第index个节点的数值
在链表的最前面插入一个节点
在链表的最后面插入一个节点
在链表第index个节点前面插入一个节点
删除链表的第index个节点
我们可以用虚拟头结点
class MyLinkedList {
public:
//定义链表节点结构体
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val):val(val), next(nullptr){}
};
//初始化链表
MyLinkedList() {
_dummyHead = new LinkedNode(0); //定义的头是一个虚拟的节点,不是真正的节点
_size = 0;
}
//获取到第index个节点数值,如果index是非法数值直接返回-1
int get(int index) {
if( index > (_size - 1) || index < 0){
return -1;
}
LinkedNode* cur = _dummyHead->next;
while (index--){
cur = cur->next;
}
return cur->val;
}
//在链表钱插入一个节点,插入后,此节点是链表的新的头结点
void addAtHead(int val) {
LinkedNode* newNode = new LinkedNode(val);
newNode->next = _dummyHead->next;
_dummyHead->next = newNode;
_size++;
}
//在链表最后面添加一个节点
void addAtTail(int val) {
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(cur->next != nullptr){
cur = cur->next;
}
cur->next = newNode;
_size++;
}
// 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
// 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果index大于链表的长度,则返回空
// 如果index小于0,则置为0,作为链表的新头节点。
void addAtIndex(int index, int val) {
if (index < 0 || index > _size){
return;
}
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(index--){
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
}
// 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的
void deleteAtIndex(int index) {
if (index >= _size || index < 0){
return;
}
LinkedNode* cur = _dummyHead;
while (index--){
cur = cur->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
_size--;
}
//打印链表
void printLinkedList(){
LinkedNode* cur = _dummyHead;
while (cur->next == nullptr){
cout << cur->next->val << " ";
cur = cur->next;
}
cout << endl;
}
private:
int _size;
LinkedNode* _dummyHead;
};
206 反转链表 Reverse Linked List
代码随想录思路:
首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。
然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。
为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。
接下来,就是循环走如下代码逻辑了,继续移动pre和cur指针。
最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur = head;
ListNode* pre = NULL;
ListNode* tmp;
while (cur){
tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
};