树与哈希表

一、树

由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,则去除这个项相乘的常数。

  • 20
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值