区间第K大(划分树)

题目:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1175

 

注意一个很明显的结论:求区间[p,q]的第K大就等于求区间[p,q]的第q-p+1-k小。

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>

using namespace std;
const int N = 100005;

int sorted[N];
int Tree[20][N];
int toLeft[20][N];

void Build(int k,int l,int r)
{
    if(l==r) return;
    int mid=(l+r)>>1;
    int i,t;
    t=mid-l+1;
    for(i=l;i<=r;i++)
       if(Tree[k][i]<sorted[mid])
          t--;
    int L=l;
    int R=mid+1;
    for(i=l;i<=r;i++)
    {
       if(i==l)
          toLeft[k][i]=0;
       else
          toLeft[k][i]=toLeft[k][i-1];
       if(Tree[k][i]<sorted[mid])
       {
           toLeft[k][i]++;
           Tree[k+1][L++]=Tree[k][i];
       }
       else if(Tree[k][i]>sorted[mid])
       {
           Tree[k+1][R++]=Tree[k][i];
       }
       else
       {
           if(t!=0)
           {
               t--;
               toLeft[k][i]++;
               Tree[k+1][L++]=Tree[k][i];
           }
           else
               Tree[k+1][R++]=Tree[k][i];
       }
    }
    Build(k+1,l,mid);
    Build(k+1,mid+1,r);
}

int Query(int k,int l,int r,int p,int q,int t)
{
    if(p==q) return Tree[k][p];
    int s,ss;
    int mid=(l+r)>>1;
    if(l==p)
    {
        s=0;
        ss=toLeft[k][q];
    }
    else
    {
        s=toLeft[k][p-1];
        ss=toLeft[k][q]-s;
    }
    int L,R;
    if(t<=ss)
    {
        L=l+s;
        R=l+s+ss-1;
        return Query(k+1,l,mid,L,R,t);
    }
    else
    {
        L=mid-l+1+p-s;
        R=mid-l+1+q-s-ss;
        return Query(k+1,mid+1,r,L,R,t-ss);
    }
}

int main()
{
    int n,m,i,j;
    while(~scanf("%d",&n))
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d",&Tree[0][i]);
            sorted[i]=Tree[0][i];
        }
        std::sort(sorted+1,sorted+n+1);
        Build(0,1,n);
        scanf("%d",&m);
        int p,q,k;
        while(m--)
        {
            scanf("%d%d%d",&p,&q,&k);
            p++;q++;k--;
            printf("%d\n",Query(0,1,n,p,q,q-p+1-k));
        }
    }
    return 0;
}


 

### 使用二分法查找第 K 小元素 对于在已排序数组中查找第 K 小的元素,可以应用二分查找方法。这种方法不仅高效而且逻辑清晰。 #### 非递归方式实现 为了在一个升序排列的整型数组 `arr` 中找到第 k 小的元素,可以通过调整标准的二分查找算法来完成这一目标: ```cpp int findKthSmallest(int arr[], int n, int k) { // 创建一个小顶堆用于存储前k个最小值 priority_queue<int, vector<int>, greater<int>> minHeap; for (int i = 0; i < n; ++i){ minHeap.push(arr[i]); } // 提取堆中的前(k-1)个元素 for (int i = 0; i < k - 1; ++i){ minHeap.pop(); } // 堆顶即为所求的第k小元素 return minHeap.top(); } ``` 上述代码实际上并未直接使用二分查找而是借助了优先队列(最小堆),这是因为直接通过二分查找定位到具体位置较为复杂。不过,在某些情况下也可以基于二分思想设计专门针对此类问题的解决方案[^3]。 #### 更高效的二分策略 另一种更贴近传统意义上的“二分”,是将整个过程视为对可能的结果空间进行划分的过程。这里的关键在于理解我们不是在原始输入序列上做二分操作,而是在潜在解的空间里执行二分搜索。这通常涉及到设定合理的上下界并不断缩小区间直到收敛于正确答案。 考虑一个优化版本,它并不依赖额外的数据结构如堆: ```cpp // 寻找无重复元素数组中的第k小数 double findKthElement(vector<double>& nums, int k) { double low = *min_element(nums.begin(), nums.end()); double high = *max_element(nums.begin(), nums.end()); while(low <= high){ double mid = low + ((high-low)/2); // 统计不于mid的数量count以及最靠近mid的最值preMid int count=0; double preMid=-DBL_MAX; for(auto num : nums){ if(num<=mid){ count++; preMid=max(preMid,num); } } if(count==k) return preMid; if(count<k) low=mid+EPSILON;// EPSILON是一个很小正数防止浮点误差 else high=mid-EPSILON; } throw invalid_argument("Invalid Input"); } ``` 这段代码展示了如何利用二分查找技术在一个给定范围内精确地锁定住所需的数值。注意这里的处理对象被假定是没有重复项的情况下的实数集合,并且引入了一个极小量 `EPSILON` 来规避由于计算机内部表示带来的精度损失问题[^4]。 #### 复杂度分析 当采用纯粹的二分查找思路解决问题时,每次迭代都会使候选集减半,因此理论上可以在 O(log N) 的时间内得到结果。然而实际性能还取决于具体的实现细节和其他因素的影响,比如是否存在量相同键值等特殊情形下可能会退化成线性扫描的时间开销[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值