自定义的数据结构

在java的编程语言中,主要有两种存储结构。一种是数组,另一种是指针。
在空间方面,数组的存储空间物理地址连续,每次要对原有的元素进行删除对后面的元素都要造成影响。事先必须定义好数组的大小,太大容易造成空间资源的浪费,太小又容易造成数据溢出。而链表的存储空间是动态的,只要内存空间有空闲,就不会造成数据溢出。而且由于链表之间是通过存储地址的节点维系的,要在中间任何位置插入或删除元素只需要修改相邻元素的节点即可,可以随意扩充。
java中自带的一些存储结构,既有数组存储(例如ArrayList),也有链表结构(例如Node)。但是当元素数量过于庞大时,尤其是在元素个数无法预测时,无论哪一种存储方式均有自己的弱点。数组存储不利于修改,链表不利于查询。
为此,我们可以结合数组存储与链表存储各自的优点,建立一个满足自己程序运用的数据结构。为此,有以下这个构想。
首先,我们可以根据数据的容量数量级建立一个数组,然后在每个数组处存储一条链表。
数组便于查询,但是如果我们必须在每次查询前快速定位要查找的数组元素,若遍历每一个数组的每一条链表进行查询速度依然很慢。因此我们要想一个办法,根据每个数组元素里存储的链表的特点给每条链表分类。例如数组的[0]元素中存储的是ID为1000的倍数的学生,[1]元素中存储的是ID为除1000余1的学生,以此类推……
这样,每次插入或查找对象时,只需根据特定规则先判断这个对象应该添加在数组哪个元素的队列里,再遍历这个队列进行插入或查询。而由于每条链表都是可以扩充的,于是空间上则能发挥出链表存储的优势。
用这种方法进行存储,在数据量比较少或定义的数组大小不合理的情况下,可能会由于数组元素与数组中存储的链表长度之比的不同显示出较强的数组性(空间占用过大,资源浪费)或较强的链表性(查找速度过慢),甚至会出现既不如单纯的数组存储,又不如单纯的链表存储的情况,因此在编写数据结构时,一定要处理好初始数组的大小。
[img]http://dl.iteye.com/upload/picture/pic/108659/82becf73-34d5-36c4-8ea1-3c9091137ad5.png[/img]
在插入链表的时候,我们可以定义一个实体类,用来存储我们每个单位持有的属性与方法。这里举一个用户类的例子:

public class user {
int userID; //用户的ID属性,可以作为分类的标准
Object DataContent; //每个用户对象的具体属性
user Lastuser;//因为本HASH结构单向链就足够,可以不必定义前节点
user Nextuser;//后节点

public user GetNext(){
return Nextuser;
}
}

接下来切入正题,我们要正式编写我们的HASH数据结构类了:
public class MyHash {
//首先定义一个大小为10000的数组,用来存放链的数组
public user[] User = new user[10000];
int size; //结构内目前存放的元素个数
int ArrayNum = 10000;//数组的大小,初始为10000,最多可存10000条链
}
接下来,在MyHash中定义添加用户的方法。首先我们需要先定义一个HASH()方法,用来对用户进行分类。
    
public int HashCode(int userID){
return userID%ArrayNum ;
}


这个方法的目的是先将用户根据ID取模的方法将所有用户分成10000类。如果在user[]数组的那一类位置的元素是空的话,就把这个用户信息存在数组的相应位置,作为链的首元素。如果user[]的相应位置不为空,说明这条链已经建成,那就从链的首位置依次向后遍历,判断这条链的元素中有没有重复的元素,如果没有,则在这条链的末尾添加这个用户对象,杜绝了重复添加现象的发生。
public boolean add(user us) {
int Loc = HashCode(us.userID);
int ArrayBeishu =1;
if (size>10000*100*beishu){
reHash();
}

//判断是否为空,若为空,这个元素就存储在根节点,否则存储在下一结点
if(User[Loc]==null){
User[Loc]=us;
size++;
return true;
}else {
for (user u=User[Loc];u!=null;u=u.GetNext()){
//判断是否重复传入,若是,则不继续添加
if (u==us){
return false;
}else{
//若不是重复传入,则添加
if (u.GetNext()==null){
u.Nextuser = us;
size++;
return true;
}
}
}
}
return false;
}

同样的思路,我们可以写一个从这个结构中删除一个用户信息的方法:

public boolean remove(int ID) {
int Loc = HashCode(ID);
for (user u=User[Loc];u!=null;u=u.GetNext()){

if (u.userID==ID){
u = u.Nextuser;
size--;
return true;
}
}
return false;
}

如果担心用户数量的扩充会使得大小为10000的用户数组不够用,可继续添加一个reHash()方法。在此不赘述。
可能上面的描述过于抽象,我们举个形象的例子,例如我们有一个图书馆,我们的HASH结构相当于首先给书分成音乐类、艺术类、文学类、体育类……好几层。拥有很多层的书架相当于一个数组,每层书架独立构成一个链表。当我们要增加一本存数时,先用HASH()函数判断一下这本书应该放到哪个书架上,然后很快地从书架中找到相应的那一层(相当于找到对应的数组元素),再从这层书架的第一本书开始依次检索是否已经收藏了这本书(是否重复存入了对象),当检索到这层书架的最后一本书都没有发现有相同的书已经被收藏后(即遍历链未发现相同元素),就将书放在那一层书架的末尾(相当于链表增加一个元素)。当我们要查找一本书的时候,同样先用HASH()函数找到那本书的大体分类,然后再对应的书架寻找那本书,直到找到位置。所谓的reHash(),就是当书架的藏书量过多时,对书籍更细致地重新分类,让每个书架上的书目量减少(简短每条链的长度,以方便查寻)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值