pat考前整理(9.9-9.10更新)

(一)常见数学函数工具
a.判别素数:

bool isprime(int a){
if(a==1||a==0)return false;
for(int i=2;i*i<=n;i++){
if(a%i==0)return false;
}
return true;
}

b.最大公约数:

int gcd(int a,int b){
if(b==0)return a;
else return gcd(b,a%b);
}

(二)stl容器的使用及自己常出现的错误
a.vector

#include<vector>
using namespace std;
vector<int>a(10)//初始化一个容量为10的vector数组;
vector<int>a[10]//初始化一个列为10行不定的二维数组;
int n,a.resize(n)//将a的容器大小改变为10;
a.push_back(1)//向a末尾加入一个1;
a.insert(a.begin()+i,1)//向a中第i个数加入1;
a.front()//返回a的第一个元素;
a.back()//返回a的最后一个元素;
a.clear()//清空a
迭代器:vector<int>::iterator it=a.begin()//常犯错误注意的it=a.end() *it获得的不是a的最后一个元素因为a.end()为空,要使用a.back();
a.pop_back()//除去vector最后一个元素;
a.erase(a.begin()+i)//除去vector中第i个元素

b.set

# include<set>
using namespace std;
set<int>st,sts[100]//建立一个set函数和100个sts数组
s.insert(0),s.insert(5),s.insert(1)//set是一个自动去重从小到大排序的数组;
for(set<int>::iterator it=st.begin();it!=st.end();it++){
cout<<*it;
}
0 1 5                              //set函数只能用迭代器形式输出原因是因为它不知持*it+i的访问;
it=st.find(5)//在st中寻找值为5的数
st.erase(1)//删除set中值为1的数

c.string的用法

#include<string>
#include<iostream>//string只能用cin输入,cout输出或者转为字符串格式很麻烦
using namespace std;
string a;//定义一个为a的数组;
getline(cin,a);//将该行的字符串输入a遇到回车返回
cin<<a;
//sscanf与ssprintf的用法
char s[1010];//若用string要把string格式转换为char即:a.c_str();
s="123abc456";
int c,d;
sscanf(s,"%dabc%d",&c,&d);//sscanf就是将s的值赋值到满足条件到右边的变量,从左到右
cout<<c<<endl<<d;
123
456                      //需要注意的和scanf一样读入格式一定要一样
sprintf是从右向左感觉pat考试用的不多这里不涉及
stoi(a)//将a的值转为int型整数的值
string c=a.substr(pos,len)//c字符串等于a的从pos位开始长度为len的字串;
string::npos//不同于vector stl和set stl该容器find不到时返回是这个值经常错,考前得看
char c[1010];
strcpy(c,a.c_str());//将a转为char数组格式并复制到c;
a.insert(3,"abc")//在a[2]之后插入abc;

d.map的用法

#include<map>
#include<unordered_map)//无序map用时会减少很多,普通map中会利用红黑树自动排好序并去重
using namespace std;
map<string,int>mp1;
unordered_map<string,int>mp2;
	string a = "a1";
	string b = "b1";
	string c = "c1";
	mp1[c] = 1, mp2[c] = 1;
	mp1[b] = 2, mp2[b] = 2;
	mp1[a] = 3, mp2[a] = 3;
	for (map<string, int>::iterator it = mp1.begin(); it != mp1.end(); it++) {
		cout<<it->first<<" "<<it->second<<" ";
	}
	for (unordered_map<string, int>::iterator it = mp2.begin(); it != mp2.end(); it++) {
		cout << it->first << " " << it->second<<" ";
	}
	a1 3 b1 2 c1 1 c1 1 b1 2 c1 3
	//结果可以看出unordered_map是不排序的,同时要注意的是迭代器是unordered_map

e.stack和queue

我本人不怎么用这两个容器,但优先队列去年考到过就着重看了一下
#include<queue>//就用队列开头即可,不同点在于没有front()和back(),只有top()其内在实现也是通过堆排序实现的;
using namespace std;
priority_queue<int>q;
q.push(1);
q.push(5);
q.push(3);
q.push(4);
cout<<q.top();
5//优先队列中默认较大的数在前;
priority_queue<int,vector<int>,less<int>>q//设置较大的数在队列前面;
priority_queue<char,vector<char>,less<char>>q//设置字母顺序表大的字母优先级高的队列;
struct fruit{
string name;
int price;
friend bool operator<(fruit f1,fruit f2){
return f1.price<f2.price;
}
};//结构体的优先队列,这是初始化时就有优先级不然的话用sort函数也可以实现;

总结一下stl容器吧去年的时候还完全不会用,今年不一样了:
a.set,map在一些求无重复值的题目中有很大作用;
b.vector数组是最常见的在临接表存储图以及存储数组等各个方面都有重要用途;
c.string函数的子函数的使用可以大大加快字符串的处理像substr,stoi,find string::npos会缩减很大的处理数据量;
d.map在数组转化,vis数组以及大数字这些方面有很好的处理过程。
(三)常见模型整理

a.bfs模型

//借用队列完成
void bfs(int a){
queue<int>q;
q.push(a);
vis[a]=false;//这个不能忘;
while(!q.empty){
int now=q.front();
q.pop();
if(now的孩子满足什么条件进队列){
进队;
对进队的元素进行操作;}
//最常见的应用应该就是树的层次遍历,同时一般不会出的这么裸会涉及一些变量的的设置常见的比如说层次遍历每一层倒叙输出,判断哪层结点最多,只要掌握最核心的都能解决基本;
//第二个应用就是最基础的bfs图判联通性那么这个时候进队条件就是是否超过边界(没被访问过并且在边界内)
}
}

b.dfs模型
相较于bfs,dfs是利用栈,只有当一个栈见底即反应完全时才可以进行下一个反应。其在pat中应用更广,我把它分为以下的几类
1)树的深度遍历,在求树高中有用到

struct node{
int v,lev;
vector<int>child;
}t[10010];//以静态树为例
void dfs(int index,int depth){
if(t[index].child.size()==0){//判断递归终点条件
按题意进行操作,比如求树最深高度;
return//return 不能忘不然会报越界错误;
}
for(i=0;i<t[index].child.size();i++){
if(满足某种条件比如未被访问过)dfs(t[index].child[i]);//如果有特殊说明也可能是从右向左遍历;
}
}

2)遍历这点和bfs类似经常再和dfstravel连用以求连通分量

struct node{
int v,lev;
vector<int>child;
b;n}t[10010];
void dfs(int index){
vis[index]=true;
for(i=0;i<t[index].child.size();i++){
if(vis[t[index].child[i]]==false)dfs(孩子结点)}
}
int dfstravel(){
int sum=0;
for(i=0;i<n;i++){//枚举所有结点
if(vis[i]==0)sum++,dfs(i);
}
return sum;
}

3)回溯常与dijkstra等算法连用,比较难的话还要剪枝

vector<int>pre(n);//在图的遍历后将各个结点的前驱存入其中;
void dfs(int v,int s){//s代表起点,v代表当前结点通常v从终点开始
if(v==s){//先判断到达终点条件,话说dfs和bfs区别就在这里,dfs是判断终点到没到,而bfs是判断从当前结点开始还有没有相邻点可以加入;
往往这种时候会进行条件判断比如
if(路径和<maxh){
更新maxh;
ans=temppath;
}
return}
for(i=0;i<pre[v].size();i++){
temppath.push_back(pre[v][i]);
dfs(pre[v][i]);
temppath.pop_back();//这种方法就是利用dfs栈的特性在dfs中temp路径中是加入这个孩子了,但是在下个孩子中又没有加入,这在贪心算法中也有应用;
}
}

4)背包问题+利用了dfs的遍历
核心

vector<int>temppath;
void dfs(int v,int sum){//v是当前结点,sum是这个对应路径的某参数和
temppath.push_back(v);
dfs(v-1,sum+mat[v]);//mat为该节点对应的参数量;这条是表示背包中放入v;
temppath.pop_back();
dfs(v-1,sum);//背包中不放入v

接下来就是定义一个遍历规则,即按某种顺序遍历到达终点即停止。比如从大到小当遍历到0时为终点时就停止;

vector<int>temppath;
void dfs(int v,int sum){//v是当前结点,sum是这个对应路径的某参数和
if(v==0)return;//加入递归终点,递归终点的选择是选择dfs遍历首先想好的;
temppath.push_back(v);
dfs(v-1,sum+mat[v]);//mat为该节点对应的参数量;这条是表示背包中放入v;
temppath.pop_back();
dfs(v-1,sum);//背包中不放入v

c.树的相关模型
1)树的建立
经典前序中序建立树

struct node{
int left,right;
node(){
left=right=-1;
}
}
int pre[101],inorder[101]//分别存放前序和中序序列数组;
int create(int prel,int prer,int inl,int inr){
if(prel>=prer)return -1;
int v=pre[prel];
i=0;
while(inorder[i]!=v)i++;
t[v].left=create(prel+1,prel+1+i-inl,inl,i-1);
t[v].right=create(prel+2+i-inl,prer,i+1,inr);
return v;
}

前序和后序建树是不一定的因为无法确定左右子树所以只能下规则让它在做或右;
补充层序和中序确定一颗唯一的树,思想:
1.层序遍历第一个数一定是树的根,将它对应的中序遍历的位置找到;
2.在中序遍历过程中左边的一定是左子树,再按顺序将二叉树中与左子树相同的数依次加入进去;
3.递归终点是当中序左端点>=右端点;

struct node {
	int left, right;
	node() {
		left = right = -1;
	}
}t[101];
int n, inoreder[101];
int create(int inl, int inr, int levl, int levr,int levo[]) {
	if (inl >= inr)return inoreder[inl];
	int now = levo[levl];
	int i = inl, j, k;
	while (inoreder[i] != now)i++;//用i来划分左右子树
	int leftl[101], rightl[101],cnt=0;//分别用来存放左子树,右子树和左右子树个数
	for (j = levl+1; j <= levr; j++) {
		for (k = inl; k < i; k++) {
			if (levo[j] == inoreder[k]) {
				leftl[cnt++]=levo[j];
				break;
			}
		}
	}
	t[now].left = create(inl, i - 1, levl , cnt, leftl);
	cnt = 0;
	for (j = levl+1; j <=levr ; j++) {
		for (k = i+1; k <= inr; k++) {
			if (levo[j] == inoreder[k]) {
				rightl[cnt++] = levo[j];
				break;
			}
		}
	}
	t[now].right = create(i + 1,inr, levl, cnt, rightl);
	return now;
}
int main() {
	int i, j, k,levelorder[101];
	cin >> n;
	for (i = 0; i < n; i++)cin >> inoreder[i];
	for (i = 0; i < n; i++)cin >> levelorder[i];
	int root = create(0, n - 1, 0, n - 1,levelorder);
	cout << t[t[root].right].left;
}
input:
7
1 7 5 3 2 4 6
7 1 2 3 4 5 6
output:3

二叉排序树的建立

void* create(node* &root,int v){
if(root==NULL){//找到位置插入并初始化;
root=new node();
root->datas=v;//补充一个vs的坑点vs中data是有含义的所以不能用data命名变量
root->left-root->right=NULL;
return;
}
else if(v>root->data){
create(root->right,v);
}
else create(root=>left,v);
}

注意的是二叉排序树的中序遍历是从小到大排列的,所以二叉排序树给出任意一种非中序遍历方式就可以得到树;
avl树就是在这个的基础上加入高度的调整,现在已从考纲删除这里不涉及;
2)树的遍历
利用bfs与dfs即可解决在前面涉及比较多了
3)一些小算法
判断是否完全二叉树:
定义after变量初始为1当遇到第一个没有后代的结点时变量变为0,若再遇到则不是完全二叉树

int after=1,flag=1;
bool iscomplete_tree(int s){
queue<int>q;
q,push(s);
while(!q.empty()){
int now=q.front();
q.pop_back();
if(t[now].left!=-1){
if(after)flag=0;
q.push(t[now].left);
}
else after=1;
 if(t[now].right!=-1){
if(after)flag=0;
q.push(t[now].right);
}
else after=1;
}
if(flag)return true;
else return false;
}

求树以及子树的高度(还是递归其实本质)

int getNum(node *root) {
 if (root == NULL) return 0;
 int l = getNum(root->left);
 int r = getNum(root->right);
 return root->val > 0 ? max(l, r) + 1 : max(l, r);
}

堆排序

void downadjust(int low,int high){
int i=low,j=i*2;
while(j<=high){
if(j+1<=high&&heap[j+1]>heap[j]){
j=j+1;
}
if(heap[j]>heap[i]){
swap(heap[j],heap[i]);
i=j;
j=i*2;
}
else break;
}
}

d.图的相关模型
经典dijkstra算法

void dijkstra(int s){
fill(d,d+maxv,inf);
fill(vis,vis+maxv,0);
d[s]=0;
for(int i=0;i<n;i++){
int minu=999999999,u=-1;
for(i=0;i<n;i++){
if(d[i]<minu&&vis[u]==0){//不要忘了判别这个结点有没有被访问过
minu=d[i];
u=i;
}
}
if(u==-1)return;
vis[u]=1;
for(int v=0;v<n;v++){
if(vis[v]==0&&g[u][v]!=inf)
{if(d[u]+g[u][v]<d[v])d[v]=g[u][v]+d[u];
}
}
}
}//通常会和dfs回溯一起连起来考不过这几年考的较少;

Bellman-Ford算法,重点复习这两年内分别考虑Floyd和拓扑排序现在几大图的最短路径就bellman还没考

struct Node{
int v,dis;
};
vector<node>Adj[MAXV];
int n;
int d[MAXV];
bool bellman(int s){
fill(d,d+MAXV,INF);
d[s]=0;
for(int i=0;i<n-1;i++){
for(int u=0;u<n;u++){
for(int j=0;v<adj[u].size();j++){
int v=Adj[u][j].v;
int dis=Adj[u][j].dis;
if(d[u]+dis<d[v])d[v]=d[s]+dis;
}
}
}

for(int u=0;u<n;u++){//再进行一轮判断是否结束了
for(int j=0;v<adj[u].size();j++){
int v=Adj[u][j].v;
int dis=Adj[u][j].dis;
if(d[u]+dis<d[v])return false;
}
}
return true;
}
}

Bellman算法时间复杂度实在太高spfa算法对此进行了改进

vector<Node>Adj[MAXV];
int n,d[MAXV],num[MAXV];
bool inq[MAXV];
bool SPFA(int s){
memeset(inq,false,sizeof(inq));
memset(num,0,sizeof(num));
fill(d,d+MAXV,INF);
queue<int>Q;
Q.push(s);
inq[s]=true;
num[s]++;
d[s]=0;
while(!Q.empty()){
int u=Q.front();
Q.pop();
inq[u]=false;
for(int j=0;j<Adj[u].size();j++){
int v=Adj[u][j].v;
int dis=Adj[u][j].dis;
if(d[u]+dis<d[v]){
if(!inq[v]){
Q.push(v);
inq[v]=true;
num[v]++;
if(num[v]>=n)return false;
}
}
}
}
return true;
}

Floyd算法春季刚考,再考几率不大,了解核心代码即可

```cpp
void Floyd(){
for(int k=0;k<n;k++){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(dis[i][k]!=inf&&dis[k][j]!=inf&&dis[i][k]+dis[k][j]<dis[i][j])
dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
}()

prim最小生成树算法:该算法和dijkstra算法基本一致,区别只在于d[u]数组代表的含义

dijkstra:d[]数组代表从起点到顶点的距离d[v]=d[u]+g[u][v];
prim:d[]数组代表顶点到已经加入的顶点集的距离d[v]=d[u];

kruskal算法顺带复习并查集

int findFather(int a){//包括路径压缩
int x=a;
while(father[x]!=x)x=father[x];
while(a!=father[a]){
int temp=father[a];
father[a]=x;
a=temp;
}
return x;
}
void unions(int a,int b){
int faA=findFather(a),faB=findFather(b);
if(faA!=faB)father[faA]=faB;
}

int kruskal(int n,int m){
int ans=0,Num_Edge=0;
for(int i=0;i<=n;i++){
father[i]=i;
}
sort(E,E+m,cmp);
for(int i=0;i<m;i++){
int faU=findFather(E[i].u);
int faV=findFather(E[i].v);
if(faU!=faV){
father[faU]=faV;
ans+=E[i].cost;
Num_edge++;
if(Num_edge==n-1)break;
}
}
if(Num_edge!=n-1)return -1;
else return false;
}

(四)几个实用的算法思想
a.暴力求解:
我把它列在第一个,所以听起来很low但是在pat考试中大多数能拿到不错的分甚至满分
b.二分法:
二分法在排序算法中属于插入排序,就是找数的一个过程
c.two point法:在最近考试的熊猫奶也可以归结为这种方法,左右扫分别满足条件

9.11更新神了这一次秋季最后一题建树和我层次与中序遍历建树可以说一模一样可惜没考满分,12月看情况要不要再来。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值