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.示例
1.反转链表
输入: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;
}
};
2. 两数相加
示例 1:
输入:l1 = [2,4,3], l2 = [5,6,4] 输出:[7,0,8] 解释:342 + 465 = 807.
示例 2:
输入:l1 = [0], l2 = [0] 输出:[0]
示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] 输出:[8,9,9,9,0,0,0,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* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *head = nullptr, *tail = nullptr;
int jinwei = 0;
while(l1 || l2){
int n = l1?l1->val:0;
int m = l2?l2->val:0;
int sum = n+ m + jinwei;
if(!head){
head = tail = new ListNode(sum % 10);
}else {
tail->next = new ListNode(sum % 10);
tail = tail->next;
}
jinwei = sum / 10;
if (l1){
l1=l1->next;
}
if (l2){
l2=l2->next;
}
}
if (jinwei > 0) {
tail->next = new ListNode(jinwei);
}
return head;
}
};