数据结构和算法 – 7.散列和 Hashtable 类

7.1.散列函数

散列是一种常见的存储数据的技术,按照这种方式可以非常迅速地插入和取回数据。散列所采用的数据结构被称为是散列表。尽管散列表提供了快速地插入、删除、以及取回数据的操作,但是诸如查找最大值或最小值这样的查找操作,散列表却无法执行地非常快。对于这类操作,其他数据结构会更适合.

 

7.2.选择散列函数

选择数组大小的时候,一个重要的原则就是要选择素数。

10007是素数,而且他没有大到会使用大量的内存来降低程序的内存。

下面例子中,散列函数SimpleHash利用霍纳(Horner)法则来计算(关于37的)多项式函数。

static void Main()
        {
            string[] names = new string[10007];
            string name;

            string[] somenames = new string[]
           { "David", "Jennifer", "Donnie","Mayo",
                "Raymond","Bernica","Mike", "Clayton","Beata","Michael"};

            int hashVal;

            for (int i = 0; i < 10; i++)
            {
                name = somenames[i];
                hashVal = SimpleHash(name, names);
                names[hashVal] = name;
            }
            ShowDistrib(names);
           
            Console.Read();
        }


        public static int SimpleHash(string s, string[]arr)
        {
           int tot = 0;
           char[] cname;
           cname = s.ToCharArray();
           for (int i = 0; i < cname.GetUpperBound(0); i++)
           {
               tot += 37 * tot + (int)cname[i];
           }
           tot = tot % arr.GetUpperBound(0);
           if (tot < 0)
           { tot += arr.GetUpperBound(0); }
           return (int)tot;
        }

        static void ShowDistrib(string[] arr)
        {
           for (int i = 0; i < arr.GetUpperBound(0); i++)
           {
               if (arr[i] != null)
               { Console.WriteLine(i+" "+arr[i]); }
           }
        }

 

7.3.查找散列表中数据

在散列表中查找数据,需要计算键的散列值,然后访问数组中的对应元素。这样的查找方式很明显要比逐个查找要效率的多。

 

7.4.解决冲突

7.4.1.桶式散列法

,是一种存储在散列表元素内的简单数据结构 ,它可以存储多个数据项。大多数实现中,这种数据结构就是一个数组,例如ArrayList类。

桶式散列法,最终要的事情,就是保持所用数组的数量尽可能地少。

插入一个数据项

用散列函数来确认用哪个数组(散列键)来存储数据项,然后查看此数据项是否已经在数组内。如果存在,就不做。如果不存在,就将数据项添加进这个数组。

移出一个数据项

用散列函数来确认用哪个数组(散列键)来移出数据项,然后查看此数据项是否已经在数组内。如果存在,就移出数据项。如果不存在,就不做。

7.4.2.开放定址法

开放定址函数会在散列表数组内寻找空单元来防止数据项。

两种不同的开放定址策略

线性探查

采用线性函数来确认试图插入的数组单元。顺次尝试单元直到找到一个空单元为止。

会出现的问题是数组内相邻单元中的数据元素会趋近成聚类,从而使得后续空单元的探查时间变得更长且效率更低。

平方探查

平方函数来确认要尝试哪个单元。

平方探查法的有趣属性是在散列表空余单元少于一半的情况下总能保证找到空的单元。

7.4.3.双重散列法

两个条件:散列函数不应该曾经计算到0,;表的大小必须是素数

 

7.5.5 Hashtable 类

可以实例化具有初始容量的散列表,或者使用默认容量。当然还可以同时指定初始容量和初始负载系数。

            Hashtable symbols = new Hashtable();
            Hashtable symbols = new Hashtable(50);
            HashTable symbols = new Hashtable(25, 3.0F);

 

7.5.1.从散列表中分别取回关键字和数值

Hashtable 类有两个非常有用的方法用来从散列表中取回关键字和数值:即 Keys 和 Values。这些方法创建了一个 Enumerator 对象,它允许使用 For Each 循环或者其他一些技术来检查关键字和数值。

Hashtable symbols = new Hashtable(25);
            symbols.Add("salary", 100000);
            symbols.Add("name", "David Durr");
            symbols.Add("age", 45);
            symbols.Add("dept", "Information Technology");
            symbols["sex"] = "Male";
            Console.WriteLine("The keys are: ");
            foreach (Object key in symbols.Keys)
                Console.WriteLine(key);
            Console.WriteLine();
            Console.WriteLine("The values are: ");
            foreach (Object value in symbols.Values)
                Console.WriteLine(value);

 

7.5.2.Hashtable 类的应用程序:计算机术语表

散列表的常见应用之一就是构造术语表或术语词典。本小节会举例说明使用散列表的一种方法就是为了这样一个应用—即计算机术语表。
程序首先从一个文本文件中读入一系列术语和定义。这个过程是在子程序 BuildGlossary 中编码实现的。文本文件的结构是:单词,定义,用逗号在单词及其定义之间进行分隔。这个术语表中的每一个单词都是单独一个词,但是术语表也可以很容易地替换处理短语。这就是用逗号而不用空格作分隔符的原因。此外,这种结构允许使用单词作为关键字,这是构造这个散列表的正确方法。
另一个子程序 DisplayWords 把单词显示在一个列表框内,所以用户可以选取一个单词来获得它的定义。既然单词就是关键字,所以能使用Keys 方法从散列表中正好返回单词。然后,用户就可以看到有定义的单词了。

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private Hashtable glossary = new Hashtable();

        private void Form1_Load(object sender, EventArgs e)
        {
            BuildGlossary(glossary);
            DisplayWords(glossary);
        }

        private void BuildGlossary(Hashtable g)
        {
            StreamReader inFile;
            string line;
            string[] words;
            inFile = File.OpenText(@"c:111.txt");
            char[] delimiter = new char[] { ',' };
            while (inFile.Peek() != -1)
            {
                line = inFile.ReadLine();
                words = line.Split(delimiter);
                g.Add(words[0], words[1]);
            }
            inFile.Close();
        }
        private void DisplayWords(Hashtable g)
        {
            Object[] words = new Object[100];
            g.Keys.CopyTo(words, 0);
            for (int i = 0; i <= words.GetUpperBound(0); i++)
                if (!(words[i] == null))
                    lstWords.Items.Add((words[i]));
        }

        private void lstWords_SelectedIndexChanged(object sender, EventArgs e)
        {
            Object word;
            word = lstWords.SelectedItem;
            txtDefinition.Text = glossary[word].ToString();
        }
    }
}

image

原文地址:https://www.cnblogs.com/tangge/p/5522571.html