【C/C++】算法笔记

#include <bits/stdc++.h>

一、数组

  1. 求数组大小
nums.size()//防止报错
  1. 数组中的按大小排序
sort(nums.begin(), nums.end());
  1. 获取最大值/最小值
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);
  1. 将数组a中的数值置为0
int a[m];
fill(a,a+m,0);

二、函数

  1. 注意当返回两个以上值
return {i,j};
或
return {};
  1. 返回数组时:
//从数组中获得初值
int b[7]={1,2,3,4,5,6,7};
vector<int> a(b,b+7);
return a;

三、vector

  1. 初始化
//一维

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);    //拷贝构造

//基本操作
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;
	}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值