卡码网语言基础课 |链表的基础操作I

卡码网语言基础课 |链表的基础操作I

  • 指针
  • 链表
    • 定义链表节点
    • 链表的插入
  • 代码编写

指针

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

想要声明指针,需要使用*符号,比如下面的代码:

int *ptr; // 声明一个指向整数的指针
int* ptr; // 也可以这样写

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

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

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

int value = *ptr; // 获取 ptr 指针指向的值

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

int arr[3] = {1, 2, 3}
int *ptr = arr; // 数组名 arr 是指向 arr[0] 的指针

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

int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // 指向数组的第一个元素
int value = *(ptr + 2); // 获取数组的第三个元素

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

链表

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

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

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

头节点是链表指向第一个节点的指针,访问链表入口,经常使用头指针表示链表,头指针是链表必须的。
头节点是为了简化操作添加的,不存储实际数据,头节点不一定是链表必需的

定义链表节点

链表是一种既包含数据域名、又包含指针域的复合结构,这就需要使用到struct结构体。

结构体是一种用户自定义的数据类型,比如想要定义一个Person的结构体:

struct Person {
	int age;
	std::string name;
}

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

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

但结构体只是个“模具”,创建的Person结构体虽然具有age、name,但它只是一个Person的概念,无法表示具体的人,只有将其“初始化”,比如"张三,18", “李四、20”,才能真正的使用。

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

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

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

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

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

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

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

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

链表的插入

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

  • 创建一个新的链表节点,初始化为val
  • 将新的节点放入到链表尾部,接入链表,也就是当前链表的尾部的next指向新节点
  • 新接入的链表节点变为链表的尾部

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

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

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

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

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

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

代码编写

基础代码结构如下:

#include 
using namespace std;

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

int main() {}

一般都会为了链表创建一个虚拟头节点:

ListNode *dummyHead = new ListNode(0);

之后需要根据输入的值构建链表,每组的第一行包含一个整数 n ,表示需要构建的链表的长度。

while (cin >> n){}

定义一个指向当前节点的指针cur,开始时指向虚拟头节点:

ListNode *cur = dummyhead;

构建一个链表需要以下几步

  • 接收输入的值,并根据读取的值val创建一个新的链表节点,初始化它的值也为val
  • 将新的节点放入到链表的尾部,接入链表,也就是当前链表的尾部的next指向新节点
  • 新接入的链表节点变为链表的尾部
for (int i = 0; i < n; i++) {
	cin >> val; // 输入链表节点的val值
	ListNode *newNode = new ListNode(val); // 根据val值构造一个新的节点
	cur -> next = newNode; // 当前指针的下一个节点为新节点,从而将新节点接入链表
	cur = cur -> next;  // cur 指向下一个节点(也就是新创建的节点)
}

当输入结束时,链表也就构建完成了,想要输出链表的节点需要从头开始重新遍历, 所以需要将cur重新指向链表的虚拟头节点,循环输出链表可以使用while循环。

cur指向最后一个节点的时候,此时cur->next是空指针,也就是说只要cur->next != null就可以一直循环下去,直到cur->next == null退出循环,在循环过程中需要做两件事情:

  • 输出链表节点的值
  • cur指向下一个节点
cur = dummyhead;

while (cur->next != null) {
	cout << cur->next->val << " ";
	cur = cur -> next;
}
cout << endl;

链表输出完成后,输出一个换行符,换行输出下一组链表:

cout << endl;

完整代码如下:

#include 
using namespace std;

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

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

你可能感兴趣的:(卡码网语言基础课,c++)