[C/C++] -- 链表

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;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值