PAT甲级 堆 相关题_C++题解

目录

  • 《算法笔记》重点摘要
  • 1147 Heaps (30)
  • 1155 Heap Paths (30)

《算法笔记》 9.7 堆 重点摘要

1. 定义

堆是完全二叉树,树中每个结点的值不小于(或不大于)其左右子结点的值,称之为大顶堆(或小顶堆)

const int maxn = 100;
int heap[maxn], n = 10; // heap 为堆,n 为元素个数

2. 向下调整

// 对 heap 数组在 [low,high] 范围进行向下调整
// 其中 low 为欲调整结点的数组下标, high 一般为堆的最后一个元素的数组下标
void downAdjust(int low, int high){
    int i = low, j = i * 2; // i 为欲调整结点,j 为其左子结点
    while (j <= high){  // 存在子结点时进行循环
        if (j + 1 <= high && heap[j+1] > heap[j)    // 若右子结点存在且值大于左子结点
            j = j + 1;
        if (heap[j] > heap[i]){ // 若子结点中最大的值比欲调整结点 i 的值大
            swap(heap[j], heap[i]);
            i = j;
            j = i * 2;
            // 保持 i 为欲调整结点,j 为其左子结点
        }
        else break;
    }
}

3. 建堆

  • 完全二叉树叶子结点数为 ceil(n/2)
  • 数组下标在 [1,floor(n/2)] 内均为非叶子节点
  • 从 floor(n/2) 开始倒着枚举结点,对每个结点 i 进行 [i,n] 范围调整
  • 保证每个节点都是以其为根结点的子树中值最大的结点
void createHeap(){
    for (int i = n / 2; i >= 1; i--)
        downAdjust(i,n);
}

4. 删除堆顶元素

用最后一个元素覆盖堆顶元素,再对根结点进行调整

void deleteTop(){
    heap[1] == heap[n--];   // 覆盖后元素个数 --
    downAdjust(1,n);
}

5. 向上调整

// 对 heap 数组在 [low,high] 范围进行向上调整
// 其中 low 一般设置为 1, high 为欲调整结点的数组下标
void upAdjust(int low, int high){
    int i = high, j = i / 2;    // i 为欲调整结点,j 为其父结点
    while (j >= low){   // 父结点在 [low,high] 范围内
        if (heap[j] < heap[i]){   // 父结点值小于欲调整结点值
            swap(heap[j],heap[i]);
            i = j;
            j = i / 2;
            // 保持i 为欲调整结点,j 为其父结点
        }
        else break;
    }
}

6. 添加元素

void insert(int x){
    heap[++n] = x;
    upAdjust(1,n);
}

7. 堆排序

  • 倒着遍历数组
  • 访问到 i 位置时,将堆顶元素与 i 位置元素交换,则 i 位置固定为 [1,i] 最大值
  • 在 [1,i-1] 范围对堆顶元素进行向下调整
void heapSort(){
    createHeap();
    for (int i = n; i > 1; i++){
        swap(heap[i], heap[1]);
        downAdjust(1, i-1);
    }
}

8. 递推判断堆类型方法

bool isMax = true, isMin = true;
for (int i = 2; i <= n; i++) {
    if (tree[i/2] > tree[i]) isMin = false;
    if (tree[i/2] < tree[i]) isMax = false;
}
printf("%s\n", isMax ? "Max Heap" : isMin ? "Min Heap" : "Not Heap");

1147 Heaps (30)

题目思路

  • 递推 判断堆类型,两个变量分别记录是否符合大顶堆 / 小顶堆
  • 递归 保存后序遍历序列,按要求输出,注意每检查一个新的堆要将保存后序序列的容器清空 post.clear()
#include<iostream>
#include<vector>
using namespace std;
int n;
vector<int> tree, post;
void postorder(int index){
	if (index > n) return;
	postorder(index * 2);
	postorder(index * 2 + 1);
	post.push_back(tree[index]);
}
int main()
{
	int m, data;
	scanf("%d%d", &m, &n);
	tree.resize(n+1);
	for (int i = 0; i < m; i++){
		for (int j = 1; j < n + 1; j++) scanf("%d", &tree[j]);
		bool isMax = true, isMin = true;
		for (int j = 2; j < n + 1; j++){
			if (tree[j/2] > tree[j]) isMin = false;
			if (tree[j/2] < tree[j]) isMax = false;
		}
		printf("%s\n", isMax ? "Max Heap" : isMin ? "Min Heap" : "Not Heap");
		post.clear();
		postorder(1);
		for (int j = 0; j < post.size(); j++)
			printf("%d%c", post[j], j + 1 == post.size() ? '\n' : ' ');
	}
	return 0;
}

1155 Heap Paths (30)

题目思路

  • DFS 递归 输出路径
    • 终止条件:到叶结点(2 * index > n),输出路径
    • 进入下一层条件:下一层的子结点存在
    • 即递归确认下一个结点存在时才进入,能进入递归的都是存在非越界的结点,所以通过其子结点越界判断到叶结点,输出到此结点的路径
  • 递归 判断是什么堆
    • 每进入一个非根节点的结点,就判断它与父结点的大小关系
    • 不符合大顶堆或小顶堆的就将对应变量置 false,符合要求的没有机会被置 false
    • 最后根据变量输出判断结果
#include<iostream>
#include<vector>
using namespace std;
int n, tree[1001];;
bool isMax = true, isMin = true;
vector<int> path;
void DFS(int index){
	if (index > 1){
		if (tree[index] > tree[index/2]) isMax = false;
		if (tree[index] < tree[index/2]) isMin = false;
	}
	if (2 * index > n){
		for (int i = 0; i < path.size(); i++)
			printf("%d%c", tree[path[i]], i == path.size()-1 ? '\n' : ' ');
		return;
	}
	if (2 * index + 1 <= n){
		path.push_back(2 * index + 1);
		DFS(2 * index + 1);
		path.pop_back();
	}
	path.push_back(2 * index);
	DFS(2 * index);
	path.pop_back();
}
int main()
{
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
		scanf("%d", &tree[i+1]);
	path.push_back(1);
	DFS(1);
	printf("%s\n", isMax ? "Max Heap" : isMin ? "Min Heap" : "Not Heap");
	return 0;
}
posted @ 2019-08-29 16:46  鲸90830  阅读(348)  评论(0编辑  收藏  举报