基于哈希表构建高效 矩阵存储结构--矩阵的一种存储方法 哈希矩阵。
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的四位哈希值 开头,后四位任意的的所有元素即可。
这种方法可能并不能比第一种高效很多,如果想要一次就找到我们想要的元素。可能需要按照八位哈希 前四位哈希和后四位哈希进行排序。或者建立三个索引。
最近写论文要用到交叉口元胞矩阵的建模,需求是这样的。给定矩阵的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的四位哈希值 开头,后四位任意的的所有元素即可。
这种方法可能并不能比第一种高效很多,如果想要一次就找到我们想要的元素。可能需要按照八位哈希 前四位哈希和后四位哈希进行排序。或者建立三个索引。
相关文章推荐
- 数据结构——线性表——散列存储结构——哈希表知识点总结 原创 2017年05月14日 10:08:40 散列(hashing)是一种重要的存储方法,也是一种常见的查找方法。 基本思想:以结点的
- 一种理想的在关系数据库中存储树型结构数据的方法
- 一种理想的在关系数据库中存储树型结构数据的方法
- 基于递归调用和链式结构的二叉树构建方法及其基本运算
- 大型多玩家在线游戏,第 1 部分: 一种基于性能的基础结构规模评估方法
- 快速构建基于代码级性能测试方法的一种思路和简单实现
- C中实现矩阵乘法的一种高效的方法
- 一种理想的在关系数据库中存储树型结构数据的方法
- 利用多叉树实现Ext JS中的无限级树形菜单(一种构建多级有序树形结构JSON的方法)
- 大型多玩家在线游戏,第 1 部分: 一种基于性能的基础结构规模评估方法
- 利用多叉树实现Ext JS中的无限级树形菜单(一种构建多级有序树形结构JSON的方法
- 一种理想的在关系数据库中存储树型结构数据的方法
- C中实现矩阵乘法的一种高效的方法
- 多叉树结合JavaScript树形控件实现无限级树形菜单(一种构建多级有序树形结构JSON(或XML)数据源的方法)
- 多叉树结合JavaScript树形控件实现无限级树形菜单(一种构建多级有序树形结构JSON(或XML)数据源的方法)
- 一种理想的在关系数据库中存储树型结构数据的方法
- 一种理想的在关系数据库中存储树型结构数据的方法
- 一种基于构造测量矩阵的称球问题求解方法
- 一种基于重载的高效c#上图片添加文字图形图片的方法
- 判断质数的一种高效方法——基于裁剪策略