一、struct对齐
编译过程中,从上到下编译:
1.按照当前最长的元素申请空间
2.按照当前元素长度对齐
比如一个比较典型的例子
struct node{
char a; //char a的时候,当前最大为1,申请空间为1,故a地址为0-1(相对node偏移)
int b; //当前最大b为4,并且空间不够,所以按照4的倍数申请空间0-8,并且b需要对齐到4倍数的地址4-8
char c; //已申请空间0-8已经用完,故再按照当前最大类型4的倍数申请空间至0-12;8-9
char f; //9-10
char g; //10-11
short d; //还剩下11-12的空间,明显不够,故再按照4倍数申请空间0-16;d放在12-14
char e; //14-15
//所以struct node的大小为16个字节
};
struct中含有struct的时候,也就是有嵌套struct的时候,子struct按照struct内最大元素对齐。规则类似。
二、c语言浮点数取整问题
一般整数都是向下取整,浮点数都是四舍五入
float s = 1.456;
printf("%.2f\n", s); // 1.46
三、虚函数与虚析构函数
父类中定义virtual函数,可以使得父类指针指向子类的时候,调用子类的函数。(多态)
父类定义虚析构函数,释放子类实体空间的时候,会调用子类的析构函数,然后调用父类的析构函数。
为什么需要有虚析构函数。那先来看没有虚析构函数的情况。
如果父类指针指向子类,释放父类指针空间的时候,只有父类的析构函数被调用。这显然是错误的。网上有解释说这是c++的undefined behavior,可以认为是“危险的操作”,比如删除空指针,引用一个已经被释放的对象等等。
如果想在父类指针下释放子类的空间怎么办?那就把父类的析构函数定义为虚析构函数。这样,delete parent的时候,先调用子类的析构函数,再调用父类的析构函数。
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class Base{
public:
int val;
void fun1();
virtual void fun2();
Base():val(1){};
virtual ~Base(){
cout << "Deleting Base" << endl;
}
};
void Base::fun1(){
cout << "Base fun1" << endl;
}
void Base::fun2(){
cout << "Base fun2" << endl;
}
class Derived: public Base{
public:
void fun1();
void fun2();
~Derived(){
cout << "Deleting Derived" << endl;
}
};
void Derived::fun1(){
cout << "derived fun1"<<endl;
}
void Derived::fun2(){
cout << "derived fun2"<<endl;
}
class Derived2: public Base{
public:
void fun1();
void fun2();
~Derived2(){
cout << "Deleting Derived2" << endl;
}
};
void Derived2::fun1(){
cout << "derived2 fun1"<<endl;
}
void Derived2::fun2(){
cout << "derived2 fun2 "<<endl;
}
int main(int argc, char *argv[]){
Base *p1 = new Base();
Base *p2 = new Derived();
Derived2 *c2 = new Derived2();
p1->fun1();
p1->fun2();
cout << endl;
p2->fun1();
p2->fun2();
cout << endl;
p2 = (Base *)c2;
p2->fun2();
cout << endl;
Derived *c1 = new Derived();
c1->fun1();
c1->fun2();
cout << endl;
delete p1;
cout << endl;
delete p2;
cout << endl;
delete c1;
return 0;
}
四、Dijkstra算法
function Dijkstra(G, w, s)
for each vertex v in V[G] // 初始化
d[v] := infinity // 将各点的已知最短距离先设成无穷大
previous[v] := undefined // 各点的已知最短路径上的前趋都未知
d[s] := 0 // 因为出发点到出发点间不需移动任何距离,所以可以直接将s到s的最小距离设为0
S := empty set
Q := set of all vertices
while Q is not an empty set // Dijkstra算法主体
u := Extract_Min(Q) //将顶点集合Q中有最小d[u]值的顶点u从Q中删除并返回u
S.append(u)
for each edge outgoing from u as (u,v)
if d[v] > d[u] + w(u,v) // 拓展边(u,v)。w(u,v)为从u到v的路径长度。
d[v] := d[u] + w(u,v) // 更新路径长度到更小的那个和值。
previous[v] := u // 纪录前趋顶点
五、Hash表的一些概念
链地址法:
固定大小的表长度,每个表项是一个桶,可以是链表或者红黑树之类的。不同键值永远不会冲突
平均查找长度
ASL= p1c1 + p2c2... + pncn
这里,pi指的是查找第i个数据的概率,ci指的是查找第i个数据所需的查找次数。
题目:
设散列表的长度为8,散列函数H(k)=k mod 7,初始记录关键字序列为(32,24,15,27,20,13),计算用链地址法作为解决冲突方法的平均查找长度是
一共有6个记录,每个记录概率相等为1/6,所以p1=p2=..=p6=1/6
散列表长度为8,关键字的键值为(4,3,1,6,6,6),故查找长度分别为(1,1,1,1,2,3)
所以平均查找长度为9/6