王道机试 数据机构篇

知识点

 

 

 

 

一、栈的应用

case1:括号匹配问题

  • 栈里放左括号的位置,遇到一个右括号就出栈,若栈为空说明该右括号匹配失败,最终栈里剩余的都是匹配失败的左括号。
  • s.top()才能取元素,s.pop()只是单纯出栈
#include<iostream>
#include<stack>
using namespace std;
stack<int>s;//存左括号所在位置
int main()
{
	string a,b;
	while(cin>>a)
	{
		b=a;//单纯为了生成一个与输入等长的串
		for(int i=0; i<a.length(); i++)
		{
			b[i]=' ';//初始化
			if(a[i]=='(')
				s.push(i);
			if(a[i]==')')
			{
				if(s.empty())//若为空则说明匹配失败,想想栈的机理,和括号匹配是一样 
					b[i]='?';
				else s.pop();//不为空则把左括号出栈 
			}
		}
		while(!s.empty())
		{
			b[s.top()]='$';
			s.pop();//pop是没有返回值,所以用top取值 
		}
		cout<<a<<endl<<b<<endl;
	}
	return 0;
}

case2:表达式求值 或 简单计算器 都AC了

step1,将中缀表达式转为后缀表达式,这里复习一下数据结构/编译原理的知识,下面这个方法是十分简易的理解方法,可以看到,本质是确定了优先级(所以下面的方法中全部加上括号),于是前后缀表达式可以不带括号唯一确定表达式:

那么具体的实现方式如下:例子

	遍历中缀表达式
	{
		if(数字) 
			加入后缀表达式
		else if('(') 
			入栈
		else if(')')
			依次将栈中元素出栈并加入到后缀表达式,直到遇到'(',并将其从栈中删除
		else if(运算符op) {
			while(栈不空,op优先级<=栈顶)//设置"("优先级最低 
			{
				依次出栈加入到后缀表达式
				(即直到遇到比op优先级低的运算符或左括号或栈空为止)             	
			}
			op入栈 
		}
	} 

step2,计算后缀表达式:遍历,数字就入栈,遇到op就pop两个数字(先出栈的是第二个op数!)形成ans入栈,最后的数字就是结果。完整实现减下方代码。注意的点如下:

  • 用struct 来作为queue和stack对象很巧妙,省去了一个stack,并且和queue一起操作浑然一体,用f标记是数字or OP可以在利用后缀表达式计算时,直接用f判断是数字还是op。
  • change函数用于转后缀表达式,就是利用上面的算法。需要注意的是每一步需要单独i++,我处理括号时就忘了;其次在遇到op时,很巧妙的做法就是直接先while(!s.empty() && opm[str[i]] <= opm[s.top().op]) 进行出栈入队,这里的while也就是if的功效了,反正最后都要op入栈,就在while后面统一入栈就好,十分简洁。
    • 用map做优先级映射,我将"("的优先级设为最低,就是为了在这里“遇到栈顶是“(”也入栈”。
    • 这里关键还是理解,op入栈必须是栈顶优先级比该op高才入栈,一样的优先级也是需要让先入栈的先计算
    • 最后需要将stack里op全部出栈
  • 队列可以直接赋值queue<node>p=q 我是实现show利用的
  • cal是计算后缀表达式,关键一点是先出栈的是第一个运算数!
  • main中
    • Input用了逗号表达式,就是在cin后,直接判断输入的值是否是终止标志,如果是直接退出循环,不需要再if break了
    • 用string的迭代器,再erase(it)来消除空格,或者用Index位置来消除
    • 要对stack清空初始化(因为,cal结束直接return top,还没pop最后那个ans。没有clear函数)
    • queue最后一定是空的,所以不需要清空
#include<iostream>
#include<string>
#include<stack>
#include<queue>
#include<map>
using namespace std;
struct node {
	double n;//值
	char op;//操作符
	int f;//用于cal:1是数字,0是op。后缀表达式没有括号
};

string str;
stack<node>s;//char类型也无法存储长数字,除非用两个栈
queue<node>q;
map<char,int> opm;//操作符优先级的映射

void change() {
	double n;
	node t;
	for(int i=0; i<str.length(); ) {
		if(str[i]>='0' && str[i]<='9') {
			t.f=1;//是数字
			t.n=str[i++]-'0';
			while(i<str.length() && str[i]>='0' && str[i]<='9') { //把完整的数字读入
				t.n=t.n*10+(str[i++]-'0');//so目前程序不能处理小数。
				//若想处理小数,则遇到小数点后的数字就要每次乘以0.1加到原数即可
			}
			q.push(t);
		} else if(str[i]=='(') { //包括下面,忘记i++了,没往下走,导致内存爆炸
			t.op='(';
			s.push(t);
			i++;
		} else if(str[i]==')') {
			while(!s.empty() && s.top().op!='(') { //好像这里不需要判空
				q.push(s.top());
				s.pop();
			}
			s.pop();//把左括号出栈
			i++;
		} else {
			t.f=0;//是op
//			if(opm[str[i]] <= opm[s.top().op])//为什么加上会报错?虽然没必要这句,但是也不应该报错啊
			while(!s.empty() && opm[str[i]] <= opm[s.top().op]) { //栈顶优先级不低,就要先出栈,因为同级的也是先进的先算,也要出栈。一定不要忘了判空!
				q.push(s.top());
				s.pop();
			}
			t.op=str[i++];//是左括号或优先级高于栈顶都入栈,其他的最终也要入栈
			s.push(t);//so 各种情况最后 都要入栈
		}
	}
	while(!s.empty()) {
		q.push(s.top());
		s.pop();
	}
}
void show_exp() {
	queue<node>p=q;//不然把q清空了,后面没法算
	while(!p.empty()) {
		if(p.front().f==1)
			cout<<p.front().n<<" ";
		else if(p.front().f==0)
			cout<<p.front().op<<" ";
		p.pop();
	}
	cout<<endl;
}
double cal() {
	node t;
	while(!q.empty()) {
		if(q.front().f==1) { //是数字
			s.push(q.front());
		}

		if(q.front().f==0) { //是op
			double t2=s.top().n;//先出栈的后运算!!
			s.pop();
			double t1=s.top().n;
			s.pop();
			if(q.front().op=='+') t.n=t1+t2;
			if(q.front().op=='-') t.n=t1-t2;
			if(q.front().op=='*') t.n=t1*t2;
			if(q.front().op=='/') t.n=t1/t2;
			t.f=1;//ans就是数字
			s.push(t);
		}
		q.pop();
	}
	return s.top().n;
}

int main() {
	opm['(']=0;//因为遇到左括号都入栈
	opm['+']=opm['-']=1;
	opm['*']=opm['/']=2;
	while(getline(cin,str), str!="0") { //是0时,程序结束
		for(string::iterator it=str.end(); it!=str.begin(); it--) { //去掉空格。倒着,因为最后有空格,起始没有
			if(*it==' ') str.erase(it);//不用迭代器 用index也可以用位置erase
		}
		while(!s.empty()) s.pop();
		change();
		show_exp();
		printf("%.2lf\n", cal());
	}
	return 0;
}

case3:判断出栈顺序是否合法

#include<iostream>
#include<stack>
using namespace std;
const int N=1005;
stack<int>st;
int a[N];

int main(){
	int m,n,T;
	cin>>m>>n>>T;
	while(T--){
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		while(!st.empty()) st.pop();
		int now=1;
		int ok=1;
		for(int i=1;i<=n;i++){
			st.push(i);
			if(st.size()>m) {
				ok=0;
				break;
			}
			while(!st.empty()&&st.top()==a[now]){//判空必须在前面写。。 
				st.pop();
				now++;
			}
		}
		if(ok && st.empty())
			printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

队列

case1:Mice and Rice 再做一遍。。。

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=100005;
struct node {
	int w;
	int r;//挂了的rank就是组数+1,因为每组一个人晋级
} mo[N]; //mo[i]表示第i个老鼠

int np,ng;//总人数,每组最大人数
int main() {
	cin>>np>>ng;
	for(int i=0; i<np; i++)
		cin>>mo[i].w;
	queue<int>q;
	for(int i=0,m; i<np; i++) {
		cin>>m;
		q.push(m);
	}

	int t=np;//每轮的人数,因为每次都是直接往q里继续放,用t来卡住该轮的人
	while(q.size()!=1) {
		int gn;//本轮的组数
		if(t%ng==0) gn=t/ng;
		else gn=t/ng+1;
		for(int i=0; i<gn; i++) {
			int k=q.front();
			for(int j=0; j<ng; j++) {
				if(i*ng+j>=t) break;//超过本轮人数了,卡断
				if(mo[q.front()].w > mo[k].w) {
					k=q.front();
				}
				mo[q.front()].r=gn+1;
				q.pop();
			}
			q.push(k);
		}
		t=gn;
	}
	mo[q.front()].r=1;
	for(int i=0; i<np-1; i++)
		cout<<mo[i].r<<' ';
	cout<<mo[np-1].r;
	return 0;
}

链表

静态链表

case1:1032 Sharing

#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
const int N=100005;
struct node{
	char data;
	int next;
	int ap;//出现 
}L[N];//L[i]表示i指向的元素 

int main(){
	int s1,s2,n;
	scanf("%d %d %d",&s1,&s2,&n);
	while(n--){
		int a,b;
		char c;
		scanf("%d %c %d",&a,&c,&b);
//		cin>>a>>c>>b;
		L[a].next=b;
		L[a].data=c;
	}
	for(int i=s1;i!=-1;i=L[i].next){//注意是i!--1,如果写next的话,如果刚开始就是-1,压根没有next呢 
		L[i].ap=1;
	}
	for(int i=s2;i!=-1;i=L[i].next){
		if(L[i].ap==1){
			printf("%05d",i);
			return 0;
		}
	}
	printf("-1");
	return 0;
}

case2:Linked List Sorting,用的静态链表,利用有效节点排序的方法,学一下,还没理解透

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=100005;
const int INF=0x7fffffff;
struct node {
	int addr,data,next;
	int flg;
} L[N];

int cmp(node a,node b){
	if(!a.flg || !b.flg) 
		return a.flg>b.flg;//大的放前面,也就是有效节点放前面 
	else return a.data<b.data;
}
int n,s1;//开始节点

int main() {
	cin>>n>>s1;
	for(int i=0; i<N; i++) L[i].flg=0; //无效节点
	for(int i=0; i<n; i++) {
		int a,b,c;
		cin>>a>>c>>b;
		L[a].addr=a;
		L[a].data=c;
		L[a].next=b;
	}
	int p=s1,cnt=0;
	while(p!=-1) {
		L[p].flg=1;//标注有效节点
		p=L[p].next;
		cnt++;
	}
	if(!cnt) printf("0 -1");
	else {
		sort(L,L+N,cmp);
		printf("%d %05d\n",cnt,L[0].addr);
		for(int i=0;i<cnt;i++){
			if(i!=cnt-1){//不到最后一点
				printf("%05d %d %05d\n",L[i].addr,L[i].data,L[i+1].addr);
			}
			else printf("%05d %d -1\n",L[i].addr,L[i].data);
		}
	}
	return 0;
}

 

哈夫曼树

是带权路径长度和最小的二叉树(最优二叉树),对应的编码是平均编码长度最短的编码。而带权路径和的求法,按照定义每个原始节点的的值 * 深度,求和,其实也就是所有中间节点的和,以此方便编程实现。

case1:哈夫曼树。关键是利用优先队列(小顶堆)动态维持升序排列,another case:搬水果 关键是想到用哈夫曼树!

#include<iostream>
#include<queue>
using namespace std;
priority_queue<int, vector<int>, greater<int>>q;//小顶堆 
int main()
{
	int n;
	while(scanf("%d", &n)!=EOF)
	{
		while(!q.empty()) q.pop();//其实是最后剩一个数,也可在每个case的末尾pop
		for(int i=0;i<n;i++)
		{
			int x;
			cin>>x;
			q.push(x);
		}
		int ans=0;
		while(q.size()>1)//最后剩下一个,so到1个元素时终止 
		{
			int a=q.top();
			q.pop();
			int b=q.top();
			q.pop();			
			q.push(a+b);
			ans+=a+b;//把每个中间结果相加就是 带权路径和
		}
		printf("%d\n", ans);
	}
	return 0;
}

二叉树(遍历、还原建树、完全二叉树)

case1:二叉树遍历(知前中 求后序遍历)。这是机好的例子,包括了Tree的数据结构、创建、遍历,以及由两遍历还原树的方法,还有需要注意的点如下:

  • (*a).b 等价于 a->b。so,node *T的T->data,而node T可以直接T.data
  • 数据结构就是data、2 child,需要层次遍历还有layer
  • node *creat()函数创建树节点。《王道》用loc记录Tree数组的index,主要是需要设置左右子树为NULL,虽然后面不会直接用到,但这本质是静态分配存储树的内存空间,我试了不用数组的话,在creat函数写node *T=new node后return T(这就是动态内存分配,需要多少开多少内存,静态必须实现申请到位)不然就要return &Tree[loc++];
  • 本题核心就是如何根据前中序遍历,还原tree:
    • 首先观察发现,方法其实就是根据前序遍历第一个元素,然后在中序遍历中找到对应的元素位置,其左右边分别是左右子树(可以根据位置在前序遍历中,确定左右子树相应的序列),最后对子树也用相同方法,递归实现tree的还原
    • 具体实现是通过build函数对str序列切分,通过str的坐标(如[str[s1],str[e1]])划分出当前要还原的部分的前中遍历,用rootidx标记中序遍历中的子树的根位置,画图看(就能方便看出 如rootidx-s2就是左子树序列的长度)

《算法笔记》 上面真的写的很好理解啊:这是后续+中序建树的

#include<iostream>
#include<cstring>
using namespace std;
struct node {
	char data;//根据要求改数据类型
	node *lchild;
	node *rchild;
	int layer;//有时候需要知道层数,eg层次遍历。本题暂时不需要
};
string str1,str2;//分别放前中遍历的字符串

node *creat() {
	node *T=new node;//一定要new,意思是分配内存 
	T->lchild=T->rchild=NULL;//左右子树为空
	return T;
}

void post_order(node *T) {//T是树根的指针
	if(T->lchild!=NULL)//不能写T.lchild,因为T是指针必须用->,若不是指针可以用T.xxx 
		post_order(T->lchild);
	if(T->rchild!=NULL)
		post_order(T->rchild);
	printf("%c", T->data);
}

node *build(int s1,int e1, int s2,int e2) {//str1中[s1,e1]为前序,str2中[s2,e2]为中序 
	node *T=creat();
	T->data=str1[s1];//前序遍历的第一个字符是root
	int rootidx;//root在str2的位置
	for(int i=s2; i<=e2; i++) {
		if(str2[i]==T->data) {
			rootidx=i;
			break;
		}
	}
	//利用中序遍历,来判断左右子树是否为空。	
	if(rootidx!=s2) //若rootidx就是最左边的元素,说明没有左子树了 
		//rootidx-s2就是左子树序列长度
		T->lchild=build(s1+1,s1+(rootidx-s2), s2,rootidx-1);
	if(rootidx!=e2)
		T->rchild=build(s1+1+(rootidx-s2),e1, rootidx+1,e2);
	return T;
}

int main() {
	while(cin>>str1>>str2) {
		node *T=build(0,str1.length()-1, 0,str2.length()-1);//build后,相当于将原数保存在Tree数组中了 
		post_order(T);//T是这棵树的root的地址 
		printf("\n");
	}
	return 0;
}

case1.5:Tree Traversals Again,用栈模拟树的遍历,有点巧吧。。说是push是先序,pop是中序

#include<iostream>
#include<stack>
#include<vector>
using namespace std;
struct node {
	int data;
	node* lchild,*rchild;
};
int n,fir=0;
vector<int>preS,inS;
stack<int>st;

node* create() {
	node *T=new node;
	T->lchild=T->rchild=NULL;
	return T;
}
void postOrder(node *T) {
	if(T->lchild!=NULL) postOrder(T->lchild);
	if(T->rchild!=NULL) postOrder(T->rchild);
	if(!fir) {
		cout<<T->data;
		fir=1;
	} else cout<<' '<<T->data;
}
node* build(int preL,int preR, int inL,int inR) {
	node *T=create();
	T->data=preS[preL];//cout<<T->data<<' ';
	int k;
	for(k=inL; k<=inR; k++) {
		if(inS[k]==T->data)
			break;
	}
	int numL=k-inL;
	if(k>inL) T->lchild=build(preL+1,preL+numL,inL,k-1);
	if(k<inR) T->rchild=build(preL+numL+1,preR,k+1,inR);
	return T;
}

int main() {
	cin>>n;
	for(int i=0,j; i<2*n; i++) {
		string a;
		cin>>a;
		if(a=="Push") {
			cin>>j;
			preS.push_back(j);
			st.push(j);
		} else {
			inS.push_back(st.top());
			st.pop();
		}
	}
	node *T=build(0,n-1,0,n-1);
	postOrder(T);
	return 0;
}

 

case2:二叉树遍历(先序建数 求中序遍历)

  • 先序遍历建树:a#b#cdef##### 的结果如右图 ;意思就是一个个读元素,按照先序顺序把元素添上去,当然了,像这里a都没有左子树的,就从右子树开始往下了,
  • 而编程就是通过递归实现:这里我用全局变量idx标记遍历到str哪个位置,每次递归都++即可,因为这里给的都是恰好能把每个子树都遍历到叶子节点,我就没有考虑idx会越str的界的情况
int idx;//标记str的index 
string s;
node *creat() {
	node *T=new node;//一定要new,意思是分配内存 
	T->lchild=T->rchild=NULL;//左右子树为空
	return T;
}
node *build(char c) {
	node *T=creat();//指针
	if(c=='#')
		T=NULL;
	else {
		T->data=c;
		T->lchild=build(s[++idx]);
		T->rchild=build(s[++idx]);
	}
	return T;
}
void in_order(node *T) {
	if(T->lchild!=NULL)
		in_order(T->lchild);
	printf("%c ", T->data);
	if(T->rchild!=NULL)
		in_order(T->rchild);
}
int main() {
	while(cin>>s) {
		idx=0;//输入每个样例都要初始化 
		node *T=build(s[0]);
		in_order(T);
		printf("\n");		
	}
	return 0;
}

完全二叉树

case1:树查找(完全二叉树,求指定层的所有元素)。一开始还惯性思维想怎么建树,可以在数组中利用2次方这个位置关系建树,不过后来想到其实利用位置关系来操作数组即可,非常简单。

#include<iostream>
#include<cmath>
using namespace std;
int a[1005];//2^10才1024,so最深11层
int n,x;
int main() {

	while(cin>>n,n!=0) {
		for(int i=1; i<=n; i++)//自己画一个完全二叉树,一般从1开始 
			cin>>a[i];
		cin>>x;
		if(pow(2,x-1)>n) printf("EMPTY");
		else for(int i=pow(2,x-1); i<pow(2,x); i++) {
			printf("%d ", a[i]);
			}
		printf("\n");
	}
	return 0;
}

case2:完全二叉树,共n个节点 求结点m所在的子树中一共包括多少个结点。其实是模拟,画图找规律,会发现这个主要需要知道,最大层数与m所在层数的差d,so最多也就把每一层的值加起来就行,由首项为1,公比为2的等比数列求和公式可以得到,最多有2^(d-1)-1个节点,但是还会有最下面一层不满的情况,这个可以根据,m的子树中最大的节点序号(找规律得其值为m*2^d+2^d-1)与n的值比较,若n小则说明不满,就需要先算到倒数第二层 2^d-1,再加上最后一层n-m*2^d+1。

另外,因为刚开始我可能TLE了(显示WA),原因可能是log或pow用的太多,于是我发现整个用的最多的就是2^d这一个pow,虽然各处用的原因不同(有的是求和公式化简来的,有的表示m的子树最下层最大节点数),但这个可以让程序只运行一次pow,从而所短时间,故用一个delta_l记录这个值,也是一个小技巧吧,最后也AC了,下面是我的code:

#include<iostream>
#include<cmath>//pow在这里 
#include<algorithm>//min在这里 
using namespace std;
typedef long long ll;
ll delta_l;//l1 l2都不要,只要知道差就行啦
ll pow_delta_l;//2^delta_l 这个会多次用到 
ll m,n,lastn;//要查元素序号,最大元素序号,求要查元素的子树中最大的元素序号(也就是在l2层上最大的序号

int main() {
	while(cin>>m>>n, m!=0) {
		ll sum=0;
		delta_l=ll(log(n*1.0/m)/log(2));//这个不能在ll外打括号!不然只是单纯的类型变了,值还是错的??
		pow_delta_l=pow(2,delta_l);
		lastn=m*pow_delta_l+pow_delta_l-1;//先根据2次方关系,算到最下面一层的第一个元素,然后加上剩下的元素书
		if(n>=lastn) sum=2*pow_delta_l-1;//等比数列的n是元素数量,不是差值
		else {
			sum+=pow_delta_l-1;//a1*(1-q^n)/(1-q)化简
			sum+=n-m*pow_delta_l+1;
		}
		printf("%lld\n", sum);
	}
	return 0;
}

 后来发现有更巧妙的做法,利用递归,一层层算,代码量极少,但耗时,不过递归的使用值得学习

#include<stdio.h>
int countNode(int m,int n){
    if(m>n) return 0;
    return countNode(2*m,n)+countNode(2*m+1,n)+1;
} 
int main(){
    int m,n;
    while(scanf("%d%d",&m,&n)!=EOF){
        if(m==0&&n==0) break;
        printf("%d\n",countNode(m,n));
    }
    return 0;
}

反转二叉树case1Invert a Binary Tree 

#include<iostream>
#include<queue>
using namespace std;
const int N=105;
struct node {
//	int data;
	int lchild,rchild;
	int isroot;
} tree[N];
int n,fir1=0,fir2=0;
void rev(int T) { //反转
	if(T==-1) return ;
	rev(tree[T].lchild);
	rev(tree[T].rchild);
	swap(tree[T].lchild,tree[T].rchild);
}
void BFS(int T) {
	queue<int>q;
	q.push(T);
	while(!q.empty()) {
		int t=q.front();
		q.pop();
		if(!fir1) {
			fir1=1;
			printf("%d", t);
		} else printf(" %d", t);
		if(tree[t].lchild!=-1) q.push(tree[t].lchild);
		if(tree[t].rchild!=-1) q.push(tree[t].rchild);
	}
}
void inOrder(int T) {
	if(tree[T].lchild!=-1) inOrder(tree[T].lchild);
	if(!fir2) {
		fir2=1;
		printf("%d", T);
	} else printf(" %d", T);
	if(tree[T].rchild!=-1) inOrder(tree[T].rchild);
}

int main() {
	cin>>n;
	for(int i=0; i<n; i++) tree[i].isroot=1;
	for(int i=0; i<n; i++) {
		char c	;
		cin>>c;
		if(c=='-') tree[i].lchild=-1;
		else {
			tree[i].lchild=c-'0';
			tree[c-'0'].isroot=0;
		}
		cin>>c;
		if(c=='-') tree[i].rchild=-1;
		else {
			tree[i].rchild=c-'0';
			tree[c-'0'].isroot=0;
		}
	}
	int root=0;
	for(; tree[root].isroot!=1; root++); //找到root
	rev(root);
	BFS(root);
	printf("\n");
	inOrder(root);
	return 0;
}

 

二叉排序/搜索树

case1:二叉排序树(按二叉排序树建树,然后输出前中后遍历)。主要就是利用要插的数<当前data值,则插入左子树否则插入右子树。编程实现有些技巧

  • node &insert函数有node *T和int x两个参数,而不是之前build函数不需要将*T传过来;且insert函数这里是在循环里一个个insert,不能像之前build在main一下就行,分析如下:
    • 排序树每次都要比较当前节点与要插的值x的大小,so函数必须有T;而每次要插的值只有一个,并且需要不断递归找到其真正的位置,所以每次递归时,要插的数不变so不能在递归时改变x,但根据某序遍历时,建树过程要每次str[++i]的(典型的就是先序建树,有#代表空节点,so每次都要移动str的位置;而根据前中遍历还原树时,也是每次会改变切分str的坐标;但这里要插的x一直不变,so要用循环一次次插
  • 刚开始T赋值为NULL,因为在后面判断子树时,也是要判断NULL的,所以这样能分辨出该数第一个元素是否为空,从而进行相应的操作,so不能在main就node *T=creat(),不然进insert不好判断了(当然了,这样写也OK,在insert函数里判断data是否已经赋值也可以判断树是否为空,eg将T->data初始化为INF?)
//没写struct node、creat函数、前中后序遍历函数了
node *insert(node *T, int x) {
	if(T==NULL) {//在判断到子树的时候,开始也是NULL。所以main里,整个树的第一个节点也是初始化为NULL 
		T=creat();
		T->data=x;
	} else if(x<T->data) {
		T->lchild=insert(T->lchild,x);
	} else if(x>T->data) {//本题说不要输出重复元素,不考虑相等的值。否则插哪都一样 
		T->rchild=insert(T->rchild,x);
	}
	return T;
}
int main() {
	int n,x;
	while(cin>>n) {
		node *T=NULL;//不写creat为了判断是第一个节点,在insert函数里面再creat
		for(int i=0; i<n; i++) {
			cin>>x;
			T=insert(T,x);//这样才能不断对 T更新
		}
		preorder(T); printf("\n");
		inorder(T); printf("\n");
		postorder(T); printf("\n");
	}
	return 0;
}

case2:二叉搜索树 判断两排序树是否相等(前中或中后两遍历结果相同->即说明两树相同)。这道题知道这个就不难做了,就是通过和case1一样的方法,通过排序树序列建树,然后分别将要比较的两棵树的 前中遍历str相加,比较两者是否相等即可。需要注意的是遍历时,注意不同的数要用不同的str分别记录,我这里用k来标记要算哪个树的前中遍历序列

//struct node、creat、insert和上面case完全一样
string s0,s;//记录排序树序列。每个样例对应每个s,都和s0比
string s1,s2;//记录两个树的 前中遍历序列
node *preorder(node *T,int k) {
	if(T!=NULL) {
		if(k==1) s1+=T->data;
		else if(k==2) s2+=T->data;
	}
	if(T->lchild!=NULL) preorder(T->lchild,k);
	if(T->rchild!=NULL) preorder(T->rchild,k);
}
node *inorder(node *T,int k) {
	if(T->lchild!=NULL) inorder(T->lchild,k);
	if(T!=NULL) {
		if(k==1) s1+=T->data;
		else if(k==2) s2+=T->data;
	}
	if(T->rchild!=NULL) inorder(T->rchild,k);
}
int main() {
	int n;
	while(cin>>n>>s0, n!=0) {
		s1="";
		node *T1=NULL;
		for(int i=0; i<s0.length(); i++) {
			T1=insert(T1,s0[i]);
		}
		preorder(T1,1);
		inorder(T1,1);
		while(n--) {
			s2="";
			cin>>s;
			node *T2=NULL;
			for(int i=0; i<s.length(); i++) {
				T2=insert(T2,s[i]);
			}
			preorder(T2,2);
			inorder(T2,2);
//			cout<<s1<<"   "<<s2<<endl;
			if(s1==s2) printf("YES\n");
			else printf("NO\n");
		}
	}
	return 0;
}

case3:1043Is It a Binary Search Tree,判断给定序列建的BST是不是BST或者镜像BST的preOrder,如果是,输出该tree的postOrder

  • 求镜像tree的遍历就是在原本某序遍历的基础上,先rchild再lchild即可,所以这题两个方案:
    • 建两个tree,一个按照升序,一个降序,分别处理就行,traverse函数还是前后遍历两个
    • 一个tree,只是traverse的时候要写mirror的前后遍历函数
#include<iostream>
#include<vector>
using namespace std;
struct node {
	int data;
	node *lchild;
	node *rchild;
};
vector<int>ori,pre,mir_pre,ans;//ans放正确的树的后序遍历
node *create() {
	node *T=new node;
	T->lchild=T->rchild=NULL;
	return T;
}
node *insert(node *T, int x) {
	if(T==NULL) {
		T=create();
		T->data=x;
	} else if(x<T->data)
		T->lchild=insert(T->lchild,x);
	else
		T->rchild=insert(T->rchild,x);
	return T;
}
void preOrder(node *T) {
	if(T!=NULL) pre.push_back(T->data);
	if(T->lchild!=NULL) preOrder(T->lchild);
	if(T->rchild!=NULL) preOrder(T->rchild);
}
void mir_preOrder(node *T) {
//	swap(T->lchild,T->rchild);
	if(T!=NULL) mir_pre.push_back(T->data);
	if(T->rchild!=NULL) mir_preOrder(T->rchild);
	if(T->lchild!=NULL) mir_preOrder(T->lchild);
}
void postOrder(node *T) {
	if(T->lchild!=NULL) postOrder(T->lchild);
	if(T->rchild!=NULL) postOrder(T->rchild);
	if(T!=NULL) ans.push_back(T->data);
}
void mir_postOrder(node *T) {
	if(T->rchild!=NULL) mir_postOrder(T->rchild);
	if(T->lchild!=NULL) mir_postOrder(T->lchild);
	if(T!=NULL) ans.push_back(T->data);
}
int main() {
	int n,x;
	cin>>n;
	node *T=NULL;
	for(int i=0;i<n;i++) {
		scanf("%d",&x);
		ori.push_back(x);
		T=insert(T,x);
	}
	preOrder(T);
	mir_preOrder(T);
	if(ori!=pre && ori!=mir_pre) {
		printf("NO");
		return 0;
	}
	if(ori==pre) postOrder(T);
	else if(ori==mir_pre) mir_postOrder(T);
	printf("YES\n");
	for(int i=0; i<n-1; i++)
		printf("%d ",ans[i]);
	printf("%d",ans[n-1]);
	return 0;
}

case1:1090Highest Price in Supply Chain,好题!关键是懂得将其抽象为树。下面有DFS与BFS两方法

#include<iostream>
#include<cmath>
#include<queue>
using namespace std;
const int N=100005;
vector<int>tree[N];//范围就是0~n-1
int n,maxDep=0,num=0;//深度不能init为-1,因为有0的情况 
double p,r;
void DFS(int T,int dep) { //如果用BFS,可以直接
	if(tree[T].size()==0) { //到叶子Node了
		if(dep>maxDep) {
			maxDep=dep;
			num=1;
		} else if(dep==maxDep) num++;
		return ;
	}
	for(int i=0; i<tree[T].size(); i++) {
		DFS(tree[T][i],dep+1);
	}
}
int layer[N]= {0};
void BFS(int T) {
	queue<int>q;
	q.push(T);
	while(!q.empty()) {
		int t=q.front();
		q.pop();
		for(int i=0; i<tree[t].size(); i++) {
			layer[tree[t][i]]=layer[t]+1;
			maxDep=layer[tree[t][0]];
			q.push(tree[t][i]);
		}
	}
}
int main() {
	cin>>n>>p>>r;
	int root;
	for(int i=0,a; i<n; i++) {
		cin>>a;
		if(a==-1) root=i;
		else tree[a].push_back(i);
	}
//	DFS(root,0);//只要知道最大层数就行了,最后pow一下
	BFS(root);
	for(int i=0; i<n; i++) {
		if(layer[i]==maxDep) num++;
	}
	printf("%.2lf %d", p*pow(1+r/100,maxDep),num);
	return 0;
}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值