PAT----树----完全二叉树(堆)

________________________________________________________

二.完全二叉树+堆


________________________________________________________

数组型树及:完全二叉树

1110

// 1.完全二叉树二层序遍历的关系: 层序遍历时,只要有以一个叶子不满, 并且之后还有有叶子的节点就不是完全二叉树
// 2.注意:只有两个节点, 并且根节点左子树为空的特殊情况
// 3.部分样例不过关不要崩:检查自己代码有没有错, 检查边界情况, 检查根本的逻辑有误错误
// 4.进一步熟练数组型树, 层序遍历
// 5.int和string的互转
//#include <bits/stdc++.h>



//1.核心: 判断一棵树是否完全二叉树:  有结点没有孩子, 它之后的节点有孩子, 就不是完全二叉树
//        (1)判断的顺序是需要的, 所以用层序遍历:  初始化 + 大循环(pop + 干正事 + push) + 后续输出
//        (2)小心只有两个节点的情况
//        (3)核心判断用flag
//        (4)说让输出完全二叉树的最后一个节点: 即levelOrder的最后一个current

//2.string和int的相互转换
//    (1)需要的两个函数都在<string>头文件里
//    (2)int转string:  [to_string]----str=to_string(数字),  不是to_String哦
//    (3)string转inti: [stoi]----in=stoi(string)

//3.数据结构: 数组形树
//    (1)看题目已给数据, 知是数组形树:  struct node + vector<node>
//    (2)需要一个循环: 寻找根节点





//1.数组型树, 第一反应是会蒙的, 所以再来一遍
//          (1)struct node: 
//              1)左右节点需要构造函数赋初值-1
//              2)可以写两个构造函数, 一个有参的, 一个无餐的 
//          (2)构造树: 
//              1)可以用含参构造函数直接构造, 然后插入树中 
//              2)同时,记录mark以标记非根节点 
//          (3)for循环寻找根节点(有时候明确说明0为根节点)

//2.层序遍历寻找最后叶子节点: 
//          (1)最后current的值就是 last;
//3.queue还有.back()这种操作, 和front()相对应 
//4.再一遍: 判断一个树是否是完全二叉树 



//!!!!!!!!!!!!这道题真的醉了, 你的习惯太差了, if else语句就不能好好写吗 
//      两个if语句并列   和   if else语句, 完全是不同的东西  

//核心:  判断完全二叉树 + 数组型树

//1.数组型树:
//      (1)需要node:  需要构造函数
//      (2)储存在vector中 
//      (3)需要寻找根节点 

//2.完全二叉树的判断: 
//             注意边界条件:    根节点: 左子树为空, 右子树不为空 

#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<algorithm>

using namespace std;

int n;

struct node {
	int left, right;

	node() {
		left = -1;
		right =-1;
	}
	node(int x, int y) {
		left = x;
		right = y;
	}
};

vector<node>tree;
vector<int>mark;

int sToi(string str) {
	if (str == "-")return -1;
	else return stoi(str);
}

void BFS(int root) {
	queue<int>q;
	q.push(root);
	int flag = 0, current;

	if (tree[root].left == -1 && (tree[root].right != -1))flag = 2;

	while (!q.empty()) {
		current = q.front();
		q.pop();

		if (flag == 0 && (tree[current].left == -1 || (tree[current].right == -1)))flag = 1;
		else if (flag == 1 && (tree[current].left != -1 || (tree[current].right != -1)))flag = 2;

		if (tree[current].left != -1)q.push(tree[current].left);
		if (tree[current].right != -1)q.push(tree[current].right);
	}

	if (flag==2)printf("NO %d\n", root); 
	else printf("YES %d\n", current);

}

int main() {
	scanf("%d", &n);
	mark.resize(n);
	for (int i = 0; i < n; i++) {
		string a, b;
		cin >> a >> b;
		node tmpNode(sToi(a), sToi(b));
		tree.push_back(tmpNode);
		if (tmpNode.left != -1)mark[tmpNode.left] = 1;
		if (tmpNode.right != -1)mark[tmpNode.right] = 1;
	}

	//找到根
	int root;
	for (root = 0; root < mark.size(); root++)
		if (mark[root] == 0)break;

	//判断是否完全二叉树 
	BFS(root);


	return 0;
}

完全二叉树+堆的判断+后序遍历(柳神代码, 自己已敲)

1147

// 1、记住第一点:   完全二叉树就是层序遍历用的那个
// 2.积累 层序下标树的 堆判断方法: i==2, if(tree[i/2]>tree[i])min==false;  判断大顶堆亦然
// 3.积累 层序下标树的 先序后续遍历:如果是从1开始, 则index*2, index*2+1


//1.核心:  大顶堆, 小顶堆, 是否堆的判断
//        (1)maxFlag=1, minFlag=1
//        (2)tree[i]>tree[i/2]:  maxFlag=0;
//        (3)tree[i]<tree[i/2]:  minFlag=1;

//2.2*n, 2*n+1访问树 前序和后序遍历终止条件:  index>n

//3.2*n, 2*n+1即层序建树, 直接按顺序放入即可




//1.核心:  完全二叉树, 堆的判断
//          (1)从第二个节点,循环到最后, 访问父节点, 进行比较

//2.完全二叉树, 子节点和父节点的访问 
//          (1)起点从1: 
//              1)子节点:   i*2    i*2+1   
//              2)父节点:   i/2    i/2 
//          (2)起点从0: 
//              1)子节点:   (i+1)*2-1    (i+1)*2 
//              2)父节点:   (i-1)/2      (i-1)/2

#include <iostream>
#include <vector>



using namespace std;

int times, n, flag;
vector<int>tree;

void postOrder(int index) {
	if (index > n)return;

	postOrder(index * 2);
	postOrder(index * 2 + 1);
	if (flag) {
		flag = 0;
		printf("%d", tree[index]);
	}
	else printf(" %d", tree[index]);
}

int main() {
	scanf("%d %d", &times, &n);
	while (times--) {
		tree.clear();
		tree.resize(n + 1);
		int isMax = 1, isMin = 1;
		flag = 1;
		for (int i = 1; i <= n; i++) scanf("%d", &tree[i]);
			
		for (int i = 2; i <= n; i++) {   //画个树就知道, 不管刚好还是int下取整, 都对应到父节点
			if (tree[i] > tree[i / 2])isMax = 0;
			if (tree[i] < tree[i / 2])isMin = 0;
		}

		if (isMax)printf("Max Heap\n");
		else if (isMin)printf("Min Heap\n");
		else printf("Not Heap\n");

		postOrder(1);
		printf("\n");
	}

	return 0;
}

回溯+完全二叉树+堆的判断

1155

//1.对于完全二叉树:  如果下标从1开始, index*2>n起就是叶子节点
//2.要把镜像处理作为一件比较简单的事情
//3.要积累回溯的思想: push和pop
//4.再次熟悉完全二叉树数组的堆判断


//1.编译错误:  max, min不能乱用, PAT编译器会出错。    同理: 出现编译错误时, 检查自己是否使用了会出现异义的变量名

//核心:  堆的判断 + 从根节点访问路径记录 

//2.从根节点记录路径:  
//      (1)先序遍历: 每次插入节点值 
//      (2)到达叶子节点: 则将路径插入结果数组。   (叶子节点的判断:  (2*index>n)&&(2*index+1>n))

//3.堆的判断:  遍历每条路径, 如果不是单增(或者单减)就不是  min(或max)

//4.“堆” 经常使用 “完全二叉树判断”

//5. "DFS" 和 “先序遍历的关系”

//6.(再一次)使用rank思想, 处理一个序列 紧挨着 两者的关系


#include <iostream>
#include <vector>
#include <string>
#include <map>


using namespace std;

int n, Min=1, Max=1;
vector<int>tree;
vector<vector<int> >results;

void preOrder(int index,vector<int>path ) {
	if (index > n)return;

	int value = tree[index];

	path.push_back(value);
	if (index* 2 > n && (index * 2 + 1 > n))results.push_back(path);


	preOrder(index * 2 + 1,  path);
	preOrder(index*2,  path);
}

int main() {
	scanf("%d", &n);
	tree.resize(n + 1);
	for (int i = 1; i <= n; i++)scanf("%d", &tree[i]);

	vector<int>tmp;
	preOrder(1,tmp);

	for (int i = 0; i < results.size(); i++) {
		for (int j = 0; j < results[i].size(); j++) {
			if (j != 0)printf(" ");
			printf("%d", results[i][j]);
            
			if (j != results[i].size() - 1 && (results[i][j] < results[i][j + 1]))Max = 0;
			else if (j != results[i].size() - 1 && (results[i][j] > results[i][j + 1]))Min = 0;
		}
		printf("\n");
	}

	if (Min)printf("Min Heap\n");
	else if (Max)printf("Max Heap\n");
	else printf("Not Heap\n");

		

	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值