数据结构——键树
2011-08-26 15:10 xiashengwang 阅读(508) 评论(0) 编辑 收藏 举报偶然在网上看见C#实现的键树,以前不知这东东是啥玩意(数据结构没过关),有何用途?于是仔细研学了一把,本人对这种算法的东西,看着就头痛,自己照着做了一遍。基本参考网上已实现的代码算法,放于此,给自己做个参考,以备查阅。以下是代码:
using System; using System.Collections.Generic; using System.Text; namespace AlgorithmTest { /// <summary> /// 键树(一般应用于大数据量的查找) /// <remarks> /// 如果一个关键字可以表示成字符的序号,即字符串,那么可以用键树(keyword tree), /// 又称数字搜索树(digital search tree)或字符树,来表示这样的字符串的集合。 /// 键树是一棵多叉树,树中每个结点并不代表一个关键字或元素,而只代表字符串中的一个字符。 /// 例如,它可以表示数字串中的一个数位,或单词中的一个字母等等。根结点不代表任何字符, /// 根以下第一层的结点对应于字符串的第一个字符,第二层的结点对应于字符串的第二个字符…… /// 每个字符串可由一个特殊的字符如“$”等作为字符串的结束符,用一个叶子结点来表示该特殊字符。 /// 把从根到叶子的路径上,所有结点(除根以外)对应的字符连接起来,就得到一个字符串。因此, /// 每个叶子结点对应一个关键字。在叶子结点还可以包含一个指针,指向该关键字所对应的元素。 /// 整个字符串集合中的字符串的数目等于叶子结点的数目。如果一个集合中的关键字都具有这样的字符串特性, /// 那么,该关键字集合就可采用这样一棵键树来表示。事实上,还可以赋予“字符串”更广泛的含义, /// 它可以是任何类型的对象组成的串。 /// </remarks> /// </summary> [Serializable()] public class KeyTree { private bool _keyChanged = false; //根节点 private KeyTreeNode rootNode = new KeyTreeNode(); //添加数到键数中 public void AddKey(long key) { string strKey = key.ToString(); KeyTreeNode tempNode = rootNode; for (int i = 0; i < strKey.Length; i++) { int index = int.Parse(strKey[i].ToString()); if (i == strKey.Length - 1) { tempNode.endFlags[index] = true; _keyChanged = true; break; } if (tempNode.pointers[index] == null) tempNode.pointers[index] = new KeyTreeNode(); tempNode = tempNode.pointers[index]; } } //删除key public void RemoveKey(long key) { string strKey = key.ToString(); KeyTreeNode tempNode = rootNode; for (int i = 0; i < strKey.Length; i++) { int index = int.Parse(strKey[i].ToString()); if (tempNode != null) { if (i == strKey.Length - 1 && tempNode.endFlags[index] == true) { tempNode.endFlags[index] = false; _keyChanged = true; } else { tempNode = tempNode.pointers[index]; } } else { break; } } } //key存在判定 public bool IsExist(long key) { string strKey = key.ToString(); KeyTreeNode tempNode = rootNode; for(int i =0;i<strKey.Length;i++) { int index = int.Parse(strKey[i].ToString()); if (tempNode != null) { if (i == strKey.Length - 1 && tempNode.endFlags[index] == true) return true; else tempNode = tempNode.pointers[index]; } else { return false; } } return false; } //key清除 public void Clear() { for (int i = 0; i < rootNode.pointers.Length; i++) { rootNode.pointers[i] = null; rootNode.endFlags[i] = false; } _keyChanged = true; _lstKey.Clear(); } //取得全部的key private List<long> _lstKey = new List<long>(); public List<long> Keys { get { if (_keyChanged) { _lstKey.Clear(); FindAllKey(rootNode, "", ref _lstKey); _keyChanged = false; } return _lstKey; } } //(这个递归,头都想大了,我太菜了。。。) private void FindAllKey(KeyTreeNode treeNode, string strKey, ref List<long> lstKey) { for (int i = 0; i < treeNode.pointers.Length; i++) { string keyTemp = strKey + i.ToString(); if (treeNode.endFlags[i] == true) { lstKey.Add(Convert.ToInt64(keyTemp)); } if (treeNode.pointers[i] != null) { FindAllKey(treeNode.pointers[i], keyTemp, ref lstKey); } } } } [Serializable] public class KeyTreeNode { public KeyTreeNode[] pointers = new KeyTreeNode[10]; public bool[] endFlags = new bool[10]; } }
测试代码:
m_keyTree.Clear(); System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); for (long i = 0; i < long.Parse(this.textBox1.Text); i++) { m_keyTree.AddKey(i); } sw.Stop(); MessageBox.Show("time:" + sw.ElapsedMilliseconds); System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); System.IO.MemoryStream ms = new System.IO.MemoryStream(); bf.Serialize(ms, m_keyTree); MessageBox.Show("byte(kb):" + ms.Length / 1024);
经过测试,发现这种键数的查找速度和HashTable基本相当,但是内存用量却只有HashTable的一半不到。这个对于大容量的数据查找还是一种可行的方法。
备注:key值为long型,对于一些字符串或对象集合的查找处理,可用对象的GetHashCode()方法转换成一个哈希值,再使用KeyTree。