计算机中两种基本的数据结构式数组及链表,前者是在自然顺序的内存中存储数据,而后者是通过其基本单元——节点的数据域和指针域来存储数据和记录数据间的相对位置,概括地说:数组中的数据位置是连续的,链表中的数据位置是离散的。
这就决定了,在数组中找一个数据很容易(只需知道数组首地址及数据在数组中的位置);而在链表中改变数据的相对位置很容易(只需改变个别节点的指针域就行)。他们各有优势,也各有劣势,并且优势与劣势恰好相反。
正是这一不同决定了用他们来实现队列时,队列的优势与劣势。
队列的特点是可以改变长度(存储数据的个数),是一种比较理想的数据结构。我们常常要对它进行“增删查改”的工作,或者更根本地说是“找”与“变”的工作。“找”就是找到索引位置的数据,数组队列非常擅长,
“变”就是改变数据间的相对位置关系,链表队列非常擅长。而当我们“变”数组队列时,往往要频繁地新建数组并逐个赋值;当我们“查”链表队列中的数据时,往往要遍历队列,工作量很大。
总之两者各有千秋,适合用于各自擅长的场合,使用时要根据情况灵活选取
传上我的队列实现代码:
/** * 队列的接口,定义一些使用方法 * @author 朱凌峰 * * @param <E> 泛型 */ public interface MyQueue<E> { //在队列末尾加上一个数据 public void add(E e); //在队列中指定位置插入一个数据 public void insert(E e,int index); //取出指定位置的一个数据 public E get(int index); //删除指定位置的一个数据 public void remove(int index); //改变指定位置的数据 public void set(E e,int index); //获取队列长度 public int size(); //按顺序打印队列中的每个数据 public void printQueue(); //检验索引位置是否越界,如越界,则抛出异常 public boolean judgeIndex(int index); }
/** * 定义我自己的数组队列 * * @author 朱凌峰 * */ public class MyArrayList<E> implements MyQueue<E> { private Object[] dataArray;// 用于存储数据的数组 int lengthIncreaseStep;// 每次数组长度增加的步长 int countData = 0;// 数据个数计数器 /** * 默认构造方法 默认数组初始长度为10,每次数组长度增加的步长为10 */ public MyArrayList() { this(10, 10); } /** * 设定数组初始长度的构造方法 * * @param initLength * 数组的初始长度 */ public MyArrayList(int initLength) { this(initLength, 10); } /** * 设定数组初始长度和每次数组长度增加的步长的构造方法 * * @param initLength * 数组初始长度 * @param lengthIncreaseStep * 数组长度增加的步长 */ public MyArrayList(int initLength, int lengthIncreaseStep) { this.lengthIncreaseStep = lengthIncreaseStep; this.dataArray = new Object[initLength]; } /** * 添加一个数据到队列的末尾 * * @param msAdd * 要添加的数据 */ @Override public void add(E e) { // 如果数据个数等于当前数组长度 if (countData == dataArray.length) { // 新建加长长度的数组 Object[] lengthenedArray = new Object[this.dataArray.length + this.lengthIncreaseStep]; // 将原有数组里的数据从后往前逐个加入到新的数组中 for (int i = 0; i < dataArray.length; i++) { lengthenedArray[i] = dataArray[i]; } // 将添加的数据加入到新建数组中 lengthenedArray[dataArray.length] = e; // 将新建数组指向原数组 dataArray = lengthenedArray; } else { dataArray[countData] = e; } countData++; } /** * 在索引位置前面一个位置插入数据(若要插在最后一个数据后面,则用add方法) */ public void insert(E e, int index) { if(this.judgeIndex(index)){ //将原来队列的最后一个数据添加到队列末尾 this.add(this.get(this.size()-1)); //将原来最后第二个数据至index向后移动一位 for(int i =this.size()-3 ;i >=index;i--){ this.set(this.get(i), i+1); } //将index位置设置为新插入的数据 this.set(e, index); } } /** * 获取队列中指定位置的数据 * * @param index * 数据在队列中的位置 * @return 指定位置的数据 */ public E get(int index) { // 检查index是否有效 if (judgeIndex(index)) { return (E) dataArray[index]; } return null; } /** * 移除索引位置对应的数据 * @param index 索引位置 */ public void remove(int index) { if(this.judgeIndex(index)){ for (int i = index; i < this.size()-1; i++) { dataArray[i]=dataArray[i+1]; } this.countData--; } } /** *设置索引位置的数据值 *@param e 数据值 *@param index 索引位置 */ public void set(E e, int index) { // 检查index是否有效 if (judgeIndex(index)) { this.dataArray[index] = e; } } /** * 获取队列的长度 * * @return 一个整数表示队列长度 */ public int size() { return countData; } /** * 按顺序打印队列中的每个数据,若队列为空则抛出异常 */ public void printQueue() { if(this.size()==0) throw new java.lang.RuntimeException("队列为空"); else { for (int i = 0; i < this.size(); i++) { System.out.println("队列中第"+i+"个数据为:"+this.get(i)); } } } /** * 判断索引位置是否有效,若索引位置有效,则返回true,否则抛出异常 * * @param index * 索引位置 * @return 若索引位置有效,则返回true */ @Override public boolean judgeIndex(int index) { // 判断索引位置是否有效 if (index < 0 || index >= this.size()) { throw new java.lang.RuntimeException("索引位置" + index + "越界:" + ",当前队列大小为:" + this.size()); } return true; } /** * 将一个队列添加到当前队列的末尾 * @param arrayList 要添加到末尾的队列 */ public void addAll(MyArrayList<E> arrayList){ //新建一个长度为当前队列和要添加到末尾的队列长度之和的数组 Object[] tempArray=new Object[this.size()+arrayList.size()]; //将当前队列的每个数据一次添加到临时数组中 for (int i = 0; i < this.size(); i++) { tempArray[i]=dataArray[i]; } //将 要添加到末尾的队列中的每个每个数据添加到临时数组中 for (int i = 0; i < arrayList.size() ; i++) { tempArray[i+this.size()]=arrayList.get(i); } //将原有数组指向临时数组 dataArray=tempArray; //重新设置数据个数 countData=this.size()+arrayList.size(); } }
/** * 链表的节点类 * @author 朱凌峰 * * @param <E> */ public class LinkNode<E> { //节点的数据 private E data; //当前节点的子节点 private LinkNode<E> child; private LinkNode<E> parent;//当前节点的父节点 /** * 带参构造器 * @param data 创建对象时赋予的初始参数 */ public LinkNode(E data){ this.data=data; } /** * 获取当前节点的数据 * @return */ public E getData(){ return this.data; } /** * 重新赋予节点数据的值 * @param data 重新赋予的值 */ public void setData(E data){ this.data=data; } /** * 设置父节点 * @param parent */ public void setParent(LinkNode<E> parent){ this.parent=parent; } /** * 设置子节点 * @param child */ public void setChild(LinkNode<E> child){ this.child=child; } /** * 获取当前节点的付节点 * @return */ public LinkNode<E> getParent(){ return this.parent; } /** * 获取当前节点的子节点 * @return */ public LinkNode<E> getChild(){ return this.child; } }
package LinkNode20130717; import zlfsQueue20130714and0716and0722.MyQueue; /** * 用链表实现队列 * @author 朱凌峰 * */ public class LinkQueue<E> implements MyQueue<E>{ //根节点 private LinkNode<E> root; //最后一个节点 private LinkNode<E> last; /** * 在队列末尾添加一个数据 * @param e 要添加的数据 */ public void add(E e){ //新建一个节点 LinkNode<E> tempNode=new LinkNode<E> (e); //如果队列中还没有节点,则新建一个根节点 if(root==null){ root=tempNode; //此时最后一个节点即为根节点 last=root; }else{ last.setChild(tempNode);//该节点为上一个节点的子节点 tempNode.setParent(last); //该节点成为当前的最后一个节点 last=tempNode; } } /** * 获取指定位置节点的数据 * @param index 数据所在位置,根节点的位置为0 * @return */ public E get(int index){ LinkNode<E> nodeOfIndex=getNodeOfIndex(index); return nodeOfIndex.getData(); } /** * 获取链表队列的大小,即数据个数 * @return */ public int size(){ int count=0; LinkNode<E> tempNode=root; while(tempNode!=null){ count++; tempNode=tempNode.getChild(); } return count; } /** * 删除指定位置的数据 * @param index */ public void remove(int index){ LinkNode<E> nodeOfIndex=getNodeOfIndex(index); //如果删除根节点 if(index==0){ //获取索引位置的节点的子节点 LinkNode<E> tempChild=nodeOfIndex.getChild(); root=tempChild; } //如果删除最后一个节点 else if(index==this.size()-1){ //获取索引位置的节点的父节点 LinkNode<E> tempParent=nodeOfIndex.getParent(); tempParent=last; } else { //获取索引位置的节点的父节点和子节点 LinkNode<E> tempParent=nodeOfIndex.getParent(); LinkNode<E> tempChild=nodeOfIndex.getChild(); //重新设置父子关系 tempParent.setChild(tempChild); tempChild.setParent(tempParent); } } /** * 重新设置指定位置的数据值 * @param e * @param index */ public void set(E e,int index){ LinkNode<E> nodeOfIndex=getNodeOfIndex(index); nodeOfIndex.setData(e); } /** * 获取索引位置对应的节点 * @param index 索引位置 * @return 索引位置对应节点 */ private LinkNode<E> getNodeOfIndex(int index){ if(this.judgeIndex(index)){ //找到索引位置对应的节点 int count = 0; LinkNode<E> tempNode=root; while(count!=index){ tempNode=tempNode.getChild(); count++; } return tempNode; } return null; } /** * 依次打印队列中每个数据 */ public void printLinkQueue(){ if(root==null){ throw new java.lang.RuntimeException("队列为空"); } else{ int count = 0; LinkNode<E> tempNode=root; while(tempNode!=null){ System.out.println("队列中第"+count+"个数据为:" +tempNode.getData()); tempNode=tempNode.getChild(); count++; } } } /** * 插入一个数据到索引位置前面一个位置 * @param e 要插入的数据 * @param index 索引位置 */ public void insert(E e, int index) { if (judgeIndex(index)) { // 新建临时节点保存要插入的数据 LinkNode<E> tempNode=new LinkNode<E>(e); if(index==0){ // 设置当前根节点和临时节点的父子关系 root.setParent(tempNode); tempNode.setChild(root); // 根节点为临时节点 root=tempNode; } else { tempNode.setChild(this.getNodeOfIndex(index)); tempNode.setParent(this.getNodeOfIndex(index-1)); this.getNodeOfIndex(index).setParent(tempNode); this.getNodeOfIndex(index-1).setChild(tempNode); } } } /** * 按顺序打印队列中的每个数据,若队列为空则抛出异常 */ public void printQueue() { if(this.size()==0) throw new java.lang.RuntimeException("队列为空"); else { for (int i = 0; i < this.size(); i++) { System.out.println("队列中第"+i+"个数据为:"+this.get(i)); } } } /** * 将一个队列添加到当前队列末尾 * @param linkQueue 要添加到末尾的队列 */ public void addAll(LinkQueue<E> linkQueue) { this.last.setChild(linkQueue.root); linkQueue.root.setParent(this.last); } /** * 判断索引位置是否有效 * @param index 索引位置 * @return 若索引位置有效,则返回true */ public boolean judgeIndex(int index) { //判断索引位置是否有效 if(index<0 || index>=this.size()){ throw new java.lang.RuntimeException("索引位置越界:"+index +",当前队列大小为:"+this.size()); } return true; } }