题目链接:二叉树
A:
题目要求:说来说去就是后序遍历转层序遍历
思路:每碰到一个小写字母,那么这个小写字母一定是叶子节点,那么用数组模拟指针,开辟一个位置,这个位置放置这个小写字母,并且挂上小写字母的信息(左儿子,右儿子,id),挂上id这个信息便于他的父亲指向他,而叶子节点的左儿子、右儿子均为-1。如果碰到一个大写字母,大写字母一定是非叶子节点,那么大写字母一定是前面两坨东西的父亲(有可能是另一个大写字母带着一堆儿子,也可以是单独的小写字母),无论如何,前面两个字母都是它的儿子,那么思路就很清晰了,直接用一个栈来存储顺序,小写字母就存了然后扔进去,大写字母就存了以后把栈顶两个取出来当儿子,自己再进去,这样一直操作下去,最终会有一个大写字母作为根节点的,然后问题就解决了,就通过这种方法将这个二叉树构建出来了。
如图,一路从叶子节点向上构造,给出样例1的二叉树构造图形
之后就很简单了。
只要通过bfs来进行层序遍历即可
印象中上学期看的《啊哈!算法》中有详细生动的关于二叉树的介绍。
二叉树遍历方式如下:
dfs:前序遍历、中序遍历、后序遍历
bfs:层序遍历
不会吧不会真的有人不会bfs吧!
层序遍历(一层一层的遍历)
用队列来实现对二叉树的bfs
最后再对字符串进行翻转输出,大功告成!
#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
#define ll long long
char s[10010];
struct node
{
char c;
int l, r, id;
node() { l = -1, r = -1; }
}tree[10010];
stack <node> st;
int id, root;
queue <node> q;
char ans[10010];
int cnt;
void bfs()
{
cnt = 0;
q.push(tree[root]);
while (q.size())
{
node u = q.front();
q.pop();
ans[cnt++] = u.c;
if(u.l!=-1) q.push(tree[u.l]);
if(u.r!=-1) q.push(tree[u.r]);
}
for (int i = cnt - 1; i >= 0; i--) printf("%c", ans[i]);
printf("\n");
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
while (q.size()) q.pop();
while (st.size()) st.pop();
scanf("%s", &s);
id = 0;
int len = strlen(s);
for (int i = 0; i < len; i++)
{
if (s[i] >= 'a' && s[i] <= 'z')//小写字母
{
tree[id].c = s[i];
tree[id].id = id;
tree[id].l = -1;
tree[id].r = -1;
st.push(tree[id]);
id++;
}
else if(s[i] >= 'A' && s[i] <= 'Z')//大写字母
{
node tmp1 = st.top(); st.pop();
node tmp2 = st.top(); st.pop();
tree[id].c = s[i];
tree[id].r = tmp1.id;
tree[id].l = tmp2.id;
tree[id].id = id;
st.push(tree[id]);
id++;
}
}
root = id - 1;//根节点编号为id-1
bfs();
}
}
B:
题目要求:求出n个节点的二叉树中,有多少种节点数不同的子树
思路:
对于完美二叉树,求它的最大深度
对于非完美二叉树,求他的个数
最终加起来得到答案
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define ll long long
ll N;
ll ans;
ll ansdep;
void find(ll x)
{
ll dep = 1;
ll l = x;
ll r = x;
while ((l << 1) <= N)
{
l <<= 1;
dep++;
}
while ((r << 1 | 1) <= N)
{
r = r << 1 | 1;
}
if (l <= r)
{
if (ansdep < dep) ansdep = dep;
}
else
{
find(x << 1);
find(x << 1 | 1);
ans++;
}
}
int main()
{
while (scanf("%lld", &N)!=EOF)
{
ans = 0;
ansdep = 0;
find(1);
printf("%lld\n", ans + ansdep);
}
}
C:
紫书P148原题
思路:直接模拟必然TLE,求目标球走的位置即可
每个小球都会落在根节点上,因此前两个小球必然是一个在左子树一个在右子树,所以只用看小球是第几个落在左子树的,就能知道小球下一步的方向。
#include <iostream>
using namespace std;
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
int D, I;
scanf("%d%d", &D, &I);
long long ans = 1;
for (int i = 1; i < D; i++)
{
if (I & 1)
{
I = (I + 1)>>1;
ans <<= 1;
}
else
{
I = I >> 1;
ans = (ans << 1) + 1;
}
}
printf("%lld\n", ans);
}
scanf("%d", &T);
return 0;
}
D:
紫书P176原题
思路:构建二叉树,模拟01串二进制求解
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
char ans[140];
int quan[10];
char chery[10];
vector <char> res;
int main()
{
int t;
int kase = 0;
while (scanf("%d", &t)&&t) {
res.clear();
for (int i = 1; i <= t; i++)
{
int num;
char x;
cin >> x >> quan[i];
}
cin >> ans + 1;
int m;
scanf("%d", &m);
while (m--)
{
cin >> chery + 1;
int q = 1 << (t - 1);
int p = 1;
for (int i = 1; i <= t; i++, q >>= 1)
{
if (chery[quan[i]] == '1')
p += q;
}
//printf("%d", p);
//printf("%c", ans[p]);
res.push_back(ans[p]);
}
printf("S-Tree #%d:\n", ++kase);
for (int i = 0; i < res.size(); i++)
printf("%c", res[i]);
printf("\n\n");
}
}
E:
紫书P155原题
思路:用中序遍历和后序遍历构建二叉树,再进行递归遍历求解。
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 10010;
vector <int> in;
vector <int> post;
int n = 0;
int minsum=1<<30, ans = -1;
struct Node
{
int v;
Node* left=NULL, * right=NULL;
};
Node* createtree(int i1,int j1,int i2,int j2)
{
//i1和j1指针管理中序遍历,i2和j2指针管理后序遍历
if (i1 >= j1 || i2 >= j2) return NULL;
Node* root = new Node;
root->v = post[j2 - 1];
int j = find(in.begin() + i1, in.begin() + j1, post[j2 - 1]) - in.begin();
root->left = createtree(i1, j, i2, i2 + (j - i1));
root->right = createtree(j + 1, j1, i2 + (j - i1), j2 - 1);
return root;
}
void dfs(Node *root,int sum)
{
if (root->left == NULL && root->right == NULL)
{
sum += root->v;
if (sum < minsum || (sum == minsum && (root->v < ans)))
{
minsum = sum;
ans = root->v;
}
return;
}
if (root->left != NULL)
{
dfs(root->left, sum + root->v);
}
if (root->right != NULL)
{
dfs(root->right, sum + root->v);
}
}
int main()
{
string line_in;
string line_post;
while (getline(cin, line_in))
{
in.clear();
post.clear();
getline(cin, line_post);
//cout << line_in << endl;
//cout << line_post << endl;
stringstream ss_in(line_in);
stringstream ss_post(line_post);
int x_in;
int x_post;
while (ss_in >> x_in) {
ss_post >> x_post;
in.push_back(x_in);
post.push_back(x_post);
}
/*for (int i = 0; i < n; i++)
{
cout << in[i] << ' ';
}
cout << endl;
for (int i = 0; i < n; i++)
{
cout << post[i] << ' ';
}
cout << endl;*/
minsum = 1<<30;
ans = -1;//初始化两个值
Node* ntree=createtree(0, in.size(), 0, post.size());
dfs(ntree, 0);
printf("%d\n", ans);
}
}
F:
思路:二叉搜索树的构建,按右子树、左子树、根顺序输出
#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
#define ll long long
int a[3030];
void dfs(int cur,int n)
{
int root = a[n];
int r;
int i;
//cout << cur << " " << n << endl;
if (cur == n)
{
printf("%d\n", a[cur]);
return;
}
for (i = cur; i <= n; i++)
{
if (a[i] >= root)
{
r = a[i];
break;
}
}
if(i<=n-1) dfs(i, n - 1);
if(cur<=i-1) dfs(cur, i - 1);
printf("%d\n", root);
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
dfs(1, n);
}
H:
思路:给出叶子节点,找出规律进行回溯,(第一发直接模拟TLE了),需要再找一个规律,当连续往同一个方向回溯的时候,可以连着跳(省效率),比如连续n次从左孩子跳回父亲。
具体上代码:
TLE代码:
#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
#define ll long long
int main()
{
int t;
scanf("%d", &t);
int kase = 0;
while (t--)
{
int a, b;
scanf("%d%d", &a, &b);
int left = 0, right = 0;
while (!(a == 1 && b == 1))
{
if (a < b)
{
right++;
b = b - a;
}
else
{
left++;
a = a - b;
}
}
printf("Scenario #%d:", ++kase);
printf("%d %d\n\n", left, right);
}
}
显然,一步一步的找回去是不行的,比如说(210e9,1)这个数据,我们需要操作210e9次,直接T掉,能够连续跳的连续跳,我就想到了以下的方式。
#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
#define ll long long
int main()
{
int t;
scanf("%d", &t);
int kase = 0;
while (t--)
{
int a, b;
scanf("%d%d", &a, &b);
int left = 0, right = 0;
while (a != 1 && b != 1)
{
if (a < b)
{
right += b / a;
b = b % a;
}
else
{
left += a / b;
a = a % b;
}
}
right += b - 1;
left += a - 1;
printf("Scenario #%d:\n", ++kase);
printf("%d %d\n\n", left, right);
}
}
K:
思路:给出这样的题面,显然是需要枚举来构造答案,然而,n==1e5,如果枚举两个数,那必定超时。只需要枚举一个数i,通过A,是可以算出原本需要枚举的另一个数j的范围,j1<=i/A<=j2,其他的j是肯定不合理的。
那么只需要枚举结束,便可以得到最终答案。
取绝对值要用fabs不要用abs!!!一定会wa掉
#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
#include <cmath>
using namespace std;
#define ll long long
int main()
{
double A;
int L;
scanf("%lf%d", &A, &L);
int ans1, ans2;
double ans = 999999;
for (int i = 1; i <= L; i++)
{
double j = i * 1.0 / A;
int j1 = (int)j;
int j2 = j1 + 1;
if (fabs(i * 1.0 / j1 - A) < ans) {
ans = fabs(i * 1.0 / j1 - A); ans1 = i, ans2 = j1;
}
if (fabs(i * 1.0 / j2 - A) < ans) {
ans = fabs(i * 1.0 / j2 - A); ans1 = i, ans2 = j2;
}
}
printf("%d %d\n", ans1, ans2);
}
精力有限,今天就写这么多吧,以后有机会再写(咕咕咕)