leetcode

//to_string()//数字转字符串

//stoi()字符串转数字

//string(1,char)字符转数字//string构造函数

1LL会在运算时把后面的临时数据扩容成long long类型,再在赋值给左边时转回int类型。

array<int, 26>

  1. array:这是 C++ 标准库中的一个模板类,位于 <array> 头文件中。它是一个固定大小的容器,类似于传统的 C++ 数组,但提供了更多功能和安全性。

  2. int:表示数组中存储的元素类型是整数。

  3. 26:表示数组的大小,即数组中有 26 个元素

unordered_map

在C++中,std::unordered_map 是一个关联容器,用于存储键值对(key-value pairs)。如果你想检查 std::unordered_map 是否包含某个键(key),可以使用 find() 方法或 count() 方法。虽然 C++20 引入了 contains() 方法,但如果你使用的是 C++20 之前的版本,就需要用其他方式来实现。

  1. 使用 find() 方法

find() 方法会在容器中查找指定的键。如果找到了,返回一个指向该键值对的迭代器;如果没有找到,返回 end() 迭代器。

2.使用 count() 方法

count() 方法会返回键在容器中出现的次数(对于 unordered_map,只能是 0 或 1)

3.使用 C++20 的 contains() 方法

如果你使用的是 C++20 或更高版本,可以直接使用 contains() 方法。它会返回一个布尔值,表示容器是否包含指定的键。

// 注意不要直接 += cnt[sj-k],如果 sj-k 不存在,会插入 sj-k

static constexpr int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

  • 这种定义通常用于二维平面的搜索问题,如迷宫搜索、棋盘游戏、网格路径搜索等。

  • 通过遍历这个数组,可以方便地实现从当前位置向四个方向的移动。

  • constexpr** 的作用**:

  • constexpr 表示这个数组是一个编译时常量,它的值在编译时就已经确定,不能在运行时修改。

  • 这样可以提高代码的效率和安全性,同时避免在运行时动态分配内存。

constexpr 是 C++11 引入的一个非常强大的关键字,它的作用是声明一个“编译时常量表达式”,即在编译阶段就能确定其值的变量或函数。使用 constexpr 可以显著提高代码的效率和安全性,同时还能让代码更加清晰和易于维护。

具体用法略

lambada表达式

这段代码是一个使用C++17标准的lambda表达式,并且使用了递归参数化捕获的特性

[&]表示捕获当前作用域中的所有变量

this auto&& dfs:这是C++17中引入的参数化捕获特性。this auto&&表示捕获当前lambda对象的引用,允许lambda在递归调用时引用自身。

-> void:表示这个lambda表达式没有返回值

auto dfs = [&](this auto&& dfs, int i) -> void {

if (i == nums.size()) {

ans++;

return;

}

};

Lambda 表达式是一种强大的工具,它结合了简洁性、匿名性、闭包特性以及与 STL 算法的无缝集成。它在现代 C++ 编程中被广泛应用,尤其是在需要定义简单函数、捕获上下文或实现递归时。

1. 简洁性

2. 捕获上下文

Lambda 表达式可以捕获外部变量(通过 [&][=]),这使得它能够直接访问和修改外部作用域的变量,而无需通过参数传递。

3. 匿名性

4. 支持闭包

Lambda 表达式本质上是一种闭包,它可以捕获外部变量并将其封装起来。这使得 Lambda 表达式可以作为函数对象(functor)使用,而无需显式定义类。

5.支持递归

从 C++17 开始,Lambda 表达式可以通过 this auto&& 捕获自身,从而实现递归调用。这使得 Lambda 表达式可以用于复杂算法(如深度优先搜索、动态规划等)

6.与 STL 算法结合

Lambda 表达式与 C++ 标准库中的算法(如 std::sortstd::for_eachstd::transform 等)结合得非常好,可以实现非常简洁的代码。

7. 减少代码冗余

在某些场景下,使用 Lambda 表达式可以避免定义多个小函数,从而减少代码冗余。例如,在多线程编程中,Lambda 表达式可以直接捕获线程需要的上下文。

emplace_back

contains

在C++中,std::unordered_set 是一个关联容器,用于存储唯一的元素。从C++20开始,std::unordered_set 提供了一个成员函数 contains,用于检查容器中是否包含某个元素。这是一个非常方便的函数,可以替代之前的 findcount 方法。

std::unordered_set::contains 的用法

  • 函数原型

  • cpp复制

1
bool contains(const key_type& key) const;
  • 参数:key 是要检查的元素。

  • 返回值:如果容器中包含该元素,则返回 true;否则返回 false

lower_bound(nums.begin(),nums.end(),target);

lower_bound() 是 C++ 标准库中的一个函数,它在有序容器(如 std::vectorstd::arraystd::deque 等)中查找不小于给定值的第一个元素。这个函数使用二分查找算法,因此它的查找效率是 O(log n)。

memset

它定义在 <cstring>(C++)或 <string.h>(C)头文件中

memset 用于在内存中填充指定的字节值.

void* memset(void* dest, int value, size_t count);

dest:

  • 指向目标内存区域的指针。

  • 该内存区域将被填充。

value:

  • 要填充的字节值。

  • 注意:value 是一个 int 类型,但它会被解释为一个 单字节值(即只使用其最低的 8 位)。因此,value 的有效范围是 0255

count:

要填充的字节数

指定从dest开始的内存区域中有多少字节需要被填充

返回值:

返回目标内存区域的指针,方便链式调用

是一个底层的内存操作函数

常见用途:

初始化内存区域

清空内存

sizeof

sizeof 是 C++ 中的一个运算符,用于获取变量、类型或表达式的大小(以字节为单位)。它是一个编译时运算符,因此它的结果在编译时就已经确定,不会在运行时计算。

函数的声明如下:

Vector

1.assign

assign 用于重新分配容器的内容,会清空当前容器,并用新的内容填充。

2.resize

resize 用于调整容器的大小,同时可以指定新元素的默认值。

3.reverse

reverse 并不是 std::vector 的成员函数,而是 C++ 标准库中的一个算法,用于反转容器中的元素顺序。

4.reserve

reserve 用于预留容器的内存空间,但不会改变容器的大小

max_element

在 C++ 中,max_element 是标准模板库(STL)中的一个算法函数,定义在头文件 <algorithm> 中。它用于查找指定范围内的最大元素。

  • max_element 的第一个和第二个参数分别是迭代器,表示要查找的范围的开始和结束。

  • 它返回一个迭代器,指向范围内的最大元素。

reduce

std::reduce 是C++17中引入的一个算法,它位于 <numeric> 头文件中。它类似于 std::accumulate,但提供了更好的并行化支持。

:将一个范围内的元素通过指定的二元操作符进行归并(reduce)

//默认使用加法

在代码中,dfs 函数的参数 const string& s 使用了引用传参,这是出于**性能优化**和**语义清晰**的考虑。以下是详细解释:


1. 性能优化:避免不必要的拷贝

在C++中,传递大型对象(如字符串、向量等)时,直接传递会触发**拷贝构造函数**,导致对象被复制一份。对于字符串 s,如果直接传递,每次递归调用都会复制整个字符串,这会带来不必要的开销,尤其是在字符串较长时。

例如:

1
void dfs(string s, int i);  // 直接传递

每次调用 dfs 时,都会复制整个字符串 s,这会导致时间复杂度和空间复杂度显著增加。

而使用引用传参:

1
void dfs(const string& s, int i);  // 引用传参

这种方式不会复制字符串,而是直接传递原始字符串的引用。这样可以显著减少内存占用和拷贝时间,提高程序的运行效率。


2. 语义清晰:明确字符串不会被修改

dfs 函数中,字符串 s 是输入参数,且在递归过程中不需要修改它。使用 const string& 表示:

  • 只读访问const 修饰符表明 s 在函数内部不会被修改,这有助于代码的可读性和安全性。

  • 明确意图:引用传参表明 s 是一个共享的输入数据,而不是每次递归调用时的独立副本。

这种写法清晰地表达了函数的语义:dfs 函数只是对输入字符串 s 进行读取操作,而不会修改它。


3. 对比:直接传递 vs 引用传递

假设字符串 s 的长度为 n,递归深度为 n

  • 直接传递:每次递归调用都会复制整个字符串,总的时间复杂度为 O(n^2),空间复杂度也为 O(n^2)

  • 引用传递:每次递归调用只是传递一个引用,时间复杂度为 O(n),空间复杂度为 O(n)(主要来自递归栈)。

因此,使用引用传参可以显著优化性能,尤其是在处理大字符串时。


4. 总结

dfs 函数中,使用 const string& s 的原因如下:

  1. 性能优化:避免不必要的字符串拷贝,减少时间和空间开销。

  2. 语义清晰:明确字符串是只读的输入参数,不会被修改。

  3. 最佳实践:在C++中,对于大型对象(如字符串、向量等),通常推荐使用引用传参,以提高效率。

这种写法是C++编程中的常见优化技巧,尤其适用于递归函数和深度优先搜索场景。

perror函数

perror 是一个在 C 语言中常用的函数,用于打印错误信息。它属于标准库 <stdio.h>,主要用于将错误信息输出到标准错误输出(通常是屏幕)。

void perror(const char *s);

perror 常用于处理系统调用或库函数失败时的错误。当这些函数失败时,它们通常会将错误码存储在 errno 中,而 perror 可以帮助开发者快速定位问题。

typdef函数

C语言允许用户使用 typedef 关键字来定义自己习惯的数据类型名称,来替代系统默认的基本类型名称、数组类型名称、指针类型名称与用户自定义的结构型名称、共用型名称、枚举型名称等

为基本数据类型定义新的类型名

为自定义数据类型(结构体、共用体和枚举类型)定义简洁的类型名称

typedef struct tagNode { char *pItem; pNode pNext; } *pNode;

其实问题并非在于 struct 定义的本身,大家应该都知道,C 语言是允许在结构中包含指向它自己的指针的,我们可以在建立链表等数据结构的实现上看到很多这类例子。那问题在哪里呢?其实,根本问题还是在于 typedef 的应用。

在上面的代码中,新结构建立的过程中遇到了 pNext 声明,其类型是 pNode。这里要特别注意的是,pNode 表示的是该结构体的新别名。于是问题出现了,在结构体类型本身还没有建立完成的时候,编译器根本就不认识 pNode,因为这个结构体类型的新别名还不存在,所以自然就会报错。因此,我们要做一些适当的调整,比如将结构体中的 pNext 声明修改成如下方式:

解决办法

1.在struct前加typdef

2.将struct与typdef分开定义

为数组定义简洁的类型名称

为指针定义简洁的名称

typedef 是用来定义一种类型的新别名的,它不同于宏,不是简单的字符串替换

assert函数

assert 是 C 语言中一个非常有用的调试工具,用于在程序运行时检查条件是否为真。如果条件为假(即表达式的结果为 0),程序会终止运行,并打印一条错误信息,指出断言失败的位置。

C语言和C++的最大数据结构和最小数据结构

头文件都问 <limits.h>

C语言最大数据结构

INT_MAX

C语言最小数据结构

INT_MIN

C++语言最大数据结构

INT32_MAX

C++语言最小数据结构

INT32_MIN

uint64_t

是一种数据类型,通常用于表示无符号的64位整数

定义

  • 它是C语言和C++语言中定义的一种标准整数类型。

  • 在C语言中,uint64_t 是通过头文件 <stdint.h> 定义的。

  • 在C++语言中,uint64_t 是通过头文件 <cstdint> 定义的。

特点

  • 无符号uint64_t 是无符号整数类型,这意味着它不能表示负数,只能表示非负整数。

  • 64位uint64_t 占用64位(8字节)的存储空间,因此它可以表示的数值范围是从0到 264−1,即从0到18446744073709551615。

  • 平台无关性uint64_t 是一种固定宽度的整数类型,它的大小在所有支持它的平台上都是固定的,不会因平台的不同而改变。

使用场景

  • 大整数计算:当需要处理较大的整数时,uint64_t 是一个合适的选择。例如,在处理大文件的偏移量、大数组的索引或者大范围的计数器时,uint64_t 可以提供足够的存储空间。

  • 跨平台开发:在跨平台的程序中,使用 uint64_t 可以确保整数的大小在不同的平台上保持一致,避免因平台差异导致的错误。

  • 性能优化:在某些情况下,使用 uint64_t 可以提高程序的性能。例如,在进行位运算或整数运算时,64位整数的运算速度可能会比32位整数更快。