算法篇-7-贪心算法-Huffman编码&Dijkstra单源最短路径&Kruskal最小生成树

来源:互联网 发布:vps绑定域名 编辑:程序博客网 时间:2024/05/29 13:02

本系列所有代码https://github.com/YIWANFENG/Algorithm-github

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[n];       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[n]; // 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[n];       intprev[n];       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;}




0 0
原创粉丝点击