C/C++ 中链表是一种常见的数据结构,用于存储和组织数据。链表由节点(Node)组成,每个节点包含数据和指向下一个节点的指针。链表相对于数组的优势在于可以动态地分配内存,插入和删除操作效率高,但访问元素的随机性能较差。
1.简介
- 链表节点结构定义:
在 C/C++ 中定义链表节点的结构通常包含两部分:数据区域和指向下一个节点的指针
struct Node {int data;Node* next;
};
- 创建链表:
链表可以通过动态内存分配来创建。通过 new(C++)或 malloc(C)函数为每个节点分配内存,并通过指针串联节点形成链表。
Node* newNode(int data) {Node* node = new Node;node->data = data;node->next = nullptr;return node;
}// 创建链表头节点
Node* head = newNode(1);
head->next = newNode(2);
head->next->next = newNode(3);
- 插入节点:
链表可以通过动态内存分配来创建。通过 new(C++)或 malloc(C)函数为每个节点分配内存,并通过指针串联节点形成链表。
Node* newNode = newNode(0);
newNode->next = head;
head = newNode;
- 删除节点:
删除节点时,需要调整前一个节点的指针,使其跳过当前节点,指向当前节点的下一个节点,并释放当前节点的内存。
// 删除头节点
Node* temp = head;
head = head->next;
delete temp;// 删除中间或尾部节点
Node* prev = nullptr;
Node* curr = head;
while (curr != nullptr && curr->data != target) {prev = curr;curr = curr->next;
}
if (curr != nullptr) {prev->next = curr->next;delete curr;
}
- 遍历链表:
使用循环结构遍历链表中的所有节点,并访问节点的数据。
Node* temp = head;
while (temp != nullptr) {cout << temp->data << " ";temp = temp->next;
}
- 查找节点:
遍历链表查找特定值或条件的节点,可以实现搜索功能。
int target = 3;
Node* temp = head;
while (temp != nullptr && temp->data != target) {temp = temp->next;
}
if (temp != nullptr) {cout << "Node found with value " << target << endl;
} else {cout << "Node not found" << endl;
}
- 双向链表:
双向链表除了有指向下一个节点的指针外,还有指向前一个节点的指针,可以实现在链表两个方向上的遍历。
- 循环链表:
循环链表的最后一个节点指向头节点,形成一个闭环。可以实现循环遍历。
- 内存管理:
在使用链表时,需要注意内存管理,确保在删除节点时释放相应的内存,避免内存泄漏。
#include <iostream>
#include <cstdlib>int main() {// 分配内存int *ptr = (int*)malloc(sizeof(int));if (ptr == nullptr) {std::cout << "内存分配失败!" << std::endl;return 1;}*ptr = 42;std::cout << "指针中的值为: " << *ptr << std::endl;// 释放内存free(ptr);return 0;
}
2.示例
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
- 递归法
/*** 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* reverseList(ListNode* head) {if(!head||!head->next){/*直到当前节点的下一个节点为空时返回当前节点由于5没有下一个节点了,所以此处返回节点5*/return head;}ListNode* newHead = reverseList(head->next);/*第一轮出栈,head为5,head.next为空,返回5第二轮出栈,head为4,head.next为5,执行head.next.next=head也就是5.next=4,把当前节点的子节点的子节点指向当前节点此时链表为1->2->3->4<->5,由于4与5互相指向,所以此处要断开4.next=null此时链表为1->2->3->4<-5返回节点5第三轮出栈,head为3,head.next为4,执行head.next.next=head也就是4.next=3,此时链表为1->2->3<->4<-5,由于3与4互相指向,所以此处要断开3.next=null此时链表为1->2->3<-4<-5返回节点5第四轮出栈,head为2,head.next为3,执行head.next.next=head也就是3.next=2,此时链表为1->2<->3<-4<-5,由于2与3互相指向,所以此处要断开2.next=null此时链表为1->2<-3<-4<-5返回节点5第五轮出栈,head为1,head.next为2,执行head.next.next=head也就是2.next=1,此时链表为1<->2<-3<-4<-5,由于1与2互相指向,所以此处要断开1.next=null此时链表为1<-2<-3<-4<-5返回节点5出栈完成,最终头节点5->4->3->2->1*/head->next->next = head;head->next = nullptr;return newHead;}
};
时间复杂度:O(n),其中 n 是链表的长度。需要对链表的每个节点进行反转操作。
空间复杂度:O(n),其中 n是链表的长度。空间复杂度主要取决于递归调用的栈空间,最多为 n 层。
- 迭代
class Solution {
public:/*3个指针,prev,curr,next,它们组成一个前中后关系,从链表的一头移向另一头,起始时 prev 为空,结束时 next 为空*/ListNode* reverseList(ListNode* head) {//过去为空,现在从头,未来不存ListNode* pre = nullptr;ListNode* cur = head;while(cur){ListNode* next = cur->next;//未来出于现在cur->next = pre;//现在指向过去pre = cur;//过去成为现在cur = next;//现在成为未来}return pre;}
};