卡码网语言基础课 | 13. 链表的基础操作Ⅰ

目录

一、 回顾

二、 指针

2.1 声明指针

2.2 指针获取地址

2.3 解引用 

2.4 指针与数组的关系

2.5 指针的算术操作及访问

2.6 空指针值

三、 链表

3.1 概念

3.2 虚拟头节点

3.3 定义链表节点

3.4 结构体的成员变量

3.5 初始化结构体

3.6 完整结构体代码

四、 链表的插入

4.1 操作思路

4.2 new运算符和箭头语法->

五、 题解


题目:

构建一个单向链表,链表中包含一组整数数据。输出链表中的所有元素。

要求:
1. 使用自定义的链表数据结构
2. 提供一个 linkedList 类来管理链表,包含构建链表和输出链表元素的方法
3. 在 main 函数中,创建一个包含一组整数数据的链表,然后调用链表的输出方法将所有元素打印出来

一、 回顾

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

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

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

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

二、 指针

C++中的指针就像是一个地址的引用,它帮助访问和操作存储在计算机内存中的数据

2.1 声明指针

声明指针,需要用到 * 符号

// 声明一个指向整数的指针a

方式一:

int *a; 


方式二:

int* a;

2.2 指针获取地址

指针想要存放某个变量的地址,需要先使用取地址符  &  获取地址

int x = 10;
int *a = &x; // 将指针a初始化为变量x的地址

2.3 解引用 

想要获取这个地址值,需要使用  * 符号来访问, 这个过程称为解引用

int value = *a; // 获取a指针指向的值(等于x的值,即10)

2.4 指针与数组的关系

指针和数组之间有密切的关系,数组名本质上是一个指向数组第一个元素指针

PS:即不需要写出arr[0],直接写出arr即可,就能知道指针指向为数组的第一个元素arr[0]

int arr[3] = {1, 2, 3}; //声明一个数组,并将其初始化

int *a = arr; // 数组名arr就是指向arr[0]的指针


//arr[0]即文字阐述部分的:数组名即指向数组第一个元素的指针

2.5 指针的算术操作及访问

指针还可以执行加法、减法等算术操作,以访问内存中的不同位置。

int arr[5] = {1, 2, 3, 4, 5};

int *a = arr;  //指向数组的第一个元素

int value = *(a + 2); // 获取数组的第三个元素(值为3)

2.6 空指针值

一个特殊的空指针值,通常表示为nullptr,用于表示指针不指向任何有效的内存地址

int *a = nullptr; // 初始化为空指针

三、 链表

3.1 概念

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

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

3.2 虚拟头节点

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

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

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

详细的链表理论基础知识

3.3 定义链表节点

使用struct结构体来包含链表的数据域名、又包含指针域的复合结构。

//声明一个结构体类型 Books,变量为 book
struct Books{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book;

struct type_name{

member_type1      member_name1;

member_type2      member_name2;

member_type3      member_name3;

. .

} object_names;

3.4 结构体的成员变量

结构体可以组合多个不同类型的成员变量,成员变量可以是各种数据类型,包括整数、浮点数、字符串、其他结构体等,所以你可以根据需要定义自己的结构体来组织数据。

// 链表节点结构体
struct ListNode {
    int val;  // 存储节点的数据
    ListNode *next; // 下一个节点也是链表节点,所以也是ListNode类型,*表示指针(地址),next是名称
}

3.5 初始化结构体

初始化结构体的方式有很多,这里我们使用构造函数的方式来进行,构造函数的名称与结构体的名称相同,和其他函数不一样的是,构造函数没有返回类型,除此之外类似于其他的函数,构造函数也有一个(可能为空)的参数列表和一个函数体(可能为空)。链表结构体的构造函数代码如下:

 ListNode(int x) : val(x), next(nullptr) {}

ListNode(int x)表示:定义一个接收整数参数XListNode的构造函数(名称和结构体相同)

 : 表示:初始化列表的开始

val(x)表示:链表数据域的值被初始化为传递的参数 x 

next(nullptr)表示:next指针被初始化为nullptr表示没有下一个节点

3.6 完整结构体代码

完整代码定义一个ListNode结构体,用于表示链表中的一个节点,包含存储节点数据的数据域和存储下一个节点地址的指针域。

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

};

四、 链表的插入

4.1 操作思路

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

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

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

PS:①中的next指向null,是指新节点的next指针指向原链表的null

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

4.2 new运算符和箭头语法->

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

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

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

五、 题解

#include <iostream>
using namespace std;

//定义一个链表节点
struct lianbiao{
    int val; //整数类型的val
    lianbiao  *next;
    lianbiao(int x) : val(x), next(nullptr){} 
};

int main(){
    int n, val;
    
    //定义虚拟头节点,其节点值为0,dummHead指向其地址
    lianbiao *dummyHead = new lianbiao(0);  
    
    while(cin >> n){
        lianbiao *w = dummyHead; //定义一个临时变量w,构建链表
        
        for(int i = 0; i < n; i++){
            cin >> val; //输入值
            
            //根据读取的值,创建一个新的链表节点,并初始化其值为val
            lianbiao *newjiedian = new lianbiao(val);
            
            w -> next = newjiedian; //将新节点newjiedian加入链表
            w = w -> next; //临时变量w指向下一个节点
        }
        w = dummyHead; //将虚拟头节点给临时变量w
        
        //遍历链表的节点并逐个输出
        while(w -> next != NULL){
            cout << w -> next -> val << " ";
            
            w = w -> next;
        }
        cout << endl;
    }
    
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我知道了。以下是对您的问题的回答: a. 动态分配内存的方法有很多,可以使用C语言的malloc函数,也可以使用C++中的new操作符,具体方法如下: C语言的malloc函数: ```c struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode)); ``` C++中的new操作符: ```c++ ListNode* node = new ListNode(); ``` b. 初始化单链表需要对链表节点的成员变量进行赋值,以下是一个示例: ```c++ struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(NULL) {} }; ListNode* head = NULL; ``` c. 头插法创建单链表的示例代码如下: ```c++ ListNode* head = new ListNode(); head->next = NULL; for (int i = 0; i < n; i++) { int x; cin >> x; ListNode* node = new ListNode(x); node->next = head->next; head->next = node; } ``` 尾插法创建单链表的示例代码如下: ```c++ ListNode* head = new ListNode(); head->next = NULL; ListNode* tail = head; for (int i = 0; i < n; i++) { int x; cin >> x; ListNode* node = new ListNode(x); node->next = NULL; tail->next = node; tail = node; } ``` d. 取链表某一位的值可以通过遍历链表来实现,以下是一个示例: ```c++ ListNode* p = head->next; int pos = 3; for (int i = 1; i < pos && p != NULL; i++) { p = p->next; } if (p == NULL) { cout << "链表长度不足" << endl; } else { cout << p->val << endl; } ``` e. 在链表的某一位进行插入操作可以通过遍历链表找到插入位置,然后插入一个新节点来实现,以下是一个示例: ```c++ ListNode* p = head; int pos = 3; for (int i = 1; i < pos && p != NULL; i++) { p = p->next; } if (p == NULL) { cout << "链表长度不足" << endl; } else { int x = 100; ListNode* node = new ListNode(x); node->next = p->next; p->next = node; } ``` f. 遍历链表可以通过循环遍历链表节点来实现,以下是一个示例: ```c++ ListNode* p = head->next; while (p != NULL) { cout << p->val << " "; p = p->next; } cout << endl; ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值