一、实现一个简单队列
1、链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表有一系节点组成,节点可以在运行时动态生成。每个节点包括两个部分,一个是存储数据元素的数据域,另一个是存储下一个节点地址的指针域。
2、数组的缺陷:
数组是有长度限制的,如果我们要往数组中存储数据,在我们不知道要存储多少的时候,要怎么初始化数组长度呢?我们只能给数组一个固定的初始长度,所以,我们不能让数组的长度随我们存储数据的增多而增加。
3、队列应运而生:
人类是很聪明的动物,于是乎,我们创造了队列这种数据结构,队列的长度可以随我们存储数据的增多而增长,也可以随我们存储数据的减少而减少。
4、链表、数组和队列的共同点:
链表、数组和队列本质上是相同的,他们都是一种存储数据的容器。
5、数组和队列的不同点:
数组长度固定,队列长度可以变化。
6、数组与链表的不同点:
1)数组是连续的,链表是离散的。
也就是说,数组表示的是一连串连续地址。链表节点表示的地址是不连续的。
2)数组利于查找,链表不利于查找。
因为数组可以看做是一种天然的数据结构,它得天独厚之处在于它的连续性,为我们的查找提供了很大的便利。
相反,对于链表,由于它的不连续性,使得每查找一个节点都要遍历一遍链表,消耗多大,不利于查找。
3)数组不利于删除和插入,链表利于删除和插入。
上天是公平的。
对于数组,它的连续性为删除和插入操作造成了阻碍。当插入或删除一个数时,都需要移动该数位置之后的所有的数,需要遍历整个数组。
对于链表,当插入或删除一个数时,只需要改变该数上一个节点的next指针。而不需要遍历整个链表。
二、用数组实现一个队列并优化:
1、用数组实现一个简单队列:
前面我们已经学习了如何用数组和链表实现一个简单队列,用数组实现简单队列add方法如下:
/**
* 实现泛型队列
* @author zr
*
* @param <E>
*/
public class STList<E> implements NetJavaList<E>{
//队列内部初始用来装对象的数组,长度为0
private Object [] srcA = new Object [0];
//向队列中加入一个对象
public void add(E e) {
//1、新建一个数组,长度是原数组长度+1
Object [] destA = new Object [srcA.length+1];//如果该数组长度为10,那么srcA.length就为9
//2、将要加入的对象放入新数组的最后一个位置
destA[srcA.length]=e;//注意:数组的位置从0开始,因为数组长度为10,0~9,故最后一个位置是9
//3、将原数组中的东西放到新数组中:使用系统提供的数组copy方法
System.arraycopy(srcA, 0, destA, 0, srcA.length);
//指向新建的数组
srcA=destA;
}
//取得队列中指定位置的一个对象,如果指定了类型,则转为指定类型返回
public E get(int index) {
E st=(E)srcA[index];
return st;
}
public int size() {
return srcA.length;
}
}
这样实现的队列,有很多缺点:
1)不能对队列中的对象进行插入和删除操作;
2)使用该队列的人不能设置数组的初始长度;
3)每增加一个对象,就要开辟一个空间,并且都要将数组中的所有元素重新赋值,使得程序效率变低。
2、用数组实现队列并优化:
针对以上问题,提出一些解决方案:
1)对队列进行扩展,加入insert和delete方法;
2)重载构造方法,传入参数,让使用者可以对某些属性进行初始化
3)增加效率(优化):
举个例子:
假如我们要开个会,有两种方案:
i) 每来一个人就去买一张椅子;
ii) 一开始就买好10张椅子,当十张椅子坐满了,再去买5张椅子,当这5张椅子也坐满了,又去买5张……
比较两种方案,显然,方案2)更节省人力。
同理,我们把实际问题应用与程序中:
因为每增加一个对象,就要开辟一个空间,并且都要将数组中的每个元素重新赋值。虽然每次所需的空间不大,但是程序运行所需的时间却很多,这就给计算机造成了负担。如果我们每次开辟多个空间,即实例化一个数组对象,这样,每add一个对象,就放到数组中,就不需要每次对数组中的所有元素重新赋值,当数组存满时,再实例化一个数组对象,这样一来,开辟空间的次数变少了,所花的时间也变少了,对数组中所有元素赋值的时间也变少了,效率自然就提高了。
/**
* 用数组实现一个队列并优化
* @author zr
*
*/
public class ArrayQueue<E> {
private int inlength;//数组的初始长度
private int count;//添加对象的次数
private int incount;//数组每次增长的长度
private int arraycount;//数组增长的次数
private Object [] obj;
// private Object [] obj=new Object[inlength];
{
System.out.println("aaa");
}
/**
* ArrayQueue aq=new ArrayQueue
* 定义一个构造函数,用来初始化inlength和incount的值
* @param inlenght 数组的初始长度
* @param incount 数组每次增长的长度
*/
public ArrayQueue(int inlength,int incount){
System.out.println("bbb");
this.inlength=inlength;
this.incount=incount;
obj=new Object[inlength];
}
public static void main(String[] args){
ArrayQueue aq=new ArrayQueue(100,10);
}
/**
* 定义一个无参构造器,分别给定了inlength和incount默认值,inlength=10,incount=5
*/
public ArrayQueue(){
this(10,5);
}
/**
* 定义了一个构造方法,用于初始化incount的值,并给定了inlength的默认值10
* @param incount
*/
public ArrayQueue(int incount){
this(10,incount);
}
/**
* 在队列中指定位置之后插入一个元素
* @param index 指定的位置
* @param e 插入的对象
*/
public void insert(int index,E e){
//当指定位置小于或等于数组长度时
if(index<obj.length){
//新建一个数组,长度是原数组长度+1
Object [] obj2=new Object[obj.length+1];
for(int i=0;i<obj.length+1;i++){
//将index位置之前(包括index)的元素放到新数组中
if(i<index||i==index){
obj2[i]=obj[i];
}
else if(i==index+1){
//将插入的元素放到index之后的位置
obj2[i]=e;
}else{
//将index之后元素的位置向后移动一个位置
obj2[i]=obj[i-1];
}
}
obj=obj2;
}
//当指定位置大于数组长度时
else{
System.out.println("您指定的位置大于队列长度,不能插入!");
}
}
/**
* 删除队列中指定位置的一个元素
* @param index 指定的位置
*/
public void delete(int index){
//当指定位置小于数组长度时
if(index<obj.length){
//新建一个数组,长度是原数组长度-1
Object [] obj2=new Object[obj.length-1];
for(int i=0;i<obj.length-1;i++){
//将index位置之前的元素放到新数组中
if(i<index){
obj2[i]=obj[i];
}
//把index位置之后的元素向前移动一个位置
else{
obj2[i]=obj[i+1];
}
}
//指向新的数组
obj=obj2;
}else{
System.out.println("您指定的位置超出了队列的长度,不能删除!");
}
}
/**
* 向队列中加入一个对象
* @param e 队列中加入的对象
*/
public void add(E e){
// System.out.println("obj.length="+obj.length);
count++;
// System.out.println("count="+count);
int length=inlength+arraycount*incount;//为保存对象预留的长度
//当数组的长度超过length时
if(count>length){
//1、新建一个数组,长度是原数组长度+incount
Object [] obj2=new Object[obj.length+incount];
arraycount++;//数组每增长一次加1
//2、将原数组中的东西放到新数组中
for(int i=0;i<obj.length;i++){
obj2[i]=obj[i];
}
//3、将要加入的对象放入新数组的最后一个位置
obj2[obj.length]=e;
//指向新建的数组
obj=obj2;
}
//当数组的长度小于length时,把对象添加到已存在的数组中
obj[count-1]=e;
}
/**
* 取得队列中指定位置的一个对象
* @param index 指定的位置
* @return 返回指定位置的对象
*/
public E get(int index){
E e0=(E) obj[index];
return e0;
}
/**
* 得到队列的长度,即队列中元素的个数
* @return 返回队列中元素的个数
*/
public int size(){
return obj.length;
}
}
三、用链表实现一个队列并扩展
《用链表实现队列》中,链表实现的队列只能添加数据。下面我们在此基础上进行扩展,定义了delete和insert方法,可以对队列中的元素进行删除和插入操作。
/**
* 链表节点类
* @author zr
*
*/
public class LinkNode {
public Object data;
public LinkNode next;
}
/**
* 用链表实现一个队列并扩展
* @author zr
*
*/
public class LinkListQueue<E> {
private LinkNode root;
LinkNode tem1=new LinkNode();//tem1作为一个临时节点
public LinkListQueue(LinkNode root){
this.root=root;
}
/**
* 在队列中指定位置之前插入一个对象
* @param index 指定的位置
* @param e 插入的元对象
*/
public void insert(int index,E e){
int length=size();
LinkNode tem=new LinkNode();
LinkNode tem2=new LinkNode();
tem2.data=e;
int c=1;
if(index<1){
System.out.println("位置从1开始!");
}else if(index==1){
tem2.next=root;
root=tem2;
}else if(index>1&&index<=length){
tem1=root;
while(c<index+1){
if(c==index-1){
tem2.next=tem1.next;
tem1.next=tem2;
tem1=tem2;
}else{
tem=tem1.next;
tem1=tem;
}
c++;
}
}else{
System.out.println("您指定的位置超出了队列长队,请重新指定!");
}
}
/**
* 删除队列中指定位置的某一元素
* @param index 指定的位置
*/
public void delete(int index){
LinkNode tem=new LinkNode();
int c=1;
int length=size();
if(index<0){
System.out.println("位置从1开始");
}else if(index==1){
root=root.next;
}else if(index<=length&&index>1){
tem1=root;
while(c<=index){
if(c==index-1){
tem=tem1.next.next;
tem1.next=tem;
tem1=tem;
}else{
tem=tem1.next;
tem1=tem;
}
c++;
}
}else{
System.out.println("您指定的位置超出了队列范围,请重新指定!");
}
}
/**
* 向队列中添加一个对象
* @param obj 所要添加的对象
*/
public void add(E e){
if(root==null){
root=new LinkNode();
root.data=e;
}else{
LinkNode tem=new LinkNode();
tem.data=e;
int length=size();
tem1=get(length);
tem1.next=tem;
tem1=tem;
}
}
/**
* 获得指定位置的节点
* @param index 指定的位置
* @return 返回指定位置的节点
*/
public LinkNode get(int index){
int length=size();
LinkNode node = new LinkNode();
LinkNode tem=new LinkNode();
if(index<1){
System.out.println("位置从1开始");
}else if(index>=1&&index<=length){
int c=1;
tem1=root;
while(c<index||c==index){
if(c==index){
node=tem1;
}
c++;
tem=tem1.next;
tem1=tem;
}
}else{
System.out.println("您指定的位置超出了队列范围,请重新指定位置!");
}
return node;
}
/**
* 计算队列的长度
* @return 返回队列的长度
*/
public int size(){
int length=0;
LinkNode tem2=new LinkNode();
LinkNode tem=new LinkNode();
tem2=root;
while(tem2!=null){
tem=tem2.next;
tem2=tem;
length++;
}
return length;
}
}
/**
* 测试用一个链表实现的队列
* @author zr
*
*/
public class LLQTest {
private static LinkNode root;
public static void main(String[] args) {
LinkListQueue<String> llq=new LinkListQueue<String>(root);
//对添加操作的测试
System.out.println("添加后:");
llq.add("a");
llq.add("b");
llq.add("c");
llq.add("d");
llq.add("e");
for(int i=1;i<=llq.size();i++){
LinkNode node=llq.get(i);
System.out.println(i+":"+node.data);
}
//对删除操作的测试
System.out.println("删除后:");
llq.delete(2);
for(int j=1;j<=llq.size();j++){
LinkNode ADnode=llq.get(j);
System.out.println(j+":"+ADnode.data);
}
//对插入操作的测试
System.out.println("插入后:");
llq.insert(2, "b");
for(int t=1;t<=llq.size();t++){
LinkNode AInode=llq.get(t);
System.out.println(t+":"+AInode.data);
}
}
}
结果:
添加后:
1:a
2:b
3:c
4:d
5:e
删除后:
1:a
2:c
3:d
4:e
插入后:
1:a
2:b
3:c
4:d
5:e