哈夫曼树与哈夫曼编码
哈夫曼树的作用:哈夫曼树是为解决哪种问题发明的
哈夫曼树的构建原理:哈夫曼树详解
下面浅谈我个人对哈夫曼树的理解及其实现:
阅读网上的哈夫曼树构造方法后,可以发现这是一个重复的过程:提取森林中权值最小的两棵树,并将它们组成新树,再将这个新树再次放入森林,然后重复以上步骤。既然是重复步骤,那么就可以递归实现(实际上,递归也是最易懂,最优雅的方法)。图解如下:
把链表第一个(b)和第二个节点©分别作为新根节点new_root的左右孩子,new_root的权值等于左右孩子权值之和,然后将new_root的权值和第三个(d)及其后面的节点权值依次比较,直到找到一个比new_root权值大的节点(找不到则放最后),并把new_root节点插入到此节点之前;重复上述步骤,图解如下:
最后把左孩子的权值改为0,右孩子权值改为1,然后遍历树,对各个字母进行编码代码实现如下:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758 ...
回溯法一:八皇后回溯
思路来源:动画演示八皇后回溯算法
视频演示的非常清楚,也有详细代码,所以此处不再过多剖析,仅展示代码。
视频中使用vector来储存每次尝试的结果;需要注意的是,vector本质是new开辟的,而下面代码的实现并未使用new开辟空间,而是采用的栈数组,所以撤销操作时,需要深度拷贝,否则函数返回后,数组将失效!
方案一:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081#include<iostream>#include<vector>using namespace std;int dir_x[8] = { -1, -1, -1, 1, 1, 1, 0, 0 };//创建两个方向数组int dir_y[8] = { 0 , 1, -1, 0, 1,-1, 1,-1 ...
const与constexpr的区别
constexpr是C++11的内容,其功能是 ==使指定的常量表达式获得在程序编译阶段计算出结果的能力,而不必等到程序运行阶段== 。
提出它的目的主要是为了解决const双重语义的问题。
"双重语义"是指”常量“与”只读“。
要搞清楚const与constexpr的关系,首先应该从”常量“与”只读“的区别入手。
只读 :侧重对变量或对象本身的属性或者权力。即某个变量没有权利(通过自身)更改其内存。如下:
12int b = 10;const int a = b;
不可通过a变量来修改a内存中的内容。
值得一提的是,这只是强调不可通过a来修改其内存,即不能继续以下操作:
a = 20;//a为只读,不可修改
但是却可以通过其他办法来改变其内存,很简单,使用指针修改即可:
int* pa = (int*)&a;
*pa = 30;//a的值就被修改成了30.
需要强调的是,C++中通过引用或指针修改const的值,只适用于赋的值为左值(变量)的情况!(即const int a = b;) 当被赋的值为右值(即字面量)时(const int a = 1 ...
kruskal算法剖析
一.问题引入
图中的6个顶点分别代表6个村庄,线段的权值代表村庄之间的距离。请问如何找到最短的路径来访问每一个村庄,且每个村庄只访问一次。
二.解决
提取图的边,并将边按权值大小从小到大排列,并放入edge数组。如下:
创建根数组(辅助数组),数组下标代表顶点节点本身,其元素代表顶点的根节点,如下:
提示:这里的“根”并不是树结构中真正的根,一棵树中只有一个根,而这里的“根”泛指长辈,可能是父节点,也可能是“爷”节点。初始化根数组为-1,代表初始状态下每个顶点都各自代表一个集合。
将edge数组中的边从小到大依次放回图中,如果后续加入的边与图中已放入的边形成了环,那么将此边丢弃,继续将下一条边放入,规则同前。
形成环,即说明加入的这条边的起点和终点已经属于一个集合,有共同的根。加入边的过程就是多个子集不断合并的过程,同一集合中的顶点不可相连。前面的辅助数组就是用来判断起点与终点是否属于一个集合。具体实现看代码注释。
放入(顶点数-1)条边后,最小生成树(Minimum Spanning Tree)构建完成,即可结束循环。
一棵树有n个节点,则有n-1条边 ...
C++11模板可变参数扩展包
本文不详细讲解如何在模板中使用可变参数,只浅谈对其中扩展包的理解。
看本文前建议先学习如何使用可变参数,推荐链接:
C++11在函数模板和类模板中使用可变参数
一.对代码格式的理解
为方便起见,直接把理解写在注释:
123456template<typename T,typename ...args>// ... 旨在说明args是个参数包类型(模板参数包)。 void vir_fun(T args, args... argv)//... 也旨在说明argv是参数包。{ //也就是说,上面两个 ... 相当于类型说明符,告诉编译器这是参数包。 cout<<argc<<endl; vir_fun(argv...);//而此处的...与上面的...作用不一样了!此处的...是在扩展参数包(即把包拆开),再依次传参}
如何理解args与argv?
args的作用和T一样,是一种类型,由此可推出 args是指(模板)参数包类型 。那么就可以顺利推出 ...