链表理论
理论知识
- 链表的定义
链表: 通过 指针串联 在一起的线性结构
由 数据域 和 指针域 组成,
头节点:链表的入口结点
- 分类
单链表、双链表、循环链表
—单链表中的指针域只能指向节点的下一个节点。
—双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点【pre、next】
—循环链表,顾名思义,就是链表首尾相连。
尾节点的next域指向 头节点
- 在内存中的存储方式
数组的存储空间是连续分布
而链表是 散乱分布在内存的地址中,通过指针相连
代码定义
链表节点的定义【一般leetcode都会给出,但自己要熟悉】
struct ListNode{
int val;
ListNode* next;
ListNode(int x) : val(x),next(null){} //节点的构造函数
}
----当自己不定义构造函数的时候,c++有默认的构造函数,不会初始化任何成员变量;
//使用默认的构造函数初始化
ListNode* head = new ListNode();
head->val = 5;
203.移除链表元素
题目
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1
输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]
题解
这里被删除的节点没有被 手动释放,如果要进行手动释放的话,如下操作:
ListNode* temp = cur->next;//将要删除的节点赋给temp节点;
xxxxxxx;
del temp; //删除temp节点
/**
* 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) {
//简单的思想--->遍历链表,遇到值为val删除
//直接在原有链表上操作的时候,头节点的操作比较特殊,需要单独处理
//当头节点的值等于val
//这里的比较顺序也有要求,如果先是head->val == val的话,如果head为空,会出现空指针异常
while( head != NULL && head->val == val){
head = head->next;
}
//处理其他节点
ListNode* cur = head;
while( cur != NULL && cur->next != NULL){
if(cur->next->val == val){ //用cur->next->val = val 等原因是要记录被删除节点的前节点
cur->next = cur->next->next;
}
else{
cur = cur->next;
}
}
return 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* dummyHead = new ListNode(0); //要new,不然会报错
dummyHead->next = head;
ListNode* cur = dummyHead;
while(cur != nullptr && cur->next != nullptr){
if(cur->next->val == val){
cur->next = cur->next->next;
}else{
cur = cur->next;
}
}
return dummyHead->next;
}
};
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
个节点。
示例:
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2); //链表变为1-> 2-> 3
linkedList.get(1); //返回2
linkedList.deleteAtIndex(1); //现在链表是1-> 3
linkedList.get(1); //返回3
题解
这是一道考察对链表的基础操作的题目;
class MyLinkedList {
public:
//这里需要自己定义链表节点
struct LinkedNode{
int val;
LinkedNode* next;
LinkedNode(int val):val(val),next(nullptr){};
};
MyLinkedList() {
dummyHead = new LinkedNode(0);
size = 0;
}
int get(int index) {
//先要进行索引有效的判断[这里就体现了size的重要性]
if(index < 0 || index > (size - 1)){
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* cur = dummyHead;
LinkedNode* newNode = new LinkedNode(val);
while(cur->next != nullptr){
cur = cur->next;
}
cur->next=newNode;
size++;
}
void addAtIndex(int index, int val) {
if (index > size || index < 0) { //这里的索引无效判断很重要,addAtIndex 和 deleteAtIndex的判断不同,要考虑特殊情况
return;
}
//增加节点的话要把cur移动到指定节点,用temp记录下一个节点
LinkedNode* cur = dummyHead;
LinkedNode* newNode =new LinkedNode(val);
while(index--){
cur=cur->next;
} //注意审题,要在节点之前添加数据
newNode->next = cur ->next;
cur->next= newNode;
size++;
}
void deleteAtIndex(int index) {
if (index >= size || index < 0) {
return;
}
//删除节点的话要把cur移动到指定节点的上一个,方便删除
LinkedNode* cur = dummyHead;
while(index--){
cur = cur->next;
}
cur->next = cur->next->next;
size--;
}
private:
//链表本身的属性
LinkedNode* dummyHead;
int size;
};
/**
* 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);
*/
写在今日
今天学习效率有点低,加上感冒有点难受,学到现在不能太专注,反转链表很重要,所以放个小假,后面会补上。加油!