锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

算法笔记(自用)

时间:2023-01-26 23:00:00 p784xlf连接器d

C Basic

C 基础/基本注意点

类型范围

int -2147483648~2147483647

long -2147483648~2147483647

long long -9223372036854775808~9223372036854775807 (别用cin,用scanf(“%lld”)) (2^64)

有时候long long 不能用long double

常用定义

正无穷 0x3f3f3f3f

正无限初始化

使用头文件:memset(v,0x3f,sizeof v) ///将v值初始化为无限大0x3f

注意:使用memset初始化只能初始化0,-1,0x3f; 如果要初始化其他值,请使用它们fill()

fill(a,a n,val); or fill(a.begin(),a.end(),val); in stl

数据输入输出

  • 大规模使用数据 scanf 读取 printf 输出

  • 输出固定n位数(不足0)

    printf("d",x) ///输出五位整数,不足0

  • 处理字符串(带空格)

    getline(cin,str); //注意,如果你按在前面,你必须回车getchar()一下!cin.ignore()

    连续读取带所需数字的字符串,如12 is the root of 35,可以使用sscanf

    sscanf(str.c_str(),"%d is the root of %d",&a,&b); 

    sprintf可以将数字或其他数字写入字符数组

    char res[5]; sprintf(res,"d",num); 
  • 小数位保留

    直接 printf(%.xlf”,xxx);在小数点之后,你可以保留几      round(x);作用是四舍五入小数点后的一个,比如 1.5->2.0 ; 1.56->2.0 

转义

%的转义是%%

位运算

  • 除2位运算:>>1,例如:(a b)/2写成a b>>1,a/=2写成a>>=1;同理,乘以2就是<<1(注:只能操作整数)

  • 例如,不等于-1i!=-1的快速写法~i

  • 对2取模,简写成x&1 例如:if(x%2==1)改写成if(x&1==1)

一些常用的位置操作

  1. 求n二进制第k位(k=0是个位): n>>k&1
  2. lowbit:返回x的最后一个1,应用:求n二进制中的1个数
int lowbit(int x){ 
          return x&-x; //或者x&(~x 1) } int res=0;
while(x){ 
        
	x-=lowbit(x);
	res++;
}
cout<<res;

浮点数的比较方法

由于计算机中浮点数的存储问题,不能直接==,需要判断他们相减的值是不是小于某个很小的数,加很多括号是为了防止宏定义出错

const double eps = 1e-8;
#define Equ(a, b) ((fabs((a) - (b))) < (eps))
#define More(a, b) (((a) - (b)) > (eps))
#define Less(a, b) (((a) - (b)) < (-eps))
#define LessEqu(a, b) (((a) - (b)) < (eps))
if(Equ(a, b)) { 
        
 cout << "true";
}

圆周率 Pi

const double Pi = acos(-1.0);

数组复制

#include

memcpy(a,b,sizeof b); //把b数组复制到a数组中

结构体

结构体可以{aa,bb,cc}这样写,例如:

struct Person { 
        
    string name;
    int age,w;
}
vector<Person> ages[N];
int main(){ 
        
    char name[10];
    int age,w;
    scanf("%s%d%d",name,&age,&w);
    ages[age].push_back({ 
        name,age,w}); //将结构体插入ages容器数组中,可以使用类似pair的写法
}

//结构体中也可以定义方法
struct Student(){ 
        
    Student(){ 
        } //构造函数
    Student(string _id):id(_id){ 
         //带参数的构造函数
        for(int i=1;i<=k;i++) grade[i] = -2;
        total = cnt = 0;
    }
    void calc(){ 
         //自定义计算方法
    	for(int i=1;i<=k;i++){ 
        
            total += max(0,grade[i]);
            if(grade[i]==p_score[i]) cnt++;
        }   
    }
    bool has_submit(){ 
         
        for(....)
            ....
        return false;
    }
    bool operator< (const Student& t) const { 
        //运算符重载
        .... return ....
    }
}

STL 操作

注意事项

  • xxx的最后一个元素的迭代指针是xxx.end()-1
  • 如果要重复用记得clear()一下…

向量 vector

  • vector找最值和对应位置,使用中的min_element()和max_element(),返回最值的迭代器
auto pos = min_element(v.begin(),v.end())max_element(v.begin(),v.end()) 
对应位置是pos-v.begin();值是*pos
  • 建立一个逆序vector

    vector B(A.rbegin(),A.rend())

  • 遍历vector

vector<Person> ages[N];
for(auto &age:ages) sort(age.begin(),age.end());
  • 找一个元素

    v.find(key) 返回下标指针

  • 去重

    unique()需要和``v.erase()`配合才是真正的去重

    v.erase(unique(v.begin(),v.end()),v.end());
    
  • 初始化二维vector固定大小

    //例如在固定大小的地图中
    int row = grid.size();
    int col = gird[0].size();
    vector<vector<int>> dp(row,vector<int>(col,0));
    
  • push_back()和emplace_back()

    emplace_back() 和 push_back() 的区别,就在于底层实现的机制不同。

    push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。

映射 map

  • 查找这个map中有没有这个keym.count(key)

unordered_map是没自动排序的map,此时通过max_element()找到的最大值,他的key不一定是最小的,若是map则两个相同的值中,找到的key是最小的。

  • 找到Map中最大的value对应的key

使用max_element() 配合cmp()函数中使用pair组成键值对

bool cmp(const pair<type1,type2> left,const pair<type1,type2> right){ 
        
	return left.second<right.second;
}
  • 对map的排序

对map排序实际上还是要转换在vector中进行,重载比较函数

集合 set

添加

s.insert(key)

删除

s.erase(key或者iterator)这里参数可以是值也可以是位置指针

注意,在multiset中erase一个数的话会把set里的数全删除,删一个数使用s.erase(s.find(x))

优先队列 priority_queue

定义

priority_queue

使用优先队列实现堆

  • 小根堆:priority_queue,greater> heap PII是pair经过typedef
  • 大根堆:priority_queue,less> heap

字符串 string

  • 删除特定字符

    使用find()erase()

  • 大小写转换

    遍历字符串,对每个字符使用头文件中的tolower(),toupper()

  • 字符串转数字、数字转字符串

    数字 --> 字符串 :to_string()

    字符串 --> 数字:

    1. 整数 stoi()2. 浮点,使用 atof(str.c_str()) //这里要传char* 也可以 stof() 和 stod()
    
  • 找到对应子串位置

    str.find("substr...");//如果没找到返回-1,找到返回其下标
    
  • 在特定位置插入字串

    s.insert(pos,size,char);

    例如在字符串开头插入n个0s.insert(0,n,'0')

  • sscanf(),sprintf()从字符串中读入数据和把数据写入字符串中

  • string char[]、char*的转换关系:

    • string -> const char*使用c_str()

    • char*和char[]可以直接转string,如:string s; char* p="hello"; s=p;

  • 带空格的字符串按空格分割,使用stringstream

    #includevector<string> res; 
    //存放分割后字符串string words="ACD BCD CCD";
    string word;
    stringstream input(words);
    while(input>>word){ 
               
     res.push_back(word);    
     cout<<word<<endl;
     }
    

关于

isalnum(char c)判断该字符是不是字符数字或者字母,是返非0,否则为0还有isupper(),islower()

队列queue、栈stack

他们不支持clear()操作

  • queue::emplace()

    将元素插入队列末尾

  • queue::push()

    插入队尾

  • queue::pop()

    队头出队

关于permutation

  • is_permutation(v1.begin(),v1.end(),v2.begin())判断v2是不是v1的序列,是1否0

  • next_permutationprev_permutation用法

    string s = "12345";
    do{ 
              
        cout<<s<<endl;
    }while(next_permutation(s.begin(),s.end()));
    
    string s = "12345";
    do{ 
              
        cout<<s<<endl;
    }while(prev_permutation(s.begin(),s.end()));
    

排序

注意要稳定排序有个stable_sort()

1. 默认函数greater()less()

sort的排序默认从小到大递增排列,也可以增加第三个参数传入一个比较函数,C++提供两个默认的比较函数

greater<>() 从大到小,递减排列

less<>() 从小到大,递增排列(默认)

注意:在创建map<>set<>等STL类型时,通常可以传入比较函数改变其默认排序

快排模板

void quick_sort(int q[],int l,int r){ 
        
    if(l>=r) return;
    int x = q[l],i=l-1,j=r+1;
    while(i<j){ 
        
        
    }
}

2. 使用运算符重载

运算符重载可以放到结构体中

//例如在Person结构体中利用运算符重载排序,要求按资产w降序,否则年龄age升序,其次姓名name升序
struct Person{
    string name;
    int age,w;//年龄和资产
    bool operator< (const Person &t) const//重载小于号
    {
        if(w!=t.w) return w > t.w; //先按资产排序,资产不同则资产大的排在前面
        if(age!=t.age) return age < t.age; //否则年龄不同的话,年龄小的排在前面
        return name < t.name; //最后姓名字典序小的排在前面
    } //注意后面使用 < 的时候,若 a

3. 使用lamda表达式

sort(a,a+n,[](int x,int y){ 
        
    if(cnt[x]!=cnt[y]) return cnt[x]>cnt[y];
    return x<y;
});
//也可以匿名函数
auto cmp = [](int x,int y){ 
        if(cnt[x]!=cnt[y]) return cnt[x]>cnt[y];return x<y;};

4. 手写cmp()函数

bool cmp(const int a,const int b){ 
         //若e[a]
    if(e[a]!=e[b]) return e[a]<e[b];
    return e[a]>e[b];
}

排序算法操作

K路归并

标准:利用堆找每一路最大/小

其次:使用遍历找

开个vector数组,然后把数组中的vector都排好序,利用遍历找最小

//遍历找最小模板
int t = -1;
for(int i=a,a<=b;i++){ 
        
    if(t==-1||ages[t][idx[t]]<ages[i][idx[i]])
        t=i;
}
//for结束后,t就是最小值的下标
while (true) { 
        
	d<<=1;
	bool eq = match();
	for (int i = 0; i < n; i += d) { 
        
		sort(a + i, a + min(n,i + d)); //这里间隔d个排序为什么用min,因为最后面不够d个只能到n了
	}
	if (eq) break;
}

关于排序题的排名输出(相同分数要求排名一样)

int rank = 1;
for(int i=0;i<list.size();i++){ 
        
    if(i&&grade[i]!=grad[i-1]) rank=i+1; //如果成绩不等rank就是i+1;
    //输出信息...
}

一些题型套路

  • 推理题都是用枚举来做
  • 链表题都丢到数组里做

基本算法技巧

双指针

一般分两大类,一类是两个指针分别指向两个序列,一类是两个指针指向一个序列

一般都是先把暴力算法写出来,然后看有没有单调性,有就用双指针优化

核心思想

单调性,将双重循环优for(i:n){for(j:n){}}化到O(n)

基本模板

for(int i=0,j=0;i<n;i++){ 
            while(j<n&&check(i,j)) j++;    //然后是具体逻辑}

离散化

用于要使用值做下标的情况下,值域非常大,但是个数不多的时候,把序列映射到从0开始的连续自然数

  1. 若序列中有重复元素则需要去重
  2. 需要能快速算出序列中的值离散化后的值(二分)【起始也可以排序后用一个unordered_map记录位置】
//1. 把所有要用到的值加入一个数组中
//2. 对数组进行排序去重
all.erase(unique(all.begin(),all.end()),all.end());
//3. 遍历一次记录对应位置关系
unordered_map<int,int> pos;
for(int i=1;i<=all.size();i++) pos[all[i-1]]=i; //如果要用到前缀和就从1开始映射

//4. 之后每次要用到原本的值,就套一个pos[x]

基础算法模板

二分

左闭右闭,找是否存在满足条件的元素,没找到返回-1

int BS(int l,int r,int key){ 
        	while(left<=right){ 
                int mid = l+r>>1; //防止溢出可以mid=left+(right-left)/2 if(A[mid]==key) return mid; else if(A[mid]>key) r = mid-1; else l=mid+1; } return -1;}

第一个满足条件的元素位置【该条件一定是从左到右先不满足,然后满足】

int BS(int l,int r){ 
            while(l<r){ 
                int mid = l+r>>1;        if(check(mid)) r=mid;        else l=mid+1;    }    return l;}

找第一个不满足性质的元素位置【从左到右满足,然后不满足】

int BS(int l,int r){ 
            while(i<r){ 
                int mid = l+r+1>>1;        if(check(mid)) l=mid;        else r=mid-1;    }	return l;}

DFS、回溯枝剪

深搜关键在于顺序

关于n皇后的技巧性(数组设置)

int row[N],col[N],dg[N],udg[N]; 
//这里col[i]表示第i行是否有皇后,dg和udg有坐标变换u-i+n和u+i//即dg[x+y]表示该斜是否有皇后,udg[x-y+n]表示另一斜是否有皇后
//dfs 原始思维 O(2^(n^2))
void dfs(int x,int y,int s) //x表示行,y表示列,s是皇后个数
{ 
            
	if(y==n) y=0,x++; //列越界,跳至下一行 
	if(x==n) { 
         //到达最后一行 
	if(s==n){ 
                    
		for(int i=0;i<n;i++) puts(g[i]);            
		puts("");        
	}        
	return;    
}        
dfs(x,y+1,s); //不放皇后 
if(!row[x]&&!col[y]&&!dg[x+y]&&!udg[x-y+n]){ 
                
	g[x][y] = 'Q'; //记录 
	row[x] = col[y] = dg[x+y] = udg[x-y+n] = true;        
	dfs(x,y+1,s+1);        
	row 

相关文章