链表的基础操作I
时间限制:1.000S 空间限制:128MB
构建一个单向链表,链表中包含一组整数数据。输出链表中的所有元素。
要求:
1. 使用自定义的链表数据结构
2. 提供一个 linkedList 类来管理链表,包含构建链表和输出链表元素的方法
3. 在 main 函数中,创建一个包含一组整数数据的链表,然后调用链表的输出方法将所有元素打印出来
包含多组测试数据,输入直到文件尾结束。
每组的第一行包含一个整数 n,表示需要构建的链表的长度。
接下来一行包含 n 个整数,表示链表中的元素。
每组测试数据输出占一行。
按照顺序打印出链表中的元素,用空格隔开,最后一个元素后没有空格。
5
1 2 3 4 5
6
3 4 5 6 7 8
1 2 3 4 5
3 4 5 6 7 8
数据范围:
1 <= n <= 1000;
那在C++中如何定义链表结构呢,传统的定义变量的方式只能使用一种数据类型,无法处理链表这种既包含数据域名、又包含指针域的复合结构,这就需要使用到struct
结构体,结构体是一种用户自定义的数据类型,比如想要定义一个Person
的结构体
// Person结构体
struct Person {
// 使用 数据类型 成员变量的形式来定义
int age; // int类型的年龄
std::string name; // string类型的名字
}
结构体可以组合多个不同类型的成员变量,成员变量可以是各种数据类型,包括整数、浮点数、字符串、其他结构体等,所以你可以根据需要定义自己的结构体来组织数据。
// 链表节点结构体
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 数据类型
, 比如下面的代码
int *arr = new int[5]; // 分配一个包含5个整数的数组的内存空间,并返回一个地址,指针arr指向这个地址
箭头语法(->
):用于通过指针访问指针所指向的对象的成员,cur
是一个指向 ListNode
结构体对象的指针,而 next
是 ListNode
结构体内部的一个成员变量(指向下一个节点的指针)。使用 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); // 定义了虚拟头结点,dummyNode指向它的地址
之后我们需要根据输入的值构建链表,每组的第一行包含一个整数 n,表示需要构建的链表的长度。
while(cin >> n) {
}
可以定义一个指向当前节点的指针 cur
,刚开始指向虚拟头结点。
ListNode *cur = dummyHead; // 指针cur指向虚拟头节点
构建一个链表需要以下几步
接收输入的值,并根据读取的值val
创建一个新的链表节点,初始化它的值也为val
将新的节点放入到链表的尾部,接入链表,也就是当前链表的尾部的next
指向新节点
新接入的链表节点变为链表的尾部
for (int i = 0; i < n; i++) { // 或者使用while(n--)
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;
// 只要cur->next != NULL,说明链表还没有遍历完
while (cur->next != NULL) {
// 输入cur的next指针的val值
cout << cur->next->val << " ";
// 将cur指向下一个节点
cur = cur -> next;
}
cout << endl;
链表输出完成后,输出一个换行符,换行输出下一组链表。
cout << endl;
c++代码实现
#include
using namespace std;
typedef struct LNode{
int data;//存储数据
LNode *next;//结构体指针,指向下一个元素
LNode(int x):data(x),next(nullptr){}
}*L;
int main(){
L dummyHead =new LNode(0);//定义了虚拟头节点
int n;
int val;
while(cin>>n){
L cur=dummyHead;
for(int i=0;i>val;
L newnode=new LNode(val);
cur->next=newnode;
cur=newnode;
}
cur=dummyHead;
while(cur->next!=NULL){
cout<next->data<<" ";
cur=cur->next;
}
cout<