【算法-贪心算法-2】

例题【4】哈夫曼编码
问题概念:
哈夫曼编码是广泛地用于数据文件压缩的十分有效的编码方法,哈夫曼编码算法用字符在文件中出现的频率表来建立一个用0,1串表示各字符的最优表示方式。

如何构造?
哈夫曼算法以自底向上的方式构造表示最优前缀码的二叉树T。
算法以|C|个叶结点开始,执行|C|-1次的“合并”运算后产生最终所要求的树
每次合并频数最小的两个节点
在这里插入图片描述
代码描述:

template <class type>
Binary Tree <int> HuffmanTree(Type f[],int n) 
	//生成单结点树,即构建以这n个结点为叶子结点的哈弗曼树
	Huffman<type > *w=new  Huffman<Type>[n+1];//创立一个哈夫曼数组 
	BinaryTree <int> z,zero;
	for(int i=1;i<=n;i++){
		z.MakeTree(i,zero,zero);
		w[i].weight=f[i];
		w[i].tree=z;
	}
 } 
 //用最小堆实现优先队列
 MinHeap<Huffman<Type>>Q(1);
 Q.Initialize(w,n,n);
 //反复合并最小频率树
 Huffman<Type>x,y;
 for(int i=1;i<n;i++){
 	Q.DeleteMin(x);
 	Q.DeleteMin(y);
 	z.MakeTree(0,x.tree,y.tree);//合并 
 	x.weight+=y.weight=z;
 	Q.Insert(x);//将新树插入优先队列Q 
 } 
 Q.DeleteMin(x);
 Q.Deactivate();
 delete[]w;
 return x.tree;

哈夫曼算法的正确性:
(1)贪心选择性质
通过合并来构造一颗哈夫曼数的过程可以从合并两个权值最小的字符开始
(2) 最优子结构性质
问题的最优解包含子问题的最优解
算法时间复杂度为O(nlogn)

例题【4】单源最短路径
问题概念:给定一个带权有向图,给定一个顶点,称为源,现在计算从源到左右其他各顶点的最短路长度,这里的长度是指边上权值之和
思想:Dijkstra算法
分析性质:
最优子结构性质:如果P(i,j)={Vi…Vk…Vs…Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径
因此可以提出以最短路径长度递增,逐次生成最短路径的算法。
简单来说就是设立一个顶点集S存放当前已经找到最短路径的顶点,一个dist数组用来存放各顶点与顶点集S的最短距离,每次都从各顶点中选择dist最小的顶点加入顶点集S,并更新Dist值,直到S包含所有顶点时停止。

代码实现:

//n:结点数  v:源结点号 dist[i]:源到第i个结点的距离
//prev[i]:第i个结点的前一个结点下标
//c[i][j]:图中第i个结点到第j个结点间的路径长
template <class Type>
void Dijkstra(int n,int v,Type dist[],int prev[],Type **c)
{ bool s[maxint];    //s为已访问的结点集合
//初始化  
for(int i=1;i<=n;i++)
  { dist[i]=c[v][i];  //初始dist[i]为图中v到i的路长
    s[i]=false;
	if(dist[i]==maxint) prev[i]=0; //若v到i之间没有边
	 else               prev[i]=v; //存在边,前一个结点v
  } 
//将源结点v放入集合s
dist[v]=0; s[v]=true;
//通过n-1次循环将每一个结点放入s
  for(i=1;i<n;i++)
  { int temp=maxint;
    int u=v;
	//从所有未被放入S的结点中找出dist最小的,将其放入s中
     for(int j=1;j<=n;j++)
	  if(!s[j]&&(dist[j]<temp))
	  { u=j;
	    temp=dist[j];//最小值更新 
	  }
	  s[u]=true;
          //将u结点放入s后,重新计算通过u连接的所有未被放入s中结点的dist值
	  for(int j=1;j<=n;j++)
	      if(!s[j]&&(c[u][j]<maxint))
		{ Type newdist=dist[u]+c[u][j];
	      if(newdist<dist[j])
             {dist[j]=newdist; prev[j]=u;}
		  }
  }
}

算法的正确性和计算复杂性
(1)贪心选择性质:它所作的贪心选择是从V-S中选择具有最短特殊路径的顶点u,从而确定从源到u的最短路径长度dist[u]
(2)最优子结构性质:只要说明原问题的最优解包含子问题的最优解就可以了 。
(3)复杂性O(n^2)

例题【6】最小生成树
问题概念:
给定一个无向连通带权图G,如果G‘是一颗包含G中所有顶点的树,则称G’为G的生成树。
生成树上各边权的总和称为该生成树的耗费,耗费最小的生成树称为G的最小生成树。
最小树拥有最小生成树性质MST:权重最小的边一定在最小生成树中。

下面介绍以MST为基础的两种算法:
Prim算法
在这里插入图片描述

Void Prim(int n,Type **c)
{  
   T=NULL;
   S={1};
   while(S!=V)
    { (i,j)=(i∈S且j ∈ V-S的最小边);
       T=T∪{(i,j)};
       S=S∪{j};
     }
}

Kruskal算法
在这里插入图片描述
首先将G的n个顶点看成n个孤立的连通分支。将所有的边按权从小到大排序。

然后从第一条边开始,依边权递增的顺序查看每一条边,并按下述方法连接2个不同的连通分支:

当查看到第k条边(v,w)时,如果端点v和w分别是当前2个不同的连通分支T1和T2中的顶点时,就用边(v,w)将T1和T2连接成一个连通分支,然后继续查看第k+1条边;

如果端点v和w在当前的同一个连通分支中,就直接再查看第k+1条边。这个过程一直进行到只剩下一个连通分支时为止。

例题【7】 多机调度问题
问题概念:给定作业集(作业编号,作业处理时间),给定m台机器,要求在尽可能短的时间内处理完成。
注意:这是NP完全问题,但是贪心选择可以设计出较好的近似算法。
策略:最长处理时间作业优先
复杂度:
当n≤m时,算法只需要O(1)时间。
当n>m时,需排序,算法所需的计算时间为O(nlogn)。
在这里插入图片描述
在这里插入图片描述
值得注意的是,每次分配完任务,都需要更新这台机器的最新完成时间,而下一次分配任务则分配到完成时间最早的机器上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值