学习文章链接:代码随想录
一、链表基础
1.链表的类型及定义
单链表
单链表的节点由两个部分组成,一部分存放数据,另一部分存放下一个节点数据的指针,最后一个节点指向的指针为NULL。
单链表的定义:
struct Single_List_Node{
int data;
Single_List_Node *next;//Single_List_Node * 表示一个指向该结构的指针
Single_List_Node(int x):data(x),next(NULL){}//节点的构造函数,用于初始化该对象的成员变量
}
单链表的赋值:
Single_List_Node* head=new Single_List_Node(1);
Single_List_Node* second = new Single_List_Node(2);
Single_List_Node* third = new Single_List_Node(3); //一开始的每个节点的next指针都指向NULL
单链接的节点链接:
head->next = second; // 将第一个节点的next指向第二个节点
second->next = third; // 将第二个节点的next指向第三个节点
遍历并输出元素:
Single_List_Node* current = head;
while (current != NULL) {
std::cout << current->data << " "; // 箭头操作符 -> 用于通过指针访问结构体或类的成员,current->data 等价于 (*current).data
current = current->next; // 移动到下一个节点
}
双链表
双链表的节点由三个部分组成,一部分存放数据,一部分存放指向上一个节点的指针,另一部分存放指向下一个节点的指针。
双链表的定义:
struct Double_List_Node {
int data;
Double_List_Node *prev; // 指向上一个节点的指针
Double_List_Node *next; // 指向下一个节点的指针
Double_List_Node(int x) : data(x), prev(NULL), next(NULL) {} // 节点的构造函数,用于初始化该对象的成员变量
};
双链表的赋值:
Double_List_Node* head = new Double_List_Node(1);
Double_List_Node* second = new Double_List_Node(2);
Double_List_Node* third = new Double_List_Node(3); // 一开始的每个节点的prev和next指针都指向NULL
双链表的节点链接:
head->next = second; // 将第一个节点的next指向第二个节点
second->prev = head; // 将第二个节点的prev指向第一个节点
second->next = third; // 将第二个节点的next指向第三个节点
third->prev = second; // 将第三个节点的prev指向第二个节点
遍历并输出元素(从前向后):
Double_List_Node* current = head;
while (current != NULL) {
std::cout << current->data << " "; // 使用箭头操作符 -> 访问结构体或类的成员
current = current->next; // 移动到下一个节点
}
遍历并输出元素(从后向前):
Double_List_Node* current = third;
while (current != NULL) {
std::cout << current->data << " "; // 使用箭头操作符 -> 访问结构体或类的成员
current = current->prev; // 移动到上一个节点
}
循环链表
循环链表与单链表类似,但最后一个节点的next指针指向链表的头节点,形成一个循环结构。
循环链表的定义:
struct Circular_List_Node {
int data;
Circular_List_Node *next; // 指向下一个节点的指针
Circular_List_Node(int x) : data(x), next(NULL) {} // 节点的构造函数,用于初始化该对象的成员变量
};
循环链表的赋值:
Circular_List_Node* head = new Circular_List_Node(1);
Circular_List_Node* second = new Circular_List_Node(2);
Circular_List_Node* third = new Circular_List_Node(3); // 一开始的每个节点的next指针都指向NULL
循环链表的节点链接:
head->next = second; // 将第一个节点的next指向第二个节点
second->next = third; // 将第二个节点的next指向第三个节点
third->next = head; // 将第三个节点的next指向第一个节点,形成循环
遍历并输出元素:
Circular_List_Node* current = head;
if (current != NULL) {
do {
std::cout << current->data << " "; // 使用箭头操作符 -> 访问结构体或类的成员
current = current->next; // 移动到下一个节点
} while (current != head); // 当再次回到头节点时停止
}
}
二、203.移除链表元素
题目链接:203.移除链表元素
思路:分情况讨论,要删除的是否是头节点;当当前值为目标值时,将前一节点指针指向的地址更新为下一个节点的值的地址
需要改进的点:要删除节点、释放内存
直接从链表删除节点:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* current = head;
while(head!=NULL&&head->val==val){
head=head->next;
current = head;
}//注意的点:先处理完头指针,再往后处理
while (current != NULL&¤t->next!=NULL) {
if(current->next->val==val){current->next=current->next->next;
}
else{current=current->next;}
}
return head;
}
};
创建虚拟头节点:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* current = dummy;
while (current != NULL&¤t->next!=NULL) {
if(current->next->val==val){current->next=current->next->next;
}
else{current=current->next;}
}
head=dummy->next;
return head;
}
};
释放内存:
ListNode* temp = node;//先将要删除的元素做个缓存
node = node->next;//由上一个节点指向当前节点的下一个节点
delete temp;//删除缓存
三、707.设计链表
题目链接:707.设计链表
思路:先定义一个链表,观察该类实现所要求功能的需要初始化的变量,定义初始空链表。在获取链表中下标为 index 的节点的值时,需要考虑index的有效性,遍历到满足条件节点时,返回节点中data值。在头部添加变量时要注意更新head的值。在尾部添加变量时要注意考虑链表长度为0的情况。添加元素和删除元素要注意index的范围,具体操作可以通过画图的方法来理解。
未设置虚拟头节点:
struct Single_List_Node{
int data;
Single_List_Node *next;//Single_List_Node * 表示一个指向该结构的指针
Single_List_Node(int x):data(x),next(NULL){}//节点的构造函数,用于初始化该对象的成员变量
};
class MyLinkedList {
public:
Single_List_Node *head;
int length;
MyLinkedList() {
head=NULL;
length=0;
}
int get(int index) {
Single_List_Node* cur=head;
if(index<0||index>=length){
return -1;
}
else{
for(int i=0;i<index;i++){
if(cur!=NULL){
cur=cur->next;
}
else{
return -1;
}
}
return cur->data;
}
}
void addAtHead(int val) {
Single_List_Node* newhead=new Single_List_Node(val);
newhead->next=head;
head=newhead;
length++;
}
void addAtTail(int val) {
Single_List_Node* current = head;
Single_List_Node* newnode=new Single_List_Node(val);
if(length==0){
addAtHead(val);
}
else{
for (int i = 0; i < length; i++) {
if(current->next!=NULL){current = current->next;}
}
current->next=newnode;
length++;
}
}
void addAtIndex(int index, int val) {
if(index==0){
addAtHead(val);
}
else if(index==length){
addAtTail(val);
}
else if(index>0&&index<length){
Single_List_Node* current = head;
Single_List_Node* newnode=new Single_List_Node(val);
for (int i = 0; i < index-1; i++) {
current = current->next;
}
Single_List_Node* tep = current->next;
current->next=newnode;
newnode->next=tep;
length++;
}
}
void deleteAtIndex(int index) {
if(index>=0&&index<length){
if(index==0){
head=head->next;
}
else{
Single_List_Node* current = head;
for (int i = 0; i < index-1; i++) {
current = current->next;
}
Single_List_Node* tep = current->next;
current->next=current->next->next;
delete tep;
}
length--;
}
}
};
四、206.反转链表
题目链接:206.反转链表
思路:考虑链表不为空的情况,定义两个指针,一个指针pre从NULL开始,另一个指针cur从head开始,cur将当前指针指向pre,然后cur向下移动,pre更新为原cur值,如此循环,直到cur为原链表的最后一个值,跳出循环后,记得将最后一个值指向pre,因为现在pre在链表的倒数第二个位置。链表为空时返回空。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre=NULL;
ListNode* cur=head;
if(head!=NULL){
while(cur->next!=NULL){
ListNode* temp= cur->next;
cur->next=pre;
pre=cur;
cur=temp;
}
cur->next=pre;
head=cur;
}
return head;
}
};