《啊哈!算法》第7章 神奇的树

第3节 堆排序

把n个元素建立一个堆,首先将这n个结点以自顶向下、从左到右的方式从1到n编码,这样可以把n个结点转换成一颗完全二叉树

紧接着从最后一个非叶子结点(结点编号为n/2)开始到根节点(结点编号为1),逐个扫描所有结点,根据需要将当前结点向下调整,直到以当前结点为根结点的子树符合堆的特性。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

//向下调整函数
void shiftdown(vector<int>& a, int n,int index){
    while(index*2<= n ){
        int t = index;
        if(a[t] > a[2*index]) t = 2*index;
        if( 2*index+1 <= n &&  a[t] > a[2*index+1] ) t = 2*index+1;
        if(t!=index) {swap(a[t],a[index]); index = t;}
        else break;
    }
}

//删除最大的元素
int deletemax(vector<int>& a , int& n){
    int res = a[1];
    swap(a[1],a[n]);
    n--;
    shiftdown(a,n,1);
    return res;
}

//向上调整函数
void shiftup(vector<int>& a, int n, int index){
    if(index == 1) return;
    while(index!=1){
        if(a[index] < a[index/2]) swap(a[index],a[index/2]);
        else break;
        index/=2;
    }

}

// 创建堆
void make_heap(vector<int>& a, int n){
    //注意索引是从0开始的
    for(int i = n/2; i> 0; -- i){
        shiftdown(a,n,i);
    }

}

void heap_sort(vector<int>& a){
    int n =a.size()-1;
    while( n > 1){
        swap(a[1],a[n]);
        n--;
        shiftdown(a,n,1);
    }
}

int main(){
    int n;
    cin >> n;
    vector<int> a(n+1,0);
    for(int i = 1 ; i <= n; ++ i){
        cin >> a[i];
    }

    //建堆
    make_heap(a,n);

    heap_sort(a);

    for(int i = 1 ; i <= n; ++ i){
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int num = n;
    for(int i = 1; i <= n; ++ i){
        cout<<deletemax(a,num)<<endl;
    }

}

堆排序

第4节 并查集

并查集的优化有两种,一种是路径压缩,一种是按秩合并,具体的可以参考《算法导论》

/*
 *并查集操作
 */

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int f[1000]={0};

void make_set(int size){
    for(int i =1; i <= size; ++ i ) f[i] = i;
}

//采用路径压缩的方法查找元素
int find_set(int x){
    if(f[x] == x) return x;
    else{
         f[x] = find_set(f[x]);  //查找x元素所在的集合,回溯时压缩路径
         return f[x];
    }

}

void union_set(int x, int y){
    int t1 = find_set(x), t2 = find_set(y);
    if(t1 != t2){
        f[t2] = t1;
    }
}

int main(){
    int n, m;
    cin >> n >> m;
    make_set(n);
    for(int i = 1; i<=m; ++ i){
        int x,y;
        cin >> x >> y;
        union_set(x,y);
    }
    int sum = 0;
    for(int i = 1; i<= n; ++ i){
        if(f[i] == i) sum++;
    }
    cout<<sum<<endl;
    return 0;
}

并查集

《啊哈!算法》第7章 神奇的树

时间: 2024-10-07 17:09:11

《啊哈!算法》第7章 神奇的树的相关文章

【坐在马桶上看算法】算法12:堆——神奇的优先队列(下)

接着上一Pa说.就是如何建立这个堆呢.可以从空的堆开始,然后依次往堆中插入每一个元素,直到所有数都被插入(转移到堆中为止).因为插入第i个元素的所用的时间是O(log i),所以插入所有元素的整体时间复杂度是O(NlogN),代码如下. n=0; for(i=1;i<=m;i++) {     n++;     h[ n]=a[ i];  //或者写成scanf("%d",&h[ n]);     siftup(); } 其实我们还有更快得方法来建立堆.它是这样的. 直接

【啊哈!算法】算法12:堆——神奇的优先队列(下)

接着上一Pa说.就是如何建立这个堆呢.可以从空的堆开始,然后依次往堆中插入每一个元素,直到所有数都被插入(转移到堆中为止).因为插入第i个元素的所用的时间是O(log i),所以插入所有元素的整体时间复杂度是O(NlogN),代码如下. n=0; for(i=1;i<=m;i++) { n++; h[ n]=a[ i]; //或者写成scanf("%d",&h[ n]); siftup(); } 其实我们还有更快得方法来建立堆.它是这样的. 直接把99.5.36.7.22

算法导论 第一章

算法导论 第一章,为了让自己基本功更加的扎实,从今天起开始学习算法导论. 我以一位学长的博客为学习的参考资料,开始我的学习吧! 附上一句话: Having a solid base of algorithm knowledge and technique is one characteristic that separates the truly skilled programmers from the novices. 是否具有扎实的算法知识和技术基础,是区分真正熟练的程序员与新手的一项重要特

【坐在马桶上看算法】算法11:堆——神奇的优先队列(上)

堆是什么?是一种特殊的完全二叉树,就像下面这棵树一样. 有没有发现这棵二叉树有一个特点,就是所有父结点都比子结点要小(注意:圆圈里面的数是值,圆圈上面的数是这个结点的编号,此规定仅适用于本节).符合这样特点的完全二叉树我们称为最小堆.反之,如果所有父结点都比子结点要大,这样的完全二叉树称为最大堆.那这一特性究竟有什么用呢? 假如有14个数分别是99.5.36.7.22.17.46.12.2.19.25.28.1和92.请找出这14个数中最小的数,请问怎么办呢?最简单的方法就是将这14个数从头到尾

普林斯顿公开课:算法第0章,课程介绍

课程介绍 这门课程核心内容是算法和数据结构. 具体的算法和数据结构如下: 数据类型:堆栈.队列.背包.并查集.优先队列. 排序:快排.并排.堆排.基数排序 查找:BST.红黑BST.哈希表 图:BFS.DFS.Prim.Kruskai.Dijkstra 字符串:KMP.正则.TST.哈夫曼.LZW 高级:B树.后缀数组.最大流 为什么要学习算法 算法在各个领域中都有应用. 算法可以提高编程效率. 算法可以将现实生活中的物理公式转换成代码,算法可以模拟现实世界,然后发现世界的奥秘. 算法是很有趣的

算法导论 第二章

2014-12-02 20:21:40 http://www.cnblogs.com/sungoshawk/p/3617652.html 上面链接指向算法导论第二章的预习博客,很值得一看,很详细. 插入算法: 1 #include <iostream> 2 3 using namespace std; 4 void insert_sort(int *datas, int length); 5 int main() 6 { 7 int a[10]={1,2,4,35,6,1,4,7,9,7};

算法第三章上机实验

算法第三章上机实验 数字三角形 给定一个由 n行数字组成的数字三角形如下图所示.试设计一个算法,计算出从三角形 的顶至底的一条路径(每一步可沿左斜线向下或右斜线向下),使该路径经过的数字总和最大. #include <iostream> using namespace std; int maxsum(int a[100][100],int n){ int b[100][100]={0}; for(int i=n-1;i>=0;i--){ for(int j=i;j>=0;j--){

揭露动态规划真面目——算法第三章上机实践报告

算法第三章上机实践报告 一.        实践题目 7-2 最大子段和 (40 分) 给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值.当所给的整数均为负数时,定义子段和为0. 要求算法的时间复杂度为O(n). 输入格式: 输入有两行: 第一行是n值(1<=n<=10000): 第二行是n个整数. 输出格式: 输出最大子段和. 输入样例: 在这里给出一组输入.例如: 6 -2 11 -4 13 -5

贪心算法?我全都要!——算法第四章上机实践报告

算法第四章上机实践报告 一.        实践题目 4-1 程序存储问题 (90 分) 设有n 个程序{1,2,…, n }要存放在长度为L的磁带上.程序i存放在磁带上的长度是 li,1≤i≤n. 程序存储问题要求确定这n 个程序在磁带上的一个存储方案, 使得能够在磁带上存储尽可能多的程序. 对于给定的n个程序存放在磁带上的长度,计算磁带上最多可以存储的程序数. 输入格式: 第一行是2 个正整数,分别表示文件个数n和磁带的长度L.接下来的1行中,有n个正整数,表示程序存放在磁带上的长度. 输出