第二章 链表part01

今日任务 

  •  链表理论基础 
  •  203.移除链表元素 
  •  707.设计链表 
  •  206.反转链表 

  详细布置 

 链表理论基础 

建议:了解一下链接基础,以及链表和数组的区别 

文章链接:代码随想录

 单链表

链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。

链表的入口节点称为链表的头结点也就是head


 双链表

单链表中的指针域只能指向节点的下一个节点。

双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。

双链表 既可以向前查询也可以向后查询

循环链表

循环链表,链表首尾相连。循环链表可以用来解决约瑟夫环问题。

// 单链表
struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};

链表的存储方式

数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。

链表是通过指针域的指针链接在内存中各个节点。

所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理

链表的定义

这是因为平时在刷leetcode的时候,链表的节点都默认定义好了,直接用就行了,所以同学们都没有注意到链表的节点是如何定义的。

node ->val调动值
node ->next调动下一个

// 单链表
struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};

 通过自己定义构造函数初始化节点:

ListNode* head = new ListNode(5);

 使用默认构造函数初始化节点:

ListNode* head = new ListNode();
head->val = 5;

链表的操作

#删除节点

只要将C节点的next指针 指向E节点就可以了。-----> 在C++里最好是再手动释放这个D节点,释放这块内存。

添加节点

 表的增添和删除都是O(1)操作,也不会影响到其他节点。要是删除第五个节点,需要从头节点查找到第四个节点通过next指针进行删除操作,查找的时间复杂度是O(n)。

链表特性和数组特性比较:

 

数组在定义的时候,长度就是固定的,如果想改动数组的长度,就需要重新定义一个新的数组。

链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景。

 203.移除链表元素  

建议: 本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。

题目链接/文章讲解/视频讲解::代码随想录

 基本方案:
分两类情况,开头,和剩下部分

每次删链条都得先拿个新的ListNode储存起来,然后,转移,next,在删掉

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        //删除头节点
        while(head != NULL && head->val == val){
            ListNode* tmp = head; //找一个储存旧head
            head = head->next;
            delete tmp;     
        } 
        ListNode* cur = head;
        while(cur!=NULL && cur->next!=NULL)  {
            if(cur->next->val == val){
                ListNode* tmp = cur->next;
                cur->next = cur->next->next;
                delete tmp;
            }else{
                cur = cur->next;
            }
        }
        return head;
    }

    //
};

 通过引入虚拟头节点,进行,不需要把头节点区分开,从真是两条开始识别,最后删掉虚拟头节点就行

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        //删除头节点
        ListNode* feakhead = new ListNode(0);
        feakhead->next = head;
        ListNode* cur = feakhead;

        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 = feakhead->next;
        delete feakhead;
        return head;
    }

    //
};

 

 707.设计链表  

建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点

题目链接/文章讲解/视频讲解:代码随想录

 结构体定义:
 

  1. 数据组织和封装:结构体允许你将多个不同的数据成员组织在一起,以表示一个复杂的数据结构。在类内部定义结构体可以帮助将相关的数据成员组织在一起,以便更容易地管理和维护。

  2. 数据抽象:结构体可以用来实现数据抽象,隐藏类的内部实现细节,提供一个公共接口供外部访问。

  3. 定义蚀刻数据结构:有时,一类可能需要使用复杂的蚀刻

  4. 提供约束性:使用结构体来表示类内部的数据成员可以提高代码的判断性。通过命名结构体来反映数据成员的关系,可以更清晰地表达类的设计意图。

  5. 简化代码:有时,一个类可能需要在内部维护

class MyClass {
public:
    struct MyStruct {
        int value;
        double data;
    };
    
    // Class methods and other members here...
private:
    MyStruct myData; 
    MyStruct my
// 使用结构体作为类的数据成员
};

  • MyLinkedList() 初始化 MyLinkedList 对象。
  •     MyLinkedList() {
            LinkedNode* FeakHead = new LinkedNode(0);
             // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
            Nodesize = 0;    
            
        }
  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
  • 通过index--刷新node
  •     
        int get(int index) {// 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1
    //例如index等于3
            if(index > (Nodesize - 1)||index < 0){
                return -1;
            }
            LinkedNode* cur = FeakHead->next; //cur 为0
            while(index--){//2 1 0
                cur = cur -> next;//cur =1 2 3
            }
            return cur->val;
    
        }

  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。 将beginnode插到feak和真正的head里边
  •     void addAtHead(int val) {//将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
            LinkedNode* beginNode = new LinkedNode(val);
            beginNode -> next = FeakHead -> next;
            FeakHead -> next = beginNode;
            Nodesize++;
        }

  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  •     void addAtTail(int val) {
            LinkedNode* endNode = new LinkedNode(val);
            LinkedNode* cur = FeakHead;
            while(cur->next != nullptr){
                cur = cur->next;
            }
            cur->next = endNode;
            Nodesize++;
    
        }
        
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  •     void addAtIndex(int index, int val) {
            LinkedNode* inneNode = new LinkedNode(val);
            LinkedNode* cur = FeakHead;
            if(index>Nodesize){
                return;
            }
            if(index<0){
                index=0;
                }
            while(index--){
                cur = cur->next;
            }
            inneNode -> next = cur -> next;
            cur->next = inneNode;
            Nodesize++; 
        }
        

  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
  •     void deleteAtIndex(int index) {
            if(index<0||index>=Nodesize){
                return;
            }
            LinkedNode* cur = FeakHead;
            while(index--){
                cur = cur->next;
            }
            LinkedNode* deleteNode = cur->next;
            cur->next = cur->next->next;
            delete deleteNode;
            deleteNode = nullptr;
            Nodesize--;
    
        }

 206.反转链表 

题目链接/文章讲解/视频讲解:代码随想录

需要三个初始化,一个pre从Null开始,一个cur,随时提前一步,一个tmp暂存cur,最后返回pre。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* tmp;
        ListNode* pre = nullptr;
        ListNode* cur = head;
        while(cur!=nullptr){
            tmp = cur;
            cur = cur->next;
            tmp->next = pre;
            pre = tmp;

        }
        return pre;


    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值