算法导论学习之线性时间求第k小元素+堆思想求前k大元素

对于曾经,假设要我求第k小元素。或者是求前k大元素,我可能会将元素先排序,然后就直接求出来了,可是如今有了更好的思路。

一.线性时间内求第k小元素

这个算法又是一个基于分治思想的算法。

其详细的分治思路例如以下:

1.分解:将A[p,r]分解成A[p,q-1]和A[q+1,r]两部分。使得A[p,q-1]都小于A[q],A[q+1,r]都不小于A[q];

2.求解:假设A[q]恰好是第k小元素直接返回,假设第k小元素落在前半区间就到A[p,q-1]递归查找。否则到A[q+1,r]中递归查找。

3.合并:这个问题不须要合并。

其相应的代码例如以下:

int RandomziedSelect(int *a,int p,int r,int k)
{
    if(p==r)///假设当前区间仅仅剩一个元素,那么这个元素一定就是我们要求的
        return a[p];
    int q=RandomParatition(a,p,r);  ///随机划分函数
    int x=q-p+1;///求出a[p,q]之间的长度
    if(x==k) ///a[q]恰好是第k小元素
        return a[q];
    if(x>k)  ///x小于k说明第k小元素在a[p,q-1]之间
        return RandomziedSelect(a,p,q-1,k);
    else  ///x大于k说明第k小元素在a[q+1,r]之间,并且是这个区间的第k-x小元素
        return RandomziedSelect(a,q+1,r,k-x);
}

事实上这个过程非常相似于快排,可是为什么快排的时间复杂度是O(nlgn),而这个算法的时间复杂度仅仅有O(n)?基本的原因在于这个算法每次仅仅要处理分解以后一半的区间,而不像快排那样两边都要处理。

当然这仅仅是一个简单的分析,更详细数学分析在这里就不说了。事实上我们也能够利用堆的性质来求出第k小元素,仅仅要我们建立一个最小堆后然后再调整k-1次即可了,这样时间复杂度是O(n)+O((k-1)lgn)。

以下给出一份完整的代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<ctime>
#include<fstream>
using namespace std;

int Paratition(int *a,int p,int r)
{
    int key=a[r];
    int i=p-1;
    for(int j=p;j<r;j++)
        if(a[j]<key)
        {
            i++;
            swap(a[i],a[j]);
        }
    swap(a[i+1],a[r]);
    return i+1;
}

int RandomParatition(int *a,int p,int r)
{
    int x=rand()%(r-p+1)+p;///产生[p,r]之间的随机数
    swap(a[x],a[r]);  ///交换a[x]和a[r]的值,事实上就是将a[x]作为划分的关键值
    return Paratition(a,p,r);
}

int RandomziedSelect(int *a,int p,int r,int k)
{
    if(p==r)///假设当前区间仅仅剩一个元素,那么这个元素一定就是我们要求的
        return a[p];
    int q=RandomParatition(a,p,r);  ///随机划分函数
    int x=q-p+1;///求出a[p,q]之间的长度
    if(x==k) ///a[q]恰好是第k小元素
        return a[q];
    if(x>k)  ///x小于k说明第k小元素在a[p,q-1]之间
        return RandomziedSelect(a,p,q-1,k);
    else  ///x大于k说明第k小元素在a[q+1,r]之间,并且是这个区间的第k-x小元素
        return RandomziedSelect(a,q+1,r,k-x);
}

int main()
{
    int b[100];
    ifstream fin("lkl.txt");
    int n,k;
    //cout<<"请输入n,k: ";
    fin>>n>>k;
   //cout<<"请输入"<<n<<"个元素: "<<endl;
    for(int i=1;i<=n;i++)
        fin>>b[i];
    int ans=RandomziedSelect(b,1,n,k);
    sort(b+1,b+n+1);
    for(int i=1;i<=n;i++)
        cout<<b[i]<<" ";
    cout<<endl;
    cout<<"第"<<k<<"小元素为: "<<ans<<endl;
  return 0;
}

二.利用堆求前k大元素

这个算法的思想比較简单: 假设我们要求n个元素中前k大的元素。我们就先将这n个元素中的前k个元素建立一个最小堆,然后从k+1。

。。

n依次推断。假设某个元素大于堆中最小的元素,我们就将其替代堆中的最小元素,并且调整一下堆。

这样将全部元素都检查完了之后,堆中的k个元素也就是这n个元素中的前k大元素了。时间复杂度O(k)+O((n-k)lgk)。

代码例如以下

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<fstream>
using namespace std;

#define maxn 100

///最小堆调整函数
void MinHeadfly(int *a,int i,int HeadSize)
{
    int l=i*2,r=2*i+1;
    int largest;
    if(a[i]>a[l]&&l<=HeadSize)
        largest=l;
    else
        largest=i;
    if(a[largest]>a[r]&&r<=HeadSize)
        largest=r;
    if(largest!=i)
    {
        swap(a[i],a[largest]);
        MinHeadfly(a,largest,HeadSize);
    }
}

///最小堆建立函数
void MinHeadBuild(int *a,int n)
{
    for(int i=n/2;i>=1;i--)
        MinHeadfly(a,i,n);
}

///最小堆排序函数,从大到小排序
void MinHeadSort(int *a,int HeadSize)
{
    int k=HeadSize;
    for(int i=HeadSize;i>=2;i--)
    {
        swap(a[i],a[1]);
        k--;
        MinHeadfly(a,1,k);
    }
}

///求b数组的前k大元素
void prek(int *a,int *b,int n,int k)
{
    MinHeadBuild(a,k);
    for(int i=k+1;i<=n;i++)
        if(b[i]>a[1])
        {
            a[1]=b[i];
            MinHeadfly(a,1,k);
        }
    MinHeadSort(a,k);
    cout<<"前"<<k<<"大元素为:"<<endl;
    for(int i=1;i<=k;i++)
        cout<<a[i]<<" ";
    cout<<endl;
}

int a[maxn],b[maxn];

int main()
{
    ifstream fin("lkl.txt");
    int n,k;
    //cout<<"请输入n,k: ";
    fin>>n>>k;
   //cout<<"请输入"<<n<<"个元素: "<<endl;
    for(int i=1;i<=n;i++)
    {
        fin>>b[i];
        if(i<=k)
            a[i]=b[i];
    }
    prek(a,b,n,k);
  return 0;
}
时间: 2024-10-06 00:07:48

算法导论学习之线性时间求第k小元素+堆思想求前k大元素的相关文章

算法导论学习之线性时间排序+排序算法稳定性终结

前面我们学习的几种排序算法都是基于比较的,对于任何输入数据他们都是适用的,其最坏的时间复杂度不会低于nlgn: 但对于一些比较特殊的输入数据,我们可以不采取比较的方法而是采用其它的方法对其进行排序,以达到线性的时间复杂度.下面就来介绍三种这样的算法:计数排序,基数排序,桶排序(因为这几种算法不常见,我只实现了计数排序,其它两种排序用伪代码表示). 一.计数排序 算法思想:给定n个位于0–k之间的数(k是一个不太大的整数),我们可以统计出每个数前面有多少个小于它的数,然后就可以直接确定这个数在数组

算法导论——lec 08 线性时间排序

之前我们介绍了几种O(nlgn)的排序算法:快速排序.合并排序和堆排序,本节我们介绍基于比较的排序算法的下界以及几个线性时间的排序算法--计数排序.基数排序.桶排序. 一. 比较排序算法的下界 1. 决策树模型:比较排序可以被抽象的视为决策树.一棵决策树是一棵满二叉树,表示某排序算法 作用于给定输入所做的所有比较. 排序算法的执行对应于遍历一条从树的根到叶结点的路径.在每个内节结点处要做比较. 要使排序算法能正确的工作,其必要条件是,n 个元素的n!种排列中的每一种都要作为决策树 的一个叶子而出

【算法导论学习-23】两个单链表(single linked)求交点

问题:A.B两个单链表如果有交点,返回第一个交点在A中的位置(链表头结点位置为0). 分析:A.B如果有交点,交点的后继一定也是交点,所以一定是Y型相交,所以算法的思想如下 1)  求得A.B的长度,比如ALength,Blength 2)  判断ALength,Blength谁大,比如Alength>Blength 3)  Alength移动到Alength-Blength的位置,开始判断每个节点是否相等,相等则退出. 以本博客中"[算法导论学习-20]单链表(single linked

算法导论之六:线性时间排序之 决策树&amp;计数排序

本系列前五篇都是讲述的比较排序算法,从本文开始,将进入线性时间排序.什么是比较排序,简单的说,就是排序的过程依赖于数组中数据大小的比较,从而来确定数据在排好序输出时的位置. 比较排序法比较直观,但是也有它的不足,我们容易证明任何比较排序法,在最坏的情况下的时间复杂度的下限都是 nlgn.要证明这个问题,我们首先要搞清楚一个模型:决策树模型. 一.决策树模型 什么是决策树?决策树从形态上来讲,是一颗完全二叉树,它除叶子节点之外,其他层的节点都是满的.它的每一个叶子节点表示对输入数据组合的一种排序可

算法导论第八章线性时间排序

一.线性时间排序算法历史概览 计数排序首先是由 Harold H. Seward 于1954年提出,而且他还提出将计数排序和基数排序进行结合的思想:基数排序是L.J.Comrie于1929年首次在一篇描述卡片穿孔机文档中提出的一种方法,它是从最低有效位开始,对一个有多位数组成的数进行排序的方法:而桶排序的基本思想则由E.J.Isaac和R.C.Singleton于1956年提出的,之后很多研究人员在这三种算法的基础上针对不同的应用场景又进一步改进,到了今天一个很成熟.很通用的地步. 二.O(nl

算法导论 第八章 线性时间排序(python)

比较排序:各元素的次序依赖于它们之间的比较{插入排序O(n**2) 归并排序O(nlgn) 堆排序O(nlgn)快速排序O(n**2)平均O(nlgn)} 本章主要介绍几个线性时间排序:(运算排序非比较排序)计数排序O(k+n)基数排序O() 第一节:用决策树分析比较排序的下界 决策树:倒数第二层满,第一层可能满的二叉树,它用来表示所有元素的比较操作{于此来分析下界},忽略控制,移动操作 1:2 #A[1]和A[2]比 <= 走左边 >走右边 <3,1,2> 最后的结果 下标对应排

【算法导论学习-015】数组中选择第i小元素(Selection in expected linear time)

1.算法思想 问题描述:从数组array中找出第i小的元素(要求array中没有重复元素的情况),这是个经典的"线性时间选择(Selection in expected linear time)"问题. 思路:算法导论215页9.2 Selection in expect linear time 2.java实现 思路:算法导论216页伪代码 /*期望为线性时间的选择算法,输入要求,array中没有重复的元素*/ public static int randomizedSelect(i

算法导论 学习资源

学习的过程会遇到些问题,发现了一些比较好的资源,每章都会看下别人写的总结,自己太懒了,先记录下别人写的吧,呵呵. 1  Tanky Woo的,每次差不多都看他的 <算法导论>学习总结 - 1.前言 <算法导论>学习总结 - 2.第一章 && 第二章 && 第三章 <算法导论>学习总结 - 3.第四章 && 第五章 <算法导论>学习总结 - 4.第六章(1) 堆排序 <算法导论>学习总结 - 5.第六

【算法导论学习-016】两个已排过序的等长数组的中位数(median of two sorted arrays)

问题来源 <算法导论>P223 9.3-8: Let X[1..n] and Y[1..n] be two arrays, each containing nnumbers already in sorted order. Give an O(lgn)-time algorithm to find themedian of all 2n elements in arrays X and Y. 翻译过来即:求两个等长(n个元素)的已排序数组A和B的中位数 方案1:对两个数组进行归并直到统计到第n