一、树
由n个节点组成的有限集
有一个根节点;其他节点只有一个前驱节点,但可以有多个后继节点。(一对多)
n = 0, 空树
叶子节点(终端结点):只有前驱结点没有后继结点,非叶子节点(分支节点)
结点度:子节点的个数称之为度
树的(广)度:树中各节点度的最大值
深度:从根节点到最底层节点的层数
森林:n个互不相交的树的集合
二叉树:任意一个节点的子节点个数不能超过2个(树的度为2),且子节点的位置不可更改。
满二叉树:在不增加树的层数的前提下,无法再增加一个节点的二叉树
特性:满二叉树第K层有2^(k-1)个节点
K层满二叉树总共有2^k-1个节点
完全二叉树:只是删除了满二叉树最底层最右边的连续若干个节点,形成了完全二叉树
二叉树的遍历:前序遍历:根 左 右
//前序遍历
void pre_order(TNode_t *proot)
{
if(NULL == proot)
{
return;
}
printf("%c", proot->data);
pre_order(proot->pl);
pre_order(proot->pr);
}
中序遍历:左 根 右
//中序遍历
void pre_mid(TNode_t *proot)
{
if(NULL == proot)
{
return;
}
pre_mid(proot->pl);
printf("%c", proot->data);
pre_mid(proot->pr);
}
后续遍历:左 右 根
//后序遍历
void pre_end(TNode_t *proot)
{
if(NULL == proot)
{
return;
}
pre_end(proot->pl);
pre_end(proot->pr);
printf("%c", proot->data);
}
层序遍历:逐层向下遍历,涉及到队列的相关知识
void pre_queue(TNode_t *proot)
{
QDataType outdata;
Queue_t *qlink = create_queue();
if(NULL == qlink)
{
printf("fail create queue\n");
return ;
}
push_queue(qlink, proot);
while(!is_empty_queue(qlink))
{
pop_queue(qlink, &outdata);
printf("%c", outdata->data);
printf("\n");
if(outdata->pl != NULL)
{
push_queue(qlink, outdata->pl);
}
if(outdata->pr != NULL)
{
push_queue(qlink, outdata->pr);
}
}
destroy_queue(qlink);
}
二叉树的遍历特性:
已知前序遍历序列和中序遍历序列,可以唯一确定一棵二叉树;
已知后序遍历序列和中序遍历序列,可以唯一确定一棵二叉树;
二、哈希表
在记录的存储位置和它的关键字之间建立一种去特定的对应关系,使得每个关键字key对应一个存储位置;查找时,根据确定的对应关系,找到给定的key的映射,我们把这种关系f称为哈希函数(散列函数),采用这种散列技术将记录存储在一块连续的存储空间,这块连续存储开空间称为哈希表或散列表。
HSNode_t *hashtable[HASH_SIZE] = {NULL};
int hash_function(char key)
{
if(key >= 'a' && key <= 'z')
{
return key - 'a';
}
else if(key >= 'A' && key <= 'Z')
{
return key - 'A';
}
else
{
return HASH_SIZE - 1;
}
}
int insert_hashtable(HSDataType data)
{
int addr = hash_function(data.name[0]);
HSNode_t * pnode = (HSNode_t *)malloc(sizeof(HSNode_t));
if(NULL == pnode)
{
printf("malloc fail\n");
return -1;
}
pnode->data = data;
pnode->pnext = NULL;
pnode->pnext = hashtable[addr];
hashtable[addr] = pnode;
return 0;
}
哈希冲突
哈希冲突:当两个不同的数经过哈希函数计算后得到了同一个结果,即他们会被映射到哈希表的同一个位置时,即称为发生了哈希冲突。简单来说就是哈希函数算出来的地址被别的元素占用了。
解决方法:
1、开放定址法:我们在遇到哈希冲突时,去寻找一个新的空闲的哈希地址。
2、再哈希法:同时构造多个不同的哈希函数,等发生哈希冲突时就使用第二个、第三个……等其他的哈希函数计算地址,直到不发生冲突为止。虽然不易发生聚集,但是增加了计算时间。
3、链地址法:将所有哈希地址相同的记录都链接在同一链表中。
4、建立公共溢出区:将哈希表分为基本表和溢出表,将发生冲突的都存放在溢出表中。
存储时,通过散列函数计算出记录的散列地址;
查找时,根据同样的散列函数计算记录的散列地址,并按此散列地址访问记录。
算法的设计:1.正确性,语法正确,合法的输入能得到合理的结果。对非法的输入,给出满足要求的规格说明,对精心选择,甚至刁难的测试都能正常运行,结果正确
2. 可读性,便于交流,阅读,理解 高内聚 低耦合
3. 健壮性,输入非法数据,能进行相应的处理,而不是产生异常
4. 高效率(时间复杂度)
5. 低存储(空间复杂度)
时间复杂度:执行这个算法所花时间的度量
将数据量增长和时间增长用函数表示出来,这个函数就叫做时间复杂度。一般用大O表示法:O(n)----时间复杂度是关于数据n的一个函数,随着n的增加,时间复杂度增长较慢的算法时间复杂度低
时间复杂度的计算规则:1,用常数1 取代运行时间中的所有加法常数
2,在修改后的运行函数中,只保留最高阶项。
3,如果最高阶存在且系数不是1,则去除这个项相乘的常数。