C++牛客网编程(六)

目录

求两个数的最大公约数,欧几里得算法

数组不去重的最小k个数

数据流的中位数


求两个数的最大公约数,欧几里得算法

#include <iostream>
using namespace std;

int change(int a, int b) {
	if (a%b == 0)
		return b;
	else return (b, a%b);
	
}

int main() {
	int a, b;
	cin >> a >> b;
	cout << change(a, b) << endl;
	
	return 0;
}

数组不去重的最小k个数

给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可)。

数据范围:0≤k,n≤100000,数组中每个数的大小0≤val≤10000

要求:空间复杂度 O(n) ,时间复杂度 O(nlogn)

思路:使用快排排序

class Solution {
public:
    int Partition(vector<int> &input,int low,int high){
        auto pivot=input[low];
        while(low<high){
            while(low<high&&input[high]>=pivot) --high;
            input[low]=input[high];
            while(low<high&&input[low]<=pivot) ++low;
            input[high]=input[low];
        }
        input[low]=pivot;
        return low;
    }
    void Traverse(vector<int> &input,int low,int high){
        if(low<high){
            int pivot=Partition(input,low,high);
            Traverse(input,low,pivot-1);
            Traverse(input,pivot+1,high);
        }
    }
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        if(k==0)
            input.resize(0);
        else{
            Traverse(input,0,input.size()-1);
            input.resize(k);
        }
        return input;
    }
};

重点:

1.vector传参时要用引用,才能修改vector的值

2.修改vector的大小由resize()函数完成

3. vector中每一个都是向量,不能直接int i=input[low],用auto类型

数据流的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

数据范围:数据流中数个数满足 1≤n≤1000  ,大小满足 1≤val≤1000

进阶: 空间复杂度 O(n)  , 时间复杂度 O(nlogn)

思路:每次Insert就快排一次,所以时间复杂度不低

看其它同学:可以进行插入排序,堆排序

堆排序的思路:

 假设[0 ... median - 1]的长度为l_len, [median + 1 ... arr.sise() - 1]的长度为 r_len.
1.如果l_len == r_len + 1, 说明,中位数是左边数据结构的最大值
2.如果l_len + 1 == r_len, 说明,中位数是右边数据结构的最小值
3.如果l_len == r_len, 说明,中位数是左边数据结构的最大值与右边数据结构的最小值的平均值。

所以,GetMedian()操作算法过程为:

  • 初始化一个大根堆,存中位数左边的数据,一个小根堆,存中位数右边的数据
  • 动态维护两个数据结构的大小,即最多只相差一个

 Insert()操作算法过程为:

  • 当我们已经添加的数据个数为奇数个时,大顶堆的数据个数比小顶堆多一个,此时若要再添加一个数据,必定添加在小顶堆Rmin上,但是我们不知道当前添加的数是否大于中位数,故要先可插入Lmax然后将Lmax中的最大数放入Rmin中,同时删除Lmax中的最大数,即删除堆顶
  • 当我们已经添加的数据个数为偶数个时,小顶堆的数据个数等于大顶堆,此时若要再添加一个数据,必定添加在大顶堆Lmax上,添加的手段和前者相同。
class Solution {
public:
    priority_queue<int> Lmax;
    priority_queue<int, vector<int>, greater<int> > Rmin;
    int cnt = 0;         //记录当前已经添加数据的个数
    void Insert(int num) {
        if(cnt & 1) {
            //已经添加了奇数个,当前添加至Rmin
            Lmax.push(num);
            Rmin.push(Lmax.top()); Lmax.pop();
        } else {
            //已经添加了偶数个,当前添加至Lmin
            Rmin.push(num);
            Lmax.push(Rmin.top()); Rmin.pop();
        }
        cnt ++ ;
    }

    double GetMedian() { 
         if(cnt & 1) return Lmax.top();        //奇数
         else return (Lmax.top() + Rmin.top()) / 2.0;  //偶数
     }

};

重点:

priority_queue<Type, Container, Functional>

Type为数据类型, Container为保存数据的容器,Functional为元素比较方式。

如果不写后两个参数,那么容器默认用的是vector,比较方式默认用operator<,也就是优先队列是大顶堆,队头元素最大。
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值