算法例题之冒泡排序,huffman树,优先级队列

我发现自己有一个贱毛病,学习之前喜欢先抒情或者表达一些什么特别的感情,之后再开始学习,玩的时候倒是很快能够进入状态,比如说今天的感慨就是:
不管学习什么都要对自己有耐心,学习一点是一点,不浮不躁。好了,我说完了,开始进入今天的正式内容。

重编码问题

重编码
问题描述
有一篇文章,文章包含 n 种单词,单词的编号从 1 至 n,第 i 种单词的出现次数为 w[i]。

现在,我们要用一个 2 进制串(即只包含 0 或 1 的串) s[i] 来替换第 i 种单词,使其满足如下要求:对于任意的 1≤i,j≤n(i≤j),都有 s[i] 不是 s[j] 的前缀。(这个要求是为了避免二义性)

你的任务是对每个单词选择合适的 s[i],使得替换后的文章总长度(定义为所有单词出现次数与替换它的二进制串的长度乘积的总和)最小。求这个最小长度。

字符串 S1(不妨假设长度为 n)被称为字符串 S2 的前缀,当且仅当:S2 的长度不小于 n,且 S1 与 S2 前 n 个字符组组成的字符串完全相同。

输入格式
第一行一个整数 n,表示单词种数。

第 2 行到第 n+1 行,第 i+1 行包含一个正整数 w[i],表示第 i 种单词的出现次数。

输出格式
输出一行一个整数,表示整篇文章重编码后的最短长度。

样例输入
4
1
1
2
2
样例输出
12
思路一:暴力方法,构建哈夫曼树,逐个节点构建(程序貌似没法正确运行,不过我先贴出来了,作为一个思路)


///完整版本的程序
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
using namespace std;
long long getAnswer(int n,vector <long long> w); 
int main(){
    int n;
    scanf("%d",&n);
    //之所以使用向量,是因为向量不用限制长度,如果用数组表示,必须首先指出使用了多少的空间 
    vector<long long> w;
    for(int i=0;i<n;++i){
        long long x;
        scanf("%lld",&x);
        w.push_back(x);
    }
    sort(w.begin(),w.end());
    //print("%lld\n",getAnswer(n,w));
    cout<<getAnswer(n,w)<<endl;
    return 0;

}
//返回的是一个数值 

long long getAnswer(int n,vector <long long> w){

    long long sum,count=0;

        for (auto i=w.begin();i!=w.end()-1;){
            auto k=i+1;//表示当前迭代器的下一个迭代器
            sum=*i+*k;//表示两个最小元素的加和 
            count+=sum;
            int flag=0;
            cout<<*i<<*k<<sum<<endl;
            w.erase(i);
            w.erase(i);//参数是迭代器 
            if(w.size()==0)
                return count;
            for (auto j=w.begin();j!=w.end();j++){
                if(sum<*j){
                    w.insert(j,sum);
                    flag=1;
                } 
            } ///
            //如果大于所有的,就补充在最后 
            if(flag==0)
                w.insert(w.end(),sum);//在最后插入元素 

            }

    } 

//insert 表示在指定位置(loc)表示迭代器,之前插入元素 


思路二:采用栈和队列两种数据结构

#include<bits/stdc++.h>
#include<iostream>
using namespace std;
int main(){
    vector<int> v;
    int n;
    int tmp;//表示输入的临时变量 
    cout<<"请输入元素的个数:";
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>tmp;
        v.push_back(tmp); 
    } 
    //对向量进行排序
    sort(v.begin(),v.end());
    reverse(v.begin(),v.end());
    stack<int> s;
    queue<int> q;//初始化两个数据结构
    //将已经有顺序的向量入栈,逆着顺序,在栈里面从上到下是逐渐增加
    for(vector<int>::iterator it =v.begin();it!=v.end();it++){
        s.push(*it);

    } 
    int sum=0,count=0;//表示最小的两个数的加和 
    int max=1000000;//表示一个很大的数值 
    int s_a,s_b,q_a,q_b;

    if(s.size()==0)
        cout<<"一派胡言!";
    if(s.size()==1)
        count=s.top();

    else{
        while(q.size()==0||!(q.size()==1&&s.size()==0)){//最小的尺寸是等于1或者最初的时候 
            if(s.size()>0){
                s_a=s.top();        
            }
            else
                s_a=max;
                s_b=max;
            ///同理对于队列而言
            if(q.size()>0){
                q_a=q.front();

            }
            else
                q_a=max;
                q_b=max; 
            //找出四个数之中最小的两个数值
            if(s_a<q_a){
                s.pop();
                if(!s.empty())
                    s_b=s.top();
                else
                    s_b=max;
                if(s_b<q_a){
                    sum=s_a+s_b;
                    s.pop();
                }               
                else{
                    sum=s_a+q_a;
                    q.pop();
                }                           
            } 
            else{
            q.pop();
            if(!q.empty())
                q_b=q.front();
            else
                q_b=max;
            if(s_a<q_b){
                sum=q_a+s_a;
                s.pop();
            } 
            else{
                sum=q_a+q_b;
                q.pop(); 
            }

        }                   

        count+=sum;
        q.push(sum);
    }

    }

    cout<<"count:"<<count<<endl;

//一定要仔细对待边界条件 

}

思路三:采用优先级队列(priority queue)


//使用优先级队列重编码 
#include<bits/stdc++.h>
using namespace std;

int main(){
    int n;//表示具体的数目
    long long tmp;//用来临时存储输入的数据 
    priority_queue<int,vector<int>,greater<int> > pq;//定义一个优先队列 
    vector<long long> v;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>tmp;
        v.push_back(tmp); 
    } 
    sort(v.begin(),v.end());
    for(vector<long long>::iterator it=v.begin();it!=v.end();it++){
        pq.push(*it);//使用一个叫做pq的树状结构表示向量 
    }
    /*
    while(!pq.empty()){
        cout<<pq.top()<<endl;
        pq.pop();
    } 
    */
    long long count=0;
    long long a,b,sum=0;

    //经过测试,现在数据已经完全被写入树中哈哈哈,优先队列只能从顶端访问数值(我猜的)
    if(pq.size()==0)
        cout<<"开玩笑~";
    if(pq.size()==1)
        count=pq.top();//如果只有一个元素,最上面的元素就是我们需要的 
    while(pq.size()>1){
        a=pq.top();
        pq.pop();
        b=pq.top();
        pq.pop();
        sum=a+b;
        pq.push(sum);
        count+=sum;
    } 
    cout<<"count:"<<count<<endl; 
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱码仕1024

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

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

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

打赏作者

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

抵扣说明:

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

余额充值