PAT甲级 排序
stringstream 类的一些操作
如何自定义stl在sort中的排序规则
PAT甲级 DFS
acwing上的一些冠以dfs的题解。
[toc]
PAT-recording
进位制 1482 进制 分析:
两个数,其中的一个进制是给定的,并且这个数的进制不超过36。
但是另外一个数的进制就不好说了,可能是一个很大的数字。(重点)枚举的区间就是1~target :target=给定的数最大能表示的范围也就是 。
显然,不能通过枚举的方法找,使用二分是一个不错的选择。
枚举另一个数 将其转化为枚举的进制数字 或者都转化为10进制数字。
需要一个
1 string fuc (string a, int in,int out) ;
这样的函数将a转化为out 进制数
这个方法实在是太繁琐了,(这个函数我不会写)
计算一下两个数如果转化为十进制数 int 或者long long 能否能存下
N是不超过十位的数字 最大就是 十个z = 可以使用 计算机自带的计算器计算这个数看看是否溢出。(不溢出)
那么就可以找这样的函数
1 long long fuc (string a,int out) ;
主要思路就是:二分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int main () { int l = floor,r = ceil; while (l<r){ int mid = (l+r)>>1 ; if (check ()) r = mid; else l = mid+1 ; } while (l<r){ int mid = (l+r+1 )>>1 ; if (check ()) l = mid; else r = mid-1 ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <iostream> #include <algorithm> #include <cmath> using namespace std;typedef long long LL;const LL N = 1e16 ;int get (char c) { if (c<='9' ) return c-'0' ; else return c-'a' +10 ; } LL convert (string a,LL radix) { LL res = 0 ; LL c = 1 ; for (int i = a.length ()-1 ;i>=0 ;--i){ res += (get (a[i])*c); c *= radix; if (res>1e16 ) return 1e16 ; if (res<0 )return 1e16 ; } return res; } int main () { string a,b; int tag,radix; cin>>a>>b>>tag>>radix; if (tag==2 ) swap (a,b); LL a_n = convert (a,radix); LL l = 1 ,r = a_n+1 ; for (auto c : b) l = max (l, (LL)get (c) + 1 ); while (l<r){ LL mid = (l+r)>>1 ; if (convert (b,mid)>=a_n) r = mid; else l = mid+1 ; } if (convert (b,r)==a_n) cout<<r<<endl; else cout<<"Impossible\n" ; return 0 ; }
上下界必须要根据当前这个数来确定进制上下界搜索的范围。
做进制转换的时候数据可能会溢出,所以要加以判断,由于确定数最大就是 所以如果当前这一步计算的结果大于这个数就可以直接return一个很大的但是不溢出的数字。
由于进制计算的步骤问题,有可能当前判断的时候溢出已经发生所以判断当前计算结果是否小于0 如果是则返回一个很大的数。
1492 可逆质数 简单题,用到的模板就是质数判断
1504 火星颜色 简单题 不记录了
值得记录的知识点:stringstream类的一些操作
StringStream 包含的头文件: sstream.h
1 2 3 4 5 6 7 stringstream ssin; string a; string b; ssin<<a; cout<<ssin.str ()<<endl; ssin>>b;
1 2 3 4 5 6 int d;string ds; stringstream ssin; ssin<<d; ssin>>ds;
1 2 3 4 5 6 7 string str; getline (cin,str);stringstream ssin (str) ;string word; while (ssin>>word){ operation)(); }
1590 火星数字 stringstream ssin()
输入输出
排序 1484 最佳排名 数据结构:
一个结构体、重载<运算符。规则 有四列成绩,每个学生只有其最好的排名。
数据范围:
学生和查询次数在2000以内 每科成绩100以内的正数
思路:
每次查询的时候对每个学生排序 输出所有排序中成绩最好的。时间复杂度不超。
不清楚的点:
排名怎么算?如果有同分数的人怎么算排名?按照分数排名还是按照别的规则。同分数名次一致。
如何解决:
将所有分数排序,然后按照每个同学的分数 直接去查这个排名表,查到的最考前的数的下标就是其排名。
1499 数字图书馆 要求:
五个关键字段:书名、作者(唯一)、出版商、关键词(不超过五个空格分开。)、日期.
id是可能有前导0的。
思路:
五个map >分别存放书名、出版商、关键词、日期 、作者与相关id的对应查询字典。最后输出查询的时候只要更具hash表找到对应的vector然后对其排名就可以了
1502 PAT 排名 思路:
对每个地区的学生信息输入完成后生成一个地区排名表,将输入完成的学生的地区排名信息输入。
所有的地区的学生信息输入完成后生成一个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 #include <iostream> #include <vector> #include <map> #include <algorithm> using namespace std;struct info { string id; int grade; int final_rank,location_number,local_rank; bool operator <(const info &a) const { if (final_rank==a.final_rank) return id<a.id; return final_rank<a.final_rank; } }; vector<int > total_grade_list; vector<info> total_students_list; int get_rank (int grade,vector<int > gl) { int l = 0 ,r = gl.size ()-1 ; while (l<r){ int mid = (l+r+1 )>>1 ; if (gl[mid]<=grade) l = mid; else r = mid-1 ; } return gl.size ()-l; } bool cmp (int &a,int &b) { return a>b; } int main () { int n; cin>>n; for (int i = 1 ;i<=n;++i){ int k; cin>>k; vector<pair<string,int >> local_info; vector<int > local_grade_list; while (k--){ string id; int grade; cin>>id>>grade; local_grade_list.push_back (grade); local_info.push_back ({id,grade}); total_grade_list.push_back (grade); } sort (local_grade_list.begin (),local_grade_list.end (),cmp); for (auto p:local_info){ string id = p.first; int g = p.second; int rank = get_rank (g,local_grade_list); total_students_list.push_back ({id,g,-1 ,i,rank}); } } sort (total_grade_list.begin (),total_grade_list.end (),cmp); for (int i = 0 ;i<total_students_list.size ();++i){ info *p = &total_students_list[i]; int g = p->grade; p->final_rank = get_rank (g,total_grade_list); } sort (total_students_list.begin (),total_students_list.end ()); cout<<total_students_list.size ()<<endl; for (auto stu:total_students_list){ cout<<stu.id<<" " <<stu.final_rank+1 <<" " <<stu.location_number<<" " <<stu.local_rank+1 <<endl; } return 0 ; }
毫不意外的超时了捏。
应该是get_rank导致三重循环所以超时了吧。
1505列表排序 很简单的一道排序题,但是很容易超时,就想着这样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <iostream> #include <vector> #include <algorithm> using namespace std;struct student { string id,name; int score; }; bool cmp1 (student &a,student &b) { return a.id<b.id; } bool cmp2 (student &a,student &b) { return a.name==b.name ? a.id<b.id : a.name>=b.name; } bool cmp3 (student &a,student &b) { return a.score==b.score ? a.id<b.id : a.score>=b.score; } vector<student> students; int main () { int n,c; cin>>n>>c; while (n--){ string id,name; int grade; cin>>id>>name>>grade; students.push_back ({id,name,grade}); } if (c==1 ){ sort (students.begin (),students.end (),cmp1); }else if (c==2 ){ sort (students.begin (),students.end (),cmp2); }else { sort (students.begin (),students.end (), cmp3); } for (auto s:students){ cout<<s.id<<" " <<s.name<<" " <<s.score<<endl; } return 0 ; }
试着用堆排序实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <iostream> #include <vector> #include <algorithm> #include <queue> using namespace std;struct student { string id,name; int score; }; int c;bool operator >(const student a,const student b) { if (c==1 ) return a.id>b.id; else if (c==2 ) return a.name==b.name ? a.id>b.id : a.name>=b.name; else return a.score==b.score ? a.id>b.id : a.score>=b.score; } int main () { int n; scanf ("%d%d" ,&n,&c); priority_queue<student , vector<student> , greater<student> > heap; while (n--){ string id,name; id.resize (6 ); name.resize (8 ); int grade; scanf ("%s%s%d" ,&id[0 ],&name[0 ],&grade); heap.push ({id,name,grade}); } while (!heap.empty ()){ auto s = heap.top (); heap.pop (); printf ("%s %s %d\n" ,s.id.c_str (),s.name.c_str (),s.score); } return 0 ; }
依旧超时,因为在一个n循环中使用了堆排序所以时间复杂度是 所以会超时。
这里应该着重优化输入输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include <iostream> #include <vector> #include <algorithm> #include <sstream> using namespace std;const int N = 1e5 +1 ;struct student { string id,name; int score; }; student students[N]; bool cmp1 (student &a,student &b) { return a.id<b.id; } bool cmp2 (student &a,student &b) { return a.name==b.name ? a.id<b.id : a.name<=b.name; } bool cmp3 (student &a,student &b) { return a.score==b.score ? a.id<b.id : a.score<=b.score; } int main () { int n,c; scanf ("%d%d" ,&n,&c); for (int i = 0 ;i<n;++i){ string name,id; int grade; name.resize (8 ); id.resize (6 ); scanf ("%s%s%d" ,&id[0 ],&name[0 ],&grade); students[i].id = id; students[i].name = name; students[i].score = grade; } if (c==1 ){ sort (students,students+n,cmp1); }else if (c==2 ){ sort (students,students+n,cmp2); }else { sort (students,students+n, cmp3); } for (int i = 0 ;i<n;++i){ auto s = students[i]; printf ("%s %s %d\n" ,s.id.c_str (),s.name.c_str (),s.score); } return 0 ; }
1523 学生课程列表 思路:
使用哈希表 map查询 将每个学生的name为索引 跟上其选课的列表 vector
时间复杂度分析:
hash表的插入查询:都是 的时间复杂度
输入时间复杂度:最多输入2500(课程最大数量)multi 200 (单个课程最多容纳人数)== 450000 输入
查询操作时间复杂度:查询400000个人 最多不会超过450000(假设存在学生选了所有的课,但是这样的学僧最多不超过200个人,如果这个情况成立,那么也就是450000次)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <iostream> #include <map> #include <vector> #include <algorithm> using namespace std;map<string,vector<int >> students; int n,k;int main () { scanf ("%d%d" ,&n,&k); while (k--){ int c_id,member_num; scanf ("%d%d" ,&c_id,&member_num); while (member_num--){ string s; s.resize (4 ); scanf ("%s" ,&s[0 ]); students[s].push_back (c_id); } } while (n--){ string s; s.resize (4 ); scanf ("%s" ,&s[0 ]); printf ("%s " ,s.c_str ()); if (students.find (s)!=students.end ()){ sort (students[s].begin (),students[s].end ()); printf ("%d " ,students[s].size ()); for (auto c:students[s]) printf ("%d " ,c); }else { printf ("0" ); } printf ("\n" ); } return 0 ; }
应为在最后的查询里面会有一部排序的操作,所以挺怕他超时的,但是最后也没超时。
1538 链表排序 思路:
创建一个map 将所有的元素输入进去,按照链表遍历将在链表中的元素加入vector里面 然后排序,最后输出。
TLE 使用了map 使用unordered_map不超时 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <vector> #include <algorithm> #include <string> #include <map> using namespace std;struct node { string addr; int key; string addr_next; bool operator <(const node& a) const { return key < a.key; } }; map<string, node> hash_table; vector<node> nodes; int main () { string ad; int n; cin >> n >> ad; while (n--) { string s; string o; int key; cin >> s >> key >> o; hash_table[s] = { s,key,o }; } while (ad != "-1" ) { auto top_node = hash_table[ad]; nodes.push_back (top_node); ad = top_node.addr_next; } sort (nodes.begin (),nodes.end ()); cout<<nodes.size ()<<" " ; if (nodes.size ()) cout<<nodes[0 ].addr<<endl; else { cout<<"-1\n" ; return 0 ; } for (int i = 0 ;i<nodes.size ();++i) { auto n = nodes[i]; if (i!=nodes.size ()-1 ){ auto next = nodes[i+1 ]; printf ("%s %d %s\n" , n.addr.c_str (), n.key, next.addr.c_str ()); } else { printf ("%s %d -1\n" , n.addr.c_str (), n.key); } } return 0 ; }
TLE了。感觉又是cin的事情。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <vector> #include <algorithm> #include <string> #include <unordered_map> using namespace std;struct node { string addr; int key; string addr_next; bool operator <(const node& a) const { return key < a.key; } }; unordered_map<string, node> hash_table; vector<node> nodes; int main () { char c1[10 ]; char c2[10 ]; char c3[10 ]; int n,key; scanf ("%d%s" ,&n,c1); string ad (c1) ; while (n--) { scanf ("%s%d%s" ,c2,&key,c3); string s (c2) ,o (c3) ; hash_table[s] = { s,key,o }; } for (string head = ad;head!="-1" ;head = hash_table[head].addr_next){ nodes.push_back (hash_table[head]); } sort (nodes.begin (),nodes.end ()); printf ("%d " ,nodes.size ()); if (nodes.size ()) printf ("%s\n" ,nodes[0 ].addr.c_str ()); else { printf ("-1\n" ); return 0 ; } for (int i = 0 ;i<nodes.size ();++i) { auto n = nodes[i]; if (i!=nodes.size ()-1 ){ auto next = nodes[i+1 ]; printf ("%s %d %s\n" , n.addr.c_str (), n.key, next.addr.c_str ()); } else { printf ("%s %d -1\n" , n.addr.c_str (), n.key); } } return 0 ; }
能用scanf就用scanf,能用unordered_map就不用map。
1561 PAT 评测 思路:
树 树的遍历(重点最好记一下)
给出的所有的点最多能到1百万个
复杂度为
给定的所有点的权值都是唯一的,如果不唯一那么树的形状可能不唯一。
给定后序和中序遍历 输出层序遍历
1、需要熟悉手动的中序+后续 构建树的过程、以及中序+前序构建树的过程。(在此基础上需要要知道如何快速找到中序遍历中的root下标)
2、要知道本题中树是如何存储的。二叉树的存储可以简化到使用两个unordered_map 存储
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <iostream> #include <queue> #include <unordered_map> using namespace std;const int N = 40 ;int postordered[N],inordered[N];unordered_map<int , int > l,r,ipost; int n;int build (int pl,int pr,int il,int ir) { int root = postordered[pr]; int k = ipost[root]; if (il<k){ l[root] = build (pl,pl+k-1 -il,il,k-1 ); } if (ir>k){ r[root] = build (pl+k-1 -il+1 ,pr-1 ,k+1 ,ir); } return root; } void bfs (int root) { queue<int > q; q.push (root); while (!q.empty ()){ auto t = q.front (); q.pop (); cout<<t<<" " ; if (l.count (t)) q.push (l[t]); if (r.count (t)) q.push (r[t]); } } int main () { cin>>n; for (int i = 0 ;i<n;i++){ cin>>postordered[i]; } for (int i = 0 ;i<n;i++){ cin>>inordered[i]; ipost[inordered[i]] = i; } int root = build (0 ,n-1 ,0 ,n-1 ); bfs (root); return 0 ; }
理解:
通过前序遍历/后续遍历中的区间确定一个根节点,再找这个根节点在中序中的位置
1498 最深的根 思路:使用并查集求解给定的树是否符合要求,以及不符合要求的话有多少个连通块。(这一步主要是对merge的过程进行修改。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 #include "iostream" #include "vector" #include <cstring> using namespace std;const int N=1e4 +10 ,M=N*2 ; int h[N],e[M],ne[M],idx; int n,k; int p[N];int find (int a) { if (p[a]!=a) p[a] = find (p[a]); return p[a]; } void add (int a,int b) { e[idx]=b; ne[idx]=h[a]; h[a]=idx++; } void merge (int a,int b) { int fa = find (a),fb = find (b); p[fa] = fb; } int dfs (int u,int father) { int depth = 0 ; for (int i=h[u];~i;i=ne[i]) { int j=e[i]; if (j==father) continue ; depth=max (depth,dfs (j,u)+1 ); } return depth; } int main () { scanf ("%d" ,&n); k=n; for (int i = 0 ;i<=n;++i) p[i] = i; memset (h,-1 ,sizeof h); for (int i = 0 ;i<n-1 ;++i){ int x,y; scanf ("%d%d" ,&x,&y); int fx = find (x),fy = find (y); if (fx!=fy){ p[fx] = fy; k--; } add (x,y);add (y,x); } if (k>1 ) printf ("Error: %d components\n" ,k); else { vector<int >nodes; int max_depth=-1 ; for (int i=1 ;i<=n;i++) { int depth=dfs (i,-1 ); if (depth>max_depth) { max_depth=depth; nodes.clear (); nodes.push_back (i); } else if (depth==max_depth) nodes.push_back (i); } for (auto v:nodes) cout<<v<<endl; } return 0 ; }
判断二叉搜索树 问题一:如何根据二叉搜索树的先序遍历恢复树结构?
二叉搜索树的中序遍历其实是已知的。这个问题其实就是根据二叉搜索树的中序和前序遍历得到二叉树。
问题二:将问题转换为 上述的树的遍历后,中序遍历中节点值不唯一如何解决?
由于中序遍历是一个有序数列,所以可以使用二分的方法去查找到当前前序遍历中的根节点在中序遍历中的位置
问题三:如何判断不合法的情况?
如果当前在中序区间中找根节点的过程中没有找到那么就返回false;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 #include <iostream> #include <cstring> #include <algorithm> using namespace std;const int N = 1e3 +10 ;int preorder[N],postorder[N],inorder[N];int cnt;int n;bool build (int il,int ir,int pl,int pr,int type) { bool res = true ; int root = preorder[pl]; int k = -1 ; if (il>ir) return res; if (type==0 ){ int l = il,r = ir; while (l<r){ int mid = (r+l)>>1 ; if (inorder[mid]>=root)r = mid; else l = mid+1 ; } if (inorder[l]==root) k = l; else return false ; }else { int l = il,r = ir; while (l<r){ int mid = (r+l+1 )>>1 ; if (inorder[mid]>=root) l = mid; else r = mid-1 ; } if (inorder[l]==root) k = l; else return false ; } if (!build (il,k-1 ,pl+1 ,pl+1 +(k-1 -il),type)) res = false ; if (!build (k+1 ,ir,pl+1 +(k-1 -il)+1 ,pr,type)) res = false ; postorder[cnt++] = root; return res; } int main () { scanf ("%d" ,&n); for (int i = 0 ;i<n;++i){ scanf ("%d" ,&preorder[i]); inorder[i] = preorder[i]; } sort (inorder,inorder+n); if (build (0 ,n-1 ,0 ,n-1 ,0 )){ printf ("YES\n%d" ,postorder[0 ]); for (int i = 1 ;i<n;++i) printf (" %d" ,postorder[i]); } else { reverse (inorder,inorder+n); cnt = 0 ; if (build (0 ,n-1 ,0 ,n-1 ,1 )){ printf ("YES\n%d" ,postorder[0 ]); for (int i = 1 ;i<n;++i) printf (" %d" ,postorder[i]); }else { printf ("NO\n" ); } } return 0 ; }
1550 完全二叉搜索树 对于我来说的难点:
给定了每个节点的权值如何取构建一个完全二叉搜索树;
如何使用数组存储树结构。
通过完全二叉树的性质,可以定义二叉树的dfs边界,通过中序遍历将二叉树构建出来,并存储到一个数组中,再通过层序遍历将其输出。
首先将节点按照从小到大的顺序排序,
通过中序遍历建树
直接输出存储树的数组就是中序遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <iostream> #include <algorithm> using namespace std;const int N = 1010 ;int w[N];int tree[N];int n,cnt;void pre_order (int u) { int r_tree = u<<1 ; int l_tree = (u<<1 )+1 ; if (r_tree<=n) pre_order (r_tree); tree[u] = w[cnt++]; if (l_tree<=n) pre_order (l_tree); } int main () { cin>>n; for (int i = 0 ;i<n;++i)cin>>w[i]; sort (w,w+n); pre_order (1 ); cout<<tree[1 ]; for (int i = 2 ;i<=n;++i) cout<<" " <<tree[i]; return 0 ; }
1576 再次树遍历 这一题和 3384. 二叉树遍历 - AcWing题 很像
给出的堆栈操作是二叉树的先序遍历,可以使用先序遍历的dfs方法建树。
可能涉及到字符串的查找判断。(可以简化,也可以省略)
思路:
首先根据先序遍历遍历二叉树,如果遇到pop就是回溯,
然后再先序遍历过程中(由于是一个递归过程所以其中所有的节点都是存储下来的)通过后序的顺序输出每个节点的内容。
一个重要的解题方法就是可以边先序输入边后序输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <iostream> #include <cstring> using namespace std;const int N = 40 ;int n;void preorder (int root) { int w; string str; cin>>str; if (str[1 ]=='u' ){ cin>>w; preorder (root*2 ); preorder (root*2 +1 ); cout<<w; if (root!=1 )cout<<" " ; } } int main () { cin>>n; preorder (1 ); return 0 ; }
1589 构建二叉搜索树 搜索树天生就知道其中序遍历。那么这题就是给出了树的结构和中序遍历,输出层序遍历。
接下来就是解决具体问题了:
定义存储树的结构
定义inorder遍历的dfs函数
定义层序遍历的bfs函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <iostream> #include <queue> #include <algorithm> using namespace std;const int N = 110 ;struct node { int w; int l,r; }; int n;node tree[N]; int v[N];int cnt;void inorder (int root) { if (root==-1 )return ; int l = tree[root].l; int r = tree[root].r; inorder (l); tree[root].w = v[cnt++]; inorder (r); } void bfs (int root) { queue<int > q; q.push (root); while (!q.empty ()){ auto front = q.front (); int l_tree = tree[front].l, r_tree = tree[front].r; q.pop (); cout<<tree[front].w<<" " ; if (l_tree>=0 ) q.push (l_tree); if (r_tree>=0 ) q.push (r_tree); } } int main () { cin>>n; for (int i = 0 ;i<n;++i) cin>>tree[i].l>>tree[i].r; for (int i = 0 ;i<n;++i)cin>>v[i]; sort (v,v+n); inorder (0 ); bfs (0 ); return 0 ; }
1605 二叉搜索树最后两层结点数量 一定要注意题目中的描述 特别是 BST的描述。
难点在于如何实现构建这一个BST。我自己不知道如何实现这一个insert函数
使用一个结构体数组存储树的结构
在insert构建树的过程中将所有的节点的深度一并记录
最后遍历节点将满足深度值的节点的个数计算出,最后得到答案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <iostream> #include <algorithm> using namespace std;const int N = 1010 ;struct node { int depth; int w; int l,r; }; node tree[N]; int n,idx;int max_depth;void insert (int &root,int w,int depth) { if (!root){ root = ++idx; tree[root].w = w; tree[root].depth = depth; max_depth = max (max_depth,depth); } else if (tree[root].w>=w) insert (tree[root].l,w,depth+1 ); else insert (tree[root].r,w,depth+1 ); } int main () { cin>>n; int root = 0 ; int n1 = 0 ,n2 = 0 ; for (int i = 0 ;i<n;++i){ int w; cin>>w; insert (root,w,0 ); } for (int i = 1 ;i<=n;++i){ if (tree[i].depth==max_depth) n1++; if (tree[i].depth==max_depth-1 ) n2++; } cout<<n1<<" + " <<n2<<" = " <<n1+n2<<endl; return 0 ; }
1609 前序和后序遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <iostream> using namespace std;const int N = 50 ;int pre[N],post[N];int n;int dfs (int l1,int r1,int l2,int r2,string &in) { if (l1>r1) return 1 ; if (pre[l1]!=post[r2]) return 0 ; int cnt = 0 ; for (int i = l1;i<=r1;++i){ string lin,rin; int lcnt = dfs (l1+1 ,i,l2,l2+i-l1-1 ,lin); int rcnt = dfs (i+1 ,r1,l2+i-l1,r2-1 ,rin); if (lcnt*rcnt){ cnt += lcnt*rcnt; in = lin+to_string (pre[l1])+' ' +rin; if (cnt>1 ) break ; } } return cnt; } int main () { cin>>n; for (int i = 0 ;i<n;++i) cin>>pre[i]; for (int i = 0 ;i<n;++i) cin>>post[i]; string inorder; int cnt = dfs (0 ,n-1 ,0 ,n-1 ,inorder); if (cnt<=1 )puts ("Yes" ); else puts ("No" ); inorder.pop_back (); cout<<inorder<<endl; return 0 ; }
1620 Z(S)字形遍历二叉树
通过中序+后序构建二叉树
通过dfs 将每一层的节点加入,
如果是奇数层就倒序输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include <iostream> #include <queue> #include <unordered_map> #include <vector> using namespace std;const int N = 40 ;int post[N],in[N];int n,max_depth;unordered_map<int , int > in_mp,r,l; vector<int >cnt[N]; int build (int il,int ir,int pl,int pr) { int root = post[pr]; int idx = in_mp[root]; if (il<idx) l[root] = build (il,idx-1 ,pl,pl+idx-1 -il); if (idx<ir) r[root] = build (idx+1 ,ir,pl+idx-il,pr-1 ); return root; } void bfs (int root) { queue<int > q; q.push (root); string str; while (!q.empty ()){ auto t = q.front (); str = str+to_string (t)+' ' ; q.pop (); if (l.count (t)) q.push (l[t]); if (r.count (t)) q.push (r[t]); } str.pop_back (); cout<<str<<endl; } void dfs (int u,int depth) { if (u==0 )return ; max_depth=max (max_depth,depth); cnt[depth].push_back (u); dfs (l[u],depth+1 ); dfs (r[u],depth+1 ); } int main () { cin>>n; for (int i = 0 ;i<n;++i){ cin>>in[i]; in_mp[in[i]] = i; } for (int i = 0 ;i<n;++i) cin>>post[i]; int root = build (0 ,n-1 ,0 ,n-1 ); dfs (root,1 ); for (int i=1 ;i<=max_depth;i++) { auto & v=cnt[i]; if (i%2 ) for (int i=v.size ()-1 ;i>=0 ;i--)cout<<v[i]<<' ' ; else for (int i=0 ;i<v.size ();i++)cout<<v[i]<<' ' ; } return 0 ; }
1631 后序遍历 1552 AVL树的根
update函数的作用
左旋和右旋函数的书写以及理解
insert函数
辅助函数的设置
树结构如何存储
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 #include <iostream> using namespace std;const int N = 40 ;struct node { int v; int l,r; int h; }; node tree[N]; int idx;void update (int u) { int r = tree[u].r,l = tree[u].l; tree[u].h = max (tree[r].h,tree[l].h)+1 ; return ; } void R (int &u) { int l = tree[u].l; tree[u].l = tree[l].r; tree[l].r = u; update (u),update (l); u = l; } void L (int &u) { int r = tree[u].r; tree[u].r = tree[r].l, tree[r].l = u; update (u),update (r); u = r; } int getbalance (int u) { return tree[tree[u].l].h-tree[tree[u].r].h; } void insert (int &u,int v) { if (!u){ u = ++idx; tree[u].v = v; }else if (tree[u].v<=v){ insert (tree[u].r,v); if (getbalance (u)==-2 ){ if (getbalance (tree[u].r)==1 ) R (tree[u].r); L (u); } }else { insert (tree[u].l,v); if (getbalance (u)==2 ){ if (getbalance (tree[u].l)==-1 ) L (tree[u].l); R (u); } } update (u); } int main () { int n,root = 0 ; cin>>n; for (int i = 0 ;i<n;++i){ int v; cin>>v; insert (root,v); } cout<<tree[root].v<<endl; return 0 ; }
1616 判断完全 AVL 树
首先创建一个AVL树
用数组存储占位信息,遍历一遍这个树
最后查看数组是否是存储满的,从而判断是否是完全二叉树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 #include <iostream> #include <queue> using namespace std;const int N = 30 ;bool st[4000 ];struct node { int v,r,l,h; }; node tree[N]; int idx;void update (int u) { int l = tree[u].l,r = tree[u].r; tree[u].h = max (tree[l].h,tree[r].h)+1 ; return ; } void R (int &u) { int p = tree[u].l; tree[u].l = tree[p].r, tree[p].r = u; update (u),update (p); u = p; } void L (int &u) { int p = tree[u].r; tree[u].r = tree[p].l,tree[p].l = u; update (u),update (p); u = p; } int getbalance (int u) { return tree[tree[u].l].h - tree[tree[u].r].h; } void insert (int &u,int v) { if (!u){ u = ++idx; tree[u].v = v; } else if (tree[u].v<=v){ insert (tree[u].r,v); if (getbalance (u)==-2 ){ if (getbalance (tree[u].r)==1 ) R (tree[u].r); L (u); } }else { insert (tree[u].l,v); if (getbalance (u)==2 ){ if (getbalance (tree[u].l)==-1 ) L (tree[u].l); R (u); } } update (u); } void dfs (int root,int pos) { if (!root) return ; st[pos] = true ; dfs (tree[root].l,pos<<1 ); dfs (tree[root].r,(pos<<1 ) + 1 ); } void bfs (int root) { queue<int > q; q.push (root); while (!q.empty ()){ int f = q.front (); q.pop (); cout<<tree[f].v<<" " ; if (tree[f].l) q.push (tree[f].l); if (tree[f].r) q.push (tree[f].r); } } int main () { int root = 0 ; int v,n; bool flag = true ; cin>>n; for (int i = 0 ;i<n;++i){ cin>>v; insert (root,v); } dfs (root,1 ); bfs (root); cout<<endl; for (int i = 1 ;i<=n;++i){ if (!st[i]){ flag=false ; break ; } } if (flag) cout<<"YES\n" ; else cout<<"NO\n" ; return 0 ; }
PAT甲级网站刷题记录 A1046 前缀和。 A1065 A+B and C (64bit) 在使用longlong 类型的运算与另一个long long数据类型判断大小的时候(如下的情况)
不能直接使用 (运算) 判断符号 LL这样的格式来判断
1 2 3 LL a,b,c,res; res = a+b; res>c;
因为不能确定两个longlong数据类型在做加法的时候是否发生了溢出,如果发生溢出机器会自动截断,因而加法运算会在不知道哪一步停止。
DFS dfs的搜索顺序是一个树形的。
DFS的经典运用:
输出组合数 acwing 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <iostream> using namespace std;const int N = 10 ;int num[N];int res[N];bool is_select[N];int n;void print_result () { for (int i = 1 ;i<=n;++i){ cout<<res[i]<<" " ; } cout<<endl; } void dfs (int step) { if (step == n+1 ) { print_result (); return ; } for (int i = 1 ;i<=n;++i){ if (!is_select[i]){ is_select[i] = true ; res[step] = i; dfs (step+1 ); is_select[i] = false ; } } } int main () { cin>>n; dfs (1 ); return 0 ; }
PAT甲级中的DFS题目
一些c++ 的性质的记录(我记不住所以记一下) c++在函数声明后面有const 1 bool operator <(const T &a) const {...}
已定义成const的成员函数,一旦企图修改数据成员的值,则编译器按错误处理
该函数内部不能修改成员变量的值。
遇到需要使用效率更高的输入scanf 输入\printf 输出一行内含有string和in的混合类型。 一种可行的方法是resize string的大小,这种方法使用的前提是你得知道这个待输入的string有多大。然后如下使用
1 2 3 4 5 6 7 8 9 10 int main () { string a; string b; int c; a.resize (10 ); b.resize (10 ); scanf ("%s%s%d" ,&a[0 ],&b[0 ],&c); printf ("a: %s b: %s c: %d\n" ,a.c_str (),b.c_str (),c); }
输出的时候要转换为c_str()。
字符串的比较失效问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <vector> #include <algorithm> #include <string> #include <map> using namespace std;struct node { string addr; int key; string addr_next; bool operator <(const node& a) const { return key < a.key; } }; map<string, node> hash_table; vector<node> nodes; int main () { string ad; ad.resize (7 ); int n; scanf ("%d%s" , &n, &ad[0 ]); while (n--) { string s; string o; s.resize (7 ); o.resize (7 ); int key; scanf ("%s%d%s" , &s[0 ], &key, &o[0 ]); hash_table[s] = { s,key,o }; if (o == "-1" )cout << "yes\n" ; } while (ad != "-1" ) { auto top_node = hash_table[ad]; nodes.push_back (top_node); ad = top_node.addr_next; } for (auto n : nodes) { printf ("%s %d %s" , n.addr.c_str (), n.key, n.addr_next.c_str ()); } return 0 ; }
sanf 读入字符串的原因,导致比较失效了。不建议使用scanf读入string 类型的数据。
关于二分找边界: 暂时没弄明白,先记着。
基本的二分模板 找有序数组中的某一个数的左边界 找有序数组中的某一个数的右边界 1 2 3 4 5 6 7 8 9 10 int bs (int q[],int t) { int l = 0 ,r = n-1 ; while (l<r){ int mid = (l+r)>>1 ; if (q[mid]<t) l = mid; else r = mid+1 ; } if (q[l]!=t) return -1 ; return q[l]; }
1 2 3 4 5 6 7 8 9 10 11 12 int bs_left (int nums[],int t) { int r = n-1 ,l = 0 ; while (l<r){ int mid = (l+r)>>1 ; if (nums[mid]>=t) r = mid; else l = mid+1 ; } if (nums[l]==t) return l; return -1 ; }
1 2 3 4 5 6 7 8 9 10 11 int bs_right (int nums[],int t) { int r = n-1 ,l = 0 ; while (l<r){ int mid = (l+r+1 )>>1 ; if (nums[mid]<=t) l = mid; else r = mid-1 ; } if (nums[l]==t) return l; return -1 ; }
关于树的遍历 通过前序\后序+中序建树 看这里
通过前序+后续建树 看这里
搜索树的构建 insert函数怎末写 看这里
关于STL以及algorithm中的函数使用 sort如何实现自定义排序 对结构体,以及自定义的结构的排序可以选择重载结构体中的< 让 return 大<小;
对于int 默认是从小到大排序;希望从大到小排序可以将函数指针部分替换为 greater<int>()