________________________________________________________
二.完全二叉树+堆
________________________________________________________
数组型树及:完全二叉树
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", ×, &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;
}