您的位置:首页 > 其它

基于哈希表构建高效 矩阵存储结构--矩阵的一种存储方法 哈希矩阵。

2011-12-29 14:15 323 查看
注:这是一个自己的探索性的研究,自己对数据结构这门课本身掌握的不是非常扎实,纰漏之处敬请评论指正:

最近写论文要用到交叉口元胞矩阵的建模,需求是这样的。给定矩阵的x,y坐标,求该坐标上的元胞网格。

共有几种运算。

1 判断矩阵(x,y)处 有没有元胞

2 获取(x,y)处的矩阵元素

3 移动元素在矩阵中的位置,从(xO,yO)移动到(xD,yD)

4 遍历矩阵中的元素并且进行处理

个人觉得十字链表太恶心,添加删除节点,内部的数据如指针等都要自己维护,烦得很。三元组的三个域也很有意思(x,y,你想要存储的数据),多个三元组本身还有一个存储的问题,当然最简单的就是线形存贮。利用三元素数组进行矩阵记录,本身又引入了三元组数组的管理问题。二维数组的方式简单直观,二维数组存贮稀疏矩阵空间浪费严重。这里,我是使用哈希表做矩阵的存贮结构。

插播个广告:

个人认为数据结构这门课,不管是什么语言实现,引入新的数据结构都使用最原始的数据类型如int char 进行重建,各种数据结构类型之间并没有很好相互利用,有点每次都从头开始的意思。而且不鼓励思考,直接给出实现方法的内容安排,也是容易让人认为这种数据结构的实现就书本上的几种方法。这不利于我们工程研究中设计出更好的更快速的数据结构。换句话说,这门课没有给它的学习者留下想象和提高的空间,直接给出答案,缺乏对学习者自身的思考创新的鼓励。

利用哈希表存贮矩阵好处简直一大堆,例如你不需要考虑你要存贮的矩阵是不是稀疏,分贝对矩阵采用不同的存储方式,哈希矩阵足够节省空间了(浪费的空间数就是哈希表为了解决冲突引入的额外的的空间数),如果你是完美主义者,喜欢要更好更节省空间的矩阵存储方法,那么恭喜你,你可以不用思考如何存贮矩阵了,直接思考如何构造更高效的哈希函数即可,说不定还能在前人的基础上提高那么几个百分点,这很难,按照现有的理论已经是或者是接近存储空间最优了。个人认为:哈希表使用了一种很好的思想,就是将如何找到
最优间节省的存贮结构 这样一个问题转变为如何找到一个高效的哈希函数,提取了思考的重点,缩小了思考的范围,将实现与设计的分离,降低了复杂性。(有点离题了呵呵)

理论对实践的指导意义是不言而喻的。关键是有时候解决技术问题,需要工程师自己抽象出其中的并且应用理论。

这个存储结构是自己的一个探索性质的项目,这里面有几个新的问题

1.矩阵的枚举遍历

就是遍历所有的矩阵元素这个操作。这个及其容易实现。利用C# 的哈希表本身提供枚举(getEnumerator)的功能即可。这种方法可以避免 在矩阵中进行盲目搜索:例如如下代码

for (int i = 0; i < this.iMaxWidth; i++)

{

for (int i = 0; i < this.iMaxWidth; i++)

{

//this.hashMat.TryGetValue(hasi.

}

}

这就造成了浪费,

正确的遍历方法是利用哈希表自身提供的枚举器:如下(参见完整代码)

internal IEnumerator<T> GetEnumerator()

{

return this.hashMat.Values.GetEnumerator();

}

遍历方法为:

I Enumerator<Cell> cellEnum = rn.GetEnumerator();

cellEnum.Reset();

while (cellEnum.MoveNext())

{

Cell ce = cellEnum.Current;

//do something else

}

2.遍历矩阵内部某一行的所有元素

3. 遍历矩阵内部某一列的所有元素

问题2和问题3实际上是一个问题,解决也有两种方法:

先说第一种:在HashMatrix矩阵中增加两个域int iMaxRow,int iMaxColumn;分别保存最大行元素和最大列元素,然后利用这两个域遍历指定的行即可

在说第二种之前,给出第一种的实现方法。

新的HashMatrix 以泛型实现,便于存储其他类型的数据。使用的时候只需声名为正确的类型即可。例如 ,要存储的类型是 一个class A,则声明 为 HashMatrix<A> _myClassAHashMatrix = new HashMatrix<A> ();

代码如下:

public class HashMatrix<T>

{

int iMaxRow;//矩阵中最大行的坐标,进行矩阵的行遍历使用

int iMaxColumn;//矩阵中最大列的坐标,进行列矩阵的遍历

public class CellHashKey

{

//利用(x,y)计算新存储结构的哈希值,以支持利用x.y快速访问矩阵元素

public static int GetHashCode(int ix, int iy)

{

return (ix.GetHashCode() + iy.GetHashCode()).GetHashCode();

}

}

private Dictionary<int,T> hashMat = new Dictionary<int,T>();

/// <summary>

/// 判断元胞是否被占用了

/// </summary>

/// <param name="x"></param>

/// <param name="y"></param>

/// <returns></returns>

public bool IsCellBlocked(int x, int y)

{

return hashMat.ContainsKey(CellHashKey.GetHashCode(x,y));

}

/// <summary>

/// 把元宝从o点移动到d点

/// </summary>

public void MoveCell(int xO, int yO, int xD, int yD)

{

int iHashkey = CellHashKey.GetHashCode(xO, yO);

T cac;

if (hashMat.TryGetValue(iHashkey, out cac) == true)

{

hashMat.Remove(CellHashKey.GetHashCode(xO, yO));

hashMat.Add(CellHashKey.GetHashCode(xD, yD), cac);

}

}

public void AddCell(int x,int y,T cell)

{

//更新行和列的最大索引

this.iMaxColumn = x > this.iMaxColumn ? x : this.iMaxColumn;

this.iMaxRow = y > this.iMaxRow ? y : this.iMaxRow;

int iHKey = CellHashKey.GetHashCode(x,y);

if (!hashMat.ContainsKey(iHKey))

{

hashMat.Add(iHKey,cell);

}

}

public void RemoveCell(int x, int y)

{

hashMat.Remove(CellHashKey.GetHashCode(x, y));

}

internal IEnumerator<T> GetEnumerator()

{

return this.hashMat.Values.GetEnumerator();

}

}

参照行遍历可以写出列遍历的代码,不再啰嗦。

话分两头,当然,别忘了我们说还有第二种方法------重新构造哈希函数,令其支持按行或者按列遍历,

比如。假设我们构造的哈希函数是8位数字。 那么前四位数 为行x的哈希,后四位数为列 y的哈希。

那么当我们输入(2,3)时候,可以唯一确定一个元素,快速取值,当我们想要按照行或者是列存储时候,例如(2,y)y 为任意正整数, 的时候哈希字典返回以 哈希值以 2的四位哈希值 开头,后四位任意的的所有元素即可。

这种方法可能并不能比第一种高效很多,如果想要一次就找到我们想要的元素。可能需要按照八位哈希 前四位哈希和后四位哈希进行排序。或者建立三个索引。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐