链表
指针
指针就是一个整数,一个存储内存地址的数字
指针变量保存的是内存地址
int *ptr;//声明的是一个指向整数的指针
int* ptr;//两种写法都行
int x = 10;
int *ptr = &x;// & 取地址符
其中&x表示的是x的内存地址
ptr是指针标量
*ptr表示的是ptr指的地址所表示的内容
一个特殊的空指针值,通常表示为nullptr,用于表示指针不指向任何有效的内存地址。
代码举例
终端处的结果
数组名只能视为一个指针常量
具体举例
int *ptr1 = &arr;
此处代码会报错
int *ptr = arr;
正常运行,获得的是arr[0]的地址值 ,数组名本质上是一个指向数组第一个元素的指针
对了,指针也可以进行加减
eg:
#include <iostream>
using namespace std;
int main(){
int arr[3] = {1,2,3};
int *ptr = arr;//此时指向的是数组第一个元素
int value = *(ptr+2);//此时指向的就是数组的第三个元素
cout << *ptr << endl;
cout << value << endl;
}
结果
具体逻辑就是:
指针加上一个整数n,表示指针ptr指向的地址值加上n*sizeof(指针指向的类型)(sizeof函数表示查询数据类型的内存大小,这里的指针指向int类型,4个字节大小)
指针减去整数n,类似;
两个指针相减就是表示相差几个指针指向的类型的元素
链表结点的实现
传统的定义变量的方式只能使用一种数据类型,无法处理链表这种既包含数据域名、又包含指针域的复合结构,因此我们需要结构体
结构体(struct)
结构体:是将多种数据类型组合成一个集合,这个集合就是结构体。
C++ 中还有类,在C++中类和结构体几乎很类似,在技术上的差别只是类中的所有东西在没有提前public时,都i是私有的,只能由类中的东西进行访问,而struct正好相反,一开始都是public
语法
struct 结构体名{
### 数据类型 变量名来定义
}
eg:
struct person{
int age;
string name;
}
构造链表结构体具体代码
struct Listnode{
int value;// 定义第一个变量,链表值,数据类型整型
Listnode *next;// 第二个变量 ,指针域,数据类型是Listnode,也就是上(刚开始的指针板块)面说的一个指向链表结点的指针next,注意赋值要求是地址
ListNode(int x) : val(x), next(nullptr) {}
}
有关最后一行代码,是这个结构体的初始化,如果没有这一行代码的话,这个结构体只是个链表结点的模具,无法具体表示一个链表节点。因此我们需要初始化这个结构体。
结构体的初始化方法
主要有三种:
这边有个博客不错 : 传送门 xiu
这里的代码是使用构造函数的方式来进行,构造函数是一种特殊类型的方法,一种每次你构造一个对象时都会调用的方法。构造函数的名称与结构体的名称相同,和其他函数不一样的是,构造函数没有返回类型,除此之外类似于其他的函数,构造函数也有一个(可能为空)的参数列表和一个函数体(可能为空)。
构造函数初始化主要有两种:
1.赋值初始化
#include <iostream>
// #include <stdio.h>
using namespace std;
struct Person{
int age;
string name;
Person(){
age = 18;
name = "TangLoopy";
//判断是否自动运行了构造函数
cout << "永远年轻 cool" << endl;
}
};
int main(){
Person person1;
return 0;
}
代码运行显示:
2.初始化列表
就是前面链表定义构造函数初始化
结构体名(传入参数): 成员变量1(参数1),成员变量2(参数2){}
ListNode(int x) : val(x), next(nullptr) {}
这里的ListNode(int x)表示定义一个接收整数参数 x的名称为ListNode的构造函数(名称和结构体相同),“:”表示初始化列表的开始,val(x)表示链表数据域的值被初始化为传递的参数 x ,next(nullptr)则表示next指针被初始化为nullptr,表示没有下一个节点。
#include <iostream>
// #include <stdio.h>
using namespace std;
struct Listnode{
int value;
Listnode *next;
Listnode(int x) : value(x) , next(nullptr) {}
};
int main(){
Listnode node = Listnode(3);
cout << node.value << endl;
cout << node.next << endl;
}
结果:
如果此时改成这样
或这样
会有报错,因为我们前面的构造函数的参数只能接受一个整型参数。不过输入一个字符 ,
例如’A’,是不会报错的,因为会自动转换为对应的ASCII值。
如果我们想要灵活地初始化一个链表节点呢?
struct中的构造函数可以多个,因此可以通过定义多个构造函数来实现不同类型的初始化
#include <iostream>
using namespace std;
// 定义链表节点
struct Listnode{
int value;
Listnode *next;
Listnode() : value(0) , next(nullptr) {} // 无参构造函数,定义的结点值为0,指针域为null
Listnode(int x) : value(x) , next(nullptr) {} // 单整型参数的构造函数
Listnode(int x, Listnode *next) : value(x), next(next) {} // 值为int x ,指针域为下一个结点的地址值
};
int main(){
Listnode node1 = Listnode(1);
Listnode node2 = Listnode(1, &node1);
Listnode node3 ;
cout << node1.value << endl;
cout << node2.next << endl;
cout << &node1 << endl;
cout << node3.value << endl;
}
结果