算法篇-7-贪心算法-Huffman编码&Dijkstra单源最短路径&Kruskal最小生成树
2016-12-27 21:51
567 查看
本系列所有代码https://github.com/YIWANFENG/Algorithm-github
依据给定字符以及相应频率构造该字符的哈夫曼编码。
算法思路分析与相关公式:
Haffman即前缀码,用一棵二叉树即可标示,树叶表示给定的字符,每个字符的前缀码就是从树根到该字符所在的树叶的一条道路。二叉树的每一路分支的路径我们在向右时即为1 ,向左时即为0。构造一棵haffman树即可得出我们的编码。
要使空间节约,那么使用频率低的字符编码长点,使用频率高的编码短点,对应的为相应的叶子节点的深度值大或者小。我们每次选择总频率小的子树的合并为新子树(频率为子树总频率之和),然后如此重复来得到最终编码树。(叶子结点视为频率为该节点的频率)。
程序源代码:
给定一带权有向图G=(V,E),求指定顶点X顶点的最短路径长度。
算法思路分析以及相关公式:
基本思想:设置顶点集合S并不断贪心扩充此集合。
首先确认两点,1:设从X到最短的顶点为x1(肯定是直达的),那么比这次短的顶点一定是直达的或者是从x1间接到达。 2:由1可知,设从X 到其他顶点第k短的是xk,(xk加入到S中),那么第k+1短的顶点一定是直达或者经过S中某点间接到达的。
做法是设置集合S,来收集已经知道X到该点的最短路径的点。E保存不知最短路径的点
1. 首先选择一个属于E的点u,使X到u路径最短。
2. 将u加入到S,在E中取出u,并计算在u加入S后对在E中的点与X的最短距离的影响。
即判断所有在E中的点c,是否存在从X到u,再从u到c的距离比原本X到c的距离小,若小则更新,否则不处理。
1.若S包含所有点,则结束否则重复上述过程。
程序源代码:
已知一幅图的顶点数n、边e以及边的权重w[i],求一权重最小的生成树。
算法思路分析以及相关数学公式:
将G的n个顶点看成n个孤立的联通分支,将所有边按权从小到大排序,然后依次增加权重最小的边来连接两个独立的联通分支,在加边时,注意不可以让加入的边在图中构成回路。
直至加入了n-1条边或者加入的边数为e。
另一种求最小生成树的算法是prim算法,他是从顶点的角度考虑。
huffman编码
题目:依据给定字符以及相应频率构造该字符的哈夫曼编码。
算法思路分析与相关公式:
Haffman即前缀码,用一棵二叉树即可标示,树叶表示给定的字符,每个字符的前缀码就是从树根到该字符所在的树叶的一条道路。二叉树的每一路分支的路径我们在向右时即为1 ,向左时即为0。构造一棵haffman树即可得出我们的编码。
要使空间节约,那么使用频率低的字符编码长点,使用频率高的编码短点,对应的为相应的叶子节点的深度值大或者小。我们每次选择总频率小的子树的合并为新子树(频率为子树总频率之和),然后如此重复来得到最终编码树。(叶子结点视为频率为该节点的频率)。
程序源代码:
class cmp { public: booloperator() (const int &a,const int &b) { if(a>b) return true; return false; } }; class CHuffmanChar { public: charc_; //保存字符 CHuffmanChar*parent_; //指向父节点 intchild_type_ ; //1==left子节点 0 == right }; class CHuffmanChar_Heap { public: intindex_; //原始编号 floatweight_; //权重 booloperator < (const CHuffmanChar_Heap &c2) const { return weight_ > c2.weight_; } }; void HuffmanEncoding(int n, const chardata[], float fre[]) { //Huffman编码 //n字符数量 //data[]原始字符 //fre[]字符出现的频率 CHuffmanCharhc[n + n-1]; //中间出现节点数n-1 inti, j; for(i=0;i<n; ++i) hc[i].c_ = data[i]; for(i=0;i<n+n-1; ++i) hc[i].parent_ = NULL; priority_queue<CHuffmanChar_Heap>q; CHuffmanChar_Heaph, hr; for(i=0;i<n; ++i) { h.index_= i; h.weight_ = fre[i]; q.push(h); } for(i=0;i<n-1; ++i) { h = q.top(); q.pop(); hr = q.top(); q.pop(); hc[h.index_].parent_ = &hc[n+i]; hc[h.index_].child_type_ = 1; hc[hr.index_].parent_ = &hc[n+i]; hc[hr.index_].child_type_ = 0; h.weight_ += hr.weight_; h.index_ = n+i; q.push(h); } charcode ; for(i=0;i<n; ++i) { //从子节点寻根而上 j = 0; CHuffmanChar *pc = &hc[i]; while(pc->parent_!=NULL) { code[j++]='1'-pc->child_type_; pc= pc->parent_; } cout<<hc[i].c_<<':'; for(j--; j>=0; j--) { cout<<code[j]; } cout<<'\n'; } }
Dijkstra单源最短路径
题目:给定一带权有向图G=(V,E),求指定顶点X顶点的最短路径长度。
算法思路分析以及相关公式:
基本思想:设置顶点集合S并不断贪心扩充此集合。
首先确认两点,1:设从X到最短的顶点为x1(肯定是直达的),那么比这次短的顶点一定是直达的或者是从x1间接到达。 2:由1可知,设从X 到其他顶点第k短的是xk,(xk加入到S中),那么第k+1短的顶点一定是直达或者经过S中某点间接到达的。
做法是设置集合S,来收集已经知道X到该点的最短路径的点。E保存不知最短路径的点
1. 首先选择一个属于E的点u,使X到u路径最短。
2. 将u加入到S,在E中取出u,并计算在u加入S后对在E中的点与X的最短距离的影响。
即判断所有在E中的点c,是否存在从X到u,再从u到c的距离比原本X到c的距离小,若小则更新,否则不处理。
1.若S包含所有点,则结束否则重复上述过程。
程序源代码:
template <class T_> void Dijkstra_ShortestPath(int n,int v,T_dist[],int prev[], const T_ *c,const T_ INF) { //n:(in)顶点数量 //v:(in)源顶点索引(0-...) //dist[]"(out)从源定点 到每个定点的距离 //prev[]:(out) 在最短路径上每个定点的前面的一个顶点 //c(in):两个顶点间的距离 bools ; // s[i]表示该点是否在最短路径已知的顶点集合内。 for(inti=0;i<n;i++) s[i] = false; for(inti=0; i<n; ++i) { dist[i]=c[v*n+i]; if(dist[i]==INF) prev[i] = -1; else prev[i]=v; } //addv to s dist[v]= 0; s[v]= true; for(inti=1; i<n; ++i) { T_ dist_tmp = INF; int u = v; //选出该次到v最短的点,该点不属于s for(int j=0; j<n; ++j) { if(s[j])continue; if(dist_tmp==INF|| dist[j]<dist_tmp) { dist_tmp = dist[j]; u = j; } } s[u] = true; //加入该点后对其他点的影响 for(int j=0; j<n; ++j) { if(s[j]|| c[u*n+j]==INF) continue; T_dist_new = dist[u]+c[u*n+j]; if(dist_new<dist[j]){ dist[j]=dist_new; prev[j] = u; } } } } template<class T_> void Out(int n,int v,T_ dist[],int prev[]) { //n:(in)顶点数量 //v:(in)源顶点索引(0-...) //dist[]"(out)从源定点 到每个定点的距离 //prev[]:(out) 在最短路径上每个定点的前面的一个顶点 intpath_points[n*(n-1)/2+1]; inti,j,k; for(i=0;i<n; ++i) { if(i==v) continue; cout<<"顶点"<<i<<",最短路径长度="<<dist[i]<<endl; j=1; path_points[0] =i; k = i; while(prev[k]!=v && prev[k]!=-1){ path_points[j++]= prev[k]; k=prev[k]; } for(--j; j>=0; j--) { cout<<path_points[j]<<" "; } cout<<endl; } } int main() { intn=5; constfloat INF = 65535; floatdist ; intprev ; floatc[n*n]; constfloat graph_edges[] = { 0,10,INF,30,100, 10,0,50,INF,INF, INF,50,0,20,10, 30,INF,20,0,60, 100,INF,10,60,0 }; for(inti=0;i<n*n;i++) { c[i]=graph_edges[i]; } Dijkstra_ShortestPath<float>(n,0,dist,prev,c,INF); Out<float>(n,0,dist,prev); cin.get(); return0; }
Kruskal最小生成树
题目:已知一幅图的顶点数n、边e以及边的权重w[i],求一权重最小的生成树。
算法思路分析以及相关数学公式:
将G的n个顶点看成n个孤立的联通分支,将所有边按权从小到大排序,然后依次增加权重最小的边来连接两个独立的联通分支,在加边时,注意不可以让加入的边在图中构成回路。
直至加入了n-1条边或者加入的边数为e。
另一种求最小生成树的算法是prim算法,他是从顶点的角度考虑。
//////////并差集实现////////////////// class CUnionFind { public: CUnionFind(); ~CUnionFind(); voidInit(int num_elem) ; intFindSubSet(int elem_id) ; voidSubSetUnion(int set1,int ste2); protected: intn_; //元素数量 int*parent_id_; //每个元素的父节点索引,-1表示该点为根节点 int*depth_; //每个元素所属子树的深度 voidRelease(); }; void CUnionFind::Release() { n_= 0; if(parent_id_){ delete [] parent_id_; parent_id_ = NULL; } if(depth_){ delete [] depth_; depth_ = NULL; } } CUnionFind::CUnionFind() { n_= 0; parent_id_= NULL; depth_= NULL; } CUnionFind::~CUnionFind() { Release(); } void CUnionFind::Init(int num_elem) { //元素编号从1开始 Release(); n_= num_elem; parent_id_= new int[n_]; depth_= new int[n_]; for(inti=0;i<n_;++i) { parent_id_[i]=-1; depth_[i]=0; } } int CUnionFind::FindSubSet(int elem_id) { //返回元素elem_id所属自己的编号 inti = elem_id-1; while(parent_id_[i]!=-1) i = parent_id_[i]; returni; } void CUnionFind::SubSetUnion(int set1,intset2) { //合并set1,set2 if(set1== set2) return ; if(depth_[set1]==depth_[set2]){ parent_id_[set2] = set1; depth_[set1]++; }else if(depth_[set1]<depth_[set2]){ parent_id_[set1] = set2; }else { parent_id_[set2] = set1; } }
class Edge{ public: intweight; intu,v; }; class cmp { public: booloperator() (Edge &a,Edge&b) { return a.weight > b.weight; } }; bool Kruskal(int n,int e,Edge E[],Edge t[]) { //n顶点数 //e边数 //E[]具体边 //t[](out)筛选出的边 priority_queue<Edge,vector<Edge>,cmp> q; for(inti=0; i<e; ++i) { q.push(E[i]); } CUnionFindU; U.Init(n); intk =0; while(e&& k<n-1) { //正常树应该会有n-1条边 Edge x; x = q.top(); q.pop(); e--; int a = U.FindSubSet(x.u); int b = U.FindSubSet(x.v); if(a!=b) { t[k++]= x; //cout<<"["<<x.u<<','<<x.v<<"]"<<endl; U.SubSetUnion(a,b); } } } void Out(int n,Edge t[]) { //n顶点数 //t[] 筛选出的边 for(inti=0;i<n-1;++i) { cout<<"["<<t[i].u<<','<<t[i].v<<"]"<<endl; } } int main() { intn = 6; EdgeE[10]; Edget[10]; E[0].u= 1; E[1].u = 1; E[2].u = 1; E[0].v= 2; E[1].v = 3; E[2].v = 4; E[0].weight= 6; E[1].weight = 1; E[2].weight = 5; E[3].u= 3; E[4].u = 5; E[5].u = 3; E[3].v= 2; E[4].v = 2; E[5].v = 4; E[3].weight= 5; E[4].weight = 3; E[5].weight = 5; E[6].u= 3; E[7].u = 3; E[8].u = 4; E[6].v= 5; E[7].v = 6; E[8].v = 6; E[6].weight= 6; E[7].weight = 4; E[8].weight = 2; E[9].u= 5; E[9].v= 6; E[9].weight= 6; Kruskal(n,10,E,t); Out(n,t); cin.get(); return0; }
相关文章推荐
- 浅析最小生成树和单源最短路径的区别(含Prim、Kruskal、Dijkstra、Bellman-Ford)
- 贪心算法 最小生成树prim与单源最短路径dijkstra
- 关于图的常用算法——Dijkstra单源最短路径、Floyd多源最短路径、Prim和Kruskal最小生成树算法
- Kruskal 最小生成树 & Dijkstra 最短路径
- 最小生成树算法(Prime、Kruskal)和最短路径算法(Dijkstra、Floyd)
- Spark GraphX之Dijkstra(单源最短路径)、Prime(最小生成树)、FloydWarshall(多源最短路径)
- 稀疏图(邻接链表),并查集,最短路径(Dijkstra,spfa),最小生成树(kruskal,prim)
- 稠密图(邻接矩阵),并查集,最短路径(Dijkstra,spfa),最小生成树(kruskal,prim)
- Prim && Kruskal 生成MST(最小生成树)及最短路径问题
- C++实现矩阵图的遍历·最小生成树(prim,kruskal)·最短路径(Dijkstra,floyd)
- 图(Graph)——最小生成树、最短路径、Kruskal、Dijkstra、Floyd
- 贪心算法之最小生成树prim与单源最短路径dijkstra
- poj2253 Frogger(最小生成树求最大边/最短单源路径变形)
- 贪心算法(Greedy Algorithm)之最小生成树 克鲁斯卡尔算法(Kruskal's algorithm)
- 最小生成树、拓扑排序、单源最短路径
- dijkstra最短路径算法和普里姆最小生成树算法优化的关键
- 贪心算法(Greedy Algorithm)之最小生成树 克鲁斯卡尔算法(Kruskal's algorithm)
- POJ1797 Heavy Transportation (最短路径/最小生成树kruskal)
- POJ1797 Heavy Transportation (最短路径/最小生成树kruskal)
- POJ1797 Heavy Transportation (最短路径/最小生成树kruskal)