一、链表理论基础
概念:
链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
链表的入口节点称为链表的头结点也就是head。
链表类型:
单链表,双链表,循环链表
内存方式:
链表是通过指针域的指针链接在内存中各个节点。
所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。
定义代码:
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
二、移除链表元素
题目链接/文章讲解/视频讲解::代码随想录
1.c++注意要释放节点
2.特别需要意识到这个else的作用!
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) {
// 删除头结点
while (head != NULL && head->val == val) { // 不是if,可能出现连续多次删除头节点
ListNode* del = head;
head = head->next;
delete del;
}
// 删除非头结点
ListNode* cur = head;
while (cur != NULL && cur->next!= NULL) {
if (cur->next->val == val) {
ListNode* del = cur->next;
cur->next = cur->next->next;
delete del;
} else {
cur = cur->next;
}
}
return head;
}
};
方法二:虚拟头节点
代码:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode *prehead=new ListNode();
prehead->next=head;
ListNode *cur=prehead;
//一般操作链表的指针指向要被操作的节点前一个节点
//所以cur遍历到倒数第二个节点的位置
//while终止条件的判断就是最后一个进去要满足什么,这里如果是cur则最后一位也进去了
//cur->next->next则是倒数第三个
while(cur->next)
{
if(cur->next->val==val)
{ ListNode *del=cur->next;
cur->next=cur->next->next;//需保证cur->next不为NULL
delete del;
}
//用else的原因就是,如果需要删除节点的话,cur已经进行next了,不用再cur=cur->next;
//已经在新的需要判断的节点的前一个节点了
else
{
cur=cur->next;
}
}
return prehead->next;
}
};
方法三:双指针,有点鸡肋
代码:
/**
* 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 *prehead=new ListNode();
prehead->next=head;
ListNode *fast=head;
ListNode *slow=prehead;
while(fast)
{
if(fast->val==val)
{ ListNode *del=fast;
//因为这里slow的next已经更新了,所以不用slow=slow->next;
slow->next=slow->next->next;
fast=fast->next;
delete del;
}
else
{
fast=fast->next;
slow=slow->next;
}
}
return prehead->next;
}
};
三、设计链表
1.要对头节点进行操作,使用虚拟头节点比较方便
2.while(index--) index-0(终止条件) 即进入循环index次
3.链表的两个属性:头节点(虚拟头节点)
size(方便进行下标是否合理判断)
4.注意有些函数是符合要求才进行size加减的
代码:
class MyLinkedList {
public:
struct LinkedNode
{
int val;
LinkedNode *next;
LinkedNode(int n):val(n),next(NULL) {};
};
MyLinkedList() {
prehead=new LinkedNode(0);
_size=0;//用于判断下标无效的情况
}
int get(int index) {
LinkedNode *cur=prehead;
if(index>=_size||index<0)
{
return -1;
}
else
{
while(index--)
{
cur=cur->next;
}
}
return cur->next->val;
}
void addAtHead(int val) {
LinkedNode *temp=new LinkedNode(val);
if(prehead->next!=NULL)
{ LinkedNode *tep=prehead->next;
prehead->next=temp;
temp->next=tep;
}
else
{
prehead->next=temp;
}
_size++;
}
void addAtTail(int val) {
LinkedNode *cur=prehead;
LinkedNode *temp=new LinkedNode(val);
while(cur->next)
{
cur=cur->next;
}
cur->next=temp;
_size++;
}
void addAtIndex(int index, int val) {
LinkedNode *temp=new LinkedNode(val);
LinkedNode *temp1;
LinkedNode *cur=prehead;
if(index>_size)
{
return;
}
else{
while(index--)
{
cur=cur->next;
}
temp1=cur->next;
cur->next=temp;
temp->next=temp1;
_size++;
}
}
void deleteAtIndex(int index) {
LinkedNode *cur=prehead;
if(index<_size)
{
while(index--)
{
cur=cur->next;
}
LinkedNode *del=cur->next;
cur->next=cur->next->next;
delete del;
_size--;
}
}
private:
int _size;
LinkedNode* prehead;
};
四、反转链表
题目链接/文章讲解/视频讲解:代码随想录
方法:双指针法
1.反转链表就是每个节点的next指针进行修正,即可得到链表的反转
2.一开始pre=head,cur=head->next;
没有对第一个节点的next进行修正,导致报错(pre->next=nullptr后就正确或head->next=nullptr)
可以直接pre=NULL,cur=head;
代码:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *pre=NULL;
ListNode *cur=head;
ListNode *temp;
while(cur)
{
temp=cur->next;
cur->next=pre;
pre=cur;
cur=temp;//边界是把null赋值给了cur
}
return pre;
}
};
方法二:递归法
循环变成了递归,前一个节点指针和当前节点指针作为函数参数输入,不断变化直到cur为NULL结束
代码:
class Solution {
public:
ListNode* reverse(ListNode* pre,ListNode* cur){
if(cur == NULL) return pre;//递归的最后终止情况
ListNode* temp = cur->next;
cur->next = pre;
return reverse(cur,temp);
}
ListNode* reverseList(ListNode* head) {
return reverse(NULL, head);
}
};