常见数据结构的Java实现
在jdk1.2 之后,Java提供了实现常见数据结构的类,创建链表等数据结构和创建数组一样简单,不再需要你去写具体的算法。
12.1链表
12.2 堆栈
12.3 树集
12.4 散列表
12.5 向量
12.1 链表
如果需要处理一些类型相同的数据,人们习惯上使用数组这种数据结构,但数组在使用之前必须定义大小,而且不能动态定义大小。
链表是由若干个称作节点的对象组成的一种数据结构,分为两种类型:
单链表:每个节点含有一个数据和下一个节点对象的引用;
双链表:含有一个数据并含有上一个节点对象的引用和下一个节点对象的引用。
数据
引用
数据
引用
数据
引用
数据
引用
单链表示意图
数据
引用
引用
数据
引用
引用
数据
引用
引用
数据
引用
引用
双链表示意图
1 .创建链表
使用java.util 包中的LinkedList类创建一个链表。
例如,
LinkedList mylist=new LinkedList();
//创建了一个空双链表
//增加节点
mylist.add(“It”);
mylist.add(“is”);
mylist.add(“a”);
mylist.add(“door”);
这时,就形成了下图示意的链表,4个节点是自动连接在一起的。
is
0xAb9
0xbc
It
0x2b
null
a
0xbb9
Ox56
用LinkedList创建的一个链表
door
null
0xb6
mylist可以使用方法public Object get(index i)获取第i个节点中存储的数据。
存放在节点中的数据都被看作是一个Object对象。
下面的例子构造一个含有4个节点链表,并输出节点中的数据。
例子1
import java.util.*;
public class LinkListOne
{public static void main(String
args[])
{ LinkedList mylist=new LinkedList();
mylist.add("It"); //链表中的第一个节点。
mylist.add("is");//链表中的第二个节点。
mylist.add("a");//链表中的第三个节点。
mylist.add("door");//链表中的第四个节点。
int
number=mylist.size();//获取链表的长度。
for(int
i=0;i
{String
temp=(String)mylist.get(i);
System.out.println("第"+i+"节点中的数据:"+temp);
}
}
}
注:由于任何类都是Object类的子类,因此可以把任何一个对象作为链表的节点对象。
需要注意的是当使用get()获取一个节点对象后,要用类型转换运算符转化回原来的类型。
2.LinkedList类中的常用方法:
boolean add(Object element) :向链表的末尾填加一个新的节点对象elememt。
void add(int index ,Object element)
:向链表的指定位置尾填加一个新的节点
对象elememt。
void addFirst(Object element)
:把节点对象elememt填加到链表的表头。
void addLast(Object element)
:把节点对象elememt填加到链表的末尾。
void clear():删除链表的所有节点对象。
Object remove(int index):删除指定位置上的节点对象。
boolean remove(Object
element):将首次出现的节点对象element删除。
Obiect removeFirst():删除第一个节点对象,并返回这个节点对象。
Obiect removeLast():删除最后一个节点对象。
Object get(int index):得到链表中指定位置处的节点对象。
Object getFirst():得到链表中第一个节点对象。
Object getLast():得到链表中最后一个节点对象。
int indexOf(Object
element):返回节点对象element在链表中首次出现的位置,
如果链表中无此节点对象则返回-1。
public int lastIndexOf(Object
element):返回节点对象element在链表中最后出现的位
置,如果链表中无此节点对象则返回-1。
public Object set(int index ,Object
element):用节点对象element替换链表中指定位置
处的节点对象。并返回链表中先前位置处的节点对象。
public int size():返回链表的长度,即节点的个数。
public boolean contains(Object
element):判断链表节点对象中是否含有element。
下面的例子2中包含了LinkedList类中的一些常用方法。
例子2
import java.util.*;
public class LinkListTwo
{public static void main(String
args[])
{ LinkedList mylist=new LinkedList();
mylist.add("is");
mylist.add("a");
int number=mylist.size();
System.out.println("现在链表中有"+number+"个节点:");
for(int
i=0;i
{String temp=(String)mylist.get(i);
System.out.println("第"+i+"节点中的数据:"+temp);
}
mylist.addFirst("It");
mylist.addLast("door");
number=mylist.size();
System.out.println("现在链表中有"+number+"个节点:");
for(int
i=0;i
{String temp=(String)mylist.get(i);
System.out.println("第"+i+"节点中的数据:"+temp);
}
mylist.remove(0);
mylist.remove(1);
mylist.set(0,"open");
number=mylist.size();
System.out.println("现在链表中有"+number+"个节点:");
for(int
i=0;i
{String temp=(String)mylist.get(i);
System.out.println("第"+i+"节点中的数据:"+temp);
}
}
}
3.使用Iterator类遍历链表
在例子1和例子2中我们借助get方法实现了遍历链表。
我们可以借助Iterator对象实现遍历链表,
一个链表对象可以使用iterator()方法获取一个Iterator对象,
后者使用next()方法遍历链表。
例子3,我们把学生的成绩存放在一个链表中,并实现了遍历链表。
import java.util.*;
class Student
{String name ;int number;float
score;
Student(String name,int number,float
score)
{this.name=name;this.number=number;this.score=score;
}
}
public class LinkListThree
{public static void main(String
args[])
{ LinkedList mylist=new LinkedList();
Student stu_1=new
Student("赵好民" ,9012,
80.0f),
stu_2=new
Student("钱小青" ,9013,90.0f),
stu_3=new
Student("孙力枚" ,9014,78.0f),
stu_4=new
Student("周左右" ,9015,55.0f);
mylist.add(stu_1);
mylist.add(stu_2);
mylist.add(stu_3);
mylist.add(stu_4);
Iterator iter=mylist.iterator();
while(iter.hasNext()){
Student te=(Student)iter.next();
System.out.println(te.name+" "+te.number+"
"+te.score);
}
}
}
下面是一个较复杂的例子,用链表来管理商品的库存情况。
通过节点存放一个商品对象,该对象具有代号、名称、库存量和单价等属性。
例子4
import java.util.*;import
java.awt.event.*;import java.awt.*;
import javax.swing.*;import
java.io.*;
class商品
extends Panel
{String 代号,名称;int 库存;float 单价;
商品(String 代号,String 名称,int 库存,float 单价)
{this.代号=代号;this.名称=名称;this.库存=库存;this.单价=单价;
}
}
class ShowWin extends JFrame implements
ActionListener
{
LinkedList goods_list=null;
JTextField 代号文本框=new JTextField(),
名称文本框=new JTextField(),
库存文本框=new JTextField(),
单价文本框=new JTextField(),
删除文本框=new JTextField();
JButton b_add=new
JButton("添加商品"),
b_del=new
JButton("删除商品"),
b_show =new
JButton("显示商品清单");
JTextArea 显示区=new JTextArea();
ShowWin()
{goods_list=new LinkedList();
Container
con=getContentPane();
JScrollPane pane=new
JScrollPane(显示区);
显示区.setEditable(false);
JPanel save=new JPanel();
save.setLayout(new
GridLayout(5,2));
save.add(new
Label("输入代号:"));save.add(代号文本框);
save.add(new
Label("输入名称:"));save.add(名称文本框);
save.add(new
Label("输入库存:"));save.add(库存文本框);
save.add(new
Label("输入单价:"));save.add(单价文本框);
save.add(new
Label("点击添加:"));save.add(b_add);
JPanel del=new JPanel();del.setLayout(new
GridLayout(2,2));
del.add(new
Label("输入删除的代号:"));del.add(删除文本框);
del.add(new
Label("点击删除:"));del.add(b_del);
JPanel show=new
JPanel();show.setLayout(new BorderLayout());
show.add(pane,BorderLayout.CENTER);show.add(b_show,BorderLayout.SOUTH);
JSplitPane
split_one,split_two;
split_one=new
JSplitPane(JSplitPane.VERTICAL_SPLIT,save,del);
split_two=new
JSplitPane(JSplitPane.HORIZONTAL_SPLIT,true,split_one,show);
con.add(split_two,BorderLayout.CENTER);
b_add.addActionListener(this);b_del.addActionListener(this);
b_show.addActionListener(this);
}
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==b_add){
String
daihao=null,mingcheng=null;
int kucun=0;
float danjia=0.0f;
daihao=代号文本框.getText();
mingcheng=名称文本框.getText();
kucun=Integer.parseInt(库存文本框.getText());
danjia=Float.valueOf(单价文本框.getText()).floatValue();
商品
goods=new 商品(daihao,mingcheng,kucun,danjia);
goods_list.add(goods);
try {
FileOutputStream file=new
FileOutputStream("goods.txt");
ObjectOutputStream out=new
ObjectOutputStream(file);
out.writeObject(goods_list);
out.close();
}catch(IOException event){}
}
else if(e.getSource()==b_del)
{
String daihao=删除文本框.getText();
try {
FileInputStream come_in=new
FileInputStream("goods.txt");
ObjectInputStream in=new
ObjectInputStream(come_in);
goods_list=(LinkedList)in.readObject();
in.close();
}catch(ClassNotFoundException
event){}
catch(IOException event){}
int number=goods_list.size();
for(int
i=0;i
{商品
temp=(商品)goods_list.get(i);
if(temp.代号.equals(daihao))
{
goods_list.remove(i);
}
try{
FileOutputStream file=new
FileOutputStream("goods.txt");
ObjectOutputStream out=new
ObjectOutputStream(file);
out.writeObject(goods_list);
out.close();
}
catch(IOException event){}
}
}
else
if(e.getSource()==b_show)
{ 显示区.setText(null);
try {
FileInputStream come_in=new
FileInputStream("goods.txt");
ObjectInputStream in=new
ObjectInputStream(come_in);
goods_list=(LinkedList)in.readObject();
}
catch(ClassNotFoundException
event){}
catch(IOException event){}
Iterator iter=goods_list.iterator();
while(iter.hasNext())
{ 商品
te=(商品)iter.next();
显示区.append("商品代号:"+te.代号+"
");
显示区.append("商品名称:"+te.名称+"
");
显示区.append("商品库存:"+te.库存+"
");
显示区.append("商品单价:"+te.单价+"
");
显示区.append("\n");
}
}
}
}
public class LinkListFour
{public static void main(String
args[])
{ ShowWin win=new
ShowWin();
win.setSize(100,100);
win.setVisible(true);
win.addWindowListener(new
WindowAdapter()
{public void windowClosing(WindowEvent
e)
{System.exit(0);}});
}
}
注:在上面的例子中,商品类故意扩展了java.awt包中的Panel,
因为Java提供给我们的绝大多数对象都是所谓序列化的,
比如组件等,这是为了保证把对象写入到文件中后,
能再把对象正确读回到程序中的缘故。
商品类是我们自己写的的类,没有序列化,只要随便扩展一个序列化的类即可。
12.2 堆栈
堆栈是一种“后进先出”的数据结构,只能在一端进行输入或输出数据的操作。堆栈把第一个放入该堆栈的数据放在最底下,而把后续放入的数据放在已有数据的顶上,如图所示。
数据
数据
数据
数据
新数据
输出数据
压栈
弹栈
堆栈结构示意图
向堆栈中输入数据的操作称为“压栈”,从栈中输出数据的操作称为“弹栈”。
使用java.util包中的Stack类创建一个堆栈对象,堆栈对象可以使用
Object push(Object data);输入数据,实现压栈操作.
Object pop();输出数据,实现弹栈操作。
boolean empty();判断堆栈是否还有数据,有数据返回false ,否则返回true。
Object peek();查看堆栈顶端的数据,但不删除该数据。
public int search(Object data);获取数据在堆栈中的位置,最顶端的位置是1,向下依次增加,如果堆栈不含此数据,则返回-1。
注:由于任何类都是Object类的子类,因此可以把任何一个对象压入堆栈。
下面的例子5将数字1,2,3,4,5,6压入堆栈,然后弹出。
例子5
import java.util.*;
class StackOne
{public static void main(String
args[])
{Stack mystack=new Stack();
mystack.push(new Integer(1));
mystack.push(new Integer(2));
mystack.push(new Integer(3));
mystack.push(new Integer(4));
mystack.push(new Integer(5));
mystack.push(new Integer(6));
while(!(mystack.empty()))
{Integer
temp=(Integer)mystack.pop();
System.out.print("
"+temp.toString());}
}
}
堆栈是很灵活的数据结构,使用堆栈可以节省内存的开销。
比如,递归是一种很消耗内存的算法,我们可以借助堆栈消除大部分递归,
达到和递归算法同样的目的。
Fibonacii整数序列是我们熟悉的一个递归序列,
它的第n项是前两项的和,第一项和第二项是1。
下面的例子6用堆栈输出该递归序列的若干项。
import java.util.*;
class StackTwo
{public static void main(String
args[])
{Stack mystack=new Stack();
mystack.push(new Integer(1));
mystack.push(new Integer(1));
int k=1;
while(k<=10)
for(int
i=1;i<=2;i++){
Integer F1=(Integer)mystack.pop();
int f1=F1.intValue();
Integer F2=(Integer)mystack.pop();
int f2=F2.intValue();
Integer temp=new Integer(f1+f2);
System.out.println(""+temp.toString());
mystack.push(temp);
mystack.push(F2);
k++;
}
}
}
注:如果将上述程序中
mystack.push(temp);
mystack.push(F2);
两个语句的顺序颠倒,输出的结果如何?