第三课 线性表中的常用结构:栈和队列

第三课 线性表中的常用结构:栈和队列

  • 1 栈
    • 1.1 栈的定义
    • 1.2 顺序存储栈(顺序栈)
    • 1.3 链栈
  • 2 队列
    • 2.1 队列的定义
    • 2.2 顺序队列
    • 2.3 循环队列
    • 2.4 链队列

1 栈

1.1 栈的定义

栈(Stack)是限制在表的一端进行插入和删除运算的线性表,通常称插入、删除的这一端
为栈顶(Top),另一端为栈底(Bottom)。当表中没有元素时称为空栈。
假设栈S=(a1,a2,a3,…an),则a1称为栈底元素,an为栈顶元素。栈中元素按a1,a2,a3
,…an的次序进栈,退栈的第一个元素应为栈顶元素。因此,栈的修改是按后进先出的原
则进行的,所以,栈称为后进先出(先进后出)表(LIFO,FILO)。

1.2 顺序存储栈(顺序栈)

本质是顺序表,并且只能在链表的一端进行增加和删除

(1)定义
栈是线性表的特例,因此线性表的存储结构对栈也适应。
栈的顺序存储结构简称为顺序栈,可用数组来实现顺序栈。因为栈底位置是固定不变的,
所以可以将栈底位置设置在数组的两端的任何一个端点;栈顶位置是随着进栈和退栈操作
而变化的,故需用一个整型变量top(栈顶指针)来指出栈顶。

(2)实现的基本操作

package chapter3.stack;

import java.util.Arrays;

/**
* 顺序栈
* @author Administrator
*
*/
public class SeqStack {
   /**
    * 1.成员变量
    */
   private Object[] elementData;//定义一个数组用于保存顺序栈的元素,最后一个元素是栈顶  
     
     private int capacity;//保存数组的长度,栈的容量  
     
     private int top ;//栈顶指针,保存栈顶元素的下标,-1表示没有元素
     
     /**
      * 2.构造方法:初始化栈
      */
     public SeqStack() {  //不指定栈的初始容量,采用默认的初始容量
   	    capacity = 10 ;
   	    elementData = new Object[capacity];//默认长度为10的栈
   	    top=-1;
   	  }  
   	  
   	  public SeqStack(int initSize) {  //指定栈的初始容量
   		  capacity = initSize ;
   	    elementData = new Object[capacity];//默认长度为10的栈  
   	    top = -1 ;
   	  }  
   	  
   	  public SeqStack(Object element) { //不指定栈的初始容量,用默认的,并且存一个元素
   	    this();  
   	    top++;
   	    elementData[top] = element;  
   	  }  
   	  
   	  public SeqStack(Object  element, int initSize) {  //指定栈的初始容量,并且指定第一个元素
   	   this(initSize);
   	    elementData[top] = element;  
   	  }  
   	  
   	  /**
   	   * 判断是否有空间可以存放元素:没有的话进行数据扩容
   	   */
   	  private void ensureCapacity(int minCapacity) {  
   		    //如果数组的原有长度小于目前所需的长度  
   		    int oldCapacity = capacity;  
   		    if (minCapacity > oldCapacity) {  //判断原来的容量是否满足最小容量要求
   		      int newCapacity = (oldCapacity * 3) / 2 + 1;  //依据某个规则进行扩容
   		      if (newCapacity < minCapacity)  
   		        newCapacity = minCapacity;  //根据需求进行扩容
   		      // minCapacity is usually close to size, so this is a win:  
   		      elementData = Arrays.copyOf(elementData, newCapacity);  
   		      capacity = newCapacity ;
   		    }  
          }  
   	  /**
   	   * 判断是否是空栈
   	   * @return
   	   */
   	  public boolean isEmpty() {  
   		    return top==-1;  
   		  }  
   		  
   	  /**
   	   * 3.进栈
   	   * @param element
   	   */
   	  public void push(Object  element){  
   	      ensureCapacity(top + 2);//top是位置,从0开始,原来的元素个数是top+1
   	                                        //这里表示加入一个元素后所需的容量  
   	      elementData[top+1] = element;  
   	      top++;
   	    }
   	  
   	/**
   	 *4.出栈 
   	 */
   	  /** 
   	   * @Title: pop      
   	   * @Description: 出栈     
   	   * @return 
   	   */  
   	  public Object  pop() {  
   	    if (!isEmpty()) {  
   	      Object  oldValue = (Object) elementData[top];  
   	      //释放栈顶元素  
   	      elementData[top] = null;  
   	      top-- ;
   	      return oldValue;  
   	    } else {  
   	    	throw new IndexOutOfBoundsException("空栈异常");  
   	    }  
   	  }  
   	  
   	  /**
   	   * 5.取栈顶元素,不删除
   	   */
   	  public Object  peek() {  
   	    if (!isEmpty()) {  
   	      return (Object) elementData[top];  
   	    } else {  
   	     return null ;  
   	    }  
   	  }  
   	  /**
   	   * 6.清空顺序栈
   	   */
   	  /** 
   	   * @Title: clear      
   	   * @Description: 清空顺序栈 
   	   */  
   	  public void clear() {  
   	    //将底层数组所有元素赋为null  
   	    Arrays.fill(elementData, null);  
   	    top = -1 ;
   	  }  
   	  
   	 /**
   	  *7.打印栈,输出栈的内容 
   	  */
   	  public String toString() {  
   		    if (top==-1) {  
   		      return "[]";  
   		    } else {  
   		      StringBuilder sb = new StringBuilder("[");  
   		      for (int i =top;i > -1; i--) {  
   		        sb.append(elementData[i].toString() + ", ");  
   		      }  
   		      int len = sb.length();  
   		      return sb.delete(len -2, len).append("]").toString();  //先删掉最后一个逗号(len-1位置)
   		    }  
   		  }  
   	  
        /**
         * 8.测试		  
         * @param args
         */
   	  public static void main(String[] args){
   		  SeqStack st = new SeqStack(10) ;
   		  for(int i = 0 ; i < 30 ; i++){
   			  st.push(i);
   		  }
   		  System.out.println(st.top+1);
   		  System.out.println(st.toString());
   	  }
}

1.3 链栈

本质是单向非循环链表,并且只能在链表的一端进行增加和删除

(1)定义
栈的链式存储结构称为链栈,插入和删除操作仅限制在链头位置上进行。栈顶指针就是链
表的头指针。
(2)实现的基本操作

package chapter3.stack;
/**
 * 链栈
 * @author Administrator
 *
 */
public class LinkStack {
/**
 * 1.节点
 */
	 private class Node{  
		    private Object  data; //保存节点的数据   
		      
		    private Node next; //指向下个节点的引用  
		      
		    public Node(){  
		    }
		    public Node(Object  data, Node next){  
		        this.data = data;  
		        this.next = next;  
		      }
		    
			public Object getData() {
				return data;
			}
			public void setData(Object data) {
				this.data = data;
			}
			public Node getNext() {
				return next;
			}
			public void setNext(Node next) {
				this.next = next;
			} 
		    
	 }
	 /**
	  * 2.成员变量
	  */
	 private Node top;  //保存该链栈的栈顶元素     
	 
	  /**
	   * 3.构造方法:初始化栈
	   */
	  public LinkStack(){  //空栈
		    top = null;  
		  }  
		    
		  public LinkStack(Object  element) {  //有一个元素的栈
			  this() ;
		    top = new Node(element , null);     
		  }  
		  /**
		   * 判断栈是否是空的
		   * @return
		   */
		 private  boolean isEmpty() {  
			    return top==null ;  
		 }
		 /** 
		   * @Title: push      
		   * @Description: 4.入栈     
		   * @param element 
		   */  
		  public void push(Object  element){  
			 if(isEmpty()){
				 top = new Node(element , null);     
			 }else{
				Node node= new Node(element , null); 
				node.next = top ;
				top = node ;
			 }
		  }  
		  /** 
		   * @Title: pop      
		   * @Description:5. 出栈     
		   * @return 
		   */  
		  public  Object  pop(){  
			  if(isEmpty()){
				  throw new IndexOutOfBoundsException("空栈异常");  
			  }else{
				   Node oldTop = top;  
				    top = top.next;  
				    oldTop.next = null;  
				    return oldTop.data;  
			  }
		  }  
		    /**
		     * 6.访问栈顶元素,但是不删除
		     */
		  public Object  peek(){  
		    return top.data;  
		  }  
		  /**
		   * 7.清空链栈
		   */
		  public void clear() {     
		    top = null;//将栈所有元素赋为null  
		  }  
		  /**
		   * 8.输出栈元素
		   */
		  public String toString() {     
		        //链栈为空链栈时     
		    if (isEmpty()) {     
		      return "[]";     
		    } else {     
		      StringBuilder sb = new StringBuilder("[");     
		      for (Node current = top ; current != null ; current = current.next ) {     
		        sb.append(current.data.toString() + ", ");     
		      }     
		      int len = sb.length();     
		      return sb.delete(len - 2 , len).append("]").toString();     
		    }     
		  }
		  /**
		   * 9.测试
		   * @param args
		   */
	   public static void main(String[] args){
		   LinkStack lt = new LinkStack() ;
		   for(int i=0 ; i<10 ; i++){
			   lt.push(i) ;
		   }
		   System.out.println(lt);
	   }
}

2 队列

2.1 队列的定义

队列(Queue)也是一种运算受限的线性表。它只允许在表的一端进行插入,而在另一端进行删除
。允许删除的一端称为队头(front),允许插入的一端称为队尾(rear)。例如:排队购物。操作系
统中的作业排队。先进入队列的成员总是先离开队列。因此队列亦称作先进先出(First In First Out)
的线性表,简称FIFO表。当队列中没有元素时称为空队列。在空队列中依次加入元素a1,a2,…an之
后,a1是队头元素,an是队尾元素。显然退出队列的次序也只能是a1,a2,…an ,也就是说队列的修
改是依先进先出的原则进行的。

2.2 顺序队列

本质是顺序表,并且允许在顺序表的一端插入(队尾),在另一段删除(队头)

(1)定义
队列的顺序存储结构称为顺序队列,用一个向量空间来存放当前队列中的元素。由于队列
的队头和队尾的位置是变化的,因而要设两个指针分别指示队头和队尾元素在队列中的位
置,它们的初始值在队列初始化时均应置为-1。入队时将新元素插入所指的位置,然后队
尾指针加1。出队时,删去所指的元素,然后头指针加1并返回被删元素。由此可见,当
头尾指针相等时队列为空。注意,在非空队列里,头指针始终指向队头元素的前一个位
置,而尾指针始终指向队尾元素

(2)操作的基本实现

package chapter3.queue;

import java.util.Arrays;

/**
 * 顺序队列
 * @author Administrator
 *
 */
public class SeqQueue {
/**
 * 1.成员变量
 */
    private Object[] elementData;     //定义一个数组用于保存顺序队列的元素   
    private int capacity;    //队列的容量
    private int front ;//对头指针,不指向元素的时候为-1 ,指向的是对头元素的前一个位置  
    private int rear;   //队尾指针,不指向元素的时候为-1,指向队尾元素   
    
    /**
     * 2.构造方法:初始化队列
     */
    public SeqQueue()   //空队列,使用队列默认容量
    {   
        capacity = 10;   
        elementData = new Object[capacity];   
        front = -1 ;
        rear = -1 ;
    }   
    public SeqQueue(int initSize){//空队列,指定队列容量
    	capacity =initSize ;
    	 elementData = new Object[capacity];   
         front = -1 ;
         rear = -1 ;
    }
    public SeqQueue(Object  element)   //有一个元素的队列,使用队列默认容量
    {   
        this();   
        rear++ ;
        elementData[rear] = element;   
    }   
    /**  
     * 以指定长度的数组来创建顺序队列  
     * @param element 指定顺序队列中第一个元素  
     * @param initSize 指定顺序队列底层数组的长度  
     */  
    public SeqQueue(Object  element , int initSize)   //有一个元素的队列,指定队列初始容量
    {   
    	this(initSize) ;
        rear++ ;
        elementData[rear] = element;   
    }   
    /**
	   * 判断是否有空间可以存放元素:没有的话进行数据扩容
	   */
	  private void ensureCapacity(int minCapacity) {  
		    //如果数组的原有长度小于目前所需的长度  
		    int oldCapacity = capacity;  
		    if (minCapacity > oldCapacity) {  //判断原来的容量是否满足最小容量要求
		      int newCapacity = (oldCapacity * 3) / 2 + 1;  //依据某个规则进行扩容
		      if (newCapacity < minCapacity)  
		        newCapacity = minCapacity;  //根据需求进行扩容
		      // minCapacity is usually close to size, so this is a win:  
		      elementData = Arrays.copyOf(elementData, newCapacity);  
		      capacity = newCapacity ;
		    }  
     }  
  //判断顺序队列是否为空队列   
    private  boolean empty()   
    {   
        return  rear == front;   
    }   
    /**
     * 3.入队
     *rear指向的是最后一个元素
     */
    public void add(Object  element)   
    {   
    	 ensureCapacity(rear+2);//near是位置,从0开始,rear+1是原来所需的容量
         //这里表示加入一个元素后所需的容量  
        elementData[rear+1] = element;
        rear++ ;
    }   
   /**
    * 4.出队
    * font指向的是对头元素的前面一个位置
    */
    public Object  remove()   
    {   
        if (empty())   
        {   
            throw new IndexOutOfBoundsException("空队列异常");   
        }   
        //保留队列的rear端的元素的值   
        Object   oldValue = (Object)elementData[front+1];   
        //释放队列的rear端的元素   
        elementData[front+1] = null;    
        front++ ;
        return oldValue;   
    }   
  /**
   * 5.取对头元素,不删除
   */
    public Object  element()   
    {   
        if (empty())   
        {   
           return null ;
        }   
        return (Object)elementData[front+1];   
    }   

    /**
     * 6.清空顺序队列
     */
    public void clear()   
    {   
        //将底层数组所有元素赋为null   
        Arrays.fill(elementData , null);   
        front =-1;   
        rear = -1;   
    }   
/**
 * 7.打印队列,输出
 */
    public String toString()   
    {   
        if (empty())   
        {   
            return "[]";   
        }   
        else  
        {   
            StringBuilder sb = new StringBuilder("[");   
            for (int i = front+1 ; i <= rear ; i++ )   
            {   
                sb.append(elementData[i].toString() + ", ");   
            }   
            int len = sb.length();   
            return sb.delete(len - 2 , len).append("]").toString();   
        }   
    }   
/**
 * 8.测试
 */
    public static void main(String[] args){
    	SeqQueue sq = new SeqQueue() ;
    	for(int i=0; i<20; i++){
    		sq.add(i) ;
    	}
    	System.out.println(sq);
    }

}

2.3 循环队列

本质是单向循环顺序表,表的最后一个元素的下一个元素是第一个元素

(1)定义
因为删除头元素后,会出现很多空闲的空间,为充分利用向量空间。克服上述现象,
把向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量,存储在其中的
队列称为循环队列(Circular Queue)。在循环队列中进行入队操作时,尾指针仍要加
1,出队操作,对头加1,朝前移动。只不过当头尾指针指向向量上界(MaxSize-1)时
,其加1操作的结果是指向向量的下界0。显然,因为循环队列元素的空间可以全被
利用,除非向量空间真的被队列元素全部占用,否则不会上溢。因此,除一些简单
的应用外,真正实用的顺序队列是循环队列。入队时尾指针向前追赶头指针,出队
时头指针向前追赶尾指针,故队空和队满时头尾指针均相等。因此,我们无法通过
front=rear来判断队列“空”还是“满”。解决此问题的方法至少有三种:
其一:另设一个布尔变量以区分队列的空和满;
其二:少用一个元素的空间,约定入队前,测试尾指针在循环意义下加1后是否等于头指针,
若相等则认为队满;(但此时实际还有一个空位置)
其三 :是使用一个计数器记录队列中元素的总数(实际上是队列长度)。
通常采用第二种方案,并且注意对头指针指向队头元素的前面一个位置,尾指针指向
队尾元素

(2)基本操作的实现

package chapter3.queue;

import java.util.Arrays;
/**
 * 循环队列
 * @author Administrator
 *
 */
public class CycleQueue {
/**
 * 1.成员变量
 */
  
    private int capacity;     //保存数组的长度。   
    private Object[] elementData;    //定义一个数组用于保存循环队列的元素   
    private int front ;//对头指针,不指向元素的时候为-1 ,指向的是对头元素的前一个位置  
    private int rear;   //队尾指针,不指向元素的时候为-1,指向队尾元素   
/**
 * 2.构造方法:初始化队列
 */
    public CycleQueue()   //空队列,指定队列容量
    {   
        capacity =10;   
        elementData = new Object[capacity];   
        front = -1 ;
        rear =  -1 ;
    }   
    public CycleQueue(int initSize){//空队列,指定队列容量
    	capacity =initSize ;
    	 elementData = new Object[capacity];   
         front = -1 ;
         rear = -1 ;
    }
    public CycleQueue(Object element)   // 有一个元素的队列,使用队列默认容量
    {   
        this();   
        rear++;   
        elementData[rear] = element;   
    }   
    /**  
     * 以指定长度的数组来创建循环队列  
     * @param element 指定循环队列中第一个元素  
     * @param initSize 指定循环队列底层数组的长度  
     */  
    public CycleQueue(Object  element , int initSize)   //有一个元素的队列,指定队列初始容量
    {   
    	this(initSize) ;
    	rear++ ;
        elementData[rear] = element;    
    }   
    //判断循环队列是否为空队列   
    private  boolean empty()   
    {   
        //rear==front且front处的元素为null   
        return rear == front    
            && elementData[front] == null;   
    }   
    /**
	   * 可以将这个队列改造成队满的时候自动扩容
	   */
	  private void ensureCapacity(int minCapacity) {  
	  
      }  
   /**
    * 3.入队
    */
  //插入队列   
    public void add(Object  element)   
    {   
        if (rear == front    
            && elementData[front+1] != null)   
        {   
        	new  Exception("队列已满异常!") ;
        	 
        }   
          //如果rear已经到头,那就转头   
        rear = ((rear+1) == capacity ? 0 :( rear+1));  
        elementData[rear] = element;   
    }   
/**
 * 4.出队
 */
    public Object  remove()   
    {   
        if (empty())   
        {   
            throw new IndexOutOfBoundsException("空队列异常");   
        }   
        front = ((front+1) == capacity ? 0 :( front+1));  //如果front已经到头,就转头
        Object oldValue = (Object)elementData[front];   
        //释放队列的rear端的元素   
        elementData[front] = null;  
        return oldValue;   
    }   
/**
 * 5.返回栈顶元素,但是不删除
 */
  //返回队列顶元素,但不删除队列顶元素   
    public Object  element()   
    {   
        if (empty())   
        {   
           return null ;
        }   
        
        front = ((front+1) == capacity ? 0 :( front+1));  //如果front已经到头,就转头
        Object oldValue = (Object)elementData[front];   
        //释放队列的rear端的元素   
        return oldValue;   
    }   
/**
 * 7.清空队列    
 */
    //清空循环队列   
    public void clear()   
    {   
        //将底层数组所有元素赋为null   
        Arrays.fill(elementData , null);   
        front = -1;   
        rear = -1;   
    }   
/**
 * 8.打印队列内容
 */
    public String toString()   
    {   
        if (empty())   
        {   
            return "[]";   
        }   
        else  
        {   
            //如果front < rear,有效元素就是front到rear之间的元素   
            if (front < rear)   
            {   
                StringBuilder sb = new StringBuilder("[");   
                for (int i = front+1  ; i <=rear ; i++ )   
                {   
                    sb.append(elementData[i].toString() + ", ");   
                }   
                int len = sb.length();   
                return sb.delete(len - 2 , len).append("]").toString();   
            }   
            //如果front >= rear,有效元素为front->capacity之间、0->front之间的   
            else  
            {   
                StringBuilder sb = new StringBuilder("[");   
                for (int i = front+1; i < capacity ; i++ )   
                {   
                    sb.append(elementData[i].toString() + ", ");   
                }   
                for (int i = 0 ; i <=rear ; i++)   
                {   
                    sb.append(elementData[i].toString() + ", ");   
                }   
                int len = sb.length();   
                return sb.delete(len - 2 , len).append("]").toString();   
            }   
        }   
    }   
    /**
     * 9.测试
     */
    public static void main(String[] args){
    	CycleQueue cq = new CycleQueue() ;
    	for(int i=0 ; i<10;i++){
          cq.add(i) ;    		
    	}
    	System.out.println(cq);
    }
}

2.4 链队列

本质是单向非循环链表,允许在链表的一端插入,另一端删除

(1)定义
链式存储结构作为为链队列,它是限制仅在表头删除和表尾插入的单链表。显然仅有单链表
的头指针不便于在表尾做插入操作,为此再增加一个尾指针,指向链表的最后一个结点,
注意此时的头指针指向的是头节点,和顺序队列不同

(2)基本操作的实现

package chapter3.queue;
/**
 * 链队列
 * @author Administrator
 *
 */
public class LinkQueue {
	/**
	 * 1.节点
	 */
	 //定义一个内部类Node,Node实例代表链队列的节点。   
    private class Node   
    {   
        //保存节点的数据   
        private Object  data;   
        //指向下个节点的引用   
        private Node next;   
        //无参数的构造器   
        public Node()   
        {   
        }   
        //初始化全部属性的构造器   
        public Node(Object  data ,  Node next)   
        {   
            this.data = data;   
            this.next = next;   
        }
		public Object getData() {
			return data;
		}
		public void setData(Object data) {
			this.data = data;
		}
		public Node getNext() {
			return next;
		}
		public void setNext(Node next) {
			this.next = next;
		}   
        
    }   
    /**
     * 2.成员变量
     */
    private Node front;   //指向头节点
    private Node rear;   //指向尾节点
    
/**
 * 3.构造方法:初始化链队列
 */
  //创建空链队列   
    public LinkQueue()   
    {   
        //空链队列,front和rear都是null   
        front = null;   
        rear = null;   
    }   
    //以指定数据元素来创建链队列,该链队列只有一个元素   
    public LinkQueue(Object  element)   
    {   
        Node node = new Node(element , null);   
        //只有一个节点,front、rear都指向该节点   
        front = node ;
        rear = node ;
    }   
    /**
	   * 判断队列是否是空的
	   * @return
	   */
	 private  boolean isEmpty() {  
		    return front==null&& rear==null ;  
	 }
/**
 * 4.入队
 */
    public void add(Object  element)   
    {   
        //如果该链队列还是空链队列   
        if (isEmpty())   
        {   
            front = new Node(element , null);   
           rear= front;   
        }   
        else  
        {   
            //创建新节点   
            Node newNode = new Node(element , null);   
            //让尾节点的next指向新增的节点   
            rear.next = newNode;   
            //以新节点作为新的尾节点   
            rear = newNode;   
        }   
    }   
/**
 * 5.出队
 */
    public Object  remove()   
    {   
    	if(isEmpty()){
    		 throw new IndexOutOfBoundsException("空队列异常");  
    	}else{
    	    Node oldFront = front;   
            front = front.next;   
            oldFront.next = null;   
            return oldFront.data;   
    	}
    }   
/**
 * 6.取对头元素,但是不删除
 */
    //访问链式队列中最后一个元素   
    public Object  element()   
    {   
    	if(isEmpty()){
    		return null ;
    	}
        return rear.data;   
    }   
/**
 * 7.清空队列
 */
  //清空链队列   
    public void clear()   
    {   
        //将front、rear两个节点赋为null   
        front = null;   
        rear = null;   
    }   

/**
 * 8.打印队列内容
 */
    public String toString()   
    {   
        //链队列为空链队列时   
        if (isEmpty())   
        {   
            return "[]";   
        }   
        else  
        {   
            StringBuilder sb = new StringBuilder("[");   
            for (Node current = front ; current != null   ; current = current.next )   
            {   
                sb.append(current.data.toString() + ", ");   
            }   
            int len = sb.length();   
            return sb.delete(len - 2 , len).append("]").toString();   
        }   
    }   
 /**
  * 9.测试
  */
    public static void main(String[] args){
    	LinkQueue lq = new LinkQueue() ;
    	for(int i=0;i<10;i++){
    		lq.add(i);
    	}
    	System.out.println(lq);
    }

}

你可能感兴趣的:(数据结构,java,数据结构,队列,链表,栈)