数组是一个对象,是命名的同类型变量的集合。形象的说,数组就是由一个个格子组成,在格子中可以装东西,而这些东西就是数据,一个数组中要求这些数据是同类型的。
数组简单地说就是一个可以储存数据的容器。在编程中经常会用到这样的容器,比如画图板图形的重绘,以及在五子棋游戏开发中用到的数组储存棋子的位置和颜色参数等等。
数组的声明和定义比较简单,就不在赘述,但要注意当定义引用数据类型的数组,并要向其中赋值时需要先实例化对象。
在实际应用中,会遇到这样的情况,我们不知道所要多大的容器来存储需要的数据,这时数组就显露了它的缺陷,如果我们定义过长就会浪费内存空间,而定义过短又不够用,
因为数组的长度是不可变的,缺少弹性。我们就想办法运用数组造出一个可以边长的新容器——队列。队列是怎样运用数组包装起来呢,以下是实现过程:
/**
* 实现一个队列
* @author小韦 2013-04-11
*
*/
public class Myset<E> {
private int initcount;
private int increcount;
private int count=0;
private Object[] A;
/**
* 指定创建时队列的大小和每次增长的空间
* @param initcount 初始大小
* @param increcount 增长空间
*/
public Myset(int initcount,int increcount){
this.initcount=initcount;this.increcount=increcount;
A=new Object[initcount];
}
/**
* 无参构造器,实例化对象时不设参数默认队列的初始长度为10,每次增加长度为2;
*/
public Myset(){
this(10,2);
}
/**
* 可设置每次增长空间的构造函数
*/
public Myset(int increcount){
this(10,increcount);
}
/**
* 向队列中添加元素对象
* @param e对象
*/
public void add(E e){
count++;
//如果队列中的元素的个数超过了队列容器的长度,自动增加队列的长度,减少搬运操作
if(count>A.length){
Object[] B=new Object[A.length+increcount];
for(int i=0;i<A.length;i++){
B[i]=A[i];
}
A=B;
}
//将元素赋给队列数组
A[count-1]=e;
}
/**
* 向队列的指定位置传入一个对象
* @param e传入对象
* @param index位置参数
*/
public void insert(E e,int index){
count++;
if(index<0||index>this.size()){
System.out.println("下标越界");
}else{
//判断队列是否应该增长大小,应该增加时执行相应操作
if(count>A.length){
Object[] B=new Object[A.length+increcount];
for(int i=0;i<A.length;i++){
B[i]=A[i];
}
A=B;
}
//在插入前将指定位置及后边的元素往后移一个单位
for(int i=count-1;i>index;i--){
A[i]=A[i-1];
}
//将插入值赋给指定位置
A[index]=e;
}
}
/**
* 删除队列中指定位置的对象元素
* @param index位置参数
*/
public void delete(int index){
count--;
if(index<0||index>this.size()){
System.out.print("下标越界");
}else{
//将指定位置之后的值都向前移动一位
for(int i=index;i<count;i++){
A[i]=A[i+1];
}
//将最后一位置空
A[count]=null;
//如所需队里长度减少,将每次减少相应长度,直至队列变为初始长度
if(count<=A.length-increcount&&count>=initcount){
Object[] B=new Object[A.length-increcount];
for(int i=0;i<count;i++){
B[i]=A[i];
}
A=B;
}
}
}
/**
* 取得队列中任意位置的对象,如指定类型,则转入该类型返回
* @param index 指定的位置
* @return返回指定对象
*/
public E get(int index){
//将数组队列的对象转换为泛型
E st=(E)A[index];
return st;
}
/**
* 获得队列的长度,即元素的多少
* @return队列长度
*/
public int size(){
return count;
}
}
不难发现,这个用数组实现的队列进行了一点优化,就是先给队列数组一个初始长度,并在数据元素个数增加到一定量时一次增加一个长度,这样做的一个优点就是减少了队列数组的置换操作,即在数组转换时减少了数据的搬运 。为了便于测试,此处编码的增加长度比较少,较大时效率的增加会更明显,当然这样做也会牺牲一定的空间,运用时给队列适合的增长参数。
数组在空间上是连续的,其实现的队列空间也是连续的,而另一种基本的数据结构——链表在空间上是离散的。他们的属性决定了他们有着不同的优缺点。数组在查找方面效率是比较高的,而插入和删除是比较困难的。而链表恰好相反,在查找时存在缺陷,而其优势便在于插入和删除等操作。在运用他们时要根据情况权衡利弊。以下是有链表实现的队列:
/**
*创建一个链表节点
*/
public class MyNode {
public Object data;
public MyNode next;
public MyNode(Object data){
this.data=data;
}
public MyNode(){
}
}
/**
*创建链表类
*/
public class LinkList<E> {
private MyNode root;
private MyNode Last=new MyNode();
private int count=0;
/**
*向链表中存放节点元素
* @param m 节点对象
*/
public void add(E e){
//将要添加的对象加入节点
MyNode node=new MyNode(e);
count++;
//如果根节点为空,则将新加入的对象节点置为根节点
if(root==null){
root=new MyNode();
root.data=node.data;
Last=root;
}else{
//如根节点不为空,则先将最后的节点(即last所指的节点)的下一个节点指向新添加的节点,再讲last指向新节点
MyNode tem=new MyNode();
tem.data=node.data;
Last.next=tem;
Last=tem;
}
}
/**
* 向链表指定位置插入元素
* @param m 插入的节点
* @param x 位置参数
*/
public void insert(E e,int x){
if(x<0||x>=this.size()){
System.out.println("下标越界");
}else{
MyNode node=new MyNode(e);
//如果插入的位置为0号,则将所插入节点置为根节点,否则将新节点指向下一个节点,并将指定位置的上个节点指向新节点
if(x==0){
node.next=this.get(x);
root=node;
}else{
node.next=this.get(x);
this.get(x-1).next=node;
}
count++;
}
}
/**
* 从链表指定位置删除元素
* @param x位置参数
*/
public void delete(int x){
if(x<0||x>this.size()){
System.out.println("下标越界");
}else{
//如果删除第一个节点,则将下一个节点置为根节点,否则将指定位置的上个节点指向指定位置的下个节点
if(x==0){
root=this.get(x+1);
}else{
this.get(x-1).next=this.get(x+1);
}
count--;
}
}
/**
* 从链表中读出指定位置的元素
* @param x位置参数
* @return指定位置元素
*/
public MyNode get(int x){
if(x<0||x>this.size()){
System.out.println("下标越界");
return null;
}else{
//从根节点开始,使用循环直到找出指定位置节点为止
MyNode index=new MyNode();
MyNode tem=new MyNode();
index=root;
for(int i=0;i<x;i++){
tem=index.next;
index=tem;
}
return index;
}
}
/**
* 获得链表的长度
* @return链表长度
*/
public int size(){
return count;
}
}
本人实现的队列在效率和优化方面还有很多不足之处,望多多指教