Day03 周末疯狂补进度篇之链表

今日开始补C++,

在C++中,与Python相比,链表的基本操作有一些区别:

  1. 节点定义和指针操作:在C++中,需要使用结构体定义链表节点,其中包括数据和指向下一个节点的指针。通过指针操作,可以在链表中进行节点的插入、删除和遍历。

  2. 内存管理:在C++中,需要手动分配和释放内存。创建新节点时使用new关键字分配内存,而释放节点内存时需要使用delete关键字。这是因为C++不具备自动内存管理的能力,需要程序员手动管理。

  3. 异常处理:在C++中,需要显式处理一些异常情况,比如节点不存在或链表为空时的操作。在上面的代码示例中,当要删除的节点不存在时,会输出相应的错误信息

LinkedNode* newNode = new LinkedNode(val);
  1. LinkedNode* newNode: 这是一个指针声明。newNode是指向LinkedNode类型的指针。
  2. new LinkedNode(val): 使用new关键字在堆上动态分配一个新的LinkedNode对象,并调用其构造函数,传入参数val
  3. =: 将新创建的LinkedNode对象的地址赋值给newNode指针。

 203.移除链表元素  

题意:删除链表中等于给定值 val 的所有节点。

class Solution{
public:
    ListNode* removeElements(ListNode* head,int val){
        ListNode* dummyHead=new ListNode(0); //设置一个虚拟节点
        dummyHead->next= head;
        ListNode* cur=dummyHead;
        while (cur->next!=NULL){
            if (cur->next->val==val){
                ListNode* tmp=cur->next;
                cur->next=cur->next->next;
                delete tmp;
            }else{
                cur=cur->next;
            }
        }
        head = dummyHead->next;
        delete dummyHead;
        return head;
    }
};

 707.设计链表  

感觉还是开头,结尾,删除和插入的基本操作,但是自己构建链表不太会,还要定义private,自己释放空间。

当设计链表类时,通常将链表节点的定义设为私有(private),这样外部代码就不能直接访问或修改链表节点,只能通过链表类提供的公共(public)接口来操作链表。

class MyLinkedList{

public:
    struct LinkedNode{
        int val;  // val:用于存储节点的值。
        LinkedNode* next;    // next:指向下一个节点的指针。
        LinkedNode(int val):val(val),next(nullptr){}   //通过参数初始化列表将传入的val赋值给成员变量val,并将next初始化为nullptr
    }; 


    MyLinkedList(){
        _dummyHead=new LinkedNode(0); //new LinkedNode(0): 使用new关键字在堆上动态分配一个新的LinkedNode对象,并调用其构造函数,传入参数0
        _size=0;

    }
    //获取第index节点数值
    //如果index是非法数值,直接返回-1,注意index是从0开始的,第0个节点就是头节点
    int get(int index){
        if (index>(_size-1) || index<0){
            return -1;
        }
        LinkedNode* cur=_dummyHead->next;
        while(index--){
            cur=cur->next;
        }
        return cur->val;

    }

    //在链表最前面插入一个节点,插入后,新插入的节点成为链表的新的头节点。
    void addAtHead(int val){
        LinkedNode* newNode=new LinkedNode(val);
        newNode->next=_dummyHead->next;
        _dummyHead->next=newNode;
        _size++;
    }

    //在链表最后添加一个节点
    void addAtTail(int val){
        LinkedNode* newNode=new LinkedNode(val);
        LinkedNode* cur=_dummyHead;

        while (cur->next!=nullptr){
            cur=cur->next;
        }
        cur->next=newNode;
        _size++;
    }

    void addAtIndex(int index,int val){
        if (index>_size) return ;
        if (index<0) index=0;
        LinkedNode* newNode=new LinkedNode(val);
        LinkedNode* cur=_dummyHead;
        while(index--){
            cur=cur->next;

        }
        newNode->next=cur->next;
        cur->next=newNode;
        _size++;
    }

    //删除第index个节点,如果index大于等于链条的长度,直接return
    void deleteAtIndex(int index){
        if (index >= _size || index<0){
            return;
        }
        LinkedNode* cur = _dummyHead;
        while(index--){
            cur=cur->next;
        }
        LinkedNode* tmp=cur->next; // tmp指针指向要删除的节点  
        cur->next=cur->next->next; // 删除操作:跳过tmp指向的节点,直接将cur的next指向tmp的next
        delete tmp;// 释放tmp指向的节点的内存  

        tmp=nullptr;// 将tmp置为空,防止悬挂指针  
        _size--;
    }

    //打印链表
    void printLinkedList(){
        LinkedNode* cur=_dummyHead;
        // 当cur指向的节点的下一个节点不为空时,执行循环  
        // 这里有一个小问题,如果链表为空(即_dummyHead->next为nullptr),则循环体内的代码不会执行  
        // 这意味着即使链表为空,函数也不会输出任何信息,这可能不是预期的行为  
        while (cur->next!=nullptr){
            cout<<cur->next->val<<" ";  //cout << ... << " ";: 使用cout输出流打印cur->next->val的值,并在其后添加一个空格。
            cur=cur->next;
        }
        if (_dummyHead->next == nullptr) {  
            cout << "链表为空" << endl;  
            return;  
        }
        cout<<endl;
    }
private:
    int _size;
    LinkedNode* _dummyHead;


};

206.反转链表 

只来得及双指针法,给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

我忘了最后返回链表!!! return pre

分析思路

  1. 初始化
    • temp 用于暂存 cur 的下一个节点。
    • cur 是当前节点,开始时指向头节点。
    • pre 是 cur 的前一个节点,开始时为 NULL
  2. 迭代反转
    • 在每次循环中,首先保存 cur 的下一个节点到 temp
    • 然后将 cur 的 next 指针指向 pre,实现反转。
    • 接着更新 pre 和 cur 的值,以便进入下一次循环。
  3. 断联和重连
    • 断联:当 cur->next 被指向 pre 时,cur 原本指向的下一个节点实际上就被“断联”了,因为 cur->next 不再指向它。
    • 重连cur 和 pre 的更新确保了链表的连续性。cur 变为 temp(即原本 cur 的下一个节点),而 pre 变为 cur(即新的反转后的当前节点)。
  4. 结束条件
    • 当 cur 为 NULL 时,表示已经遍历到链表的末尾,此时 pre 就是反转后的新头节点。

为什么先断联再重连

  • 先断联:是为了改变当前节点 cur 的指向,使其不再指向原来的下一个节点,这是反转操作的关键一步。
  • 再重连:是为了保持链表的连续性,确保 cur 的前一个节点 pre 能够通过 cur 访问到 cur 的下一个节点(现在暂存在 temp 中)。

例如,对于链表 A -> B -> C,反转过程如下:

  1. 初始化:cur = Apre = NULLtemp 未使用。
  2. 第一次循环:temp = BA->next = NULL (断联A和B),pre = Acur = B。此时链表变为 B -> C 和孤立的 A
  3. 第二次循环:temp = CB->next = A (断联B和C并重连B和A),pre = Bcur = C。此时链表变为 C 和反转的部分 A -> B
  4. 第三次循环:temp = NULLC->next = B (断联C的下一个节点并重连C和B),pre = Ccur = NULL。此时链表完全反转,为 C -> B -> A
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        //保存cur下一个节点
        //当前节点是头节点
        ListNode*temp;
        ListNode* cur=head;
        ListNode* pre=NULL;
        while(cur){
            temp=cur->next;
            cur->next=pre;
            pre =cur;
            cur=temp;
        }
        return pre;
    }
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值