栈和队列

 

栈和队列
对线性表增加一些额外的限制和约束,例如,去除普通线性表中通过索引访问数据元素的功能,去除可以在任意位置添加、删除元素的功能,改成只允许在线性表某段添加、删除元素,这时候普通线性表就会变为两种特殊的线性表:栈和队列。


Stack,一种特殊的线性表,这种线性表只能在固定一端(通常认为是线性表的尾端)进行插入、删除操作。
对于栈而言,允许进行插入、删除操作的一端被称为栈顶(top),另一端则被称为栈底(bottom)
如果一个栈不包含任何元素,那么这个栈就被称为空栈
从栈顶插入一个元素被称为进栈,对应的英文说法为push
从栈顶删除一个元素被称为出栈,对应的英文说法为pop
归纳:栈就是一种先进后出的线性表

 



栈的常用操作:
栈是一种被限制过的线性表,通常不应该提供线性表中的如下方法:
获取指定索引处的元素
按值查找数据元素的位置
向指定索引处插入数据元素
删除指定索引处的元素
也就是说,栈不应该提供从中间任意位置访问元素的方法。也就是说,栈允许在栈顶插入、删除元素
栈的常用操作如下:
初始化:
返回栈的长度:
入栈:
出栈:
访问栈顶元素
判断栈是否为空
清空栈
栈同样可以采用顺序结构来存储栈内元素,也可采用链式结构来存储栈内元素

 

栈的顺序存储结构以及实现:

package com.test_one;

import java.util.Arrays;

/**
 * Created by Administrator on 2017/3/7.
 */
public class SequenceStack<T> {
    private int DEFAULT_SIZE = 10;
    private int capacity;
    private int capacityIncrement = 0;
    private Object[] elementData;
    private int size;

    public SequenceStack(){
        capacity = DEFAULT_SIZE;
        elementData = new Object[capacity];
    }

    public SequenceStack(T element){
        this();
        elementData[0] = element;
        size++;
    }

    public SequenceStack(T element , int initSize){
        this.capacity = initSize;
        elementData = new Object[capacity];
        elementData[0] = element;
        size++;
    }

    public SequenceStack(T element , int initSize , int capacityIncrement){
        this(element , initSize);
        this.capacityIncrement = capacityIncrement;
    }

    public int length(){
        return size;
    }

    public void push(T element){
        ensureCapacity(size + 1);
        elementData[size++] = element;
    }

    private void ensureCapacity(int minCapacity){
        if(minCapacity > capacity){
            if(capacityIncrement > 0){
                while(capacity < minCapacity){
                    capacity += capacityIncrement;
                }
            }else{
                while(capacity < minCapacity){
                    capacity <<= 1;
                }
            }
            elementData = Arrays.copyOf(elementData , capacity);
        }
    }

    public T pop(){
        T oldValue =  (T)elementData[--size];
        elementData[size] = null;
        return oldValue;
    }

    public T peek(){
        return (T)elementData[size - 1];
    }

    public boolean empty(){
        return size == 0;
    }

    public void clear(){
        Arrays.fill(elementData , null);
        size = 0;
    }

    public String toString(){
        if(size == 0){
            return "[]";
        }else{
            StringBuilder sb = new StringBuilder("[");
            for(int  i = size - 1; i >-1 ; i--){
                sb.append(elementData[i].toString() + ", ");
            }
            int len = sb.length();
            return sb.delete(len - 2 , len).append("]").toString();
        }
    }

}

public class SequenceStackTest {
    public static void main(String[] args){
        SequenceStack<String> stack = new SequenceStack<String>();
        stack.push("aaaa");
        stack.push("bbbb");
        stack.push("cccc");
        stack.push("dddd");
        System.out.println(stack);
        System.out.println("访问栈顶元素:" + stack.peek());
        System.out.println("第一次弹出栈顶元素:" + stack.pop());
        System.out.println("第二次弹出栈顶元素:" + stack.pop());
        System.out.println("两次pop之后的栈:" + stack);
    }
}
View Code

 

栈的链式存储结构以及实现:

package com.test_one;

/**
 * Created by Administrator on 2017/3/7.
 */
public class LinkStack<T> {
    private class Node{
        private T data;
        private Node next;
        public Node(){

        }
        public Node(T data , Node next){
            this.data = data;
            this.next = next;
        }
    }

    private Node top;
    private int size;

    public LinkStack(){
        top = null;
    }
    public LinkStack(T element){
        top = new Node(element , null);
        size++;
    }

    public int length(){
        return size;
    }

    public void push(T element){
        top = new Node(element , top);
        size++;
    }

    public T pop(){
        Node oldTop = top;
        top = top.next;
        oldTop.next = null;
        size--;
        return oldTop.data;
    }

    public T peek(){
        return top.data;
    }

    public boolean empty(){
        return size == 0;
    }

    public void clear(){
        top = null;
        size = 0;
    }

    public String toString(){
        if(empty()){
            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();
        }
    }
}
public class LinkStackTest {
    public static void main(String[] args){
        LinkStack<String> stack = new LinkStack<String>();
        stack.push("aaaa");
        stack.push("bbbb");
        stack.push("cccc");
        stack.push("dddd");
        System.out.println(stack);
        System.out.println("访问栈顶元素:" + stack.peek());
        System.out.println("第一次弹出栈顶元素:" + stack.pop());
        System.out.println("第二次弹出栈顶元素:" + stack.pop());
        System.out.println("两次pop之后的栈:" + stack);

    }
}
View Code

 

 


队列
队列是一种特殊的线性表,只允许在表的前端进行删除操作,只允许在表的后端进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。
队列先进先出

 


队列的顺序存储结构及实现
顺序队列的front总是存着队列中即将出队列的元素的索引;顺序队列中的rear总是保存着下一个进行队列的元素的索引。队列中元素的个数为rear-front.
顺序队列使用数组实现,每个队列元素在数组中的位置是固定不变的,变的只是rear和front两个整形变量。但有元素进入队列时,rear变量的值+1;当有元素从队列中移除时,front变量的值+1.

 

package com.test_one;

import java.util.Arrays;

/**
 * Created by Administrator on 2017/3/10.
 */
public class SequenceQueue<T> {
    private int DEFAULT_SIZE = 10;
    private int capacity;
    private Object[] elementData;
    private int front = 0;
    private int rear = 0;

    public SequenceQueue(){
        capacity = DEFAULT_SIZE;
        elementData = new Object[capacity];
    }

    public SequenceQueue(T element){
        this();
        elementData[0] = element;
        rear++;
    }

    public SequenceQueue(T element , int initSize){
        this.capacity = initSize;
        elementData = new Object[capacity];
        elementData[0] = element;
        rear++;
    }

    public int length(){
        return rear - front;
    }

    public void add(T element){
        if(rear > capacity - 1) {
            throw new IndexOutOfBoundsException("队列已满的异常");
        }
        elementData[rear++] = element;
    }

    public T remove(){
        if(empty()){
            throw new IndexOutOfBoundsException("空队列异常");
        }
        T oldValue = (T)elementData[front];
        elementData[front++] = null;
        return oldValue;
    }

    public T element(){
        if(empty()){
            throw new IndexOutOfBoundsException("空队列异常");
        }
        return (T)elementData[front];
    }

    public boolean empty(){
        return rear == front;
    }

    public void clear(){
        Arrays.fill(elementData , null);
        front = 0;
        rear = 0;
    }

    public String toString(){
        if(empty()){
            return "[]";
        }else{
            StringBuilder sb = new StringBuilder("[");
            for(int  i = front; i < rear; i++){
                sb.append(elementData[i].toString() + ", ");
            }
            int len = sb.length();
            return sb.delete(len - 2 , len).append("]").toString();
        }
    }
}

package com.test_one;

/**
 * Created by Administrator on 2017/3/10.
 */
public class SequenceQueueTest {
    public static void main(String[] args){
        SequenceQueue<String> queue = new SequenceQueue<String>();
        queue.add("aaaa");
        queue.add("bbbb");
        queue.add("cccc");
        queue.add("dddd");
        System.out.println(queue);
        System.out.println("访问队列的front端元素:"  + queue.element());
        System.out.println("移除队列的front端元素:" + queue.remove());
        System.out.println("移除队列的front端元素:" + queue.remove());
        System.out.println("两次调用remove方法后的队列:" + queue);
    }
}
View Code

上面写的程序可能出现假满现象:

 

对于假满问题,程序有如下解决方法:
永远将元素移出队列时都将队列中的所有元素向front端移动一位,但这种方式front永远为0,元素插入队列时rear+1,元素移除队列时rear-1.但是这种方式非常浪费时间,因为每次将元素从队列移除都需要进行“整体搬家”
加那个数组存储区堪称一个首尾相接的环形区域,当存放到数组的最大地址后,rear的值再次变为0.次啊用这种技巧存储的队列称为循环队列。

循环队列:
对于右边的循环队列,不管队列是空还是满,都会出现一个情况:front ==rear。如果底层数组中elementData[front] == null表明此时队列为空,否则表明此队列已满。

package com.test_one;

import java.util.Arrays;

/**
 * Created by Administrator on 2017/3/10.
 */
public class LoopQueue<T> {
    private int DEFAULT_SIZE = 10;
    private int capacity;
    private Object[] elementData;
    private int front = 0;
    private int rear = 0;
    public LoopQueue(){
        capacity = DEFAULT_SIZE;
        elementData = new Object[capacity];
    }

    public LoopQueue(T element){
        this();
        elementData[0] = element;
        rear++;
    }

    public LoopQueue(T element , int initSize){
        this.capacity = initSize;
        elementData = new Object[capacity];
        elementData[0] = element;
        rear++;
    }

    public  int length(){
        if(empty()){
            return 0;
        }
        return rear > front ? rear - front : capacity - (front - rear);
    }

    public void add(T element){
        if(rear == front && elementData[front] != null){
            throw new IndexOutOfBoundsException("队列已满的异常");
        }
        elementData[rear++] = element;
        rear = rear == capacity ? 0 :rear;
    }

    public T remove(){
        if(empty()){
            throw new IndexOutOfBoundsException("空队列异常");
        }
        T oldValue = (T) elementData[front];
        elementData[front++] = null;
        front = front == capacity ? 0 :front;
        return oldValue;
    }

    public T element(){
        if(empty()){
            throw new IndexOutOfBoundsException("空队列异常");
        }
        return (T)elementData[front];
    }

    public boolean empty(){
        return rear == front && elementData[rear] == null;
    }

    public void clear(){
        Arrays.fill(elementData , null);
        front = 0;
        rear = 0;
    }

    public String toString(){
        if(empty()){
            return "[]";
        }else{
            if(front < rear){
                StringBuilder sb = new StringBuilder("[");
                for(int i = front; i < rear; i++){
                    sb.append(elementData[i].toString() + ", ");
                }
                int len = sb.length();
                return sb.delete(len - 2 , len).append("]").toString();
            }else{
                StringBuilder sb = new StringBuilder("[");
                for(int i =  front; 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();
            }

        }
    }
}
package com.test_one;

/**
 * Created by Administrator on 2017/3/10.
 */
public class LoopQueueTest {
    public static void main(String[] args){
        LoopQueue<String> queue = new LoopQueue<String>("aaaa" , 3);
        queue.add("bbbb");
        queue.add("cccc");
        System.out.println(queue);
        queue.remove();
        System.out.println("删除一个元素后的队列:" + queue);
        queue.add("dddd");
        System.out.println(queue);
        System.out.println("队列满时的长度:" + queue.length());
        queue.remove();
        queue.add("eee");
        System.out.println(queue);
    }
}
View Code

 

队列的链式存储结构及实现
类似于使用链式结构保存线性表,也可以使用链式结构存储队列的各元素,采用链式存储的结构的队列也被称为链队列。
对于链表而言,程序从rear端添加元素,从front端移除元素,因此考虑对链队列增加front、rear两个引用变量,使它们分别指向链队列的头、尾两个节点。由于链队列采用链式结构保存劣种所有元素,因此不存在队列满问题。

 

 



java集合中的队列:
从JDK1.5开始,java的集合框架中提供了一个Queue接口,该接口代表了一个队列,实现该接口的类可以当成队列使用。JDK1.5还提供了Deque接口,这个接口代表另一种特殊的队列--双向队列。

 


双向队列:
可以在两端同时进行插入、删除操作

 

posted @ 2017-03-10 15:40  guodaxia  阅读(175)  评论(0)    收藏  举报