分别用数组和链表实现集合数据结构

1,怎样来定义一个数据结构?

一般来说,你要

1)先定义这个数据结构的抽象机制(即ADT),用一个接口来表示,

2)用一个类(即表示数据结构)来实现这个接口(即实现这个数据结构)

3)考虑怎样来实现这个数据结构(例如存储形式,维护那些变量等)


例如,要写一个集合的数据结构,1)先要根据你需要的功能设计好集合里面需要定义哪些操作(方法),可以把它写成一个接口ADT,

2)然后写一个集合的类来实现上述接口,这样你就实现了集合这个数据结构

3)当然,你要考虑第三个问题,怎么来实现,即集合以一种什么形式组织在一起

当然,也可以不写接口,你要定义这个数据结构拥有哪些操作直接写一个这样的类也行,先写一个接口是为了抽象机制,让逻辑清楚点。



先来考虑第一个问题把,现在要写一个集合,那么需要根据你的需求定义在在集合里需要实现哪些操作,一般来说肯定包含判断是否为空的方法,求集合大小的方法,向集合添加新元素的方法,删除某个元素的方法,这些最基本的,你要根据需求来实现或多或少的方法。下面是我定义的一个集合的抽象数据类型:


复制代码
   
   
package Bag;

import java.util.Iterator;


// 用一个接口来表示集合的ADT定义
public interface BagADT {

public void add(Object element);

public void addAll(BagADT bag);

public Object removeRandom() throws EmptyBagException;

public Object remove(Object element) throws EmptyBagException;

public BagADT union(BagADT set);

public boolean contains(Object target);

public boolean equals(ArrayBag bag) throws EmptyBagException;

public boolean isEmpty();

public int size();

public Iterator iterator();

public String toString();
}
复制代码


然后来考虑第二步---写一个类实现这个接口,即写一个集合的数据结构的实现(必须实现接口中的所有方法,也可以再添加方法),这个很容易,具体怎么去实现了,首先你得考虑存储结构,即这个数据结构由什么来实现,就把它定义在这个数据结构里作为私有成员。

例如可以把集合元素全部存储在一个数组里,即用数组实现

或者用结点来实现(一个结点包含自身的元素与指向下一个节点的指针)

下面分别讨论-----


2,集合的数组实现


1)想好了用数组来实现集合(把集合元素存储在数组里),其实具体实现起来还要考虑很多细节,---即需要维护那些变量就可以实现ADT里定义的操作

要管理集合的容量,所以至少还得维持一个count变量,指示当前大小。

  
  
private int count; // count不仅指示了现在容量是多少,还是下一个可放置元素的索引。

private Object[] contents; // 数组存放结点


为了实现一些编程的方便或者其他功能,也可以再定一些维持的私有成员。

  
  
private static Random rand = new Random(); // 支持删除随机元素方法

private final int DEFAULT_CAPACITY = 100 ;

private final int NOT_FOUND = - 1 ;


2)in fact,完成那些方法就是要根据方法的定义来做一些操作,并 维护上述私有成员不断变化的过程


3)为了实现一个完整的数据结构,经常还需要写一些除了接口中的方法外,类所必须的方法,例如用数组实现某个数据结构的时候,常常要写一个expand方法,在容量不够的时候扩展容量,还有一个常常要写的构造方法,使用户可以以方便的方式来构造这个数据结构的对象:

复制代码
  
  
// 两个构造方法
public ArrayBag(){
count
= 0 ;
contents
= new Object[DEFAULT_CAPACITY];
}

public ArrayBag( int init_CAPACITY){
count
= 0 ;
contents
= new Object[init_CAPACITY];
}

// 支持方法,注意它声明为private,提供给类内部的方法使用的,并不提供给用户使用。
private void expandCapacity(){
Object[] larger
= new Object[contents.length * 2 ];
for ( int index = 0 ;index < contents.length;index ++ )
larger[index]
= contents[index];
contents
= larger; // 注意java可以直接用数组名进行复制哦,跟C/C++不一样,java数组名是数组的引用
}
复制代码


考虑清楚这些后,接下来就可以安心的一个个的实现接口中的方法啦!

集合的数组实现:


复制代码
   
   
package Bag;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Random;

/* 1,一般用接口给出一个数据结构的ADT定义(即这个数据结构要实现哪些方法),然后用一个类去实现这个接口(当
* 然可以添加新方法),即实现了这个数据结构.面向对象跟C来实现数据结构方式还是不太一样,要转换一下思维。
* 2,当然,你也可以直接写一个类来实现某个数据结构,不用预设一个接口。规范的方法是1,有了1就方便用不同的方
* 式来实现这个数据结构
* 3,一个数据结构的实现全部封装在一个类里,用户不需要知道是怎么实现的,只需要用这个数据结构的类的对象,
* 就能操作这种数据结构
*
*/
public class ArrayBag implements BagADT {

private static Random rand = new Random(); // 支持删除随机元素方法

private final int DEFAULT_CAPACITY = 100 ;
private final int NOT_FOUND = - 1 ;

private int count; // count不仅指示了现在容量是多少,还是下一个可放置元素的索引。
private Object[] contents; // 数组存放结点

// 两个构造方法
public ArrayBag(){
count
= 0 ;
contents
= new Object[DEFAULT_CAPACITY];
}

public ArrayBag( int init_CAPACITY){
count
= 0 ;
contents
= new Object[init_CAPACITY];
}

// 支持方法,注意它声明为private,提供给类内部的方法使用的,并不提供给用户使用。
private void expandCapacity(){
Object[] larger
= new Object[contents.length * 2 ];
for ( int index = 0 ;index < contents.length;index ++ )
larger[index]
= contents[index];
contents
= larger; // 注意java可以直接用数组名进行复制哦,跟C/C++不一样,java数组名是数组的引用
}


// 一一实现接口方法,哎,太多了,如果你用不上,其实可以少实现几个,空方法即可

public int size() {
return count;
}

public boolean isEmpty() {
return (count == 0 );
}

public void add(Object element) {
if (size() == contents.length)
expandCapacity();
contents[count]
= element;
count
++ ;
}

public void addAll(BagADT bag) {
Iterator scan
= bag.iterator(); // 其实引入Iterator就是为了实现数组无序化操作。
while (scan.hasNext())
add(scan.next());
}

public Object removeRandom() throws EmptyBagException {
if (isEmpty())
throw new EmptyBagException();
int choice = rand.nextInt(count);
Object result
= contents[choice];

contents[choice]
= contents[count - 1 ]; // 用最后一个元素填充删除的随机元素
contents[count - 1 ] = null ;
count
-- ;

return result;
}

public Object remove(Object element) throws EmptyBagException {
int search = NOT_FOUND;
if (isEmpty())
throw new EmptyBagException();

for ( int index = 0 ;index < contents.length && search == NOT_FOUND;index ++ )
if (contents[index] == element) search = index;

if (search == NOT_FOUND)
throw new NoSuchElementException();

Object result
= contents[search];

contents[search]
= contents[count - 1 ];
contents[count
- 1 ] = null ;
count
-- ;

return result;
}

public ArrayBag union(BagADT set) {
ArrayBag both
= new ArrayBag();
for ( int index = 0 ;index < contents.length;index ++ )
both.add(contents[index]);

Iterator scan
= set.iterator();
while (scan.hasNext())
both.add(scan.next());
return both;
}

public boolean contains(Object target) {
int search = NOT_FOUND;
for ( int index = 0 ;index < contents.length && search == NOT_FOUND;index ++ )
if (contents[index] == target) search = index;
return (search != NOT_FOUND);
}

public boolean equals(ArrayBag bag) throws EmptyBagException { // 注意虽然我们是用数组实现的,但比较的是集合
boolean result = false ;
ArrayBag temp1
= new ArrayBag();
ArrayBag temp2
= new ArrayBag();
Object obj;
if (size() == bag.size())
{
temp1.addAll(
this );
temp2.addAll(bag);

Iterator scan
= bag.iterator();
while (scan.hasNext())
{
obj
= scan.next();
if (temp1.contains(obj))
{
temp1.remove(obj);
temp2.remove(obj);
}
}
result
= (temp1.isEmpty() && temp2.isEmpty());
}
return result;
}

public Iterator iterator() {
return new ArrayIterator(contents,count);
}

public String toString(){
String result
= "" ;
for ( int index = 0 ;index < count;index ++ )
result
= result + contents[index].toString() + " \n " ;
return result;
}
}
复制代码



具体的实现参考注释,不再一一详述。这里说几点:1)对于非法操作,定义了自己声明的异常类,显得比较复杂,异常类的定义就不写在这了,在以后的写法中,对于非法操作,直接提示非法然后返回方法或者退出程序,不再去声明一些异常,把重点集中在算法的实现上。2)用不上的方法,可以不去实现,作为一个空方法即可。



3,链式实现

下面来看怎么用链式实现,其实用什么实现就是讨论上面说的怎么实现一个数据结构的第三个问题。


用什么来实现,这个数据结构就包含实现它的那些东西,例如上面用数组,数组大小这几个变量就可以来表示一个集合。

同样,如果用链式实现,那么每一个集合元素都放在一个结点中,一个集合又由结点的链接来维护。

集合中至少要维护一个指向链表头得结点,和一个count来表示链表的长度:根据功能需求再定义一个随机数发生器

  
  
private LinearNode contents; // 因为袋中元素无序,我们将contents记为链的第一个元素
private int count;

private Random rand = new Random();

那么,结点应该是什么样的呢,这得根据你的需求来定义,对于单链表,结点应该包括自身结点中存储的元素+指向下一个节点的指针--

复制代码
   
   
package Bag;

public class LinearNode { // 结点类:要实现C中的指针功能(包含自身结点的元素和指向下一个结点的指针)

private Object element;
private LinearNode next;

public LinearNode()
{
element
= null ;
next
= null ;
}

// 用一个元素来构造一个结点
public LinearNode(Object element)
{
this .element = element;
next
= null ;
}

// 关于next结点的读取,设置操作
public LinearNode getNext()
{
return next;
}

public void setNext(LinearNode node)
{
next
= node;
}

// 关于自身元素的读取,设置操作
public Object getElement()
{
return element;
}

public void setElement(Object element)
{
this .element = element;
}

}
复制代码



集合的链式实现:(只写了两三个方法,而且没写一个显式的构造函数)


复制代码
   
   
package Bag;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Random;

public class LinkedBag implements BagADT {

private LinearNode contents; // 因为袋中元素无序,我们将contents记为链的第一个元素
private int count;
private Random rand = new Random();

public int size() {
int size = 0 ;
while (contents != null )
{
contents
= contents.getNext();
size
++ ;
}
return size;
}

public boolean isEmpty() {
return (size() == 0 );
}

public void add(Object element) {
LinearNode node
= new LinearNode(element);
node.setNext(contents);
node
= contents;
count
++ ;
}

public Object remove(Object element) throws EmptyBagException {
boolean found = false ;
LinearNode previous,current;
Object result
= null ;

if (isEmpty())
throw new EmptyBagException();

if (contents.getElement().equals(element)) // 如果删除的是第一个元素
{
result
= contents.getElement();
contents
= contents.getNext();
}
else
{
previous
= contents;
current
= contents.getNext();

for ( int look = 1 ;look < count && ! found;look ++ )
if (current.getElement().equals(element))
found
= true ;
else
{
previous
= current;
current
= current.getNext();
}

if ( ! found)
throw new NoSuchElementException();

result
= current.getElement();
previous.setNext(current.getNext());
// 删除current
}

count
-- ;
return result;
}


public void addAll(BagADT bag) {
// TODO Auto-generated method stub

}

public boolean contains(Object target) {
// TODO Auto-generated method stub
return false ;
}

public boolean equals(ArrayBag bag) throws EmptyBagException {
// TODO Auto-generated method stub
return false ;
}

public Iterator iterator() {
// TODO Auto-generated method stub
return null ;
}

public Object removeRandom() throws EmptyBagException {
// TODO Auto-generated method stub
return null ;
}

public BagADT union(BagADT set) {
// TODO Auto-generated method stub
return null ;
}

}
复制代码




上面写了这么多,主要是想通过一个集合数据结构的数组和链式实现方式,

1)来说明怎么去定义和实现一个数据结构,

2)在数组实现里,要维护一个数组,把元素存在数组里,在链式实现里,要维护一条链,(维护其首节点和长度即可)。

3)以及结点类型如何去定义!

理清楚了这些过程,在后面实现栈,队列,链表,树,图的过程中,就不再这么详细的描述细节了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值