散列表之直接寻址表

  • 散列表之直接寻址表
  • 直接寻址表的定义
  • 直接寻址表的操作
  • 直接寻址表的代码实现
    • dataNode的定义
    • 直接寻址表的定义
    • 测试文件
    • 编译运行
  • 总结

注意:

本文中的所有代码你可以在这里:

https://github.com/qeesung/algorithm/tree/master/chapter11/11-1/directAddr(这里的会及时更新)

或者这里:

http://download.csdn.net/detail/ii1245712564/8793509

找到

散列表之直接寻址表

散列表是实现字典操作的一个有效的数据结构,尽管在最坏情况下,散列表的查找和删除的最坏时间代价为Θ(n),但是在实际中,散列表的性能是很好的,在一些合理的假设下,散列表的查找和删除操作的平均时间代价为Θ(1),下面将要介绍的就是诸多散列表中的最简单直接的一种:直接散列表

在讲直接寻址表之前,先来看个栗子:假设现在有十个排成一排带锁的箱子,每一个箱子都有一把钥匙,将所有的钥匙从[1-10]编号,现在随便给你其中的一把钥匙keyi,要你找到要是对应的箱子。一个一个试么?正常的小伙伴肯定是通过钥匙的编号i找到第i个箱子就行啦。

在许多的编程语言里面都用到了这种直接通过key找到数据的思想,最简单的就是数组啦,数组是可以直接访问的,通过数组的起始地址加上一个偏移量得到目标数据的存储地址。这种方法是很快的,可以在Θ(1)时间内完成。直接寻址表就借助了数组这种可以直接访问的优势。

直接寻址表的定义

假设有一个数据集合U={d1,d2,d3,...,dn},该数据集合里面的每一个元素di都有一个对应的键值keyi和数据datai。集合中的任意一个keyi都是在[0,m]之间的整数。新建一个数组A[0..m],遍历一遍集合U,将其中的数据di放到A[keyi]中。

直接寻址表的操作

直接寻址表的基本操作有:

  • INSERT:插入一个元素
  • DELETE:删除一个元素
  • SEARCH:查找一个元素

问题:要是集合U的元素键值并不唯一,即keyi和keyj(i≠j)有可能相等,那么在相等的情况下该怎么办呢?

回答:采用链表的形式将相同键值的元素串成一串,全都挂在A[keyi]的后面,比如键值1重复:

直接寻址表的代码实现

首先看一下有那些文件

├── dataNode.cc 数据节点源文件
├── dataNode.h 数据节点头文件
├── directAddr.cc 直接寻址法源文件
├── directAddr.h 直接寻址法的头文件
├── directAddr_test.cc cppunit测试直接寻址源文件
├── directAddr_test.h cppunit测试设备的定义头文件
├── main.cc 测试主文件
├── Makefile
└── README.md
0 directories, 9 files

dataNode的定义

dataNode是基础的数据节点

dataNode.h

#ifndef  DATANODE_INC
#define  DATANODE_INC

#include <cstdio>

/**
 *        Class:  DataNode
 *  Description:  基本的节点
 */
template < class T >
class DataNode
{
    public:
        /** ====================  LIFECYCLE     ======================================= */
        DataNode (int key = 0 , T data=T() ,DataNode<T> * next = NULL);                             /** constructor */
        /** ====================  ACCESSORS     ======================================= */
        void set_key(int);
        int get_key() const;

        void set_data(T);
        T get_data() const;

        DataNode<T> * next;
    private:
        /** ====================  DATA MEMBERS  ======================================= */
        int key;// 对应的键值
        T data;// 对应的数据
}; /** ----------  end of template class DataNode  ---------- */

#include "dataNode.cc"
#endif   /* ----- #ifndef DATANODE_INC  ----- */

dataNode里面有两个基本数据:key和data

dataNode.cc

/**
 *       Class:  DataNode
 *      Method:  DataNode
 * Description:  构造函数
 */
template < class T >
DataNode < T >::DataNode (int _key , T _data , DataNode<T> * _next):key(_key),data(_data),next(_next)
{
}  /** ----------  end of constructor of template class DataNode  ---------- */

/**
 *       Class:  DataNode
 *      Method:  get_key
 */
template < class T >
inline  int DataNode<T>::get_key (  ) const
{
    return key;
}       /** -----  end of method DataNode<T>::get_key  ----- */

/**
 *       Class:  DataNode
 *      Method:  set_key
 */
template < class T >
inline  void DataNode<T>::set_key ( int value )
{
    key = value;
    return ;
}       /** -----  end of method DataNode<T>::set_key  ----- */

/**
 *       Class:  DataNode
 *      Method:  get_data
 */
template < class T >
inline  T DataNode<T>::get_data (  ) const
{
    return data;
}       /** -----  end of method DataNode<T>::get_data  ----- */

/**
 *       Class:  DataNode
 *      Method:  set_data
 */
template < class T >
inline  void DataNode<T>::set_data ( T value )
{
    data    = value;
    return ;
}       /** -----  end of method DataNode<T>::set_data  ----- */

直接寻址表的定义

directAddr.h

#ifndef  DIRECTADDR_INC
#define  DIRECTADDR_INC

#include "dataNode.h"
#include <vector>
#include <string>

/**
 *        Class:  DirectAddr
 *  Description:  直接寻址接口
 */
template < class T >
class DirectAddr
{
    public:
        /** ====================  LIFECYCLE     ======================================= */
        DirectAddr (int _key_min=0 , int _key_max=0);                             /** constructor */
        ~DirectAddr();

        bool direct_delete(DataNode<T> &);
        bool direct_insert(DataNode<T> &);
        std::vector<T> direct_search(int);
        void printToVec(std::vector<std::vector<T> > &);
        void clear();
    private:
        /** ====================  DATA MEMBERS  ======================================= */
        void deleteAllNode(DataNode<T> * nodePtr);
        DataNode<T>* array;
        const int table_size; // 存储表的大小
        const int key_min; // 存储键值的最小值
        const int key_max;// 存储键值的最大值
}; /** ----------  end of template class DirectAddr  ---------- */

#include "directAddr.cc"
#endif   /* ----- #ifndef DIRECTADDR_INC  ----- */

directAddr.cc

#include <cstdio>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <typeinfo>

/**
 *       Class:  DirectAddr
 *      Method:  DirectAddr
 * Description:  构造函数
 */
template < class T >
DirectAddr < T >::DirectAddr (int _key_min , int _key_max):key_min(_key_min),                                                               key_max(_key_max),table_size(_key_max - _key_min+1)
{
    array = new DataNode<T>[table_size]();
    for ( int i = 0 ; i< table_size ; ++i )
    {
        array[i].next = NULL;
    }

}  /** ----------  end of constructor of template class DirectAddr  ---------- */

/**
 *       Class:  DirectAddr
 *      Method:  ~DirectAddr
 * Description:  destructor
 */
    template < class T >
DirectAddr< T >::~DirectAddr ()
{
    clear();
    delete [] array;
}  /** ----------  end of destructor of template class DirectAddr  ---------- */

/**
 *  插入一个新的元素
 */
    template < class T >
bool DirectAddr<T>::direct_insert (DataNode<T> & node)
{
    if(node.get_key() <key_min || node.get_key()>key_max)
        return false;
    DataNode<T> * tempNode = array[node.get_key() - key_min].next;
    DataNode<T> * newNode = new DataNode<T>(node.get_key() , node.get_data(), tempNode);
    if(newNode == NULL)
        return false;
    array[node.get_key() - key_min].next = newNode;
    return true;
}       /** -----  end of method DirectAddr<T>::direct_insert  ----- */

template < class T >
bool DirectAddr<T>::direct_delete (DataNode<T> & node)
{
    if(node.get_key()<key_min || node.get_key()>key_max)
        return false;
    DataNode<T> * targetNode = &array[node.get_key() - key_min];
    /*  找到需要删除的点 */
    while(targetNode->next != NULL && targetNode->next->get_data()!=node.get_data())
    {
        targetNode = targetNode->next;
    }
    if(targetNode->next == NULL)
        return false;
    // 找到节点,开始删除
    DataNode<T> * tempNode = targetNode->next->next;
    delete targetNode->next;
    targetNode->next = tempNode;
    return true;
}       /** -----  end of method DirectAddr<T>::direct_delete  ----- */

template < class T >
std::vector<T> DirectAddr<T>::direct_search (int key)
{
    if(key<key_min || key>key_max)
        return std::vector<T>();
    std::vector<T> retVec;
    DataNode<T> * tempNode = array[key - key_min].next;
    while(tempNode != NULL)
    {
        retVec.push_back(tempNode->get_data());
        tempNode = tempNode->next;
    }
    return retVec;
}       /** -----  end of method DirectAddr<T>::direct_search  ----- */

    template < class T >
void DirectAddr<T>::clear ()
{
    for(int i = 0 ; i < table_size ; ++i)
    {
        deleteAllNode(array[i - key_min].next);
    }
    return ;
}       /** -----  end of method DirectAddr<T>::clear  ----- */

template < class T >
void DirectAddr<T>::deleteAllNode (DataNode<T> * nodePtr)
{
    if(nodePtr ==  NULL)
        return;
    while(nodePtr != NULL)
    {
        DataNode<T> * tempNode = nodePtr->next;
        delete nodePtr;
        nodePtr = tempNode;
    }
    return ;
}       /** -----  end of method DirectAddr<T>::deleteAllNode  ----- */

template < class T >
void DirectAddr<T>::printToVec (std::vector<std::vector<T> > & vec)
{
    for ( int i = 0 ; i < table_size;++i )
    {
        std::vector<T> tempVec;
        DataNode<T> * tempNode = array[i - key_min].next;
        while(tempNode!=NULL)
        {
            tempVec.push_back(tempNode->get_data());
            tempNode=tempNode->next;
        }
        vec.push_back(tempVec);
    }
}       /** -----  end of method DirectAddr<T>::printToStr  ----- */

测试文件

测试直接寻址表是否可以正确运行

main.cc

#include <stdlib.h>
#include "directAddr.h"
#include "directAddr_test.h"
#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestSuite.h>

/**
 * +++  FUNCTION  +++
 *         Name:  main
 *  Description:  主函数
 */
int main ( int argc, char *argv[] )
{
    CppUnit::TextUi::TestRunner runner;
    CppUnit::TestSuite * suite = new CppUnit::TestSuite();

    suite->addTest(new CppUnit::TestCaller<DirectAddrTest>("Test insert by qeesung",     &DirectAddrTest::testInsert));
    suite->addTest(new CppUnit::TestCaller<DirectAddrTest>("Test delete by qeesung",     &DirectAddrTest::testDelete));
    suite->addTest(new CppUnit::TestCaller<DirectAddrTest>("Test search by qeesung",     &DirectAddrTest::testSearch));
    runner.addTest(suite);
    runner.run("",true);
    return EXIT_SUCCESS;
}               /** ----------  end of function main  ---------- */

编译运行

编译方法

  • 如果你的电脑有cppunit的动态链接库
make 
  • 如果没有cppunit的动态链接库,你可以在你的源文件yourSourceFile.cc里面包含#include "directAddr.h",就可以使用直接寻址表
g++ yourSourceFile.cc -o target

执行程序

./testDirectAddr(or ./yourTarget)

注意:

本文中的所有代码你可以在这里:

https://github.com/qeesung/algorithm/tree/master/chapter11/11-1/directAddr(这里的会及时更新)

或者这里:

http://download.csdn.net/detail/ii1245712564/8793509

找到

总结

直接寻址表比较适用于键值范围不大,而且键值分布比较均匀的情况,比如我们只有两个数据(key:1,data),(key:10000,data),如果使用直接寻址表,那么就要分配100001大小的数组来保存这两个数据,太浪费了,所以在键值范围太大,而且数据太少的情况下不建议使用直接寻址表。

时间: 2024-10-11 09:06:18

散列表之直接寻址表的相关文章

散列表(哈希表)

序言: 如果将一系列的记录按照关键字的某种函数存储,那么在查找某个数据的时候就可以直接通过关键字计算出来了,而不在需要“比较”,这样会非常高效,这就是散列技术. 所以散列技术就是:     存储位置=f(关键字)        不管是记录的存储还是查找,都用这种方法 散列技术具有很高的效率,但是使用起来有一些限制.如1个关键字对应多个记录的情况(比如在一个学校的学生中按性别查找,则对应太多的记录),此外散列技术同样不适合于范围查找和排序等操作. 一.散列函数的构造 在设计散了函数的时候主要考虑两

[BS]散列表 哈希表 Hash table

<第五章> 散 列 散列表的实现常常叫做散列(hashing).散列是一种用于以常数平均时间执行插入.删除和查找的技术. 关于散列有一个很重要的概念:散列函数.散列函数是散列的关键处之一,散列函数又是基于映射机制的一种对应关系(一般是多对一的关系). 这章可以分为5个部分:一般想法,散列函数,分离链接法,开放定址法(可分为线性探测.平方探测.双散列).再散列.可扩散列. 本文只写到前四节.即:一般想法,散列函数,分离链接法,开放定址法(可分为线性探测.平方探测.双散列)() 第五章第一节:一般

散列表之链接法

散列表之链接法 散列表的定义 散列表的基本操作 散列表的编码实现 散列表的设计 主测试文件 编译运行 结论 注意: 本文中的所有代码你可以在这里 https://github.com/qeesung/algorithm/tree/master/chapter11/11-2/hashTable(这里会及时更新) 或者这里 http://download.csdn.net/detail/ii1245712564/8804203 找到 散列表之链接法 在之前的文章<散列表之直接寻址表>中介绍了散列表

《算法导论》— Chapter 11 散列表

1 序 在很多应用中,都要用到一种动态集合结构,它仅支持INSERT.SEARCH以及DELETE三种字典操作.例如计算机程序设计语言的编译程序需要维护一个符号表,其中元素的关键字为任意字符串,与语言中的标识符相对应.实现字典的一种有效数据结构为散列表. 散列表是普通数组的推广,因为可以对数组进行直接寻址,故可以在O(1)的时间内访问数组的任意元素.对于散列表,最坏情况下查找一个元素的时间与在链表中查找的时间相同,为O(n),但是在实践中,散列表的效率通常是很高的,在一些合理的假设下,散列表中查

[数据结构] 散列表(哈希表)

散列表(哈希表) 比较难理解的官方定义:散列表/哈希表(Hash table),是根据关键码值(Key value)而直接进行访问的数据结构.它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表. 举个例子,我们在查找中文字典时.假设我们要查找"翁weng",我们根据weng找到了对应的页码233,这个过程就是根据关键码值映射得到了表中的位置.然后我们在字典这个散列表中,根据我们刚才得到的位置 233页,直接访问了"

算法导论-散列表(Hash Table)

目录 引言 直接寻址 散列寻址 散列函数 除法散列 乘法散列 全域散列 完全散列 碰撞处理方法 链表法 开放寻址法 线性探查 二次探查 双重散列 随机散列 再散列问题 完整源码(C++) 参考资料 内容 1.引言 如果想在一个n个元素的列表中,查询元素x是否存在于列表中,首先想到的就是从头到尾遍历一遍列表,逐个进行比较,这种方法效率是Θ(n):当然,如果列表是已经排好序的话,可以采用二分查找算法进行查找,这时效率提升到Θ(logn);  本文中,我们介绍散列表(HashTable),能使查找效率

散列表(hash table)&mdash;&mdash;算法导论(13)

1. 引言     许多应用都需要动态集合结构,它至少需要支持Insert,search和delete字典操作.散列表(hash table)是实现字典操作的一种有效的数据结构. 2. 直接寻址表     在介绍散列表之前,我们前介绍直接寻址表.     当关键字的全域U(关键字的范围)比较小时,直接寻址是一种简单而有效的技术.我们假设某应用要用到一个动态集合,其中每个元素的关键字都是取自于全域U={0,1,-,m-1},其中m不是一个很大的数.另外,假设每个元素的关键字都不同.    为表示动

算法导论 第11章 散列表

散列表是主要支持动态集合的插入.搜索和删除等操作,其查找元素的时间在最坏情况下是O(n),但是在是实际情况中,散列表查找的期望是时间是O(1),散列表是普通数组的推广,因为可以通过元素的关键字对数组进行直接定位,所以能够在O(1)时间内访问数组的任意元素. 1.直接寻址表 当关键字的全域较小,即所有可能的关键字的量比较小时,可以建立一个数组,为所有可能的关键字都预留一个空间,这样就可以很快的根据关键字直接找到对应元素,这就是直接寻址表,在直接寻址表的查找.插入和删除操作都是O(1)的时间.. 2

漫画 | 什么是散列表(哈希表)?

创建与输入数组相等长度的新数组,作为直接寻址表.两数之和的期望是Target,将Target依次减输入数组的元素,得到的值和直接寻址表比较,如果寻址表存在这个值则返回:如果不存在这个值则将输入数组中的元素插入寻址表,再进行输入数组中的下一个元素. 再进一步优化可以将输入数组直接作为直接寻址表,控制对应的下标就好,代码如下: Code:直接寻址表 class Solution { public int[] twoSum(int[] nums, int target) { for (int i =