6.30 POJ 刷题记录【树、堆】

目录

4079 二叉搜索树

二叉搜索树的基本操作,包括创建树、遍历输出树

4078 实现堆结构

堆的基本操作,包括最小堆堆的建立和堆纠正

3720 文本二叉树

建树然后先序、中序、后序遍历输出

1577:Falling Leaves

建树(字典序)然后输出

 2255:重建二叉树

根据先序和中序遍历建树,然后后序遍历输出

2309:BST

二叉搜索树的最左节点和最右节点,找规律

2499:Binary Tree

根据规律倒着推树

2665:Trees

水题

4081:树的转换

树做的话好复杂 动态规划不会 递归好难找 不会!!!

POJ其他相关练习题


4079 二叉搜索树

二叉搜索树的基本操作,包括创建树、遍历输出树
#include<cstdio>
#include<cstdlib>

typedef struct Node{
	int data;
	Node *left;
	Node *right;
}*Tree; //Tree代表 Node* 

Tree Insert(Tree BST,int x){
	if(BST==NULL){
		BST = (Tree)malloc(sizeof(Node));
		BST->data = x;
		BST->left=BST->right=NULL;
	}
	else{
		if(x<BST->data){//左节点 
			BST->left = Insert(BST->left,x); 
		}
		else if(x>BST->data){//右节点
			BST->right = Insert(BST->right,x); 
		} 
		//数据已存在的话就不插入 
	}
	return BST; 
}
//void没有返回  bool返回对错  double 返回数据类型 
void Print(Tree BST){
	if(BST!=NULL){
		printf("%d ",BST->data);
		Print(BST->left);
		Print(BST->right);
	}
}
// 前序:中左右  中序:左中右  后序:左右中 

int main(){
	Tree bst=NULL;
	int x;
	/*插入*/
	while(scanf("%d",&x)!=EOF){
		bst = Insert(bst,x);
	}
	/*遍历*/
	Print(bst);
	return 0;
} 

4078 实现堆结构

堆的基本操作,包括最小堆堆的建立和堆纠正
//实现堆的基本操作
#include<iostream>
#include<vector>
using namespace std;
int N,ans=0;
vector<int>val;
void DownHeap(){//堆纠正  
	int k=1;
	while(2*k<=ans){//如果有左孩子 k的左孩子为2k 右孩子为2k+1 
		int j=2*k;//指向左孩子
		if(j<ans&&val[j]>val[j+1])//k的左右孩子进行相比较 
			j=j+1;//若右孩子小于左孩子 j指向右孩子 
		if(val[k]<=val[j])//比较小的孩子小 
			break; 
		else//说明父亲节点k要比孩子j大 需要交换 
			swap(val[k],val[j]);//交换位置 
		k=j;//k交换到新的位置,继续向下比较,一直沉到底部 
	}
}
void CreateHeap(int n){//建堆 
	val[++ans]=n;
	int i=ans;
	while(1){
		if(i<=1)
			break;
		if(val[i/2]>val[i])
			swap(val[i/2],val[i]);//进行互换 
		i=i/2;
	}
}
int main(){
	cin>>N;
	val.resize(N+2);
	int type,num;
	for(int i=0;i<N;i++){
		cin>>type;
		if(type==1){
			cin>>num;
			CreateHeap(num); 
		}
		else{
			cout<<val[1]<<endl;//最小值 小顶堆 最小值在第一个
			swap(val[1],val[ans]);
			--ans;//交换之后 长度减一 相当于删除这个最小的元素 在赋值的话会把原来ans位置的元素覆盖掉 不影响后面程序执行 
			DownHeap();//进行堆调整
		}
	}
	return 0;
} 

3720 文本二叉树

建树然后先序、中序、后序遍历输出,看不懂这个题意啊,什么鬼
//http://bailian.openjudge.cn/practice/3720
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
char a[110];
int n,c;//c是-的个数  
struct Node{
	char val;
	Node *left;
	Node *right;
};
Node *CreateTree(Node *x, int k){   //k表示第几层 
	if(a[0]=='0') return x;  
	int len=strlen(a);
	c=len-1;
	if(c<k) return x; //插入右子树 
	if(c==k&&x==NULL){
		x=new Node;
		x->val=a[len-1];
		x->left=NULL;
		x->right=NULL;
		cin>>a;
		x->left=CreateTree(x->left,k+1); //递归调用返回到这里 
	} 
	if(c==k&&x) return x;  //返回到父节点去调用右子树
	if(c>k) x->right=CreateTree(x->right,k+1); //右子树的插入 
	return x;
}
void PreTree(Node *x){
	if(x->val!='*')cout<<x->val;
    if(x->left!=NULL)PreTree(x->left);
    if(x->right!=NULL)PreTree(x->right);
}

void MidTree(Node *x){
    if(x->left!=NULL)MidTree(x->left);
    if(x->val!='*')cout<<x->val;
    if(x->right!=NULL)MidTree(x->right);
}

void BackTree(Node *x){
    if(x->left!=NULL)BackTree(x->left);
    if(x->right!=NULL)BackTree(x->right);
    if(x->val!='*')cout<<x->val;
}

int main(){
	cin>>n;
	while(n--){
		cin>>a;
		Node *root=NULL;
		root=CreateTree(root,0);
		PreTree(root);
		cout<<endl;
		BackTree(root);
		cout<<endl;
		MidTree(root);
		cout<<endl<<endl;
	}
}

这段代码中的CreateTree函数通过分析给定的树的文本表示法的规律来构建树的结构。下面是对各种情况的解释和原理说明:

1) 如果行号c大于层次k,则插入右子树,并递归调用CreateTree函数来构建右子树。这是因为根据文本表示法的规律,当前节点的行号大于层次k,说明该节点是当前层次的右子节点。需要将下一行的内容作为右子树的构建输入,并递归调用CreateTree函数来构建右子树。

2) 如果行号c等于层次k且当前节点为空,说明要插入左子树。这是因为根据文本表示法的规律,当前节点的行号等于层次k,且当前节点为空,说明下一行的内容是当前节点的左子节点。需要将当前行的最后一个字符作为节点的值val,创建一个新的节点,并将其赋给当前节点指针x。然后,从输入中读取下一行,并递归调用CreateTree函数来构建左子树。

3) 如果行号c等于层次k且当前节点不为空,则返回到父节点去调用右子树的构建。这是因为根据文本表示法的规律,当前节点的行号等于层次k,且当前节点不为空,说明当前节点的左子树已经构建完成,需要返回到父节点来构建右子树。在返回之前,需要处理下一行的内容。

4) 如果行号c小于层次k,则直接返回空指针。这是因为根据文本表示法的规律,当前节点的行号小于层次k,说明该节点在树的层次中没有合适的位置来插入子节点,因此返回空指针。

通过以上的逻辑判断和递归调用,CreateTree函数可以依据树的文本表示法来构建树的结构,并返回根节点的指针。这样就实现了根据给定的文本表示法构建树的功能。

1577:Falling Leaves

建树(字典序)然后输出
#include <cstdio>
#include <cstring>
#include <vector>
#include <iterator>
#include <iostream>
#include <algorithm>
// bits/stdc++.h会编译错误
using namespace std;

struct Node
{
	char data;
	Node *left;
	Node *right;
};
typedef struct Node* Tree;

string temp, str;

Tree Insert(Tree BST,char x)
{
	if(BST==NULL)
	{
		BST = (Tree)malloc(sizeof(Node));
		BST->data = x;
		BST->left=BST->right=NULL;
	}
	else
	{
		if(x<BST->data)//左节点 
		{
			BST->left = Insert(BST->left,x); 
		}
		else if(x>BST->data)//右节点
		{
			BST->right = Insert(BST->right,x); 
		} 
		//数据已存在的话就不插入 
	}
	return BST; 
}
Tree createBST(){  //建树
    Tree root = NULL;
    for(int i=0;i<str.length();i++)
    	root = Insert(root,str[i]);
    return root;    //返回根结点
}


void Print(Tree BST)
{
	if(BST!=NULL)
	{
		printf("%c",BST->data);
		Print(BST->left);
		Print(BST->right);
	}
}

int main(){
    while(cin >> temp){  //输入字符串
        if(temp != "*" && temp != "$"){
            //如果输入的字符串不是组结束符或结尾符
            str += temp;    //将出入的字符串加到str字符串尾部
            continue;
        }
        //如果输入的时组结束符或结尾符
        reverse(str.begin(),str.end()); //将str数组反转
        Tree bst = createBST();   //建树
        str = "";   //将str清空
        Print(bst); //输出前序遍历
        printf("\n");
        if(temp == "$")//如果是结尾符跳出循环
            break;
    }
    return 0;
}

 2255:重建二叉树

根据先序和中序遍历建树,然后后序遍历输出

方法一:找规律

#include<stdio.h>
#include<string.h>
void bulid(int n,char s1[],char s2[])//n是树的节点数,s1是记录先序遍历,s2中序遍历 
{
    if(n<=0)  return;//没有节点了,找到边界return 
    int p=strchr(s2,s1[0])-s2;
    // strchr(s2,s1[0])是s2的地址+s1[0]第一次出现的位置i。s2是s2的地址 
    //所以p是 s1[0]第一次出现在s2中的位置
    bulid(p,s1+1,s2);
    //数的节点数为s1[0]在s2中之前的所有节点。s1+1是把s1数组整体向前移动一位(s1[0]是以前的s1[1],s1[1]改为以前s1[2]...
    //以前s1[0]是根,现在不需要,所以删除。要它左子树的根节点 
    //s2不变(假设中序遍历中根节点的位置是a,那么后序遍历中0~a个点是树的左子树节点) 
    bulid(n-p-1,s1+p+1,s2+p+1);
    //数的节点数为s1[0]在s2中之后的所有节点(因为数组从0开始,所以-1)
    //以前s1[0]是根,现在不需要,左子树也不需要,所以都删除。要它右子树的根节点
    //s2+p+1(假设中序遍历中根节点的位置是a,a~n-2(数组从0开始)个点是树的右子树节点 ) 
    printf("%c",s1[0]);//输出 
}
int main()
{
    char s1[10007],s2[10007];//s1是树的前序遍历,s2是树的中序遍历 
    while(scanf("%s %s",s1,s2)!=EOF)//多组数据 
    {
        bulid(strlen(s1),s1,s2); 
        printf("\n");//记得输出\n 
    }
    return 0;
}/*
先序遍历第一个节点一定是根节点。
中序遍历根节点左侧就是树的左子树,右侧就是树的右子树。
后序遍历最后一个节点一定是根节点,而假设中序序列里根节点位置是m,那么
后序序列里0至m-1个节点是树的左子树节点,m至倒数第二个点是树的右子树节
点。
*/

方法二:根据二叉树的基本性质

#include<iostream>
#include<string.h>
using namespace std;
//http://bailian.openjudge.cn/practice/2255/
//每次根据前序中找到根节点,然后对中序左右分别处理直到空 
char a[10000],b[10000];
int len1,len2;
void f(int l,int l2,int r2){    //l1是前序左端点,l2,r2是中序左右端点
    if(l2==r2)return;  //子树为空 
    char k=a[l];//前序中的第一个值是根节点
    int rr;
    for(int i=l2;i<r2;i++){
    //从中序中找根节点,分出左右子树
        if(b[i]==k){
            rr=i;
            break;//rr左边是左子树,右边是右子树 
        }
    } 
    f(l+1,l2,rr);
    f(l+(rr-l2+1),rr+1,r2);
    cout<<k;
}
int main(){
    while(cin>>a>>b){
        len1=strlen(a);
        len2=strlen(b);
        f(0,0,len2);
        cout<<endl;
    }
} 

这段代码是用来根据给定的先序遍历和中序遍历构建出对应的二叉树。它首先读入先序遍历和中序遍历的序列,然后调用递归函数来构建二叉树。递归函数中,首先找到先序遍历的第一个节点作为根节点,然后在中序遍历中找到根节点的位置,将其左侧作为左子树,右侧作为右子树。然后将左右子树分别作为新的子问题,继续递归构建。最后输出根节点即可。

△构建二叉树的过程可以分为以下几个步骤

1. 在递归函数中,传入三个参数:l表示先序遍历的起始位置,l2表示中序遍历的左边界,r2表示中序遍历的右边界。

2. 首先判断中序遍历的左边界和右边界是否相等,如果相等,则说明当前子树为空,直接返回。

3. 取出先序遍历的第一个元素a[l],即为当前子树的根节点。

4. 在中序遍历中找到根节点的位置,即在区间[l2, r2)中找到值为a[l]的元素的位置rr。

5. 根据根节点在中序遍历中的位置,划分出左子树和右子树的中序遍历区间。左子树的中序遍历区间为[l2, rr),右子树的中序遍历区间为[rr+1, r2)。

6. 根据左子树的中序遍历区间[l2, rr)的长度,可以在先序遍历中找到左子树的先序遍历区间为[l+1, l+(rr-l2+1))。

7. 递归调用构建左子树,传入参数为左子树的先序遍历区间[l+1, l+(rr-l2+1)),中序遍历区间[l2, rr)。

8. 根据右子树的中序遍历区间[rr+1, r2)的长度,可以在先序遍历中找到右子树的先序遍历区间为[l+(rr-l2+1), l2+1, l2+(rr-l2+1)+1)。

9. 递归调用构建右子树,传入参数为右子树的先序遍历区间[l+(rr-l2+1), l2+1, l2+(rr-l2+1)+1),中序遍历区间[rr+1, r2)。

10. 输出根节点a[l]。

通过不断递归构建左子树和右子树,最终可以构建出完整的二叉树。

△怎么后序遍历输出的结果?

在代码中,输出根节点的位置在递归函数的最后,即在左子树和右子树都构建完成后输出。这保证了先输出左子树的后序遍历结果,再输出右子树的后序遍历结果,最后输出根节点。这样就能够获得整个二叉树的后序遍历结果。

具体来说,在递归函数中,在递归调用构建左子树后,再调用递归构建右子树。然后在输出根节点之前,先输出左子树的后序遍历结果(调用递归函数传入左子树的先序遍历区间和中序遍历区间)。然后再输出右子树的后序遍历结果(调用递归函数传入右子树的先序遍历区间和中序遍历区间)。最后,输出根节点。

这样,通过递归的方式,每次先处理左子树,再处理右子树,最后处理根节点,就可以得到二叉树的后序遍历结果。

2309:BST

二叉搜索树的最左节点和最右节点,找规律

(26条消息) POJ_2309_BST_Kirigaya__Kazuto的博客-CSDN博客

#include <cstdio>
#include <math.h>
using namespace std;
int main(){
    int T,n;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        int k=n&(-n);
        printf("%d %d\n",n-k+1,n+k-1);
    }
    return 0;
}

你会发现以n为根的子树的左下限就是把n的二进制值的最后一位1移动到最后一位,右下限就是把n的最后一位1到最后一位之间全部替换成1.

那么现在就简单了首先就是求出最后一个1所在的位置  k = (n & -n);

然后左下限为 n-k+1;右下限为n+k-1;

2499:Binary Tree

根据规律倒着推树
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <sstream>

using namespace std;

typedef long long ll;

const int inf = 0x3f3f3f3f;
const int maxn = 30;

int main()
{
    int t,cnt = 0;
    cin>>t;
    while(t--){
        int a,b;
        cnt++;
        cin>>a>>b;
        ll left = 0,right = 0;
        while(a != 1&&b != 1){
            if(a >b){
                left += a / b;
                a = a % b;
            }
            else{
                right += b / a;
                b = b % a;
            }
        }
        if(a == 1){
            right += b - 1;
        }
        if(b == 1){
            left += a - 1;
        }
        cout<<"Scenario #"<<cnt<<":"<<endl;
        cout<<left<<' '<<right<<endl<<endl;
    }
    return 0;
}

(26条消息) poj 2499 Binary Tree_poj2499_Cu1ii*的博客-CSDN博客

2665:Trees

水题
#include<stdio.h>
int main()
{
    int l,n;
    while(scanf("%d%d",&l,&n),l)
    {
        int res=l+1;//树的数量 
        int l,r;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&l,&r);
            res-=(r-l+1);//减去修路区域的树 
        }
        printf("%d\n",res);
    }
    return 0;
}

4081:树的转换

树做的话好复杂 动态规划不会 递归好难找 不会!!!

方法一:

用先根遍历创建一棵树,首先构建一个节点类型的指针栈,首先将根(root)压入栈底,当输入字符为'd'时,创建一个新的节点temp,将栈顶元素赋值给father,作为新节点的父亲节点。如果father的lchild == NULL,说明temp是father的第一个孩子节点。如果father的lchild !=NULL,则通过循环找到father->lachild的最右端,将temp赋值给它。当输入字符为'u'时,回溯到父亲节点,栈执行pop操作。

      栈中所有曾经存在的元素都有可能是新节点的父亲节点,栈中元素个数最多的时候也是这棵树最深的时候。如果字符串中du顺序正确的话,最后栈中只剩下root节点。

(26条消息) POJ4081树的转换以及deque iterator not dereferencable 问题_独孤金泽的博客-CSDN博客

#include<iostream>
#include<stack>
using namespace std;
typedef struct _Node
{
	int value;
	struct _Node *lchild, *rchild;
}Node;
stack<Node *> s;
const int Max = 20010;//最多一万个节点,也就是说10000个d和10000个u
unsigned int max_stack_size = 0;//这个值代表数的深度
Node* main_solution(char *c1, Node *root,unsigned int &max_stack_size)
{
	//Node *father;
	//Node *child;
	char *c = c1;
	int value = 1;
	while (*c != '\0')
	{
		if (*c == 'd')
		{	
			Node *temp = (Node *)malloc(sizeof(Node));
			temp->lchild = NULL;
			temp->rchild = NULL;
			temp->value = value++;
 
			Node *father = s.top();//栈顶元素是父亲节点
			if (father->lchild == NULL)//temp节点是father节点的第一个子节点
			{
				father->lchild = temp;//
			}
			else//temp节点不是其父节点的第一个子节点
			{
				Node *child = father->lchild;
				while (child->rchild) { child = child->rchild; }
				child->rchild = temp;
			}
			s.push(temp);
			if (s.size() > max_stack_size) { max_stack_size = s.size(); }//统计栈曾经达到的最多元素个数
			
		}
 
		else//回溯到父节点
		{
			s.pop();
		}
		c++;//移动到下一个字符
	}
	return root;//这棵数已经建好
}
//统计左子右兄树的深度(递归计算)
int Cal_Tree(Node *root)
{
	int num_l, num_r;
 
	if (root == NULL){ return 0; }
	else
	{
		num_l = 1 + Cal_Tree(root->lchild);
		num_r = 1 + Cal_Tree(root->rchild);
		if (num_l > num_r) { return num_l; }
		else { return num_r; }
	}
}
//内存释放函数(先根遍历释放内存)
void DELETE(Node *root)
{
	if (root)//root不为NULL时
	{
		DELETE(root->lchild);//左分支子树已经释放完毕
		DELETE(root->rchild);//右分支子树已经释放完毕
		delete root;//释放指针变量对应的内存空间
		root = NULL;//防止指针乱跑
	}
	//root等于NULL时,啥都不做
}
int main()
{
	Node *root = (Node *)malloc(sizeof(Node));
	char dic[Max];
	cin.getline(dic, Max, '\n');//可以接受空格,当输入流遇到换行时,停止输入
	//cin >> dic;
	/*   初始化根节点   */
	root->lchild = NULL;
	root->rchild = NULL;
	root->value = 0;//根节点的标号是0
 
	s.push(root);
	root = main_solution(dic, root, max_stack_size);
	int last_transform = Cal_Tree(root) - 1;
	int pre_tansform = max_stack_size - 1;
	cout << pre_tansform << " => " << last_transform << endl;
	//DELETE(root);
	system("pause");
	return 0;
}

方法二:DFS

/* Bailian4081 树的转换 */

#include <bits/stdc++.h>

using namespace std;

const int N = 10000;
char s[3 * N];
int k, r1, r2;

void dfs(int d1, int d2)
{
    r1 = max(r1, d1);
    r2 = max(r2, d2);

    int cnt = 1;
    while(s[k]) {
        if(s[k] == 'd') {
            k++;
            dfs(d1 + 1, d2 + cnt++);
        } else {
            k++;
            break;
        }
    }
}

int main()
{
    scanf("%s", s);

    r1 = r2 = -1;
    k = 0;
    dfs(0, 0);

    printf("%d => %d\n", r1, r2);

    return 0;
}

方法三:树的基础结构

#include <iostream>
#include<string.h> 
#include<stdio.h>
using namespace std; 
const int M=41000;//用的比较多 
//http://bailian.openjudge.cn/practice/4081/
//神奇的题,想了很久递归去构造树,结果做不出来,下面是参考的 
//使用dp来做,当前节点在二叉树表示中的高度=父节点高度+左兄弟数目+1
int h1=0,h2=0,len;
int f[M],dep[M],lch[M];//f数组是保存的上次的值,相当于对一个个字符dp 
char a[M];
int main(){
    cin>>a;
    len=strlen(a);
    int h=0;//用于循环过程中控制高度 
    for(int i=1;i<=len;i++){
        //i其实存储的是i-1节点的信息
        char tmp=a[i-1];
        if(tmp=='d'){
            h++;
            h1=max(h1,h);
            f[i]=i-1;
            dep[i]=dep[i-1]+lch[i-1]+1;
            lch[i-1]++;//对于i来讲,有一个左兄弟 
            h2=max(h2,dep[i]); 
        } 
        else if(tmp=='u'){
            h--;
            f[i]=f[f[i-1]];
            dep[i]=dep[f[i-1]];
            lch[i]=lch[f[i-1]];
        }
    }
    printf ("%d => %d\n",h1, h2);
} 

POJ其他相关练习题

//4080 4081 4082 2788 2665 2483 2486 2499 2309 2255 2054  
//1848 1760 1610 1635 1645 1105 1610

都是和树有关的,时间不够了(或者太难了做不了了)就做这些,有时间的可以接着做

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值