leetcode刷题笔记本

leetcode 刷题笔记本

基础数据结构

1.字符串

substr()用法: s.substr(i,j)表示从下标为i的位置开始截取j位
形式 : s.substr(pos, len)
返回值: string,包含s中从pos开始的len个字符的拷贝(pos的默认值是0,len的默认值是s.size() - pos,即不加参数会默认拷贝整个s)
异常 :若pos的值超过了string的大小,则substr函数会抛出一个out_of_range异常;若pos+n的值超过了string的大小,则substr会调整n的值,只拷贝到string的末尾

#include<iostream>
#include<string>
using namespace std;
int main()
{
  string s="123abc";
  string a=s.substr(2,2);//从下标为2的位置开始,拷贝两个字符返回。
  cout<<a;
  return 0;
}

程序运行结果: 3a

①不加参数会拷贝整个s。

#include<iostream>
#include<string>
using namespace std;
int main()
{
  string s="123abc";
  string a=s.substr();//不加参数会拷贝整个s
  cout<<a;
  return 0;
}

程序运行结果: 123abc
②只加参数pos,会从pos位置开始拷贝剩余全部字符。

#include<iostream>
#include<string>
using namespace std;
int main()
{
  string s="123abc";
  string a=s.substr(3);//从下标为3的位置开始,拷贝剩余全部字符返回。
  cout<<a;
  return 0;
}

程序运行结果: abc

find_first_of()函数和find_last_of()函数

1、find_first_of()函数
正向查找在原字符串中第一个与指定字符串(或字符)中的某个字符匹配的字符,返回它的位置。若查找失败,则返回npos。(npos定义为保证大于任何有效下标的值。)

2、find_last_of()函数
逆向查找在原字符串中最后一个与指定字符串(或字符)中的某个字符匹配的字符,返回它的位置。若查找失败,则返回npos。(npos定义为保证大于任何有效下标的值。)

查找字符串a是否包含子串b,不是用strA.find(strB) > 0 而是 strA.find(strB) != string:npos
其中string:npos是个特殊值,说明查找没有匹配

// leetcode 1704. 判断字符串的两半是否相似
class Solution {
public:
    bool halvesAreAlike(string s) {
        string a = s.substr(0, s.size() / 2);
        string b = s.substr(s.size() / 2);
        string h = "aeiouAEIOU";
        int sum1 = 0, sum2 = 0;
        for (int i = 0; i < a.size(); i++) {
            if (h.find_first_of(a[i]) != string::npos) {
                sum1++;
            }
        }
        for (int i = 0; i < b.size(); i++) {
            if (h.find_first_of(b[i]) != string::npos) {
                sum2++;
            }
        }
        return sum1 == sum2;
    }
};
作者:力扣官方题解
链接:https://leetcode.cn/problems/determine-if-string-halves-are-alike/solutions/1960619/pan-duan-zi-fu-chuan-de-liang-ban-shi-fo-d21g/
来源:力扣(LeetCode)

char 转 int

  1. 直接转换:
char a = '0'
int ia = a - '0';

** 判断字符是否为数字字符**

 string  s = "aa123";
 // 方法1  isdigital()函数
isdigital(s[i]);
// 方法2 
s[i] >= '0' && s[i] <= '9'

2.函数stoi(),atoi(),to_string();

  • atoi():把字符串转换成整型数。使用该函数时要注意atoi返回的是int类型,注意输入str的范围不要超出int类型的范围

判断字符是否为字母
字母(不区分大小写):isalpha()
大写字母:isupper()
小写字母:islower()

2. 哈希表unordered_map()

常见的创建 unordered_map 容器的方法:
1.通过调用 unordered_map 模板类的默认构造函数,可以创建空的 unordered_map 容器。如
std::unordered_map<std::string, std::string> umap;
2.在创建 unordered_map 容器的同时,可以完成初始化操作。如

std::unordered_map<std::string, std::string> umap{
    {"Python教程","http://c.biancheng.net/python/"},
    {"Java教程","http://c.biancheng.net/java/"},
    {"Linux教程","http://c.biancheng.net/linux/"} };

3.调用 unordered_map 模板中提供的复制(拷贝)构造函数,将现有 unordered_map 容器中存储的键值对,复制给新建 unordered_map 容器。
std::unordered_map<std::string, std::string> umap2(umap);

常用函数:

  • at(key) 返回容器中存储的键 key 对应的值,如果 key 不存在,则会抛出 out_of_range 异常。
  • emplace() 向容器中添加新键值对,效率比 insert() 方法高。
  • insert() 向容器中添加新键值对。
  • count(key) 在容器中查找以 key 键的键值对的个数。
  • find(key) 查找以 key 为键的键值对,如果找到,则返回一个指向该键值对的正向迭代器;反之,则返回一个指向容器中最后一个键值对之后位置的迭代器(如果 end() 方法返回的迭代器)。

哈希表遍历:

unordered_map<int,int> map;
//方法一
for(auto [num,occ] : map){
	int key = num;
	int val = occ;
	cout<<"key: "<<key<<" val: "<<val<<endl;
}
//方法二
for(auto &it:map){
	int key = it.first;
	int val = it.second;
	cout<<"key: "<<key<<" val: "<<val<<endl;
}

3.二叉树

前序遍历:根-左-右
中序遍历:左-根-右
后序遍历:左-右-根
前序/中序/后序可以看根节点所在位置。

递归遍历方法

// 后序遍历
class Solution {
public:
    void travel(TreeNode* root, vector<int> &v){
        if(root->left) travel(root->left, v);
        if(root->right) travel(root->right,v);
        v.push_back(root->val);//前序、中序和后序遍历不同在于这一句的位置
    }
    vector<int> postorderTraversal(TreeNode* root) {
        if(!root){
            return {};
        }
        vector<int> vec;
        travel(root,vec);
        return vec;
    }
};

迭代遍历方法
迭代遍历方法用栈实现,迭代方法前序、中序、后序会不太一样,具体见《代码随想录:二叉树的迭代遍历》
二叉搜索树(Binary Search Tree)
定义:

  • 当前节点的左子树中的数均小于当前节点的数;
  • 当前节点的右子树中的数均大于当前节点的数;
  • 所有左子树和右子树自身也是二叉搜索树。

形象地,可看作左小右大的二叉树,,可以用中序遍历(左根右)的方式

4.unordered_set

5.动态数组类型Vector

常见用法在此省略,写一些不常见的

  • reference back(): 返回vector最后一个元素的引用。在某些题会有不错的效果,eg.56.合并区间
    如果是一个m*2的矩阵grid,对其最后元素的访问可以为grid.back()[0],grid.back()[1]

STL常用函数

  • sort()
    sort() 函数位于头文件中,因此在使用该函数前,程序中应包含如下语句:#include <algorithm>
    sort()函数可以对给定区间所有元素进行排序。它有三个参数sort(begin, end, cmp),其中begin为指向待sort()的数组的第一个元素的指针,end为指向待sort()的数组的最后一个元素的下一个位置的指针,cmp参数为排序准则,cmp参数可以不写,如果不写的话,默认从小到大进行排序。
    常用sort() + lambda 表达式实现自定义排序[9].

  • accumulate():accumulate定义在#include<numeric>中,作用有两个,一个是累加求和,另一个是自定义类型数据的处理。
    1.累记求和
    int sum = accumulate(vec.begin() , vec.end() , 42);
    accumulate带有三个形参:头两个形参指定要累加的元素范围,第三个形参则是累加的初值。
    可以使用accumulate把string型的vector容器中的元素连接起来:
    string sum = accumulate(v.begin() , v.end() , string(" "));
    这个函数调用的效果是:从空字符串开始,把vec里的每个元素连接成一个字符串。

2.自定义数据类型的处理

lambda表达式

一个lambda 表达式具有以下形式:
[capture list](parameter list) -> return type { function body }
其中,capture list(捕获列表)是一个lambda所在函数中定义的局部变量的列表(通常为空);return type、parameter list和function body 和任何普通函数一样,分别表示返回类型、参数列表和函数体。
一个完整的lambda 表达式如下:

auto f = [](int a) -> int { return a + 1; };
std::cout << f(1) << std::endl;  // 输出: 2

可以省略参数列表和返回类型,但必须包含捕获列表和函数体:

auto f = [ ] { return 42;}

使用捕获列表
lambda 表达式还可以通过捕获列表捕获一定范围内的变量:

  • [] 不捕获任何变量。
  • [&] 捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。
  • [=] 捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。
  • [=,&foo] 按值捕获外部作用域中所有变量,并按引用捕获 foo 变量。
  • [bar] 按值捕获 bar 变量,同时不捕获其他变量。
  • [this] 捕获当前类中的 this 指针,让 lambda 表达式拥有和当前类成员函数同样的访问权限。如果已经使用了 & 或者 =,就默认添加此选项。捕获 this 的目的是可以在 lamda 中使用当前类的成员函数和成员变量

算法

动态规划

动态规划的核心思想是数学归纳法。动态规划只能应用于有最优子结构的问题,最优子结构的意思指局部最优解能决定全局最优解。

动态规划和其他遍历算法(如深/广度优先搜索)都是将原问题拆成多个子问题然后求解然后求解,他们之间的最本质的区别在于,动态规划保存子问题的解,避免重复计算。

可以通过对动态规划进行空间压缩,起到节省空间消耗的效果。

在一些情况下,动态规划可以看成带有状态记录(memoization)的优先搜索。状态记录的意思为,如果一个子问题在优先搜索时已经计算过一次,可以把它的结果存储下来,在遍历到该子问题时可以直接使用该结果。

动态规划是自下向上的,即先解决子问题,再解决父问题;而用带有状态记录的优先搜索是自上而下的,即从父问题搜索到子问题,若重复搜索到同一个子问题则进行状态记录,防止重复计算。
动态规划解题:找到"状态"和"选择" -> 明确dp数组/函数的定义 ->寻找"状态"之间的关系

进一步细化为:

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

动态规划的难点和关键在于找到正确的状态转移方程,然后通过计算和存储子问题的解来求解最终问题。

ACM 模式训练

OJ在线编程常见输入输出练习场

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
cin.get() == '\n'

其他

C/C++中的取整函数

#include <math.h>
double floor (double x) // 对x进行向下取整
double ceil (double y) // 对y进行向上取整

前缀和

计算一个数组范围内的相关个数可以考虑前缀和(可减少时间复杂度)。
Leetcode 6347. 统计范围内的元音字符串数

位操作

& 按位与运算符:两位同时为1,结果才为1,否则为0。
| 按位或运算符:两位中有一个为1,结果就为1。
^ 异或运算符:两位值不同,结果为1,否则为0。
~ 取反运算符:将0变1,1变0,就是反着来。
<< 左移运算符:各二进制位全部左移若干位,左边丢弃,右边补0。
>> 右移运算符:各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
两个不同长度的数据进行位运算时,系统会将二者按右端对齐,然后进行位运算。短的那个数据如果是负数,左边补1,否则补0。
#include <iostream>
using namespace std;
int main() {
    int n;
    while (cin >> n) {
        if (!(n & (n - 1)))
            cout << "ACM" << endl;
        else cout << "MCA" << endl;
    }
    return 0;
}

参考:判断一个数是不是2的整数次幂(两种方法)

常见问题

内存溢出

Line 8: Char 17: runtime error: signed integer overflow: 2147000000 + 1000000 cannot be represented in type 'int' (solution.cpp)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior prog_joined.cpp:17:17

解决方法

将变量定义为 long long int, int、long int、long long int的范围为:

int -2147483648~2147483647
long int -2147483648~2147483647
long long int -9223372036854775808~9223372036854775807

相应的无符号类型的各自表示范围为:
unsigned int 0~4294967295
unsigned long int 0~4294967295
unsigned long long int 0~18446744073709551615

参考链接:
[1] 字符串操作——substr用法
[2] 什么是核心代码模式,什么又是ACM模式?
[3] [ACM模式]牛客网ACM机试模式Python&Java&C++主流语言OJ输入输出案例代码总结
[4] C++ STL unordered_map容器用法详解
[5] C++中accumulate的用法
[6] C++string中find_first_of()函数和find_last_of()函数
[7] C++11 lambda表达式精讲
[8] C++ 中的 Lambda 表达式
[9] C++ sort()排序详解
[10] C++ lambda表达式与函数对象
[11] 代码随想录
[12] labuladong的算法小抄
[13] LeetCode 101 :A Leetcode Gringding Guide(C++)
[14] int,long,long long类型的数值范围
[15] int、long int 和 long long int 的取值范围

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

90的尾巴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值