56、【树和二叉树】堆排序(C/C++版)

题目描述

输入一个长度为 n 的整数数列,从小到大输出前 m小的数。

输入格式

第一行包含整数 n和 m。
第二行包含 n个整数,表示整数数列。

输出格式

共一行,包含 m个整数,表示整数数列中前 m小的数。

数据范围

1≤m≤n≤105 ,
1≤数列中元素≤109

输入样例:

5 3
4 5 1 3 2

输出样例:

1 2 3

前置知识

图解排序算法(三)之堆排序

性质:
(1)堆是一颗采用顺序存储结构的完全二叉树;
(2)堆的根结点是关键字序列中的最小(或最大)值,称为小(或大)根堆;
(3)从根结点到每一叶子上的元素组成的序列为非递减(或非递增)序列;
(4)堆下的子树也是堆。

题目分析

规定从数组下标1开始进行存储,这样子做便于查找左右儿子,即结点i的左儿子为2 * i ,右儿子为2 * i + 1 。

操作主要分为down()、up()、删除、添加、修改和输出最小(或大)操作:

(1)down()操作:从上至下调整堆;

(2)up()操作:从下至上调整堆;

(3)删除操作
1. 删除最小值:从数组最末端取元素替换堆顶元素,再down();
2. 删除任意值: 从数组最末端取元素替换该位置元素,再down()或up();

(4)添加操作:在数组末端添加元素,再up();

(5)修改操作:修改完元素后,再down()或up();

(6)输出最值:输出堆顶元素值,然后再执行删除操作。

在进行建堆的过程中,由树高为log2n,每次调整堆的时间复杂度为O(log2n),一共需要插入n个结点,故时间复杂度为O(nlog2n)。

可通过对其进行优化,将时间复杂度降为O(n)。
选用的方式是从树的倒数第二层开始自底向上递归的建堆,由于每次一层堆都是在下面层已经建立好的基础上构建出来的因此,可保证自下而上在建堆时,每个儿子已经是建立完整的堆。
在这里插入图片描述
设整个树的结点数量为n,

除最后一层外,其余上层元素总和为n / 2。倒数第二层的元素个数为n / 4,至最后一层需要调整堆的层数为1层。倒数第三层的元素个数为n / 8,至最后一层需要调整堆的层数为2层。 倒数第四层的元素个数为 n / 16,至最后一层需要调整堆的层数为3层…

数学证明:
设调整次数为T,结点数量为n,则
在这里插入图片描述
得到一个等差和等比结合的数列,对其进行研究,令
在这里插入图片描述

让 ⑵ - ⑴ 可得
在这里插入图片描述
因此,可知
在这里插入图片描述
故从n/2开始建堆,时间复杂度将被优化为为O(n)

算法实现

#include <stdio.h>

const int N = 1e5 + 10;
int h[N], size;

void swap(int &a, int &b) {   int tmp = a; a = b; b = tmp;    }

void down(int u){
    int t = u;
    // 找到u、u*2和u*2 + 1中最小的那个元素
    if (u * 2 <= size && h[u * 2] < h[t])     t = u * 2;
    if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t])     t = u * 2 + 1;
    if(t != u){             // 若根结点不是最小的元素,则与其最小的元素进行交换
        swap(h[u], h[t]);
        down(t);
    }
}

void up(int u){
    while(u / 2 && h[u / 2] > h[u]){
        swap(h[u / 2], h[u]);
        u /= 2;
    }
}


int main(){
    int n, m;       scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)     scanf("%d", &h[i]);
    size = n;
    
    // 建堆,用此方法可将时间复杂度从O(nlog n)优化至O(n)
    for(int i = n / 2; i; i--)      down(i);        // 从倒数第二层开始建堆,由下至上递归,可保证下方的树均为建好的树
    //for(int i = 1; i <= n; i++)      up(i);
    
    while(m--){
        printf("%d ", h[1]);         // 输出堆顶元素
        h[1] = h[size--];         // 将树中最后一个结点覆盖根结点
        down(1);                        // 调整堆
    }
    
    return 0;
}

无注释代码

#include <stdio.h>

const int N = 1e5 + 10;
int heap[N], size;

void swap(int &a, int &b)       {   int tmp = a; a = b; b = tmp;    }

void down(int u){
    int t = u;
    if(u * 2 <= size && heap[u * 2] < heap[t])      t = u * 2;
    if(u * 2 + 1 <= size && heap[u * 2 + 1] < heap[t])      t = u * 2 + 1;
    if(u != t){
        swap(heap[u], heap[t]);
        down(t);
    }
}

void up(int u){
    while(u / 2 > 0 && heap[u / 2] > heap[u]){
        swap(heap[u / 2], heap[u]);
        u /= 2;
    }
}

int main(){
    int n, m;       scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)     scanf("%d", &heap[i]);
    size = n;
    for(int i = n / 2; i; i--)      down(i);
    //for(int i = 1; i <= n; i++)      up(i);
    
    while(m--){
        printf("%d ", heap[1]);
        heap[1] = heap[size--];
        down(1);
    }
    
    return 0;
}

图示过程参考:
1.7 堆排序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
二叉排序(Binary Search Tree,BST),也称为二叉查找或二叉搜索,是一种特殊的二叉树,它的左子中的所有节点的键值小于根节点的键值,右子中的所有节点的键值大于根节点的键值。以下是C++代码实现: ```c++ #include <iostream> using namespace std; struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; class BST { private: TreeNode* root; public: BST() { root = NULL; } void insert(int val) { if (root == NULL) { root = new TreeNode(val); return; } TreeNode* node = root; while (node != NULL) { if (val < node->val) { if (node->left == NULL) { node->left = new TreeNode(val); return; } else { node = node->left; } } else { if (node->right == NULL) { node->right = new TreeNode(val); return; } else { node = node->right; } } } } TreeNode* search(int val) { TreeNode* node = root; while (node != NULL) { if (node->val == val) { return node; } else if (val < node->val) { node = node->left; } else { node = node->right; } } return NULL; } }; int main() { BST bst; bst.insert(5); bst.insert(3); bst.insert(7); bst.insert(1); bst.insert(9); TreeNode* node = bst.search(7); if (node != NULL) { cout << "Found: " << node->val << endl; } else { cout << "Not Found" << endl; } return 0; } ``` 以上代码实现了二叉排序的插入和查找功能。其中,insert()函数用于插入节点,search()函数用于查找节点。在main()函数中,首先创建一个BST对象,然后插入5、3、7、1、9这5个节点,最后查找值为7的节点。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰阳星宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值