1. 单机作业调度(n个顾客等待同一服务)
问题描述
设有n个顾客同时等待同一服务,顾客i需要的服务时间为t,1<=i<=n,应如何安排n个顾客的服务次序才能使总的等待时间达到最小?总的等待时间是各顾客等待服务的总和。试给出你的做法的理由。
使用贪心策略,将服务所需时间按从小到大进行排列,优先安排服务时间短的。
证明(交换论证)
2. 最优前缀码-Huffman编码
定义
二元前缀码:表示一个字符的0,1字串不能是表示另一个字符的0,1字串的前缀。
B=∑f(xi)d(xi)是存储一个字符的平均值。d(xi)为码长。
平均码长达到最小的前缀编码方案称为字符集C的一个最优前缀编码
不同的字符 | a | b | c | d | e | f |
频率f(千次) | 45 | 13 | 12 | 16 | 9 | 5 |
定长码 | 000 | 001 | 010 | 011 | 100 | 101 |
变长码 | 0 | 101 | 100 | 111 | 1101 | 1100 |
伪代码
证明
C++代码
// 最优前缀码-Huffman编码
//二元前缀码:表示一个字符的0, 1字串不能是表示另一个字符的0, 1字串的前缀。
//B = ∑f(xi)d(xi)是存储一个字符的平均值。d(xi)为码长。
//平均码长达到最小的前缀编码方案称为字符集C的一个最优前缀编码。
//例:字符集及其频率如
//表.长为100000的文件:
//定长码:300000 bit,
//变长码:224000, 省25 %
// 算法:要点:编码字符均在叶子节点上,数据结构:队列中装树的结点,队列递增排序,取最小元素赋值给树节点左边右边,插入新节点后仍然递增,注意频率
//算法复杂度:
//2行排序O(nlogn)
//3行循环n - 1次
//8行循环体内插入操作
//O(logn),其它操作常量
//所以T(n) = O(nlogn)
// 关键词:贪心
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue> // 使用队列
#include<cstring>
#include <string>
using namespace std;
struct Huffman { // 存字符及其对应的频率 结构
string character; // 编码字符
float frequency;
};
struct Node { // 树结点结构
Huffman data;
Node* lChild;
Node* rChild;
Node(Huffman data)
{
this->data = data;
this->lChild = NULL;
this->rChild = NULL;
}
};
class MinHeap {//最小堆 min堆的实现 参考 https://blog.csdn.net/u010900754/article/details/54804303
private:
int size = 0;
vector<Huffman> heap;
public:
int getSize() {
return heap.size();
}
Huffman getElement(int i) {
return heap[i];
}
void add(Huffman &data) { // 添加一个元素,需要插在哪里的问题
heap.push_back(data);
int i = heap.size() - 1;
while (i > 0 && heap[i].frequency < heap[(i - 1) / 2].frequency) { //比较的是频率大小
swap(i, (i - 1) / 2);
i = (i - 1) / 2;
}
}
Huffman top() { //取走root结点,也需要调整堆中结点的位置
/*size--;*/
Huffman r = heap[0];
//swap(heap.size()-1, 0); // 交换是个什么操作??
vector<Huffman> T;
T.insert(T.begin(), heap.begin()+1, heap.end());
heap = T;
minheapify(0);
return r;
}
void minheapify(int i) {
int l = 2 * i + 1;
int r = 2 * i + 2;
int small = i;
if (l < heap.size() && heap[i].frequency > heap[l].frequency) {
small = l;
}
if (r < heap.size() && heap[r].frequency < heap[small].frequency) {
small = r;
}
if (i != small) {
swap(i, small);
minheapify(small);
}
}
void swap(int i, int j) {
Huffman temp = heap[i];
heap[i] = heap[j];
heap[j] = temp;
}
};
class Ques20201017
{
public:
void test(vector<string>& characters, vector<float>& frequencys) {
vector<Huffman> huffmans=init(characters, frequencys);
// 将数组中的元素进行排序,还是直接在一开始就赋值给队列?==>维护最小堆
MinHeap minheap = MinHeap();
for (int i = 0; i < characters.size(); i++) {
minheap.add(huffmans[i]);
}
//huffmans = minheap.getElement();// 得到排好序的数组
// 开辟叶结点
vector<Node*> que;
while(minheap.getSize()!=1) {
Huffman h1 = minheap.getElement(0);// 得到最小元素
Node* node1 = new Node(h1);
que.push_back(node1);
minheap.top();
Huffman h2 = minheap.getElement(0);// 得到第2小元素
Node* node2 = new Node(h2);
que.push_back(node2);
minheap.top();// 放入队列后就可以删除了
float fx = h1.frequency;
float fy = h2.frequency;
float fz = fx + fy;
// 新的第3个节点
Huffman temp;
temp.frequency = fz; // 无字符
temp.character = "O";
Node* mergeNode = new Node(temp);
minheap.add(temp); // 将第3个新增加的也添加进最小堆排序
mergeNode->lChild = node1;
mergeNode->rChild = node2;
}
Huffman last = minheap.getElement(0);// 得到最小元素
Node* lastnode = new Node(last);
que.push_back(lastnode);
// 打印结果
for (int i = que.size()-1; i >=0 ;i--) {
cout << " " << que[i]->data.character;
cout << endl;
}
}
private:
vector<Huffman> init(vector<string> &characters, vector<float> &frequencys) {
vector<Huffman> huffs;
for (int i = 0; i < characters.size();i++) {
Huffman h = Huffman();
h.character = characters[i];
h.frequency = frequencys[i];
huffs.push_back(h);
}
return huffs;
}
};
int main() {
Ques20201017 ques = Ques20201017();
vector<string> s = {"a","b","c", "d", "e","f"};
vector<float> freq = {45,13,12,16,9,5 };
ques.test(s, freq);
return 0;
}
运行效果
貌似还有些不完善的,比如如何去遍历这棵树的问题,作业太多就先不搞了= =