二进制堆的C++实现及其在机器调度(LPT)上的简单应用
说明:禁止转载,对源码的要求是禁止把这个东西原封不动或非常小量改动后用于课程设计(我很建议你自己动手实现,你会做的比我更好),源码仅供学习参考,思路仅供参考,仍有不足,欢迎评论指出。
3.1 总体功能设计
实现二进制堆的查找、插入、删除、合并、修改操作,及简单应用
3.2 数据结构设计
抽象数据类型的定义
typedef int T;
struct bin_node
{
T key;//值
int degree;//度
bin_node *first_child;//第一个孩子
bin_node *next;//兄弟
bin_node *parent;//父亲
bin_node() :degree(0),first_child(NULL), next(NULL), parent(NULL) {}//默认构造函数
};
3.3 函数原型设计
//查找操作
bin_node* binomial_search(bin_node* heap, T key)
{
果p的值等于key,return p;(开始p为根节点)
否则进入循环,只要p不为空
{
如果有((child = binomial_search(parent->first_child, key)) != NULL)
return child;
parent = parent->next;
}
return NULL;
}
//修改操作:修改节点的值
static void binomial_update_key(bin_node* heap, bin_node* node, T key)
{
如果key小于节点node的值,就调用
binomial_decrease_key(heap, node, key);
大于则调用
binomial_increase_key(heap, node, key);
相等则print不需要修改
}
static void binomial_increase_key(bin_node* heap, bin_node *node, T key)
{
先用search找到要修改的节点,修改key的值,然后调整
如果"当前节点" < "它的左孩子",
则在"它的孩子中(左孩子 和 左孩子的兄弟)"中,找出最小的节点;
然后将"最小节点的值" 和 "当前节点的值"进行互换
交换数据之后,再对"原最小节点"进行调整,使它满足最小堆的性质:父节点 <= 子节点
}
binomial_decrease_key{方法同increase函数}
//删除操作:删除键值为key的节点,并返回删除节点后的二项树
bin_node* binomial_delete(bin_node* heap, T key)
{
调用search函数找到节点的位置
再将被删除的节点的数据上移到它所在的二项树的根节点
while (parent != NULL) parent = node->parent;
由于根节点的孩子节点按度数递减排列,故需要调用reverse函数逆序去掉了根节点的孩子节点,使其按度数升序排列
}
static bin_node* binomial_reverse(bin_node* heap)
{
就是单链表的逆序
先让头的next为空再逐个让p的下一个节点指向p,每次循环后p后移一位
}
BinomialNode* union(bin_node h1,bin_node h2)//合并
{
bin_node *heap;
bin_node *prev_x,*x,*next_x;
heap=merge(h1,h2);//调用merge使两个二项堆的根链表合并成一个链表,合并后的新链表按照'节点的度数'单调递增排序
如果heap为空,返回空值
令prev_x为空,x=heap,next_x=x->next;
当next_x不为空时执行循环
{
如果x的深度不等于next_x的深度或(next_x不指向空且next_x的深度等于next_x的next的深度)
就令prev_x=x; x=next_x;
或者x的key值小于等于next_x的key值时
就令x->next=next_x->next;
调用link函数link(next_x,x);
否则
{
当prev_x为空时,令heap=next_x
否则让prev_x->next=next_x;
再连接link(x,next_x);
让x=next_x;
}
让next_x=x->next;
}
返回heap
}
//两个二项堆的根链表合并成一个链表,合并后的新链表按照'节点的度数'单调递增排序
static bin_node* merge(bin_node h1,bin_node h2)
{
bin_node* head=NULL;
bin_node** pos=&head;
当h1和h2都不为空时执行循环
{
如果h1的度数小于h2的度数,pos指向h1的地址,h1指向h1的next
否则pos指向h2的地址,h2指向h2的next
pos指向*pos的next的地址
}
如果h1不为空,pos指向h1的地址
否则pos指向h2的地址
返回head
}
static void link(bin_node child,bin_node heap)//将"二项堆child的根节点"设为"二项堆heap的左孩子",从而将child整合到heap中去
{
让child的根节点为heap
让child指向heap的左孩子
让heap的左孩子指向child的左孩子
heap的深度加一
}
//插入
bin_node* insert(bin_node heap,int key)
{
bin_node* node;
如果该key值已存在(通过search函数搜索)
输出该key值已存在
返回heap
创建一个为key值的节点,并赋给node
如果node为空
返回heap
否则返回将node和heap合并的根节点(通过合并函数union)
}
3.4 输入输出设计
输入输出主要是内置的测试。LPT调度这个应用在用的时候,可以很容易改成输入输出模块。
3.5 主算法设计
各模块的功能
binomial_link、binomial_union和binomial_merge函数实现合并操作,主程序调用union函数,union函数调用link和merge函数
binomial_search函数实现查找操作
binomial_update_key、 binomial_decrease_key和binomial_increase_key函数改变节点的值,主函数调用update,判断要改小还是改大节点的值,改小则调用decrease和改大则调用increase函数
binomial_insert 和make_binomial_node实现插入一个节点,主函数调用insert找到要插入节点的位置,make_binomial_node申请空间新建节点
binomial_delete、binomial_search 、binomial_reverse函数实现删除一个节点,主函数调用delete函数,delete函数调用search函数找到该节点,删除节点后,delete再调用reverse将去掉了根节点的孩子节点逆序,使其按度数升序排列
binomial_print打印节点
主函数的流程
运行bin_test()函数,新建堆a,b,c分别对插入删除合并函数通过prin函数打印测试,运行LPT(),对堆的pop()进行测试,并实现简单应用。
4.3 个人设计实现(按组员分工)
4.3.1王震
//查找
bin_node* binomial_search(bin_node* heap, T key)
{
bin_node *child;
bin_node *parent = heap;
parent = heap;
while (parent != NULL)
{
if (parent->key == key)
return parent;
else
{
if((child = binomial_search(parent->first_child, key)) != NULL)
return child;
parent = parent->next;
}
}
return NULL;
}
//修改节点的值
static void binomial_update_key(bin_node* heap, bin_node* node, T key)
{
if (node == NULL)
return ;
if(key < node->key)
binomial_decrease_key(heap, node, key);
else if(key > node->key)
binomial_increase_key(heap, node, key);
else
printf("不需要修改");
}
bin_node* make_binomial_node(T key)
{
bin_node* node;
node = (bin_node*)malloc(sizeof(bin_node));
if (node==NULL)
{
printf("申请空间失败\n");
return NULL;
}
node->key = key;
node->degree = 0;
node->parent = NULL;
node->first_child = NULL;
node->next = NULL;
return node;
}
//减关键字的值:将二项堆heap中的节点node的键值减少为key。
static void binomial_decrease_key(bin_node* heap, bin_node *node, T key)
{
if ((key >= node->key) || (binomial_search(heap, key) != NULL))
{
printf("修改失败,新节点%d已经存在,或者小于要修改节点的值%d", key, node->key);
return ;
}
node->key = key;
bin_node *child, *parent;
child = node;
parent = node->parent;
while(parent != NULL && child->key < parent->key)
{
swap(parent->key, child->key);
child = parent;
parent = child->parent;
}
}
//加关键字的值:将二项堆heap中的节点node的键值增加为key。
static void binomial_increase_key(bin_node* heap, bin_node *node, T key)
{
if ((key <= node->key) || (binomial_search(heap, key) != NULL))
{
printf("修改失败,新节点%d已经存在,或者大于于要修改节点的值%d", key, node->key);
return ;
}
node->key = key;
bin_node *cur, *child, *least;
cur = node;
child = cur->first_child;
while (child != NULL)
{
if(cur->key > child->key)
{
// 如果"当前节点" < "它的左孩子",
// 则在"它的孩子中(左孩子 和 左孩子的兄弟)"中,找出最小的节点;
// 然后将"最小节点的值" 和 "当前节点的值"进行互换
least = child;
while(child->next != NULL)
{
if (least->key > child->next->key)
{
least = child->next;
}
child = child->next;
}
// 交换最小节点和当前节点的值
swap(least->key, cur->key);
// 交换数据之后,再对"原最小节点"进行调整,使它满足最小堆的性质:父节点 <= 子节点
cur = least;
child = cur->first_child;
}
else
{
child = child->next;
}
}
}
//删除节点:删除键值为key的节点,并返回删除节点后的二项树
bin_node* binomial_delete(bin_node* heap, T key)
{
bin_node *node;
bin_node *parent, *prev, *pos;
if (heap==NULL)
return heap;
// 查找键值为key的节点
if ((node = binomial_search(heap, key)) == NULL)
return heap;
// 将被删除的节点的数据数据上移到它所在的二项树的根节点
parent = node->parent;
while (parent != NULL)
{
// 交换数据
swap(node->key, parent->key);
// 下一个父节点
node = parent;
parent = node->parent;
}
// 找到node的前一个根节点(prev)
prev = NULL;
pos = heap;
while (pos != node)
{
prev = pos;
pos = pos->next;
}
// 移除node节点
if (prev)
prev->next = node->next;
else
heap = node->next;
heap = binomial_union(heap, binomial_reverse(node->first_child));
free(node);
return heap;
}
static bin_node* binomial_reverse(bin_node* heap)
{
bin_node* next;
bin_node* tail = NULL;
if (!heap)
return heap;
heap->parent = NULL;
while (heap->next)
{
next = heap->next;
heap->next = tail;
tail = heap;
heap = next;
heap->parent = NULL;
}
heap->next = tail;
return heap;
}
4.3.2刘津源
//合并
bin_node* binomial_union(bin_node* h1, bin_node* h2)
{
bin_node *heap;
bin_node *prev_x, *x, *next_x;
// 将h1, h2中的根表合并成一个按度数递增的链表heap
heap = binomial_merge(h1, h2);
if (heap == NULL)
return NULL;
prev_x = NULL;
x = heap;
next_x = x->next;
while (next_x != NULL)
{
if ( (x->degree != next_x->degree)
|| ((next_x->next != NULL) && (next_x->degree == next_x->next->degree)))
{
// Case 1: x->degree != next_x->degree
// Case 2: x->degree == next_x->degree == next_x->next->degree
prev_x = x;
x = next_x;
}
else if (x->key <= next_x->key)
{
// Case 3: x->degree == next_x->degree != next_x->next->degree
// && x->key <= next_x->key
x->next = next_x->next;
binomial_link(next_x, x);
}
else
{
// Case 4: x->degree == next_x->degree != next_x->next->degree
// && x->key > next_x->key
if (prev_x == NULL)
{
heap = next_x;
}
else
{
prev_x->next = next_x;
}
binomial_link(x, next_x);
x = next_x;
}
next_x = x->next;
}
return heap;
}
static void binomial_link(bin_node* child, bin_node* heap)
{
child->parent = heap;
child->next = heap->first_child;
heap->first_child = child;
heap->degree++;
}
static bin_node* binomial_merge(bin_node* h1, bin_node* h2)
{
bin_node* head = NULL; //heap为指向新堆根结点
bin_node** pos = &head;
while (h1 && h2)
{
if (h1->degree < h2->degree)
{
*pos = h1;
h1 = h1->next;
}
else
{
*pos = h2;
h2 = h2->next;
}
pos = &(*pos)->next;
}
if (h1)
*pos = h1;
else
*pos = h2;
return head;
}
//插入操作
bin_node* binomial_insert(bin_node* heap,T key)
{
bin_node* node;
if (binomial_search(heap, key) != NULL)
{
printf("插入失败,给定的值%d已经存在\n", key);
return heap;
}
node = make_binomial_node(key);
if (node==NULL)
return heap;
return binomial_union(heap,node);
}
void bin_test()
{
bin_heap<int> a;
bin_heap<int> b;
bin_heap<int> c;
//bin_node<int> *d= new bin_node<int>[5];
//bin_node<int> *e = new bin_node<int>[5];
for (int i = 1; i <= 5; ++i)
{
//d[0].key = i;
a.insert(i);
}
cout << "二进制对a为" << endl;
a.prin();
for (int i = 6; i <= 10; ++i)
b.insert(i);
cout << "二进制对b为" << endl;
b.prin();
c.merge(a.heap(), b.heap());
cout << "二进制对c为" << endl;
c.prin();
c.erase(6);
cout << "删除6后二进制对c为" << endl;
c.prin();
}//我通过二进制堆a,b,c分开测试功能
其中最重要的一个模块是template<class T>
void bin_heap<T>::prin ()
{
bin_node<T> *p = root;//从根节点开始
int i = 1,j = 0;
while (p != NULL)
{
cout << "第" << i << "颗二进制树" << endl;
bin_node<T> *second = p;//second代表每颗树
cout<< second->key<<endl;
second = second->first_child;//不在首节点,向下找
prin_tree(second);
/*while (second != NULL)
{
bin_node<T> *third = second;
while (third != NULL)
{
cout << third->key<<" ";
third = third->next;
}
cout << endl;
second = second->first_child;
}*/
p = p->next;
++i;
}//一颗一颗树的遍历,树按层次遍历
}
Prin函数,这里我起初看注释我是想写循环遍历的,后来发现有的节点遍历不到,所以我写了递归while (p != NULL)
{
cout << "第" << i << "颗二进制树" << endl;
bin_node<T> *second = p;//second代表每颗树
cout<< second->key<<endl;
second = second->first_child;//不在首节点,向下找
prin_tree(second);
这个递归的意思是从每棵树的第一个孩子开始,先往兄弟节点那里遍历(准确说是加入递归栈),不断遍历直到递归栈全部出来,其实我想是做层次遍历的,但这样到深层次就无法按层次,因为不同树的同一层的兄弟节点不是连在一起的,所以不是按层输出,但堆分的很明显了,也达到了检验的效果。
设计的是一个二进制堆,一个经典的应用就是LPT机器调度问题。首先何谓机器调度。假设你有m台机器,刚开始他们都是空闲的,你有n个任务,每个任务的时间是不同的,分配这m台机器,机器只有空闲的时候能够工作,并且一次只能工作一次,求最短的工作时间。
那如何模拟呢?
struct lpt_machine
{
int ma_name;
int avail;
lpt_machine(int i,int j):ma_name(i), avail(j){}
bool operator <(lpt_machine x) { return avail < x.avail; }
bool operator <=(lpt_machine x) { return avail <= x.avail; }
bool operator ==(lpt_machine x) { return avail == x.avail; }
};
void LPT()
{
bin_heap<int> c;
bin_heap<lpt_machine> machine;
int a[7] = { 2,14,4,16,6,5,3 };
int m = 3;
for (int i = 0; i <7; ++i)
c.insert(a[i]);
int b[7];
for (int i = 0; i <7; ++i)
{
b[i] = c.bin_pop();
cout << b[i] << ' ';
}//a任务排序
cout << endl;
for (int i = 0; i < 3; ++i)
{
lpt_machine x(i, 0);
machine.insert(x);
}//初始化机器
for (int i = 6; i > 0; --i)
{
lpt_machine y = machine.bin_pop();
cout << "任务长度为" << b[i] << "的任务" << "分配到机器" << y.ma_name << "时间为" << y.avail
<< "到" << (y.avail + b[i]) << endl;
y.avail += b[i];
machine.insert(y);
}//生成调度方案
}
首先先设计一个机器的结构体,为了简单,我就没设计给它名字呢,直接给了它一个int表示名字。int a[7] = { 2,14,4,16,6,5,3 };这是我人为定义的一个任务序列,内容代表时长,机器我也初始化为3个, for (int i = 0; i < 3; ++i)
{
lpt_machine x(i, 0);
machine.insert(x);
}//初始化机器
为什么用这个,因为便于检验正确性,它的正确答案应该是17
这是它一个其中的调度。然后你要做的第一件事是堆排序给任务排好顺序,然后机器是有个空闲时间的,你先给机器建立一个堆,依此把任务放进去,每次放任务进去的时候y.avail += b[i],机器的空闲时间就增加了,相当于模拟机器执行任务,然后堆顶的机器是空闲时间是最小的,直到任务调完。生成了机器调度序列。
5 测试与调试
Bin_test()
LPT()
二进制堆的C++实现及其在机器调度(LPT)上的简单应用相关推荐
- 二进制信号在信噪比为127:1的4kHz信道上传输,最大数据传输速率可以达到( )
二进制信号在信噪比为127:1的4kHz信道上传输,最大数据传输速率可以达到( B ) A. 28000b/s B. 8000b/s C. 4000b/s D. 无限大 根据香农定理,最大数据传输速率 ...
- 眼明、心智、制楔:我们在CES Asia的服务机器人大秀上看到了什么?
在今年的CES Asia上,除去集中展示的5G.AI等新技术,满场跑的服务机器人照例是一大亮点.猎豹移动.思岚科技.科大讯飞.擎朗智能等厂商都进行了相关展示,其中还有厂商将自己的展区打造成了" ...
- 机器狗背上枪成了无人杀手,6.5mm口径1200米射程,制造商已与美澳军队广泛合作...
梦晨 发自 凹非寺 量子位 报道 | 公众号 QbitAI 做机器人最出名的波士顿动力公司在合同中禁止给他们的机器人产品配备武器,但拦不住别的机器人公司这样做. 这只背着半自动步枪的机器狗,在刚刚结束 ...
- 机器“血液”登上Nature:一条假鱼靠它续航36小时,无需固态电池
鱼栗子 乾明 发自 凹非寺 量子位 报道 | 公众号 QbitAI 机器人能拥有血液系统吗? 可以,而且还能像生物的血液系统一样,为机器的各个组件传递能量. 来,给你看条鱼. 这条机器鱼,不依靠固体 ...
- 机器狗背上枪成了杀手,已经与美澳军队合作!
1. 前言 做机器人最出名的波士顿动力公司在合同中禁止给他们的机器人产品配备武器,但拦不住别的机器人公司这样做. 这只背着半自动步枪的机器狗,在刚刚结束的美国陆军协会年会上亮相,由机器人公司Gh ...
- vm ubuntu设置中文_如何在本地Ubuntu Linux机器或VM上设置LAMP服务器
vm ubuntu设置中文 The purpose of this brief guide is to take you through the process of setting up a LAM ...
- h5如何上传文件二进制流_Hadoop如何将TB级大文件的上传性能优化上百倍?
这篇文章,我们来看看,Hadoop的HDFS分布式文件系统的文件上传的性能优化. 首先,我们还是通过一张图来回顾一下文件上传的大概的原理. 由上图所示,文件上传的原理,其实说出来也简单. 比如有个TB ...
- windows虚拟机迁移到新机器连不上网络
问题描述: 从另一台windows机器将linux文件拷贝到另一个windows中,(可以使用xshell连接) VMware是新装的,没有装过虚拟机, 虚拟机中输入命令:ip add 查看虚拟机的i ...
- 吴恩达《构建机器学习项目》精炼笔记(1)-- 机器学习策略(上)
AI有道 不可错过的AI技术公众号 关注 重要通知 本公众号原名"红色石头的机器学习之路"已经改名为"AI有道",请大家留意并继续关注本公众号!谢谢! 1 Wh ...
- mysql 权限 机器_msyql 权限配置 (mysql 其他机器连不上 )
grant 权限1,权限2,-权限n on名称.表名称 to 用户名@用户地址 identified by '连接口令'; GRANT ALL PRIVILEGES ON *.* TO 'root'@ ...
最新文章
- dnsmasq搭建简易DNS服务器
- python3.5怎么安装pip-在python3.5中使用pip
- java-io之RandomAccessFile
- 视图添加字段_使用ExploreByTouchHelper辅助类为自定义视图添加虚拟视图
- python图像下采样_[Python图像处理]十二.图像向下取样和向上取样
- 水系图一般在哪里找得到_进展 | 水系钠离子电池研究取得重要进展
- 发些c/c++/vc/驱动/网络安全的好书和资料
- Java核心技术卷1: 多线程
- 最近病毒缠身,帖两个病毒的解决方法.
- python etree创建xml_python-如何使用xml.etree.Element编写XML声明
- 主成分分析(PCA)算法实现iris数据集降维
- 为啥春节抢红包总不是运气王?看完微信抢红包算法你就明白了
- CSR8615蓝牙芯片功能调试入门笔记---上
- 黑群晖二合一已损毁_黑群晖二合一ghost安装教程(蜜獾超存可用)
- xzp android webview,加载gif动态图的三种方式
- 【day02】选择题题解
- 新手上路参考驾驶手册 36计教你安全上路(收集)
- 有效括号 python_1111. 有效括号的嵌套深度
- plc梯形图转换c语言,求助将梯形图程序转换成指令表,并说明该段梯形图实现的功能...
- 机器学习和深度学习面试题
热门文章
- Theano简单入门(三)
- TSE for SketchUp Pro - 建筑行业
- 数据分析tableau 和 python的区别_数据分析师综述篇
- 【HMS core】【Ads Kit】华为广告——海外应用在国内测试正式广告无法展示
- jQuery读书笔记
- Image Super-Resolution via Iterative Refinement 论文解读和感想
- vcard文件怎么导入手机_如何从单个vCard(.vcf)文件将多个联系人导入Outlook 2013
- install Oracle JDK in Linux:安装oracle JDK in linux
- 求一个集合的所有幂集
- Springboot毕设项目管易tms运输智能监控管理系统663kq(java+VUE+Mybatis+Maven+Mysql)