c++编程基础约等于0,记录看代码随想录时不懂的问题,部分答案来源于chatgpt,部分是自己总结的。
数组
2.二分查找
前提:数组为有序数组,数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的
704.二分查找
题目:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
版本2代码:
// 版本二
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
int middle = left + ((right - left) >> 1);
if (nums[middle] > target) {
right = middle; // target 在左区间,在[left, middle)中
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,在[middle + 1, right)中
} else { // nums[middle] == target
return middle; // 数组中找到目标值,直接返回下标
}
}
// 未找到目标值
return -1;
}
};
(1)"Solution" 是一个类的名称。在这里,它表示一个解决特定问题的类。通常情况下,在编程中,"Solution" 类可能包含解决某个具体问题的方法或算法。在这段代码中,"Solution" 类包含了一个名为 "search" 的公有方法,该方法接受一个整数数组和一个目标值作为输入,并返回目标值在数组中的索引,如果目标值不存在于数组中则返回 -1。
(2)在这段代码中,"public" 是一个访问修饰符,用于指定类成员的访问级别。在 C++ 中,它用于声明类的成员(方法和变量)可以在类的外部访问。在这里,"public" 表示该方法是公有的,可以在类外部被调用和访问。
(3)int search(vector<int>& nums, int target)是一个函数声明,它声明了一个名为 `search` 的函数,该函数接受两个参数:1. `vector<int>& nums`:这是一个引用类型的参数,表示一个整数向量(即整数数组),名为 `nums`,在函数内部对这个向量的修改会影响到函数外部传入的向量。2. `int target`:这是一个整数类型的参数,表示目标值,即要在数组中搜索的值。函数的返回类型是 `int`,表示返回一个整数值。
(4)`nums.size()` 是一个函数调用,它用于获取 `nums` 这个向量(或数组)的大小,即其中包含的元素个数。在 C++ 的标准库中,`size()` 是一个成员函数,用于返回容器中元素的数量。
(5)int middle = left + ((right - left) >> 1)这段代码使用了位运算右移操作符 `>>` 来计算中间位置 `middle`。这种写法与除以 2 的效果是一样的,但在一些情况下可能比除法运算更高效。位运算右移操作符 `>>` 将一个数的二进制表示向右移动指定的位数,相当于对这个数进行了除以 2 的操作。因此,`(right - left) >> 1` 的结果是搜索区间的长度的一半,再加上 `left` 就得到了搜索区间的中间位置 `middle`。这种写法与常规的除法运算相比,可能会更加高效,因为位运算在某些平台上的实现可能会更快。因此,这种写法在性能要求较高的场景下可能会被选择。
34. 在排序数组中查找元素的第一个和最后一个位置(链接)
题目:给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。进阶:你可以设计并实现时间复杂度为 $O(\log n)$ 的算法解决此问题吗?
解法一链接:
寻找右边界思路:采用while (left <= right)的写法,区间定义为[left, right],即左闭右闭的区间。官方说因为取左闭右闭的区间时,left==right也是有意义的,所以<=。
// 二分查找,寻找target的右边界(不包括target)
// 如果rightBorder为没有被赋值(即target在数组范围的左边,例如数组[3,3],target为2),为了处理情况一
int getRightBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
while (left <= right) { // 当left==right,区间[left, right]依然有效
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
if (nums[middle] > target) {
right = middle - 1; // target 在左区间,所以[left, middle - 1]
} else { // 当nums[middle] == target的时候,更新left,这样才能得到target的右边界
left = middle + 1;
rightBorder = left;
}
}
return rightBorder;
}
寻找左边界:
// 二分查找,寻找target的左边界leftBorder(不包括target)
// 如果leftBorder没有被赋值(即target在数组范围的右边,例如数组[3,3],target为4),为了处理情况一
int getLeftBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况
while (left <= right) {
int middle = left + ((right - left) / 2);
if (nums[middle] >= target) { // 寻找左边界,就要在nums[middle] == target的时候更新right
right = middle - 1;
leftBorder = right;
} else {
left = middle + 1;
}
}
return leftBorder;
}
(1)其中nums[middle]<=target时,left = middle +1; rightBorder = left;理解:因为left<=right时会一直循环,直到left>right结束循环,所以可以直接在循环里面让rightborder = left。因为该代码中right右边一定大于target,left左边一定小于等于target,一直循环到left在right右边一格时,就找到了右边界,即left所在位置。(相当于通过leff跟right左右夹击找到target)
(2)怎么确定if后面nums[middle]是>还是>=:举个例子:
首先先明确二分法是靠Left和Right左右夹击找出需要的位置,即R右边满足什么条件,L左边满足什么条件,从而夹击出RL是一个交界。所以找左边界就在图中7和8的位置夹击,找右边界就在图中8和100的位置夹击,更新Right肯定是target小于middle或者target小于等于middle才更新right,此时不确定用不用写等于,观察到找左边界时,Right右边的数应该是大于等于target,所以更新R时应该是nums[middle]>=target,那么R=Middle-1的右边就是大于等于target;观察到找左边界时,L左边的数应该是小于target,那如果nums[middle]<target时,L=M+1的左边就是小于target。找右边界同理。(感觉我这么想有点绕。。。有没有更简洁的想法求指教)
解法二视频链接:
配合视频讲解
:找左边界(lower_bound函数):关键:循环不变量,L-1始终是小于target,R+1始终是大于等于target。根据循环不变量,R+1是我们要找的答案,由于循环结束后R+1=L,所以答案也可以用L表示。
精妙绝伦的是:大于、大于等于、小于、小于等于都可以转化为找大于等于的,所以找左边界:start = lower_bound(nums,target),end = lower_bound(nums,target + 1) - 1
代码:
class Solution {
// lower_bound 返回最小的满足 nums[i] >= target 的 i
// 如果数组为空,或者所有数都 < target,则返回 nums.size()
// 要求 nums 是非递减的,即 nums[i] <= nums[i + 1]
// 闭区间写法
int lower_bound(vector<int> &nums, int target) {
int left = 0, right = (int) nums.size() - 1; // 闭区间 [left, right]
while (left <= right) { // 区间不为空
// 循环不变量:
// nums[left-1] < target
// nums[right+1] >= target
int mid = left + (right - left) / 2;
if (nums[mid] < target)
left = mid + 1; // 范围缩小到 [mid+1, right]
else
right = mid - 1; // 范围缩小到 [left, mid-1]
}
return left; // 或者 right+1
}
// 左闭右开区间写法
int lower_bound2(vector<int> &nums, int target) {
int left = 0, right = nums.size(); // 左闭右开区间 [left, right)
while (left < right) { // 区间不为空
// 循环不变量:
// nums[left-1] < target
// nums[right] >= target
int mid = left + (right - left) / 2;
if (nums[mid] < target)
left = mid + 1; // 范围缩小到 [mid+1, right)
else
right = mid; // 范围缩小到 [left, mid)
}
return left; // 或者 right
}
// 开区间写法
int lower_bound3(vector<int> &nums, int target) {
int left = -1, right = nums.size(); // 开区间 (left, right)
while (left + 1 < right) { // 区间不为空
// 循环不变量:
// nums[left] < target
// nums[right] >= target
int mid = left + (right - left) / 2;
if (nums[mid] < target)
left = mid; // 范围缩小到 (mid, right)
else
right = mid; // 范围缩小到 (left, mid)
}
return right; // 或者 left+1
}
public:
int search(vector<int> &nums, int target) {
int i = lower_bound(nums, target); // 选择其中一种写法即可
return i < nums.size() && nums[i] == target ? i : -1;
}
};
作者:灵茶山艾府
链接:https://leetcode.cn/problems/binary-search/solutions/2023397/er-fen-cha-zhao-zong-shi-xie-bu-dui-yi-g-eplk/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
69.x的平方根
题目力扣链接:给你一个非负整数 x
,计算并返回 x
的 算术平方根 。由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5)
或者 x ** 0.5
。
力扣官方题解:
class Solution {
public:
int mySqrt(int x) {
int l = 0, r = x, ans = -1;
while (l <= r) {
int mid = l + (r - l) / 2;
if ((long long)mid * mid <= x) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return ans;
}
};
(1)在 C++ 中,`long long` 是一种整数类型,它表示的整数范围比 `int` 类型更大。具体来说,`long long` 是一种有符号整数类型,通常占用 8 个字节(64 位),它的取值范围是 `-9223372036854775808` 到 `9223372036854775807`。在这段代码中,将 `mid` 乘以 `mid` 后,使用 `long long` 来存储结果,以避免溢出。因为 `mid * mid` 的结果可能超出 `int` 类型的范围,为了确保计算的正确性,使用 `long long` 类型来存储结果。
3.移除元素(链接)
双指针法(快慢指针)判断快指针是否等于需要移除的元素val,不等于那么慢指针赋值为快指针的值,在遇到val之前快慢指针指向的是同一个位置,相当于不对数组进行操作。遇到val时,慢指针不动也不赋值,而快指针由于经历一个for循环往后移一位,当快指针再次遇到非val时才会赋值给慢指针,相当于把之前val位置的数用快指针遇到的非val数来依次覆盖。
代码随想录:
// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
};
删除重复项(力扣链接)
力扣官方题解:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int n = nums.size();
if (n == 0) {
return 0;
}
int fast = 1, slow = 1;
while (fast < n) {
if (nums[fast] != nums[fast - 1]) {
nums[slow] = nums[fast];
++slow;
}
++fast;
}
return slow;
}
};
(1)在C++中,++slow
和 slow++
表示的含义是不同的。
++slow
是前缀递增操作符,它会先将slow
的值加1,然后返回递增后的值。所以,如果你写++slow
,那么在表达式中,slow
将是递增后的值。slow++
是后缀递增操作符,它也会将slow
的值加1,但是它会返回递增前的值。这意味着,如果你写slow++
,在表达式中,slow
将是递增前的值,然后才会执行递增操作。
举个例子:
int slow = 5; int result; result = ++slow; // 此时 result 和 slow 都是 6
在这个例子中,++slow
是先递增 slow
的值,然后将递增后的值赋给 result
,所以 result
和 slow
都是 6。
int slow = 5; int result; result = slow++; // 此时 result 是 5,slow 是 6
在这个例子中,slow++
是先将 slow
的值赋给 result
,然后再递增 slow
的值,所以 result
是 5,而 slow
是 6。
844.比较含退格的字符串(力扣链接)
(1)由于一个字符是否会被删掉,只取决于该字符后面的退格符,而与该字符前面的退格符无关。因此当我们逆序地遍历字符串,就可以立即确定当前字符是否会被删掉。
(2)这里是字符串所以用的是s.length()而不是nums.size()
(3)&& 如果两个操作数都非零,则条件为真;|| 如果两个操作数中有任意一个非零,则条件为真。
(4)思想:在while大循环里嵌套两个小while循环分别使两个数组从后往前比较,小循环中遇到#或者skip大于0(#造成的需要前移的数量)就会往前移一格,直到移到不是#并且skip为0跳出小循环,两字符串的小循环分别跳出后,即遇到了最后可以不删除保留下来的字符,再比较是否相等,遇到一个不相等就返回false。如果一个字符串移到-1(不存在)的位置时,也返回false。
4.有序数组的平方(链接)
题目:给你一个按非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序排序。
思路:数组其实是有序的, 只不过负数平方之后可能成为最大数了。那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。此时可以考虑双指针法了,i指向起始位置,j指向终止位置。
(1)其中:for (int i = 0, j = A.size() - 1; i <= j;)在循环条件中,使用 i <= j
作为判断条件。这个条件的意思是,只要 i
指针不超过 j
指针,就可以继续循环。这样做是为了在遍历数组时,确保 i
指针不会越过 j
指针,从而保证了双指针的遍历范围是合法的。
5.长度最小的数组(链接)
题目:给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
滑动窗口法思想:滑动窗口的末尾位置用j表示,起始位置用i表示,j每往后移一位就让sum加上这一位的数值,如果sum大于目标值s,就计算子序列长度,更新一下最终子序列长度result,然sum减去i的数值,i++,再回到循环的开始来判断sum与s的关系。相当于是对于每一个j都要找到最短序列,找完以后j往后移一位,再找新的j对应的最短序列。
代码:
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int result = INT32_MAX;
int sum = 0; // 滑动窗口数值之和
int i = 0; // 滑动窗口起始位置
int subLength = 0; // 滑动窗口的长度
for (int j = 0; j < nums.size(); j++) {
sum += nums[j];
// 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
while (sum >= s) {
subLength = (j - i + 1); // 取子序列的长度
result = result < subLength ? result : subLength;
sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == INT32_MAX ? 0 : result;
}
};
6.螺旋矩阵(链接)
代码随想录采用的方法是每次一条边的最后一格不赋值,留给下一条边当起始位置。
力扣题解中找到一个简洁代码:这个代码每次一条边会把所有格赋值,下一条边再从下一格开始赋值。
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
int t = 0; // top
int b = n-1; // bottom
int l = 0; // left
int r = n-1; // right
vector<vector<int>> ans(n,vector<int>(n));
int k=1;
while(k<=n*n){
for(int i=l;i<=r;++i,++k) ans[t][i] = k;
++t;
for(int i=t;i<=b;++i,++k) ans[i][r] = k;
--r;
for(int i=r;i>=l;--i,++k) ans[b][i] = k;
--b;
for(int i=b;i>=t;--i,++k) ans[i][l] = k;
++l;
}
return ans;
}
};
(1)vector<vector<int>> ans(n, vector<int>(n));
这行代码是在C++中创建一个二维向量(vector of vectors),名为 ans
,其大小为 n×n,并且所有元素都被初始化为0。
vector<vector<int>>
:这是一个嵌套的向量,表示包含多个向量的向量,即二维向量。ans
:这是向量的名称。(n, vector<int>(n))
:这是向量的初始化部分。在这里,vector<int>(n)
创建了一个大小为 n 的向量,其所有元素都初始化为0,然后(n, ...)
使用拷贝构造函数初始化了 n 个这样的向量,最终构成了一个大小为 n×n 的二维向量。
因此,ans
是一个 n×n 的二维向量,其所有元素都被初始化为0。
(2)
for(int i=l;i<=r;++i,++k) ans[t][i] = k;
在C++中,for循环中逗号分隔的表达式执行顺序是从左到右依次执行的。在这种情况下:
- 首先,初始化语句
int i = l
会被执行,将i
初始化为l
的值。 - 接下来,循环条件
i <= r
会被检查,如果为真,则继续执行循环体;否则结束循环。 - 然后,循环体中的语句
ans[t][i] = k
会被执行,将ans[t][i]
赋值为k
。 - 最后,迭代表达式
++i, ++k
会被执行,将i
和k
同时递增。
因此,循环体中的语句 ans[t][i] = k
在每次迭代时都会被执行,而且 i
和 k
都会在每次迭代后递增
在 C++ 中,数组和向量的索引都是从0开始的。因此,在二维向量 `ans` 中,`ans[t][i]` 中的 `i` 和 `t` 的起始点都是0。
链表
2.移除链表元素(链接)
题目:删除链表中等于给定值 val 的所有节点
思路:
- 直接使用原来的链表分情况(头结点,非头结点)来进行删除操作。
- 设置一个虚拟头结点在进行删除操作。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 删除头结点
while (head != NULL && head->val == val) { // 注意这里不是if
ListNode* tmp = head;
head = head->next;
delete tmp;
}
// 删除非头结点
ListNode* cur = head;
while (cur != NULL && cur->next!= NULL) {
if (cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
} else {
cur = cur->next;
}
}
return head;
}
};
(1)ListNode* removeElements(ListNode* head, int val)
这个函数的名字是removeElements,它接受两个参数:一个是指向链表头部的指针 `head`,另一个是一个整数 `val`.函数的作用是移除链表中所有值为 `val` 的节点,并返回移除节点后的链表头部.`ListNode*` 表示这个函数返回一个指向 ListNode 类型的指针,即返回的是链表的头部指针。函数的实现会遍历链表,检查每个节点的值是否等于 `val`,如果是则删除该节点,直到遍历完整个链表为止。最后返回删除节点后的链表头部指针。
3.设计链表(链接)
题意:
在链表类中实现这些功能:
- get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
- addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
- addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
- addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
- deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
class MyLinkedList { public: // 定义链表节点结构体 struct LinkedNode { int val; LinkedNode* next; LinkedNode(int val):val(val), next(nullptr){} }; // 初始化链表 MyLinkedList() { _dummyHead = new LinkedNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点 _size = 0; } // 获取到第index个节点数值,如果index是非法数值直接返回-1, 注意index是从0开始的,第0个节点就是头结点 int get(int index) { if (index > (_size - 1) || index < 0) { return -1; } LinkedNode* cur = _dummyHead->next; while(index--){ // 如果--index 就会陷入死循环 cur = cur->next; } return cur->val; } // 在链表最前面插入一个节点,插入完成后,新插入的节点为链表的新的头结点 void addAtHead(int val) { LinkedNode* newNode = new LinkedNode(val); newNode->next = _dummyHead->next; _dummyHead->next = newNode; _size++; } // 在链表最后面添加一个节点 void addAtTail(int val) { LinkedNode* newNode = new LinkedNode(val); LinkedNode* cur = _dummyHead; while(cur->next != nullptr){ cur = cur->next; } cur->next = newNode; _size++; } // 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。 // 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点 // 如果index大于链表的长度,则返回空 // 如果index小于0,则在头部插入节点 void addAtIndex(int index, int val) { if(index > _size) return; if(index < 0) index = 0; LinkedNode* newNode = new LinkedNode(val); LinkedNode* cur = _dummyHead; while(index--) { cur = cur->next; } newNode->next = cur->next; cur->next = newNode; _size++; } // 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的 void deleteAtIndex(int index) { if (index >= _size || index < 0) { return; } LinkedNode* cur = _dummyHead; while(index--) { cur = cur ->next; } LinkedNode* tmp = cur->next; cur->next = cur->next->next; delete tmp; //delete命令指示释放了tmp指针原本所指的那部分内存, //被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后, //如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针 //如果之后的程序不小心使用了tmp,会指向难以预想的内存空间 tmp=nullptr; _size--; } // 打印链表 void printLinkedList() { LinkedNode* cur = _dummyHead; while (cur->next != nullptr) { cout << cur->next->val << " "; cur = cur->next; } cout << endl; } private: int _size; LinkedNode* _dummyHead; };
(1)struct LinkedNode{ int val; LinkedNode* next; LinkedNode(int val):val(val),next(nullptr){} };
这段代码定义了一个名为
LinkedNode
的结构体,用于表示一个链表节点。 -
int val;
:这是LinkedNode
结构体中的一个成员变量,表示节点的值。 -
LinkedNode* next;
:这是LinkedNode
结构体中的另一个成员变量,表示指向下一个节点的指针。这样就可以通过next
指针将链表中的节点连接起来。在这段代码中,LinkedNode* next;
是LinkedNode
结构体中的一个成员变量。它是一个指向LinkedNode
类型的指针,用于指向链表中的下一个节点。这个指针使得链表节点可以彼此链接,从而形成一个链表结构。 -
通过这个结构体,你可以创建表示链表节点的对象,并使用它们来构建链表数据结构。每个节点都包含一个值和一个指向下一个节点的指针,这样就可以形成一个链式结构。
-
LinkedNode(int val):val(val),next(nullptr){}
:这是LinkedNode
结构体的构造函数。它接受一个整数参数val
,用于初始化节点的值,并将next
指针初始化为nullptr
,表示初始时没有下一个节点。
(2)构造函数是一种特殊的成员函数,它在创建类的对象时被自动调用,用于初始化对象的数据成员。在 C++ 中,构造函数的主要作用包括以下几个方面:
-
初始化对象的数据成员:构造函数可以在对象被创建时初始化对象的数据成员,确保对象在被使用之前处于一个合法的状态。这样可以避免对象中的数据成员未被初始化而导致的不确定行为。
-
分配资源:如果对象需要在创建时分配资源(如内存、文件句柄等),构造函数可以负责执行这些分配操作,并在对象被销毁时释放这些资源,以避免资源泄漏。
-
进行必要的初始化操作:构造函数可以执行一些必要的初始化操作,例如打开文件、建立网络连接、设置默认参数等。
-
实现重载:C++ 支持构造函数的重载,即可以定义多个构造函数,每个构造函数可以接受不同的参数列表。这样可以根据不同的需求选择合适的构造函数来创建对象。
LinkedNode(int val):val(val),next(nullptr){}中,构造函数 LinkedNode(int val)
负责初始化链表节点的值,并将指向下一个节点的指针初始化为 nullptr
。这样,在创建链表节点对象时,可以直接提供节点的值作为参数,构造函数会负责将该值赋给节点的 val
成员,并将 next
指针初始化为空指针。
-
LinkedNode(int val)
: 这是构造函数的声明部分,它接受一个整数参数val
,用于初始化节点的值。构造函数在创建对象时被调用,用于初始化对象的成员变量。 -
: val(val)
: 这是成员初始化列表的一部分,用于将传入的参数val
赋值给对象的成员变量val
。这里使用的是成员初始化列表的语法,等同于在构造函数体内写this->val = val;
,但是初始化列表的方式在性能上可能更高效,尤其对于某些类型的成员变量(如常量、引用和没有默认构造函数的成员变量)。 -
next(nullptr)
: 这是成员初始化列表的另一部分,用于将成员变量next
初始化为nullptr
。nullptr
是一个特殊的值,用于表示指针不指向任何对象。在这里,将next
初始化为nullptr
,表示初始时该节点不链接到任何其他节点