字典是一种利用键值对来存储的数据结构。作为一种抽象类, dictionaryBase 类可以实现不同的结构
sortedList 是按照分类顺序基于键值来存储键值对的,它可以通过引用数据结构中的值得索引位置也可以访问存贮在结构中的数据。
Dictionary 中,存储在字段中的键值对于时间上最为 DictionaryEntry 对象来存储的。 DictionaryEntry 结构提供两个域,一个用于键,一个用于值。对于内部而言会把键值存储在 innerHashTable 的散列对象中。
public class IPAddresses : DictionaryBase
{
public IPAddresses()
{
}
public void Add(string name, string ip)
{
base.InnerHashtable.Add(name, ip);
}
public string Item(string name)
{
return base.InnerHashtable[name].ToString();
}
public void Remove(string name)
{
base.InnerHashtable.Remove(name);
}
}
static void Main()
{
IPAddresses myIPs = new IPAddresses();
myIPs.Add("Mike", "192.155.12.1");
myIPs.Add("David", "192.155.12.2");
myIPs.Add("Bernica", "192.155.12.3");
Console.WriteLine("There are " + myIPs.Count + " IP addresses");
Console.WriteLine("David's ip address: " + myIPs.Item("David"));
myIPs.Clear();
Console.WriteLine("There are " + myIPs.Count + " IP addresses");
}
class chapter
{
static void Main()
{
for (int i = 0; i < 4; i++)
Console.WriteLine();
IPAddresses myIPs = new IPAddresses(@"c:\data\ips.txt");
Console.WriteLine("There are {0} IP addresses", myIPs.Count);
Console.WriteLine("David's IP address: " + myIPs.Item("David"));
Console.WriteLine("Bernica's IP address: " + myIPs.Item("Bernica"));
Console.WriteLine("Mike's IP address: " + myIPs.Item("Mike"));
}
}
IPAddresses myIPs = new IPAddresses(@"c:\ips.txt");
DictionaryEntry[] ips = new DictionaryEntry[myIPs.Count-1];
myIPs.CopyTo(ips, 0);
泛型KeyValuePair 类,每个对象只能存储一个值
<string, int> mcmillan = new KeyValuePair<string, int>("McMillan", 99);
Console.Write(mcmillan.Key);
Console.Write(" " + mcmillan.Value);
using System;
using System.Collections.Generic;
using System.Text;
namespace Generics
{
class Program
{
static void Main(string[] args)
{
KeyValuePair<string, int>[] gradeBook = new KeyValuePair<string, int>[10];
gradeBook[0] = new KeyValuePair<string,int>("McMillan", 99);
gradeBook[1] = new KeyValuePair<string,int>("Ruff", 64);
for (int i = 0; i <= gradeBook.GetUpperBound(0); i++)
if (gradeBook[i].Value != 0)
Console.WriteLine(gradeBook[i].Key + ": " + gradeBook[i].Value);
Console.Read();
}
}
}
SortedList 类
SortedList myips = new SortedList();
myips.Add("Mike", "192.155.12.1");
myips.Add("David", "192.155.12.2");
myips.Add("Bernica", "192.155.12.3");
SortedList<Tkey, TValue>
SortedList<string, string> myips = new SortedList<string, string>();
SortedList<string, int> gradeBook = new SortedList<string, int>();
foreach(Object key in myips.Keys)
Console.WriteLine("Name: " + key + "\n" + "IP: " + myips[key]);
for(int i = 0; i < myips.Count; i++)
Console.WriteLine("Name: " + myips.GetKey(i) + "\n" + "IP: " + myips.GetByIndex(i));
myips.Remove("David");
myips.RemoveAt(1);
int indexDavid = myips.IndexOfKey("David");
int indexIPDavid = myips.IndexOfValue(myips["David"]);
散列和 HasTable
散列是一种常见的顺出数据技术,采用这种技术可以非常迅速的插入和检索数据。散列所采用的数据结构成为散列表。
散列表数据结构是围绕数组设计的。存储在数组内的每一个数据读书基于键映射到一个范围从 0 到散列表大小的数值上,这被称为是键,为了把一个元素存储到散列表内,利用所谓散列函数吧键映射到一个范围从 0 到散列表大小的数上。由于键是不受限制的,而数组的大小又是有限制的,所以散列函数比较现实的目标是把键尽可能平均分布到数组的单元内。即使一个很好的散列函数也可能会出现两个键散列到相同的数值情况 , 这种现象称为冲突。
选择散列函数
选择散列函数的依据是所用键的数据类型。如果所用的键是整数,那么最简单的函数是返回键对数组大小取莫结果(前提是数组的大小必须是素数)。然而许多应用中键都是字符串,下面一个简单利用把键内字母 Ascll 码相加,上述加和的数值与数组大小模莫就是散列值了。
using System;
class chapter
{
static void Main()
{
string[] names = new string[99];
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);
}
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 += (int)cname[i];
return tot % arr.GetUpperBound(0);
}
static void ShowDistrib(string[] arr)
{
for (int i = 0; i <= arr.GetUpperBound(0); i++)
if (arr[i] != null)
Console.WriteLine(i + " " + arr[i]);
}
}
问题出现了,键分布不均匀都聚集在数组的开始和结束处。
最终选择数组的小取决于存储在记录的确切数量。一个保险数字是 10007 ,它是素数,而且还没大到,会使用大量内存导致降低程序性能的地步。
一个好的解决方法
static int BetterHash(string s, string[] arr)
{
long 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;
}
这个函数利用霍纳法则 (Horner) 来计算多项式函数。
查找散列表中数据
static bool InHash(string s, string[] arr)
{
int hval = BetterHash(s, arr);
if (arr[hval] == s)
return true;
else
return false;
}
解决冲突
在处理散列表的时候,不可避免的会遇到这种情况,即计算出的键的散列值已经存储到了赢一个键,这个就是所谓的冲突。
筒式散列法
public class BucketHash
{
private const int SIZE = 101;
ArrayList[] data;
public BucketHash()
{
data = new ArrayList[SIZE];
for (int i = 0; i <= SIZE - 1; i++)
data[i] = new ArrayList(4);
}
public int Hash(string s)
{
long tot = 0;
char[] charray;
charray = s.ToCharArray();
for (int i = 0; i <= s.Length - 1; i++)
tot += 37 * tot + (int)charray[i];
tot = tot % data.GetUpperBound(0);
if (tot < 0)
tot += data.GetUpperBound(0);
return (int)tot;
}
public void Insert(string item)
{
int hash_value;
hash_value = Hash(item);
if (data[hash_value].Contains(item))
data[hash_value].Add(item);
}
public void Remove(string item)
{
int hash_value;
hash_value = Hash(item);
if (data[hash_value].Contains(item))
data[hash_value].Remove(item);
}
}
堆排序
如果将堆看成是一棵完全二叉树,则这棵完全二叉树每个非叶子节点的值均不大于(或不小于)其左,右孩子节点的值。非叶子节点大于左右节点值得堆称为大根堆,小于左右节点的值得堆称为小根堆。
堆的结构类似于二叉树,但是又不完全相同。首先构造堆通常采用的是数组而不是节点引用。
堆有两个非常重要的条件 ( 1 )堆必须是完整的,这就是意味着每一行都必须有数据填充。( 2 )每个节点包含的数据大于或者等于此节点下方孩子节点所包含的数据。
using System;
public class Node
{
public int data;
public Node(int key)
{
data = key;
}
}
public bool Insert(int key)
{
if (currSize == maxSize)
return false;
heapArray[currSize] = new Node(key);
currSize++;
return true;
}
public void ShiftUp(int index) // 移动合适位置。
{
int parent = (index - 1) / 2;
Node bottom = heapArray[index];
while ((index > 0) && (heapArray[parent].data < bottom.data))
{
heapArray[index] = heapArray[parent];
index = parent;
parent = (parent - 1) / 2;
}
heapArray[index] = bottom;
}
public Node Remove()
{
Node root = heapArray[0];
currSize--;
heapArray[0] = heapArray[currSize];
ShiftDown(0);
return root;
}
public void ShiftDown(int index)
{
int largerChild;
Node top = heapArray[index];
while (index < (int)(currSize / 2))
{
int leftChild = 2 * index + 1;
int rightChild = leftChild + 1;
if ((rightChild < currSize) && heapArray[leftChild].data < heapArray[rightChild].data)
largerChild = rightChild;
else
largerChild = leftChild;
if (top.data >= heapArray[largerChild].data)
break;
heapArray[index] = heapArray[largerChild];
index = largerChild;
}
heapArray[index] = top;
}
public class Heap
{
Node[] heapArray = null;
private int maxSize = 0;
private int currSize = 0;
public Heap(int maxSize)
{
this.maxSize = maxSize;
heapArray = new Node[maxSize];
}
public bool InsertAt(int pos, Node nd)
{
heapArray[pos] = nd;
return true;
}
public void ShowArray()
{
for (int i = 0; i < maxSize; i++)
{
if (heapArray[i] != null)
System.Console.Write(heapArray[i].data + " ");
}
}
static void Main()
{
const int SIZE = 9;
Heap aHeap = new Heap(SIZE);
Random RandomClass = new Random();
for (int i = 0; i < SIZE; i++)
{
int rn = RandomClass.Next(1, 100);
aHeap.Insert(rn);
}
Console.Write("Random: ");
aHeap.ShowArray();
Console.WriteLine();
Console.Write("Heap: ");
for (int i = (int)SIZE / 2 - 1; i >= 0; i--)
aHeap.ShiftDown(i);
aHeap.ShowArray();
for (int i = SIZE - 1; i >= 0; i--)
{
Node bigNode = aHeap.Remove();
aHeap.InsertAt(i, bigNode);
}
Console.WriteLine();
Console.Write("Sorted: ");
aHeap.ShowArray();
}
}