散列桶

1、桶

就是可以存放数据的结构;在这里我认为桶就是结构体!

在哈希表的改进之上,哈希表当时自己的做法是:表中存放的是指针,而不能存放数据;

现在用桶存储:哈希表中的每个元素是结构体,有数据域和链域!(根据自己的情况不同而进行规定存放数据的多少和相应的有几个链域);其后哈希结点也可以这样定义,结点存放数据多少自己都可以定义;表中的结构体和结点的结构体没有什么联系!

在这我为了简单起见,让表中元素的结构体类型和链表结点类型一致,具体的存储图如下:

2、实际问题处理

现在就是有一个存放整数的Hash表,Hash表的存储单位叫做桶,每个桶存放3个整数,当一个桶中的元素超过3个时,要将新的元素存放在溢出桶中,每个溢出桶最多也只能放3个元素,多个溢出桶用链表串起来,此Hash表的基桶数目为P,Hash表的hash函数为对P取模,初始化桶时,要求数据默认为-1,指针为NULL, 要求代码实现上述内容。

(1)、问题分析:

Hash表的存储单位叫做桶------->就是说Hash表元素的数据类型是结构体。

结构体中有3个数据域和1个指针域,溢出桶的结构体类型和表元素类型一致; 哈希函数用的是除留余数法。

这个问题的模型和上面的那个图正好一一对应。

(2)、代码实现:

在这我只实现了初始化,插入,输出的函数。

我把函数声明和定义都写在了.h文件中,只是为了方便;声明和定义本质上是应该分开写的。

以下代码都是纯C写的,与C++没有牵扯!!

#ifndef _BUCKET_HASH_H_
#define _BUCKET_HASH_H_

#include<malloc.h>

typedef unsigned char boolean;

#define TRUE    1
#define FALSE    0

#define P                    7
#define BUCKET_NODE_SIZE    3
#define NULL_DATA            -1

//表和结点都是这个结构体类型
typedef struct BUCKET_NODE{
    ElemType data[BUCKET_NODE_SIZE];
    struct BUCKET_NODE *next;
}BUCKET_NODE;

typedef BUCKET_NODE HashTable[P];

int Hash(ElemType key){
    return key % P;
}
//初始化哈希表,只不过是桶的形式。
void initHashTable(HashTable ht);
boolean insertNewElement(HashTable ht, ElemType value);
void showHashTable(HashTable ht);

void showHashTable(HashTable ht){  //显示表中元素
    int i;
    int j;
    BUCKET_NODE *p;

    for(i = 0; i < P; i++){
        printf("%d : ", i);
        if(ht[i].data[0] != NULL_DATA){
            for(j = 0; j < BUCKET_NODE_SIZE; j++){
                if(ht[i].data[j] != NULL_DATA){
                    printf("%d ", ht[i].data[j]);
                }
            }
            printf("-->");
            p = ht[i].next;
            while(p){
                for(j = 0; j < BUCKET_NODE_SIZE; j++){
                    if(p->data[j] != NULL_DATA){
                        printf("%d ", p->data[j]);
                    }
                }
                printf("-->");
                p = p->next;
            }
        }
        printf("NULL\n");
    }
}
boolean insertNewElement(HashTable ht, ElemType value){
    int index = Hash(value);
    int i;
    BUCKET_NODE *p;
    BUCKET_NODE *q;
    BUCKET_NODE *s;

    for(i = 0; i < BUCKET_NODE_SIZE; i++){ //在这里先判断表中是否可以存放元素
        if(ht[index].data[i] == NULL_DATA){
            ht[index].data[i] = value;
            return TRUE;
        }
    }
    p = &ht[index];  //取表中某元素的地址
    q = p->next;     //指向了第一个结点,目的:保存前驱结点,在新生成结点时方便连接。
    while(q){
        for(i = 0; i < BUCKET_NODE_SIZE; i++){  //对链表中的元素进行一个一个遍历,看是否可以存储。
            if(q->data[i] == NULL_DATA){
                q->data[i] = value;
                return TRUE;
            }
        }
        p = q;  //p永远保存的是前驱结点
        q = q->next;
    }

    s = (BUCKET_NODE *)malloc(sizeof(BUCKET_NODE)); //此时,的生成新节点,用来存放数据
    if(s == NULL){
        return FALSE;
    }
    for(i = 0; i < BUCKET_NODE_SIZE; i++){  //对新生成的结点进行初始化工作
        s->data[i] = NULL_DATA;   //这里可以写个函数提取出来
    }
    s->next = NULL;
    s->data[0] = value; //新生成的结点,对其赋值

    p->next = s;  //链域的连接。

    return TRUE;

}
void initHashTable(HashTable ht){
    int i;
    int j;

    for(i = 0; i < P; i++){
        for(j = 0; j < BUCKET_NODE_SIZE; j++){
            ht[i].data[j] = NULL_DATA;
        }
        ht[i].next = NULL;
    }
}

#endif

(3)、测试代码:

#include<stdio.h>

typedef int ElemType;

#include"bucketHash.h"

void main(void){
    HashTable ht;
    int ar[] = {1, 8, 15, 22, 29, 36, 43, 3, 5 ,99, 55, };
    int n = sizeof(ar) / sizeof(int);
    int i;

    initHashTable(ht);
    for(i = 0; i < n; i++){
        insertNewElement(ht, ar[i]);
    }

    showHashTable(ht);
}

(4)、测试结果

3、分析总结

(1)、我自己认为插入函数(尾插)是重点。

思路:我们在插入数据时,首先考虑的是哈希表中是否可以存放我们的数据;其次不是立马开辟空间,而是判断其后的链域空间是否可以存放;最后,就只能自己开辟空间,把数据存放到第一个位置。

(2)、这个问题还远远没有结束,还有好几个方法没有实现;

i>对新生成的结点进行前插;

ii>删除某一个数据,这个情况就比较多了,注意:删除数据,其它的应该前移,不然造成空间的浪费,对于结点中没有数据的应该释放这个空间;

iii>查找某一个结点的位置,通过返回值和参数,一个确定地址,一个确定在数组的下标;

.....................

iv>还有销毁函数;

(3)、以空间换时间的查找算法,哈希函数选取恰当,在较好的处理冲突的前提下,哈希查找的时间复杂度可以认为是O(1)。

时间: 2024-10-11 12:45:21

散列桶的相关文章

关于Java的散列桶, 以及附上一个案例-重写map集合

为速度而散列: SlowMap.java说明了创建一个新的Map并不困难.但正如它的名称SlowMap所示,它不会很快,如果有更好的选择就应该放弃它.它的问题在于对键的查询,键没有按照任何特定的顺序保存,所以只能使用简单的线性查询,而线性查询是最慢的查询方式. 散列的价值在于速度: 散列使得查询得以快速进行.由于瓶颈在于键的查询速度,因此解决方案之一就是保持键的排序状态,然后使用Collections.binarySearch()进行查询. 散列则更进一步,它将键保存在某处,以便能够很快的找到.

散列之HashTable学习

1,什么是散列? 举个例子,在日常生活中,你将日常用品都放在固定的位置,当你下次需要该东西时,直接去该地方取它.这个过程就相当于散列查找. 若将它们随意杂乱无章地存放,当需要某件东西时,只能一个地方一个地方地逐一查找,这就相当于顺序查找. 在数据结构中,数组就相当于一张散列表,因为可以根据数组下标索引直接取得该位置上的元素. 2,什么情况下用到散列? 由于散列查找相对于其它查找而言,理论上只需要O(1)时间就可以找到某元素,因此,当查找是主要的任务时,散列就是实现词典的一种很好的选择. 3,散列

容器深入研究 --- 散列与散列码(三)

如何覆盖hashCode(): 明白了如何散列之后,编写自己的hashCode()就更有意义了. 首先,你无法控制bucket数组的下标值的产生.这个值依赖于具体的HashMap对象的容量,而容量的改变与容器的充满程度和负载因子有关.hashCode()生成的结果,经过处理后称为桶位的下标. 设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值.如果在将一个对象用put()添加进HashMap时产生一个hashCode()值,而用get()

容器深入研究 --- 散列与散列码(二)

为速度而散列: SlowMap.java说明了创建一个新的Map并不困难.但正如它的名称SlowMap所示,它不会很快,如果有更好的选择就应该放弃它.它的问题在于对键的查询,键没有按照任何特定的顺序保存,所以只能使用简单的线性查询,而线性查询是最慢的查询方式. 散列的价值在于速度: 散列使得查询得以快速进行.由于瓶颈在于键的查询速度,因此解决方案之一就是保持键的排序状态,然后使用Collections.binarySearch()进行查询. 散列则更进一步,它将键保存在某处,以便能够很快的找到.

散列算法与散列码

一.引入 1 /** 2 * Description:新建一个类作为map的key 3 */ 4 public class Groundhog 5 { 6 protected int number; 7 8 public Groundhog(){ 9 } 10 public Groundhog(int number) 11 { 12 this.number = number; 13 } 14 15 @Override 16 public String toString() 17 { 18 ret

java 散列与散列码探讨 ,简单HashMap实现散列映射表运行各种操作示列

package org.rui.collection2.maps; /** * 散列与散列码 * 将土拔鼠对象与预报对象联系起来, * @author lenovo * */ //土拨鼠 public class Groundhog { protected int number; public Groundhog(int n) { number=n; } @Override public String toString() { return "Groundhog #" + number

java 散列与散列码探讨 ,简单HashMap实现散列映射表执行各种操作示列

package org.rui.collection2.maps; /** * 散列与散列码 * 将土拔鼠对象与预报对象联系起来, * @author lenovo * */ //土拨鼠 public class Groundhog { protected int number; public Groundhog(int n) { number=n; } @Override public String toString() { return "Groundhog #" + number

HashMap分析及散列的冲突处理

1,Hashing过程 像二分查找.AVL树查找,这些查找算法的时间复杂度为O(logn),而对于哈希表而言,我们一般说它的查找时间复杂度为O(1).那它是怎么实现的呢?这就是一个Hashing过程. 在JAVA中,每个对象都有一个散列码,它是由Object类的hashCode()方法计算得到的(当然也可以覆盖Object的hashCode()).而我们可以在散列码的基础上,定义一个哈希函数,再对哈希函数计算出的结果求余,最终得到该对象在哈希表的位置. 1 final int hash(Obje

《数据库系统概念》14-静态散列

顺序文件组织的缺点之一是必须通过访问索引或使用二分法搜索来定位数据,这需要较多的I/O操作.基于散列技术的文件组织方式则不需要访问索引结构,散列也提供了一种组织索引的方式.在散列(hash)技术中,用桶(bucket)来表示能存储一条或多条记录的存储单元.如果K代表所有搜索码的集合,B代表所有bucket的集合,则散列函数h表示一个从K到B的映射函数.插入搜索码为Ki的记录时,通过散列函数计算h(Ki)得出bucket的地址,如果这个bucket还有空间,就将数据插入.查询Ki时,也是先通过h(