卡码网C++基础课笔记 | 13.链表的基础操作I

本文介绍了如何使用C++的构造函数实现链表的构建,包括创建节点、插入节点到链表尾部以及输出链表元素。同时讨论了数组与链表的优缺点,以及链表结构的定义和操作技巧。
摘要由CSDN通过智能技术生成

 题目:

代码:

 1、 未构造函数实现构建链表和输出链表元素功能

#include<iostream>
using namespace std;
//链表节点结构体(定义存储数据val、指针next的整体的新类型为ListNode类型)
struct ListNode
{
    int val;//存储节点的数据
    //next指针指向下一个节点,下一个节点也是链表节点,所以也是ListNode类型
    ListNode *next;
    ListNode(int x):val(x),next(NULL) {}//使用构造函数初始化val和next
};
int main()
{
    ListNode *dummyHead=new ListNode(0);//定义了虚拟头节点
    int n;
    while(cin>>n)
    {
        //可以定义一个指向当前节点的指针 cur,刚开始指向虚拟头结点。
        ListNode *cur=dummyHead;
        while(n--)
        { 
            int val;
            cin>>val;//输入节点数据
            //根据val值构造一个新的节点
            ListNode *newNode=new ListNode(val);
            //使当前尾节点next指针指向下一个新节点,从而将新节点接入链表
            cur ->next=newNode;
            //使cur指针指向新创建的节点
            cur=cur ->next;
        }
        //将cur重新指向链表的虚拟头节点,从头循环输出链表
        cur =dummyHead;
        while(cur->next!=NULL)
        {
            cur=cur ->next;//指针指向下一个节点
            cout<<cur ->val<<' ';
        }
        cout<<endl;
    }
    return 0;
}

 注意: ListNode *cur=dummyHead;指针cur的初始化需要在大循环里面,如果在大循环外面,后面组元素的输出会加上前面组的元素输出。

 2、 通过构造函数实现构建链表和输出链表元素功能

#include<iostream>
using namespace std;
//链表节点结构体(定义存储数据val、指针next的整体的新类型为ListNode类型)
struct ListNode
{
    int val;//存储节点的数据
    //next指针指向下一个节点,下一个节点也是链表节点,所以也是ListNode类型
    ListNode *next;
    ListNode(int x):val(x),next(NULL) {}//使用构造函数初始化val和next
};
//构建链表
void getList(int n,ListNode *dummyHead)
{
    //定义一个指向当前节点的指针 cur,刚开始指向虚拟头结点。
    ListNode *cur=dummyHead;
    while(n--)
    {
        int val;
        cin>>val;
        //根据val值构造一个新的节点
        ListNode *newNode=new ListNode(val);
        //使当前尾节点next指针指向下一个新节点,从而将新节点接入链表
        cur ->next=newNode;
        //使cur指针指向新创建的节点
        cur=cur ->next;
    }
}
//打印链表元素
void printList(ListNode *dummyHead)
{
    ListNode *cur=dummyHead;
    while(cur ->next!=NULL)
    {
        cur=cur ->next;
        cout<<cur ->val<<' ';
    }
    
}

int main()
{
    ListNode *dummyHead=new ListNode(0);//定义了虚拟头节点
    int n;
    while(cin>>n)
    {
        getList(n,dummyHead);
        printList(dummyHead);
        cout<<endl;
    }
    return 0;
}

 1、字符串和数组

在之前的学习中,我们接触到了字符串和数组这两种结构,它们具有着以下的共同点

  • 元素按照一定的顺序来排列
  • 可以通过索引来访问数组中的元素和字符串中的字符

但是它们也都有着一些缺点:

  • 固定大小:数组的大小通常是固定的,一旦分配了内存空间,就难以动态地扩展或缩小,如果需要存储的元素数量超出了数组的大小,就需要重新分配更大的数组,并将原来数组的内容复制过去,需要执行很多额外的操作。
  • 内存是连续的:正是因为元素按照一定的顺序来排列,它们在计算机内存中的存储也是连续的,这也就意味着,当需要存储一些需要占用空间较大的内容,也只能找一些大块的内存区域,而空间比较小的内存区域就被浪费了,从而导致了内存资源浪费。
  • 固定的数据类型:数组要求所有元素具有相同的数据类型,字符串存储的都是字符,如果需要存储不同类型的数据,数组和字符串就显得无能为力了。

还有重要的一点是,如果我们想要往数组中新增加或者删除一个元素,会特别麻烦!

比如下面的图例,想要往数组中删除第三个元素,当完成删除后,还需要从删除元素位置遍历到最后一个元素位置,分别将它们都向前移动一个位置,也就是说后续的所有元素都要改变自己的位置,这是十分耗时的操作。

image-20230908160231538

那有没有什么数据结构能够解决上面的问题呢?

那就是我们这节课中将要学习到的链表!

2、链表

链表的理论基础参考链接https://programmercarl.com/%E9%93%BE%E8%A1%A8%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html#%E9%93%BE%E8%A1%A8%E7%9A%84%E7%B1%BB%E5%9E%8B

    与数组不同,链表的元素存储可以是连续的,也可以是不连续的,每个数据元素处理存储本身的信息(data数据域)之外,还存储一个指示着下一个元素的地址的信息(next指针域),给人的感受就好像这些元素是通过一条“链”串起来的。

image-20230911140832813

    链表的第一个节点的存储位置被称为头指针,然后通过next指针域找到下一个节点,直到找到最后一个节点,最后一个节点的next指针域并不存在,也就是“空”的,在C++中,用null来表示这个空指针。

    为了简化链表的插入和删除操作,我们经常在链表的第一个节点前添加一个节点,称为虚拟头节点(dummyNode),头节点的数据域可以是空的,但是指针域指向第一个节点的指针。

头指针是链表指向第一个节点的指针,访问链表的入口,经常使用头指针表示链表,头指针是链表必须的

头节点是为了方便操作添加的,不存储实际数据,头节点不一定是链表必须的

      那在C++中如何定义链表结构呢,传统的定义变量的方式只能使用一种数据类型,无法处理链表这种既包含数据域名、又包含指针域的复合结构,这就需要使用到struct结构体,结构体是一种用户自定义的数据类型。

      定义链表节点:

// 链表节点结构体
struct ListNode {
    int val;  // 存储节点的数据
    ListNode *next; // 指向下一个节点的指针	
  // 构造函数,用于初始化节点, x接收数据作为数据域,next(nullptr)表示next指针为空
    ListNode(int x) : val(x), next(nullptr) {}
};

      我们完成了定义链表节点的操作,那应该完成怎样的操作将链表节点插入到链表的尾端,从而形成一个完整的链表呢?至少应该包括以下操作:

  • 创建一个新的链表节点,初始化它的值为val

  • 将新的节点放入到链表的尾部,接入链表,也就是当前链表的尾部的next指向新节点

  • 新接入的链表节点变为链表的尾部

假设我们用cur来表示当前链表的尾节点

image-20230911153741274

     上面的链表的插入操作用代码来表示如下:

ListNode *newNode = new ListNode(val); // 通过new构造一个新的节点,节点的值为val
cur -> next = newNode; // cur节点的next节点是新节点,从而将新节点接入链表
cur = cur -> next;      // 新插入的节点变更为新的尾节点,即cur发生了变更

    这里有两个新的语法:new运算符和箭头语法 ->

 new是一个运算符,它的作用就是在堆内存中动态分配内存空间,并返回分配内存的地址,使用方式一般为指针变量 = new 数据类型, 比如下面的代码:

new int[5]; // 分配一个包含5个整数的数组的内存空间,并返回一个地址,指针arr指向这个地址

    箭头语法(->):用于通过指针访问指针所指向的对象的成员,cur 是一个指向 ListNode 结构体对象的指针,而 next 是 ListNode 结构体内部的一个成员变量(指向下一个节点的指针)。使用 cur->next 表示访问 cur 所指向的节点的 next 成员变量。 

  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值