#include <bits/stdc++.h>
一、数组
- 求数组大小
nums.size()//防止报错
- 数组中的按大小排序
sort(nums.begin(), nums.end());
- 获取最大值/最小值
int nums[8] = {1,2,3,8,0,33,11,9};
int max_num = *max_element(nums, nums + 8);
int min_num = *min_element(nums, nums + 8);
- 将数组a中的数值置为0
int a[m];
fill(a,a+m,0);
二、函数
- 注意当返回两个以上值
return {i,j};
或
return {};
- 返回数组时:
//从数组中获得初值
int b[7]={1,2,3,4,5,6,7};
vector<int> a(b,b+7);
return a;
三、vector
- 初始化
//一维
vector<int> a;
//定义具有10个整型元素的向量,不具有初值,其值不确定
vector<int> a(10);
//定义具有10个整型元素的向量,且给出的每个元素初值为1
vector<int> a(10,1);
//用向量b给向量a赋值,a的值完全等价于b的值
vector<int> a(b);
//将向量b中从0-2(共三个)的元素赋值给a,a的类型为int型
vector<int> a(b.begin(),b.begin+3);
//从数组中获得初值
int b[7]={1,2,3,4,5,6,7};
vector<int> a(b,b+7);
//二维
ret.push_back(vector<int>());//给ret申请空间
2.常用内置函数
#include<vector>
//一维
//insert
//在a的最后一个向量后插入一个元素,其值为5
a.push_back(5);
//在a的第一个元素(从第0个算起)位置插入数值5,
a.insert(a.begin()+1,5);
//在a的第一个元素(从第0个算起)位置插入3个数,其值都为5
a.insert(a.begin()+1,3,5);
//b为数组,在a的第一个元素(从第0个元素算起)的位置插入b的第三个元素到第5个元素(不包括b+6)
a.insert(a.begin()+1,b+3,b+6);
//delete
//删除a向量的最后一个元素
a.pop_back();
//删除a中第一个(从第0个算起)到第二个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+3(不包括它)结束
a.erase(a.begin()+1,a.begin()+3);
//返回a的第一个元素
a.front();
//返回a的最后一个元素
a.back();
//返回a的第i元素,当且仅当a存在
a[i];
//清空a中的元素
a.clear();
//判断a是否为空,空则返回true,非空则返回false
a.empty();
//返回a中元素的个数
a.size();
//返回a在内存中总共可以容纳的元素个数
a.capacity();
//将a的现有元素个数调整至10个,多则删,少则补,其值随机
a.resize(10);
//将a的现有元素个数调整至10个,多则删,少则补,其值为2
a.resize(10,2);
//将a的容量扩充至100,
a.reserve(100);
//b为向量,将a中的元素和b中的元素整体交换
a.swap(b);
//b为向量,向量的比较操作还有 != >= > <= <
a==b;
//二维
ret.back().push_back(x);
int k = mat.size() * mat[0].size();//mat.size()为mat中行的个数,mat[0].size()为mat中列的个数
3.常用算法
#include<algorithm>
vector<int> nums = {1,2,3,8,0,33,11,9};
int max_num = *max_element(nums.begin(), nums.end());//获取最大值
int min_num = *min_element(nums.begin(), nums.end());//获取最小值
sort(a.begin(),a.end());//对a中的从a.begin()(包括它)到a.end()(不包括它)的元素进行从小到大排列
reverse(a.begin(),a.end());//对a中的从a.begin()(包括它)到a.end()(不包括它)的元素倒置,但不排列,如a中元素为1,3,2,4,倒置后为4,2,3,1
copy(a.begin(),a.end(),b.begin()+1);//把a中的从a.begin()(包括它)到a.end()(不包括它)的元素复制到b中,从b.begin()+1的位置(包括它)开始复制,覆盖掉原有元素
find(a.begin(),a.end(),10);//在a中的从a.begin()(包括它)到a.end()(不包括它)的元素中查找10,若存在返回其在向量中的位置
fill(matrix[i].begin(),matrix[i].end(),0);//将二维数组matrix第i行的所有数值置为0
4.常用向向量添加元素的方式
//直接添加
vector<int> a;
for(int i=0;i<10;++i){a.push_back(i);}
//从数组中选择元素向向量中添加
int a[6]={1,2,3,4,5,6};
vector<int> b;
for(int i=0;i<=4;++i){b.push_back(a[i]);}
//从现有向量中选择元素向向量中添加
int a[6]={1,2,3,4,5,6};
vector<int> b;
vector<int> c(a,a+4);
for(vector<int>::iterator it=c.begin();it<c.end();++it)
{
b.push_back(*it);
}
//从文件中读取元素向向量中添加
ifstream in("data.txt");
vector<int> a;
for(int i;in>>i){a.push_back(i);}
//常见错误赋值方式
vector<int> a;
for(int i=0;i<10;++i){a[i]=i;}//下标只能用来获取已经存在的元素
5.从向量中读取元素
//通过下标方式获取
int a[6]={1,2,3,4,5,6};
vector<int> b(a,a+4);
for(int i=0;i<=b.size()-1;++i){cout<<b[i]<<endl;}
//通过迭代器方式读取
int a[6]={1,2,3,4,5,6};
vector<int> b(a,a+4);
for(vector<int>::iterator it=b.begin();it!=b.end();it++){cout<<*it<<" ";}
四、字符
1.遍历从字母'a'到字母'z'
for(char i = 'a';i <= 'z';i++){
}
2.char
(1)c语言对字符串常量按照字符数组进行处理
#include <stdio.h>
int main(){
char *string1="aaaa";
printf("%s\n",string1);
return 0;
}
a.c语言对字符串常量“aaaa”按照字符数组进行处理,在内存中开辟了一个字符数组用来存放该字符串常量。但是这个数组没有名字,只能用指针变量来引用。
b.对于字符指针变量string1初始化,实际上是把字符串第一个元素的地址(即存放字符串的字符数组的首元素地址)赋值给指针变量string,使string指向字符串的第一个字符。
c.可以通过指针变量输出字符串,输出时需用%s格式符,则系统会输出string所指向的第一个字符,然后自动使string1加1,使之指向下一个字符,再输出该字符…直到遇到字符串结束标志‘\0’为止
(2)字符型数据 是以 ASCII代码 存储在存储单元中
#include <stdio.h>
int main() {
char ch = '6';
int i = 54;
printf("%d ", ch); //以整形形式输出字符串ch,输出54
printf("%c",i); //以字符串形式输出整数i,输出6
return 0;
}
由结果可知char可直接转换为int型值,int也可直接转换为char字符,十进制54对应字符为‘6’
3.string
//初始化
string s1;
string s2 = s1; //string s2(s1);
string s3 = "value"; //string s3("value");
string s4(4, "c"); //string s4 = "cccc";
//利用assign函数
string s1("12345"), s2;
s3.assign(s1); // s3 = s1
s2.assign(s1, 1, 2); // s2 = "23",即 s1 的子串(1, 2)
s2.assign(4, 'K'); // s2 = "KKKK"
s2.assign("abcde", 2, 3); // s2 = "cde",即 "abcde" 的子串(2, 3)
//insert
string s1("value");
s1.insert(s1.begin(), 's');//执行后,s1为"svalue"
s1.insert(s1.end(), {'1','2'});//执行后,s1为"svalue12"
//比较
string s1 = "abcd";
if(s1 == "abcd"){
}
五、unordered_map
unordered_map<char, int> umap;//创建,前者为key,后者为value
for (char ch:s) {//s为一维string数组
++umap[ch];
}
unordered_map<char, char> pairs = { //建立一个哈希表,第一个char为key,第二个char为value
{')', '('}, //哈希表映射的右括号为键,左括号为值
{']', '['},
{'}', '{'}
};
//map中查找key值x是否存在
umap.count(x) != 0
//遍历哈希表
for(auto [k, v] : umap){
}
//clear
umap.clear();
六、unordered_set
//初始化
#include <unordered_set> //set为#include <set>
unordered_set<Node*> uset; //Node*类型
unordered_set<int> uset={2,4,8}; //直接初始化
unordered_set<int> uset2(set); //拷贝构造
unordered_set<int>(candyType.begin(), candyType.end()); //这里使用unordered_set<int>的构造函数,它接受两个迭代器作为参数,表示要插入到unordered_set中的元素范围。unordered_set是一个基于哈希表的容器,它会自动去重,即不会存储重复的元素。因此,当candyType中的元素(通过迭代器指定的范围)被插入到unordered_set中时,重复的元素会被忽略。
//基本操作
int n=uset.size(); //当前集合元素个数
//插入
uset.insert(a); //向集合中插入元素a
uset.emplace(a); //比insert效率更高的插入
//删除
uset.erase(a); //删除集合中的a
//清空
uset.clear();
//查找
auto b=uset.find(6); //查找值为6的元素,若有返回迭代器,若无返回end()
int nums=uset.count(6); //集合中值为6的元素个数
七、set
set 和 unordered_set 都是不重复的集合。set 的底层通过树实现,且会自动为集合内元素按由小到大排序。unordered_set 的底层通过哈希表实现,并不会自动排序。当创建一个不需要排序的集合时应使用 unordered_set,因为哈希表对元素的查找更快。
#include <set> //set头文件
int x;
set<int> s; //初始化一个名为s的set,默认升序排列
set<int,greater<int>> s; //初始化一个名为s的set,降序排列
set.insert(x); //在set中插入x
set.erase(x); //删除set中的x元素,返回0表示set中不存在x,1表示删除成功
set.clear(); //清空set
set.empty(); //判断set是否为空,若是返回1,否则返回0
set.size(); //返回set中元素的个数
set.find(x); //返回x的迭代器,若x不存在,则返回指向set的尾迭代器
set.count(x); //返回x出现的次数,0或1
set.lower_bound(x); //返回一个迭代器,指向第一个键值大于等于x的元素
set.upper_bound(x); //返回一个迭代器,指向第一个键值大于x的元素
set.begin(); //返回指向q中第一个元素的迭代器
set.end(); //返回指向q最后一个元素下一个位置的迭代器(尾迭代器)
八、队列
1.初始化
//queue<Type,Container> (<数据类型,容器类型>)
//初始化时必须要有数据类型,容器可省略,省略时则默认为deque类型
queue<int> q1;
queue<double> q2;
queue<char> q3;
queue<pair<char,int>> q4;
queue<char,list<char>> q1;//用list容器实现的queue
queue<int,deque<int>> q2;//用deque容器实现的queue
2.常用函数
q.push("first");//在队尾插入一个元素
q.pop();//删除队列第一个元素,没有返回值
q.size();//返回队列中元素个数
q.empty();//如果队列空则返回true
q.front();//返回队列中的第一个元素
q.back();//返回队列中最后一个元素
3.注意事项
(1)若for循环条件语句用到qure.size(),且for循环内部代码有qure.pop()操作时,要把qure.size()单独拿出来赋给变量,将for循环执行之前的值存储下来,再将这一值运用到for循环的条件语句中。
//错误
for(int i = 0;i < qure.size();i++){
int re = qure.front();
qure.pop();
}
//正确
int size = qure.size();
for(int i = 0;i < size;i++){
int re = qure.front();
qure.pop();
}
九、链表
//定义
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
//初始化
ListNode* head = new ListNode(5); //使用上述定义中的构造函数来初始化
ListNode* head = new ListNode(); //使用c++默认构造函数来初始化
head -> val = 5;
//调用
int a = head -> val;
ListNode* temp = head;
ListNode* temp = head -> next;
if (head == nullptr || head->next == nullptr) {//不是空链表且有后续节点
return false;
}
//用unordered_set来区分链表每个节点
bool hasCycle(ListNode *head) {
unordered_set<ListNode*> seen;
while (head != nullptr) {
if (seen.count(head)) {
return true;
}
seen.insert(head);
head = head->next;
}
return false;
}
//两个链表节点可以直接相比较是否相等
ListNode* slow = head;
ListNode* fast = head->next;
while (slow != fast) {
}
十、const
const名叫常量限定符,用来限定特定变量,以通知编译器该变量是不可修改的。习惯性的使用const,可以避免在函数中对某些不应修改的变量造成可能的改动。
1.const修饰一般常量及数组
int const a = 100;
const int a = 100; //与上面等价
int const arr [3] = {1,2,3};
const int arr [3] = {1,2,3};//与上面等价
2.const修饰指针(*)
2.1常量指针(值为常量)
const用来修饰指针所指向的变量,即指针指向为常量;
当为常量指针时,不可以通过修改所指向的变量的值,但是指针可以指向别的变量。
int a = 5;
const int *p =&a;
*p = 20; //error 不可以通过修改所指向的变量的值
int b =20;
p = &b; //right 指针可以指向别的变量
2.2指针常量(指针为常量)
const修饰指针本身,即指针本身是常量。
当为指针常量时,指针不能指向别的变量,但是可以通过指针修改它所指向的变量的值。
int a = 5;
int *const p = &a;
*p = 20; //right 可以修改所指向变量的值
int b = 10;
p = &b; //error 不可以指向别的变量
左定值,右定向,const修饰不变量
3.修饰函数
3.1修饰函数形参
函数体内不能修改形参a的值。
3.1.1形参为指针
void StringCopy(char* strDest, const char* strSource);
形参指针加上const 修饰之后,保护了这一块内存地址不被修改,如果刻意修改这一块内存,编译器会报错。
十一、栈
//定义
stack<int> s;
stack<char> stk;
//常用内置函数
s.empty(); //如果栈为空则返回true, 否则返回false;
s.size(); //返回栈中元素的个数
s.top(); //返回栈顶元素, 但不删除该元素
s.pop(); //弹出栈顶元素, 但不返回其值
s.push(); //将元素压入栈顶
十二、树
1.前序遍历
前序遍历首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树。
若二叉树为空则结束返回,否则:
(1)访问根结点。
(2)前序遍历左子树。
(3)前序遍历右子树 。
需要注意的是:遍历左右子树时仍然采用前序遍历方法。
如图所示二叉树
前序遍历结果:ABDECF
//递归算法
class Solution {
public:
void preorder(TreeNode *root, vector<int> &res) {
if (root == nullptr) {
return;
}
res.push_back(root->val);
preorder(root->left, res);
preorder(root->right, res);
}
vector<int> preorderTraversal(TreeNode *root) {
vector<int> res;
preorder(root, res);
return res;
}
};
2.中序遍历
首先遍历左子树,然后访问根结点,最后遍历右子树。
3.后序遍历
在二叉树中,先左后右再根,即首先遍历左子树,然后遍历右子树,最后访问根结点。
4.层序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector <vector <int>> ret;
if (!root) {
return ret;
}
queue <TreeNode*> q;
q.push(root);
while (!q.empty()) {
int currentLevelSize = q.size();
ret.push_back(vector <int> ());
for (int i = 1; i <= currentLevelSize; ++i) {
auto node = q.front();
q.pop();
ret.back().push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
}
return ret;
}
};
5.树的最大深度
//深度优先搜索
//如果我们知道了左子树和右子树的最大深度l和r,那么该二叉树的最大深度即为
//max(l,r)+1
//而左子树和右子树的最大深度又可以以同样的方式进行计算。
class Solution {
public:
int maxDepth(TreeNode* root) {
if (root == nullptr) return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
十三、位运算
00001100 - b
00110000 - b << 2 (左移两位)
00000011 - b >> 2 (右移两位)
//让n与2^i相与
if (n & (1 << i)) {
ret++;
}
十四、获取一个数字的位数
使用字符串处理函数:将数字转换为字符串,然后使用字符串的长度函数来获取位数。
#include <iostream>
#include <string>
using namespace std;
int main() {
int num = 12345;
string numStr = to_string(num);
int digits = numStr.length();
cout << "Number of digits: " << digits << endl;
return 0;
}
十五、检测输入
1.输入数据类型不当
由于函数scanf( )不做参数类型匹配检查,因此,当输入数据类型与格式字符不相符时,编译器会发生错误。
怎么解决这个问题呢?我们可以考虑检验函数scanf( )调用的返回值的方法。如果函数scanf( )调用成功(能正确读入输入数据),则返回值为已成功读入的数据项数。当用户输入不相符的数据类型时,我们可以在程序中,给出错误的提示,采用函数fflush( )来清除缓冲区中的内容,然后提示用户重新输入数据直到输入正确为止。
由于ANSI C只规定函数fflush( )处理输出数据流、确保输出缓冲区中的内容写入文件,并未对清理输入缓冲区作出任何规定,只是部分编译器(如Visual C++和Turbo C等)增加了此项功能,因此使用函数fflush( )来清除输入缓冲区中的内容,可能会带来移植问题。
int n, ret = 0;
do{
printf("请输入\n");
ret = scanf("%d", &n);
if(ret != 1){
printf("输入有误\n");
fflush(stdin);
}
}while(ret != 1);
printf("输入的值为%d", n);
2.只允许输入字符0-4
while(1){
char i[100];
scanf("%s",&i[0]);
if(strlen(i)!=1||i[0]<'0'||i[0]>'4'){ //strlen(t)!=1是为了防止输入第一个字符为0-4的含有多个字符的字符串,限制了字符串长度
printf("输入错误,请输入0-4中的数字\n");
continue;
}
return 0;
}