1004 .数叶子结点
把他输入的节点编号01,02当做1,2,3…就可以了 然后定义邻接表存储树,dfs每个层次,用cnt[N]
记录叶子结点max_depth
记录最大深度,最后输出即可
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N= 110 ;
int h[ N] , e[ N] , ne[ N] , idx;
int cnt[ N] ;
int n, m, max_depth;
void add ( int a, int b) {
e[ idx] = b, ne[ idx] = h[ a] , h[ a] = idx++ ;
}
void dfs ( int root, int u) {
if ( h[ root] == - 1 ) {
cnt[ u] ++ ;
max_depth= max ( u, max_depth) ;
return ;
}
for ( int i= h[ root] ; i!= - 1 ; i= ne[ i] ) {
int root = e[ i] ;
dfs ( root, u+ 1 ) ;
}
}
int main ( ) {
memset ( h, - 1 , sizeof ( h) ) ;
cin>> n>> m;
while ( m-- ) {
int id;
int k;
cin>> id>> k;
while ( k-- ) {
int son;
cin>> son;
add ( id, son) ;
}
}
dfs ( 1 , 0 ) ;
cout<< cnt[ 0 ] ;
for ( int i= 1 ; i<= max_depth; i++ ) cout<< " " << cnt[ i] ;
}
1020.树的遍历
思路是直接根据后序和中序遍历构造新的二叉树,在层序遍历输出即可 此题写法是直接用数组存储左右孩子节点,写法上简单不少(船新写法!!!! ) 由于pat卡输出空格,最后bfs时手动模拟队列最后一次性输出会比较简单
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N= 50 ;
int pos[ N] , ino[ N] ;
unordered_map< int , int > l, r, loc;
int build ( int pl, int pr, int il, int ir) {
int root= pos[ pr] ;
int k= loc[ root] ;
int len= k- il;
if ( il< k) l[ root] = build ( pl, pl+ len- 1 , il, k- 1 ) ;
if ( k< ir) r[ root] = build ( pl+ len, pr- 1 , k+ 1 , ir) ;
return root;
}
int q[ N] ;
void bfs ( int root) {
int hh= - 1 , tt= 0 ;
q[ ++ hh] = root;
while ( hh>= tt) {
int it= q[ tt++ ] ;
if ( l[ it] ) q[ ++ hh] = l[ it] ;
if ( r[ it] ) q[ ++ hh] = r[ it] ;
}
cout<< q[ 0 ] ;
for ( int i= 1 ; i< tt; i++ ) cout<< " " << q[ i] ;
return ;
}
int main ( ) {
int n;
cin>> n;
for ( int i= 1 ; i<= n; i++ ) cin>> pos[ i] ;
for ( int i= 1 ; i<= n; i++ ) {
cin>> ino[ i] ;
loc[ ino[ i] ] = i;
}
int root = build ( 1 , n, 1 , n) ;
bfs ( root) ;
}
1021.最深的根 ** (并查集判断是否只有一个连通块)
一个树最多只有N-1条边,那么按照题意,如果N-1条边要出现连通图的话,一定存在孤立节点
因为只有N-1条边
,所以不需要复杂的判环操作,只需要使用并查集
查看所有点是否都连通在一个子集当中即可注意,由于题目中没有给定边的方向,所以我们全部存取双向边
pps:又由于本题我们存取了双向边,所以在dfs时有可能会存在死循环搜索
的情况,因此要多加一特判条件
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <map>
using namespace std;
const int N= 10010 , M= 2 * N;
int h[ N] , e[ M] , ne[ M] , idx;
int p[ N] ;
int n;
void add ( int a, int b) {
e[ idx] = b;
ne[ idx] = h[ a] ;
h[ a] = idx++ ;
}
int find ( int x) {
if ( p[ x] != x) p[ x] = find ( p[ x] ) ;
return p[ x] ;
}
int dfs ( int root, int father) {
int lenth= 0 ;
for ( int i= h[ root] ; i!= - 1 ; i= ne[ i] ) {
int j= e[ i] ;
if ( j!= father) lenth= max ( lenth, dfs ( j, root) ) ;
}
return lenth+ 1 ;
}
int main ( ) {
cin>> n;
memset ( h, - 1 , sizeof h) ;
int cnt= n;
for ( int i= 1 ; i<= n; i++ ) p[ i] = i;
for ( int i= 0 ; i< n- 1 ; i++ ) {
int a, b;
cin>> a>> b;
add ( a, b) , add ( b, a) ;
if ( find ( a) != find ( b) ) {
p[ find ( a) ] = find ( b) ;
cnt-- ;
}
}
if ( cnt> 1 ) printf ( "Error: %d components" , cnt) ;
else {
vector< int > nodes;
int max_depth= - 1 ;
for ( int i= 1 ; i<= n; i++ ) {
int depth= dfs ( i, - 1 ) ;
if ( depth> max_depth) {
nodes. clear ( ) ;
nodes. push_back ( i) ;
max_depth= depth;
}
else if ( depth== max_depth) nodes. push_back ( i) ;
}
for ( int i= 0 ; i< nodes. size ( ) ; i++ ) cout<< nodes[ i] << endl;
}
}
1043.判断二叉搜索树 **
如果是二叉排序树,那么输出的中序遍历一定是有序的
,所以本题翻译为给定一个前序序列和中序序列,看是否能构成一颗二叉排序树 并且注意本题条件,题目要求左子树的值一定小于根节点,右子树的值大于等于根节点,所以当存在相同值时,中序序列该节点的位置是靠前的那一个
,镜像二叉排序树由于中序从大到小,所以与此情况相反 总结:本题只需分别判断是否能构成二叉排序树及其镜像,若都不能输出NO即可 注意: 本题不去建立树,直接通过前序及中序序列递归得到后序序列即可
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N= 10010 ;
int pre[ N] , ino[ N] ;
int post[ N] , cnt;
int n;
bool build ( int pl, int pr, int il, int ir, int type) {
int root= pre[ pl] ;
int k;
if ( ! type) {
for ( k= il; k<= ir; k++ ) if ( ino[ k] == root) break ;
if ( k> ir) return false ;
}
else {
for ( k= ir; k>= il; k-- ) if ( ino[ k] == root) break ;
if ( k< il) return false ;
}
int len= k- il;
bool check= true ;
if ( il< k) check= build ( pl+ 1 , pl+ len, il, k- 1 , type) ;
if ( ir> k) check= build ( pl+ len+ 1 , pr, k+ 1 , ir, type) ;
post[ cnt++ ] = root;
return check;
}
int main ( ) {
cin>> n;
for ( int i= 0 ; i< n; i++ ) {
cin>> pre[ i] ;
ino[ i] = pre[ i] ;
}
sort ( ino, ino+ n) ;
if ( build ( 0 , n- 1 , 0 , n- 1 , 0 ) ) {
cout<< "YES" << endl;
cout<< post[ 0 ] ;
for ( int i= 1 ; i< n; i++ ) cout<< " " << post[ i] ;
}
else {
reverse ( ino, ino+ n) ;
cnt= 0 ;
if ( build ( 0 , n- 1 , 0 , n- 1 , 1 ) ) {
cout<< "YES" << endl;
cout<< post[ 0 ] ;
for ( int i= 1 ; i< n; i++ ) cout<< " " << post[ i] ;
}
else cout<< "NO" ;
}
}
1064 . 完全二叉搜索树 **(自建完全二叉树)
看到这题一位要像书上那样边填边旋转操作,瞬间蒙了 本题只需要先构造一个完全二叉树即数组表示的完全二叉树
,再在对应位置填入中序遍历二叉搜索树的权值
即可
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N= 1010 ;
int w[ N] , bst[ N] ;
int n;
void dfs ( int & u, int cnt) {
if ( cnt* 2 <= n) dfs ( u, cnt* 2 ) ;
bst[ cnt] = w[ u++ ] ;
if ( cnt* 2 + 1 <= n) dfs ( u, cnt* 2 + 1 ) ;
}
int main ( ) {
cin>> n;
for ( int i= 0 ; i< n; i++ ) cin>> w[ i] ;
sort ( w, w+ n) ;
int u= 0 ;
dfs ( u, 1 ) ;
cout<< bst[ 1 ] ;
for ( int i= 2 ; i<= n; i++ ) cout<< " " << bst[ i] ;
}
1086.再次树遍历
思路一 :push操作建立的是先序遍历,pop操作建立的是中序遍历,根据先序遍历和中序遍历即可得到后序遍历 思路二:上一个操作如果是push,则当前点是上一个点的左二子,如果是pop操作则是右儿子,如果没有上一个操作就是根节点
#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
const int N= 50 ;
int ino[ N] , pre[ N] , post[ N] , loc[ N] ;
int idx, cnt;
stack< int > s;
void build ( int il, int ir, int pl, int pr, int & u) {
int root= pre[ pl] ;
int k= loc[ root] ;
int len= k- il;
if ( il< k) build ( il, k- 1 , pl+ 1 , pl+ len, u) ;
if ( ir> k) build ( k+ 1 , ir, pl+ len+ 1 , pr, u) ;
post[ u++ ] = root;
}
int main ( ) {
int n;
cin>> n;
int k= 2 * n;
while ( k-- ) {
string op;
cin>> op;
if ( op== "Push" ) {
int x;
cin>> x;
pre[ idx++ ] = x;
s. push ( x) ;
}
else {
ino[ cnt] = s. top ( ) ;
loc[ ino[ cnt] ] = cnt;
cnt++ ;
s. pop ( ) ;
}
}
int u= 0 ;
build ( 0 , n- 1 , 0 , n- 1 , u) ;
cout<< post[ 0 ] ;
for ( int i= 1 ; i< n; i++ ) cout<< " " << post[ i] ;
}
#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
const int N= 50 ;
int l[ N] , r[ N] ;
stack< int > s;
void dfs ( int u, int root) {
if ( l[ u] ) dfs ( l[ u] , root) ;
if ( r[ u] ) dfs ( r[ u] , root) ;
cout<< u;
if ( u!= root) cout<< " " ;
}
int main ( ) {
int n;
cin>> n;
int last= 0 , type;
int root;
for ( int i= 0 ; i< n* 2 ; i++ ) {
string op;
cin>> op;
if ( op== "Push" ) {
int x;
cin>> x;
if ( ! last) root = x;
else {
if ( ! type) l[ last] = x;
else r[ last] = x;
}
s. push ( x) ;
last= x;
type= 0 ;
}
else {
last= s. top ( ) ;
s. pop ( ) ;
type= 1 ;
}
}
dfs ( root, root) ;
}
1099.构建二叉搜索树 **(已知树的结构填值)
告诉已知树的结构,中序遍历填入节点的值,和完全二叉搜索树类似
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N= 110 ;
int l[ N] , r[ N] ;
int w[ N] , bst[ N] ;
int k;
int q[ N] ;
int n;
void dfs ( int u) {
if ( l[ u] != - 1 ) dfs ( l[ u] ) ;
bst[ u] = w[ k++ ] ;
if ( r[ u] != - 1 ) dfs ( r[ u] ) ;
}
void bfs ( int u) {
int tt= - 1 , hh= 0 ;
q[ ++ tt] = u;
while ( tt>= hh) {
u= q[ hh++ ] ;
if ( l[ u] != - 1 ) q[ ++ tt] = l[ u] ;
if ( r[ u] != - 1 ) q[ ++ tt] = r[ u] ;
}
cout<< bst[ q[ 0 ] ] ;
for ( int i= 1 ; i< n; i++ ) cout<< " " << bst[ q[ i] ] ;
}
int main ( ) {
cin>> n;
for ( int i= 0 ; i< n; i++ ) cin>> l[ i] >> r[ i] ;
for ( int i= 0 ; i< n; i++ ) cin>> w[ i] ;
sort ( w, w+ n) ;
dfs ( 0 ) ;
bfs ( 0 ) ;
}
1102.反转二叉树
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N= 15 ;
int l[ N] , r[ N] ;
int n;
bool has_father[ N] ;
void invert ( int root) {
if ( l[ root] != - 1 ) invert ( l[ root] ) ;
if ( r[ root] != - 1 ) invert ( r[ root] ) ;
swap ( l[ root] , r[ root] ) ;
}
int q[ N] ;
void bfs ( int root) {
int tt= - 1 , hh= 0 ;
q[ ++ tt] = root;
while ( tt>= hh) {
int x= q[ hh++ ] ;
if ( l[ x] != - 1 ) q[ ++ tt] = l[ x] ;
if ( r[ x] != - 1 ) q[ ++ tt] = r[ x] ;
}
cout<< q[ 0 ] ;
for ( int i= 1 ; i< n; i++ ) cout<< " " << q[ i] ;
cout<< endl;
}
int k;
void dfs ( int u) {
if ( l[ u] != - 1 ) dfs ( l[ u] ) ;
cout<< u;
if ( ++ k!= n) cout<< " " ;
if ( r[ u] != - 1 ) dfs ( r[ u] ) ;
}
int main ( ) {
cin>> n;
memset ( l, - 1 , sizeof l) ;
memset ( r, - 1 , sizeof r) ;
for ( int i= 0 ; i< n; i++ ) {
char a, b;
cin>> a>> b;
if ( a!= '-' ) l[ i] = a- '0' , has_father[ l[ i] ] = true ;
if ( b!= '-' ) r[ i] = b- '0' , has_father[ r[ i] ] = true ;
}
int root;
for ( int i= 0 ; i< n; i++ ) if ( ! has_father[ i] ) root= i;
invert ( root) ;
bfs ( root) ;
dfs ( root) ;
}
1110.完全二叉树 ** 判断是否是完全二叉树
写完就觉得王道书是弟中弟… 思路是根据完全二叉树的存储模式u,2*u,2*u+1....
所以如果当前树按顺序存满整个二叉树,该结构内不应该有空值,换句话说,如果最后一个节点的坐标大于n
那么它不是一棵完全二叉树
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N= 30 ;
int l[ N] , r[ N] ;
int n;
bool has_father[ N] ;
int last, max_index= - 1 ;
void dfs ( int u, int index) {
if ( u== - 1 ) return ;
dfs ( l[ u] , 2 * index) ;
dfs ( r[ u] , 2 * index+ 1 ) ;
if ( index> max_index) {
max_index= index;
last= u;
}
}
int main ( ) {
cin>> n;
memset ( l, - 1 , sizeof l) ;
memset ( r, - 1 , sizeof r) ;
for ( int i= 0 ; i< n; i++ ) {
string a, b;
cin>> a>> b;
if ( a!= "-" ) l[ i] = stoi ( a) , has_father[ l[ i] ] = true ;
if ( b!= "-" ) r[ i] = stoi ( b) , has_father[ r[ i] ] = true ;
}
int root;
for ( int i= 0 ; i< n; i++ ) if ( ! has_father[ i] ) root= i;
dfs ( root, 1 ) ;
if ( max_index<= n) {
cout<< "YES" << " " << last;
}
else cout<< "NO" << " " << root;
}
1115.二叉搜索树最后两层点的数量 ** (自定义节点序号建树) **
这是写到现在写的最nb的一个递归代码了 首先题目只给了权值而没有给编号,所以我们用idx=1,2,3,4,...
为每次插入的值编号,作为其在树中的编号 其次我们规定当一个节点的root
值为0,代表这个节点为空,还没有建立节点,这时我们新分配一个idx值
,创建一个新节点并且赋予权值 我们每次保证从根节点开始递归的向下插入节点
,如果遇到当前root不为0,那么比较一下权值,再插入这个节点的L[root]或者R[root]
一共需要插入n个节点,每次插入需要比较logn次,所以时间复杂度近似为nlogn
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N= 1010 ;
int n;
int idx;
int l[ N] , r[ N] , v[ N] ;
void insert ( int & root, int w) {
if ( ! root) {
v[ ++ idx] = w;
root= idx;
}
else if ( w<= v[ root] ) insert ( l[ root] , w) ;
else insert ( r[ root] , w) ;
}
int cnt[ N] , max_depth;
void dfs ( int root, int depth) {
if ( ! root) return ;
cnt[ depth] ++ ;
if ( depth> max_depth) max_depth= depth;
dfs ( l[ root] , depth+ 1 ) ;
dfs ( r[ root] , depth+ 1 ) ;
}
int main ( ) {
cin>> n;
int root= 0 ;
for ( int i= 0 ; i< n; i++ ) {
int w;
cin>> w;
insert ( root, w) ;
}
dfs ( root, 0 ) ;
int sum= cnt[ max_depth] + cnt[ max_depth- 1 ] ;
printf ( "%d + %d = %d" , cnt[ max_depth] , cnt[ max_depth- 1 ] , sum) ;
}
1119.前序和中序遍历 ** (先序和后序建树)
这道题由于先序遍历和后序遍历可能确定不了唯一的树,所以我们在dfs时考虑所有可能的情况 由于先序遍历=根节点(>=0)+左子树序列(>=0)+右子树序列(>=0)
,后序遍历=左子树序列(>=0)+右子树序列(>=0)+根节点(>=0)
,所以我们枚举左右子树所有可能的长度,cnt
累计所有可能的结果,当cnt大于1时 说明该树不唯一,此时我们停止枚举并且返回结果
#include <iostream>
#include <cstring>
#include <algorithm>
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 ;
int len= r1- l1;
for ( int i= 0 ; i<= len; i++ ) {
string lin, rin;
int lcnt= dfs ( l1+ 1 , l1+ i, l2, l2+ i- 1 , lin) ;
int rcnt= dfs ( l1+ i+ 1 , r1, l2+ i, r2- 1 , rin) ;
if ( lcnt && rcnt) {
in= lin+ to_string ( pre[ l1] ) + " " + rin;
cnt+ = lcnt* rcnt;
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 res;
int cnt= dfs ( 0 , n- 1 , 0 , n- 1 , res) ;
if ( cnt> 1 ) cout<< "No" << endl;
else cout<< "Yes" << endl;
res. pop_back ( ) ;
cout<< res<< endl;
return 0 ;
}
1620.Z字形遍历二叉树
这题思路比较简单,只要构建出二叉树然后再bfs时标记一下奇数层节点
,并且每遍历完奇数层节点就这一层进行翻转即可
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int N= 50 ;
int ino[ N] , post[ N] ;
unordered_map< int , int > l, r, loc;
int n;
int dfs ( int il, int ir, int pl, int pr) {
int root= post[ pr] ;
int k= loc[ root] ;
if ( il< k) l[ root] = dfs ( il, k- 1 , pl, pl+ k- il- 1 ) ;
if ( ir> k) r[ root] = dfs ( k+ 1 , ir, pl+ k- il, pr- 1 ) ;
return root;
}
int q[ N] ;
void bfs ( int root, int u) {
int hh= 0 , tt= - 1 ;
q[ ++ tt] = root;
int len= 1 ;
int pre_hh= 0 , pre_tt= 0 ;
while ( hh<= tt) {
int x= q[ hh++ ] ;
len-- ;
if ( l[ x] ) q[ ++ tt] = l[ x] ;
if ( r[ x] ) q[ ++ tt] = r[ x] ;
if ( ! len) {
len= tt- hh+ 1 ;
if ( u% 2 ) reverse ( q+ pre_hh, q+ pre_tt+ 1 ) ;
u++ ;
pre_hh= hh, pre_tt= tt;
}
}
cout<< q[ 0 ] ;
for ( int i= 1 ; i< n; i++ ) cout<< " " << q[ i] ;
}
int main ( ) {
cin>> n;
for ( int i= 0 ; i< n; i++ ) {
cin>> ino[ i] ;
loc[ ino[ i] ] = i;
}
for ( int i= 0 ; i< n; i++ ) cin>> post[ i] ;
int root= dfs ( 0 , n- 1 , 0 , n- 1 ) ;
bfs ( root, 1 ) ;
return 0 ;
}
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int N= 50 ;
int ino[ N] , post[ N] ;
unordered_map< int , int > l, r, loc;
int n;
int dfs ( int il, int ir, int pl, int pr) {
int root= post[ pr] ;
int k= loc[ root] ;
if ( il< k) l[ root] = dfs ( il, k- 1 , pl, pl+ k- il- 1 ) ;
if ( ir> k) r[ root] = dfs ( k+ 1 , ir, pl+ k- il, pr- 1 ) ;
return root;
}
int q[ N] ;
void bfs ( int root) {
int hh= 0 , tt= - 1 ;
q[ ++ tt] = root;
int cnt= 0 ;
while ( hh<= tt) {
int head= hh, tail= tt;
while ( hh<= tail) {
int x= q[ hh++ ] ;
if ( l[ x] ) q[ ++ tt] = l[ x] ;
if ( r[ x] ) q[ ++ tt] = r[ x] ;
}
if ( ++ cnt% 2 ) reverse ( q+ head, q+ tail+ 1 ) ;
}
cout<< q[ 0 ] ;
for ( int i= 1 ; i< n; i++ ) cout<< " " << q[ i] ;
}
int main ( ) {
cin>> n;
for ( int i= 0 ; i< n; i++ ) {
cin>> ino[ i] ;
loc[ ino[ i] ] = i;
}
for ( int i= 0 ; i< n; i++ ) cin>> post[ i] ;
int root= dfs ( 0 , n- 1 , 0 , n- 1 ) ;
bfs ( root) ;
return 0 ;
}
1138.后序遍历
用哈希做优化标记中序遍历值的位置,递归输出结果即可
#include <iostream>
#include <cstring>
#include <unordered_map>
using namespace std;
const int N= 50010 ;
unordered_map< int , int > l, r, loc;
int pre[ N] , ino[ N] ;
int n;
int res;
int dfs ( int pl, int pr, int il, int ir) {
int root= pre[ pl] ;
int k= loc[ root] ;
if ( il< k) return dfs ( pl+ 1 , pl+ k- il, il, k- 1 ) ;
else if ( ir> k) return dfs ( pl+ k- il+ 1 , pr, k+ 1 , ir) ;
else return root;
}
int main ( ) {
cin>> n;
for ( int i= 0 ; i< n; i++ ) cin>> pre[ i] ;
for ( int i= 0 ; i< n; i++ ) {
cin>> ino[ i] ;
loc[ ino[ i] ] = i;
}
cout<< dfs ( 0 , n- 1 , 0 , n- 1 ) ;
}
1066.AVL(二叉平衡搜索树)的根 ** (平衡树的旋转) **
本题模拟平衡树的旋转调整操作,用的是之前自定义节点编号的插入方式(NlogN),注意写法!!
#include <algorithm>
#include <iostream>
using namespace std;
const int N= 50 ;
int l[ N] , r[ N] , v[ N] , h[ N] , idx;
int n;
int update ( int u) {
h[ u] = max ( h[ l[ u] ] , h[ r[ u] ] ) + 1 ;
}
int get_balance ( int u) {
return h[ l[ u] ] - h[ r[ u] ] ;
}
void R ( int & u) {
int p= l[ u] ;
l[ u] = r[ p] , r[ p] = u;
update ( u) , update ( p) ;
u= p;
}
void L ( int & u) {
int p= r[ u] ;
r[ u] = l[ p] , l[ p] = u;
update ( u) , update ( p) ;
u= p;
}
void insert ( int & u, int w) {
if ( ! u) {
u= ++ idx;
v[ u] = w;
}
else if ( w< v[ u] ) {
insert ( l[ u] , w) ;
if ( get_balance ( u) == 2 ) {
if ( get_balance ( l[ u] ) == 1 ) R ( u) ;
else L ( l[ u] ) , R ( u) ;
}
}
else {
insert ( r[ u] , w) ;
if ( get_balance ( u) == - 2 ) {
if ( get_balance ( r[ u] ) == - 1 ) L ( u) ;
else R ( r[ u] ) , L ( u) ;
}
}
update ( u) ;
}
int main ( ) {
cin>> n;
int root= 0 ;
while ( n-- ) {
int w;
cin>> w;
insert ( root, w) ;
}
cout<< v[ root] ;
}
1123.判断完全AVL树
这道题是完全二叉树和AVL树的综合,首先根据题目构造出AVL树,然后利用完全二叉树的节点序号特质判断其是否是一个完全AVL树
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N= 30 ;
int l[ N] , r[ N] , h[ N] , v[ N] , idx;
int n;
void update ( int u) {
h[ u] = max ( h[ l[ u] ] , h[ r[ u] ] ) + 1 ;
}
int get_balance ( int u) {
return h[ l[ u] ] - h[ r[ u] ] ;
}
void R ( int & u) {
int p= l[ u] ;
l[ u] = r[ p] , r[ p] = u;
update ( u) , update ( p) ;
u= p;
}
void L ( int & u) {
int p= r[ u] ;
r[ u] = l[ p] , l[ p] = u;
update ( u) , update ( p) ;
u= p;
}
void insert ( int & u, int w) {
if ( ! u) {
u= ++ idx;
v[ u] = w;
}
else if ( w< v[ u] ) {
insert ( l[ u] , w) ;
if ( get_balance ( u) == 2 ) {
if ( get_balance ( l[ u] ) == 1 ) R ( u) ;
else L ( l[ u] ) , R ( u) ;
}
}
else {
insert ( r[ u] , w) ;
if ( get_balance ( u) == - 2 ) {
if ( get_balance ( r[ u] ) == - 1 ) L ( u) ;
else R ( r[ u] ) , L ( u) ;
}
}
update ( u) ;
}
int q[ N] , pos[ N] ;
bool res= true ;
void bfs ( int root) {
int hh= 0 , tt= - 1 ;
q[ ++ tt] = root;
pos[ root] = 1 ;
while ( hh<= tt) {
int x= q[ hh++ ] ;
if ( pos[ x] > n) res= false ;
if ( l[ x] ) q[ ++ tt] = l[ x] , pos[ l[ x] ] = pos[ x] * 2 ;
if ( r[ x] ) q[ ++ tt] = r[ x] , pos[ r[ x] ] = pos[ x] * 2 + 1 ;
}
cout<< v[ q[ 0 ] ] ;
for ( int i= 1 ; i< idx; i++ ) cout<< " " << v[ q[ i] ] ;
cout<< endl;
}
int main ( ) {
cin>> n;
int root= 0 ;
for ( int i= 0 ; i< n; i++ ) {
int w;
cin>> w;
insert ( root, w) ;
}
bfs ( root) ;
if ( res) cout<< "YES" << endl;
else cout<< "NO" << endl;
}
1135.判断红黑树 **
思路:边递归建树,边判断是否是红黑树,该树满足如下条件 1.根节点是黑色
2.红色节点的左右儿子都是黑色(注意空节点也算黑色,按照0)
3.到任意叶节点上的黑色节点一定数目相同
4.前序遍历不一定可以构建一棵二叉搜索树
PS: 注意这题节点中序遍历是不带负值
的,只是存的时候加上负数标记为红色节点
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int N= 50 ;
int pre[ N] , ino[ N] ;
unordered_map< int , int > pos;
bool res;
int build ( int il, int ir, int pl, int pr, int & sum) {
int root= pre[ pl] ;
int k= pos[ abs ( root) ] ;
if ( k< il || k> ir) {
res= false ;
return - 1 ;
}
int lt= 0 , rt= 0 , lsum= 0 , rsum= 0 ;
if ( il< k) lt= build ( il, k- 1 , pl+ 1 , pl+ k- il, lsum) ;
if ( k< ir) rt= build ( k+ 1 , ir, pl+ k- il+ 1 , pr, rsum) ;
if ( lsum!= rsum) {
res= false ;
return - 1 ;
}
sum= lsum;
if ( root< 0 ) {
if ( lt< 0 || rt< 0 ) res= false ;
return - 1 ;
}
else sum++ ;
return root;
}
int main ( ) {
int k;
cin>> k;
while ( k-- ) {
int n;
cin>> n;
for ( int i= 0 ; i< n; i++ ) {
cin>> pre[ i] ;
ino[ i] = abs ( pre[ i] ) ;
}
sort ( ino, ino+ n) ;
pos. clear ( ) ;
for ( int i= 0 ; i< n; i++ ) pos[ ino[ i] ] = i;
int sum= 0 ;
res= true ;
int root= build ( 0 , n- 1 , 0 , n- 1 , sum) ;
if ( root< 0 ) res= false ;
if ( res) cout<< "Yes" << endl;
else cout<< "No" << endl;
}
}
1053.等重路径
这题直接用邻接表保存树的路径节点,再暴搜出每一条等重路径即可 Ps:题目结果要求总大到小输出字典序,我们直接利用vector特性geater<int>()
输出大端排序结果即可
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N= 110 ;
int h[ N] , e[ N] , ne[ N] , w[ N] , idx;
int n, m, s;
void insert ( int a, int b) {
e[ idx] = b;
ne[ idx] = h[ a] ;
h[ a] = idx++ ;
}
vector< vector< int > > res;
void dfs ( int root, int sum, vector< int > & path) {
if ( h[ root] == - 1 && sum== s) res. push_back ( path) ;
for ( int i= h[ root] ; i!= - 1 ; i= ne[ i] ) {
int x= e[ i] ;
path. push_back ( w[ x] ) ;
dfs ( x, sum+ w[ x] , path) ;
path. pop_back ( ) ;
}
}
int main ( ) {
cin>> n>> m>> s;
for ( int i= 0 ; i< n; i++ ) cin>> w[ i] ;
memset ( h, - 1 , sizeof h) ;
for ( int i= 0 ; i< m; i++ ) {
int id, k;
cin>> id>> k;
while ( k-- ) {
int x;
cin>> x;
insert ( id, x) ;
}
}
vector< int > path ( { w[ 0 ] } ) ;
dfs ( 0 , w[ 0 ] , path) ;
sort ( res. begin ( ) , res. end ( ) , greater< vector< int >> ( ) ) ;
for ( auto it : res) {
cout<< it[ 0 ] ;
for ( int i= 1 ; i< it. size ( ) ; i++ ) cout<< " " << it[ i] ;
cout<< endl;
}
}
1094.最大的一代
这道题就是用dfs或者bfs搜索每一层的节点数量,最后输出最大值 提供两种写法邻接表存储的dfs
和邻接矩阵存储的bfs
#include <iostream>
#include <algprithm>
#include <cstring>
using namespace std;
const int N= 110 ;
int h[ N] , e[ n] , ne[ n] , idx;
int n, m;
void insert ( int a, int b) {
e[ idx] = b;
ne[ idx] = h[ a] ;
h[ a] = idx++ ;
}
unordered_map< int , int > has;
bool flag[ N] ;
void dfs ( int root, int u) {
if ( ! flag[ root] ) {
has[ u] ++ ;
flag[ root] = true ;
}
for ( int i= h[ root] ; i!= - 1 ; i= ne[ i] ) {
int x= e[ i] ;
dfs ( x, u+ 1 ) ;
}
}
int main ( ) {
cin>> n>> m;
memset ( h, - 1 , sizeof h) ;
while ( m-- ) {
int id, k;
while ( k-- ) {
int b;
cin>> b;
insert ( id, b) ;
}
}
int u= 1 ;
dfs ( 1 , u) ;
int max_u= 1 , max_sum= 1 ;
for ( int i= 0 ; i< has. size ( ) ; i++ ) {
int u= has. first, sum= has. second;
if ( sum> max_sum) {
max_sum= sum;
max_u= u;
}
}
cout<< max_sum<< " " << max_u;
}
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int N= 110 ;
bool map[ N] [ N] ;
vector< int > level[ N] ;
int n, m;
int main ( ) {
cin>> n>> m;
while ( m-- ) {
int id, k;
cin>> id>> k;
while ( k-- ) {
int son;
cin>> son;
map[ id] [ son] = true ;
}
}
int l= 1 ;
level[ l] . push_back ( 1 ) ;
while ( level[ l] . size ( ) ) {
for ( auto it: level[ l] ) {
for ( int j= 1 ; j<= n; j++ ) {
if ( map[ it] [ j] ) level[ l+ 1 ] . push_back ( j) ;
}
}
l++ ;
}
int k= 1 ;
for ( int i= 2 ; i< l; i++ )
if ( level[ i] . size ( ) > level[ k] . size ( ) ) k= i;
cout<< level[ k] . size ( ) << " " << k<< endl;
}
1079.供应链总销售额 (记忆化搜索/树形DP)
树形DP根据该树特点递推
出每个叶子节点到达根节点的距离,即计算f[N]
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N= 1e5 + 10 ;
int h[ N] , e[ N] , ne[ N] , idx;
int n;
double p, r, sum;
int cnt[ N] ;
void insert ( int a, int b) {
e[ idx] = b;
ne[ idx] = h[ a] ;
h[ a] = idx++ ;
}
void dfs ( int root, double value) {
if ( h[ root] == - 1 ) {
sum+ = cnt[ root] * value;
return ;
}
for ( int i= h[ root] ; i!= - 1 ; i= ne[ i] ) {
int j= e[ i] ;
dfs ( j, value* ( 1 + r/ 100 ) ) ;
}
}
int main ( ) {
cin>> n>> p>> r;
memset ( h, - 1 , sizeof h) ;
for ( int i= 0 ; i< n; i++ ) {
int k, x;
cin>> k;
if ( ! k) {
cin>> x;
cnt[ i] = x;
}
while ( k-- ) {
cin>> x;
insert ( i, x) ;
}
}
dfs ( 0 , p) ;
printf ( "%.1lf" , sum) ;
}
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N= 1e5 + 10 ;
int n;
double P, R;
int p[ N] , f[ N] , cnt[ N] ;
int dfs ( int root) {
if ( f[ root] != - 1 ) return f[ root] ;
else if ( p[ root] != - 1 ) return f[ root] = dfs ( p[ root] ) + 1 ;
else return f[ root] = 0 ;
}
int main ( ) {
cin>> n>> P>> R;
memset ( p, - 1 , sizeof p) ;
memset ( f, - 1 , sizeof f) ;
for ( int i= 0 ; i< n; i++ ) {
int k;
cin>> k;
if ( ! k) {
int num;
cin>> num;
cnt[ i] = num;
}
while ( k-- ) {
int son;
cin>> son;
p[ son] = i;
}
}
double sum= 0 ;
for ( int i= 0 ; i< n; i++ ) {
if ( cnt[ i] ) sum+ = P* cnt[ i] * pow ( ( 1 + R/ 100 ) , dfs ( i) ) ;
}
printf ( "%.1lf" , sum) ;
}
1090.供应链的最高价格
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N= 1e5 + 10 ;
int h[ N] , e[ N] , ne[ N] , idx;
int n;
double P, R;
void insert ( int a, int b) {
e[ idx] = b;
ne[ idx] = h[ a] ;
h[ a] = idx++ ;
}
double max_value;
int cnt;
void dfs ( int root, double value) {
if ( h[ root] == - 1 ) {
if ( value> max_value) {
max_value= value;
cnt= 1 ;
}
else if ( value== max_value) cnt++ ;
return ;
}
for ( int i= h[ root] ; i!= - 1 ; i= ne[ i] ) {
int j= e[ i] ;
dfs ( j, ( 1 + R/ 100 ) * value) ;
}
}
int main ( ) {
cin>> n>> P>> R;
memset ( h, - 1 , sizeof h) ;
int root= 0 ;
for ( int i= 0 ; i< n; i++ ) {
int father;
cin>> father;
if ( father== - 1 ) root= i;
else insert ( father, i) ;
}
dfs ( root, P) ;
printf ( "%.2lf %d" , max_value, cnt) ;
}
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int N= 1e5 + 10 ;
int f[ N] , p[ N] ;
int n;
double P, R;
int dfs ( int u) {
if ( f[ u] != - 1 ) return f[ u] ;
else if ( p[ u] == - 1 ) return f[ u] = 0 ;
return f[ u] = dfs ( p[ u] ) + 1 ;
}
int main ( ) {
cin>> n>> P>> R;
memset ( f, - 1 , sizeof f) ;
memset ( p, - 1 , sizeof p) ;
for ( int i= 0 ; i< n; i++ ) cin>> p[ i] ;
int cnt= 0 , path= 0 ;
double value= 0 ;
for ( int i= 0 ; i< n; i++ ) {
int len= dfs ( i) ;
if ( len> path) {
value= P* pow ( 1 + R/ 100 , len) ;
path= len;
cnt= 1 ;
}
else if ( len== path) cnt++ ;
}
printf ( "%.2lf %d" , value, cnt) ;
}
1106.供应链最低价格
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N= 1e5 + 10 ;
int h[ N] , e[ N] , ne[ N] , idx;
int n;
double P, R;
void insert ( int a, int b) {
e[ idx] = b;
ne[ idx] = h[ a] ;
h[ a] = idx++ ;
}
double min_value= 1e10 ;
int cnt;
void dfs ( int root, double value) {
if ( h[ root] == - 1 ) {
if ( value< min_value) {
min_value= value;
cnt= 1 ;
}
else if ( value== min_value) cnt++ ;
return ;
}
for ( int i= h[ root] ; i!= - 1 ; i= ne[ i] ) {
int j= e[ i] ;
dfs ( j, ( 1 + R/ 100 ) * value) ;
}
}
int main ( ) {
cin>> n>> P>> R;
memset ( h, - 1 , sizeof h) ;
for ( int i= 0 ; i< n; i++ ) {
int k;
cin>> k;
if ( ! k) continue ;
while ( k-- ) {
int son;
cin>> son;
insert ( i, son) ;
}
}
dfs ( 0 , P) ;
printf ( "%.4lf %d" , min_value, cnt) ;
}
1155.堆路径
用数组存完全二叉树,按照根右左的顺序遍历二叉树,保存每一条路径,最后判断所有路径是否统一递增或者递减
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N= 1010 ;
int w[ N] ;
int n;
vector< vector< int > > res;
vector< int > path;
void dfs ( int u, vector< int > & path) {
path. push_back ( w[ u] ) ;
if ( 2 * u> n) res. push_back ( path) ;
if ( 2 * u+ 1 <= n) dfs ( 2 * u+ 1 , path) ;
if ( 2 * u<= n) dfs ( 2 * u, path) ;
path. pop_back ( ) ;
}
int main ( ) {
cin>> n;
for ( int i= 1 ; i<= n; i++ ) cin>> w[ i] ;
dfs ( 1 , path) ;
bool gt= false , lt= false ;
for ( auto it: res) {
cout<< it[ 0 ] ;
for ( int i= 1 ; i< it. size ( ) ; i++ ) {
if ( it[ i] > it[ i- 1 ] ) gt= true ;
if ( it[ i] < it[ i- 1 ] ) lt= true ;
cout<< " " << it[ i] ;
}
cout<< endl;
}
if ( gt && lt) cout<< "Not Heap" << endl;
else if ( gt) cout<< "Min Heap" << endl;
else cout<< "Max Heap" << endl;
}
1130.中缀表达式
这道题通过中根遍历输出表达式,要点在于添加括号 首先整体不用添加括号,其次左右子节点如果是单独的叶节点通用不用添加括号,递归输出即可
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N= 25 ;
int l[ N] , r[ N] ;
string w[ N] ;
bool st[ N] , is_leaf[ N] ;
int n;
string dfs ( int u) {
string left, right;
if ( l[ u] != - 1 ) {
if ( ! is_leaf[ l[ u] ] ) left= "(" + dfs ( l[ u] ) + ")" ;
else left= dfs ( l[ u] ) ;
}
if ( r[ u] != - 1 ) {
if ( ! is_leaf[ r[ u] ] ) right= "(" + dfs ( r[ u] ) + ")" ;
else right= dfs ( r[ u] ) ;
}
return left+ w[ u] + right;
}
int main ( ) {
cin>> n;
int root= 1 ;
for ( int i= 1 ; i<= n; i++ ) {
cin>> w[ i] >> l[ i] >> r[ i] ;
if ( l[ i] ) st[ l[ i] ] = true ;
if ( r[ i] ) st[ r[ i] ] = true ;
if ( l[ i] == - 1 && r[ i] == - 1 ) is_leaf[ i] = true ;
}
while ( st[ root] ) root++ ;
cout<< dfs ( root) ;
}
1143.最低公共祖先 **(LCA)
本题是寻找最低公共祖先(LCA
)的简单实现方法 首先按照中序和先序遍历重建二叉树(可以不重建),重建过程中记录每个节点的深度以及父节点 寻找过程就是从给定两节点出发,依次向上寻找直到两节点重合即可,时间复杂度O(N)
由于本题时间限制2*10^7
,而数据范围是10^7
,所以直接使用哈希表寻找中序遍历位置时可能会超时 所以我们将数值映射到0~N-1的下标位置,并用`下标表示中序遍历和先序遍历 例如: 先序遍历1,5,7,4,3,2 对应中序遍历->1,2,3,4,5,7
中序遍历对应下标-> 0,1,2,3,4,5
,则先序遍历对应下标0,4,5,3,2,1
即在计算谁是谁的父节点时,只需要知道下标,不需要知道具体数值
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int N= 10010 ;
int in[ N] , pre[ N] , seq[ N] ;
int p[ N] , depth[ N] ;
int n, m;
unordered_map< int , int > pos;
int build ( int il, int ir, int pl, int pr, int d) {
int root= pre[ pl] ;
int k= root;
depth[ root] = d;
if ( il< k) p[ build ( il, k- 1 , pl+ 1 , pl+ 1 + ( k- 1 - il) , d+ 1 ) ] = root;
if ( ir> k) p[ build ( k+ 1 , ir, pl+ k- il+ 1 , pr, d+ 1 ) ] = root;
return root;
}
int main ( ) {
cin>> m>> n;
for ( int i= 0 ; i< n; i++ ) {
cin>> pre[ i] ;
seq[ i] = pre[ i] ;
}
sort ( seq, seq+ n) ;
for ( int i= 0 ; i< n; i++ ) {
pos[ seq[ i] ] = i;
in[ i] = i;
}
for ( int i= 0 ; i< n; i++ ) pre[ i] = pos[ pre[ i] ] ;
build ( 0 , n- 1 , 0 , n- 1 , 0 ) ;
while ( m-- ) {
int a, b;
cin>> a>> b;
if ( pos. count ( a) && pos. count ( b) ) {
a= pos[ a] , b= pos[ b] ;
int x= a, y= b;
while ( a!= b) {
if ( depth[ a] < depth[ b] ) b= p[ b] ;
else a= p[ a] ;
}
if ( a!= x && a!= y) printf ( "LCA of %d and %d is %d.\n" , seq[ x] , seq[ y] , seq[ a] ) ;
else if ( a== x) printf ( "%d is an ancestor of %d.\n" , seq[ x] , seq[ y] ) ;
else printf ( "%d is an ancestor of %d.\n" , seq[ y] , seq[ x] ) ;
}
else if ( pos. count ( a) == 0 && pos. count ( b) == 0 ) printf ( "ERROR: %d and %d are not found.\n" , a, b) ;
else if ( pos. count ( a) == 0 ) printf ( "ERROR: %d is not found.\n" , a) ;
else printf ( "ERROR: %d is not found.\n" , b) ;
}
}
1151.二叉树中的最低公共祖先
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int N= 10010 ;
int p[ N] , depth[ N] ;
int ino[ N] , pre[ N] , seq[ N] ;
unordered_map< int , int > pos;
int build ( int pl, int pr, int il, int ir, int d) {
int root = pre[ pl] ;
int k= root;
depth[ root] = d;
if ( il< k) p[ build ( pl+ 1 , pl+ 1 + k- 1 - il, il, k- 1 , d+ 1 ) ] = root;
if ( ir> k) p[ build ( pl+ 1 + k- il, pr, k+ 1 , ir, d+ 1 ) ] = root;
return root;
}
int main ( ) {
int m, n;
cin>> m>> n;
for ( int i= 0 ; i< n; i++ ) {
cin>> seq[ i] ;
ino[ i] = seq[ i] ;
pos[ ino[ i] ] = i;
ino[ i] = i;
}
for ( int i= 0 ; i< n; i++ ) {
cin>> pre[ i] ;
pre[ i] = pos[ pre[ i] ] ;
}
build ( 0 , n- 1 , 0 , n- 1 , 0 ) ;
while ( m-- ) {
int a, b;
cin>> a>> b;
if ( pos. count ( a) && pos. count ( b) ) {
a= pos[ a] , b= pos[ b] ;
int x= a, y= b;
while ( a!= b) {
if ( depth[ a] < depth[ b] ) b= p[ b] ;
else a= p[ a] ;
}
if ( a!= x && a!= y) printf ( "LCA of %d and %d is %d.\n" , seq[ x] , seq[ y] , seq[ a] ) ;
else if ( a== x) printf ( "%d is an ancestor of %d.\n" , seq[ x] , seq[ y] ) ;
else printf ( "%d is an ancestor of %d.\n" , seq[ y] , seq[ x] ) ;
}
else if ( pos. count ( a) == 0 && pos. count ( b) == 0 ) printf ( "ERROR: %d and %d are not found.\n" , a, b) ;
else if ( pos. count ( a) == 0 ) printf ( "ERROR: %d is not found.\n" , a) ;
else printf ( "ERROR: %d is not found.\n" , b) ;
}
}