码迷,mamicode.com
首页 > 其他好文 > 详细

栈和队列

时间:2014-12-21 10:17:56      阅读:285      评论:0      收藏:0      [点我收藏+]

标签:

栈和队列----功能弱化的线性表(功能受到限制,比方说增删只能在首尾)

(广度优先遍历的时候用到队列 保存节点)

(实现递归---栈)

---------------------------------------------------------------------------------------------------------------------------

Stack(栈--栈顶插入删除)

1. pop出栈、push入栈、LIFO

2. 常用操作:

     a. 初始化:构造器中,创建空栈

     b. 返回栈的长度:返回栈中元素的个数

     c. 入栈:栈顶插入一个元素,长度+1

     d. 出栈:栈顶删除一个元素,长度-1

     e. 访问栈顶元素:返回栈顶元素但是不删除元素

     f. 判断栈是否为空

     g. 清空栈

3. 顺序栈(当插入元素满了之后,要考虑数组容量重新定义)

import java.util.Arrays;

public class SequenceStack<T>  //顺序栈
{
    private int DEFAULT_SIZE=10;
    private int capacity; //数组容量
    private int capacityIncrement = 0; //程序每次增加的数组长度
    private Object[] elementData; 
    private int size=0; //当前元素的个数
    
    public SequenceStack()  //创建一个空栈
    {
        capacity = DEFAULT_SIZE;
        elementData = new Object[capacity];
    }
    
    public SequenceStack(T element) //用一个元素来初始化栈
    {
        this();
        elementData[0] = element;
        size++;
    }
    
    public SequenceStack(T element,int initSize,
            int capacityIncrement) //指定增长容量
    {
        this.capacity = initSize;
        this.capacityIncrement = capacityIncrement;
        
        elementData = new Object[capacity];
        elementData[0] = element;
        size++;
    }
    
    public int length() //获取顺序栈的大小
    {
        return size;
    }
    
    public void push(T element) //入栈
    {
        ensureCapacity(size+1); //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() //出栈:1.获取栈顶元素 2.栈元素的数量-1  3.原来栈顶引用为空
    {
        T oldValue = (T) elementData[size-1];
        elementData[--size] = null;//这1步可写成2步:--size; elementData[size]=null
        return oldValue;  
    }
    
    public T peek() //取出栈顶元素
    {
        return (T)elementData[size-1];
    }
    
    public boolean isEmpty() //判断栈是否为空
    {
        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.replace(len-2, len, "]").toString();
        }
    }
}

4. 链栈(top保存栈顶元素的引用,size记录当前栈中保存元素的个数)

    入栈---头插法,size+1

    出栈---释放引用,size-1

(头插法实现了FILO)

public class LinkStack<T> 
{
    private class Node
    {
        private T data;
        private Node next; //指向下一个节点
        
        public Node(){}

        public Node(T data, LinkStack<T>.Node next) 
        {
            super();
            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;  //原来top的指向改变
        oldTop.next = null;
        size--;
        return oldTop.data;
    }
    
    public T peek() //取栈顶元素
    {
        return top.data;
    }
    
    public boolean isEmpty() //判断是否为空
    {
        return size==0;
    }
    public void clear()
    {
        top = null;
        size = 0;
    }
    
    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.replace(len - 2, len, "] ").toString();
        }
        
    }
    
}

(整体而言,栈基本用不到顺序结构的随机访问功能,所以一般采用链栈)

(Java中Vector实现了List接口,Stack继承了Vector,所以Stack是线程安全的)

(Java.util.LinkedList提供了pop()、push()、peek(),所以它可以当做栈来使用)


 

队列:

Queue

(前端插入/后端删除  FIFO)

(前端:front;后端:rear)

常用操作:

1.  初始化队列(构造器中创建一个空队列)

2.  返回队列的长度

3.  加入元素(向队列的后端插入一个元素,队列的长度+1)(rear后移)

4.  删除元素(向队列的前端删除一个元素,队列的长度-1)(front后移)

5.  返回队列前端出队元素

6.  判断队列是否为空  (rear == front)

7.  清空队列

顺序队列:

front总是保存着队列中将要出队的元素,rear总是保存着下一个即将进入队列的元素的索引。

技术分享

顺序队列实现:(front和rear用数字索引即可,元素个数为rear-front)

import java.util.Arrays;


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(isEmpty())
        {
            throw new IndexOutOfBoundsException("队列已经空了...");
        }
        T oldValue = (T) elementData[front]; 
        elementData[front++] = null;
        return oldValue;
    }
    
    public T element()  //获取队首元素
    {
        if(isEmpty())
        {
            throw new IndexOutOfBoundsException("空队列异常...");
        }
        return (T) elementData[front];
    }
    
    public boolean isEmpty()  //判断是否为空
    {
        return rear==front;
    }
    
    public void clear()  //清空队列
    {
        Arrays.fill(elementData,null);
        front = 0;
        rear = 0;
    }
    
    public String toString() 
    {
        if(isEmpty())
        {
            return "[]";
        }
        else
        {
            StringBuilder sb = new StringBuilder("[");
            for(int i=front;i<rear;i++)
            {
                sb.append(elementData[i].toString() + ", ");
            }
            int len = sb.length();
            return sb.replace(len-2,len,"]").toString();
        }
    }
    
    
}

可能出现“假满”("假溢出")

  1.  新创建的队列----长度为n的空队列 rear和front都为0;

  2.  不停的删除元素,front不停的后移,导致rear==front

技术分享

解决办法

  1. 保持出队时front不往后移,即每次出队,把所有元素整体往front端移动(front始终指向a0不变,当元素入列rear++,元素出列rear--)

  2.  使用循环队列(front 和 rear到达底层数组的capacity-1时,就变成0)

循环队列

技术分享

注意 (rear永远指向最后一个元素之后的下一位)

    1. 队列满:rear == front;

    2. 队列空----此时 elementData[front] == null;rear == front。

队列的长度:

   rear   > front的时候: rear - front

  front > rear的时候: capacity-(front-rear)

顺序结构实现循环队列:

import java.util.Arrays;


public class CircularQueue<T> 
{
    private int DEFAULT_SIZE = 10;
    private int capacity;
    private Object[] elementData;
    
    private int front = 0;
    private int rear = 0;
    
    public CircularQueue()  //创建一个空队列
    {
        capacity = DEFAULT_SIZE;
        elementData = new Object[capacity];
    }
    
    public CircularQueue(T element) //以指定元素创建队列
    {
        this();
        elementData[0] = element;
        rear++;
    }
    
    public CircularQueue(T element,int initSize) //指定初始大小
    {
        this.capacity = initSize;
        elementData = new Object[capacity];
        elementData[0] = element;
        rear++;
    }
    
    public int length()
    {
        if(isEmpty())
        {
            return 0;
        }
        else
        {
            return rear>front?rear-front:capacity-(front-rear);
        }
    }
    
    public void add(T element)//插入队列 rear++
    {
        if(rear==front
                &&elementData[front]!=null)//队列已经满了
        {
            throw new IndexOutOfBoundsException("队列已经满了...");
        }
        elementData[rear++] = element;
        
        //队列的索引是从0到capacity-1;如果到达capacity说明回到了0位置
        rear = rear==capacity ? 0 : rear;
    }
    
    public T remove()  //出队
    {
        if(isEmpty())
        {
            throw new IndexOutOfBoundsException("空队列异常...");
        }
        T oldValue = (T) elementData[front];
        elementData[front++]=null;
        front= front==capacity ? 0 : front;
        return oldValue;
    }
    
    public T element()
    {
        if(isEmpty())
        {
            throw new IndexOutOfBoundsException("空队列异常");
        }
        return (T) elementData[front];
    }
    public boolean isEmpty()
    {
        //队列的为空的情况
        return rear==front && elementData[rear]==null;
    }
    
    public void clear()
    {
        Arrays.fill(elementData, null);
        front = 0;
        rear = 0;
    }
    
    public String toString()
    {
        if(isEmpty())
        {
            return "[]";
        }
        else
        {
            if(front<rear) //有效区间 front-->rear
            {
                StringBuilder sb = new StringBuilder("[");
                for(int i =front;i<rear;i++)
                {
                    sb.append(elementData[i].toString()+", ");
                }
                int len = sb.length();
                return sb.replace(len-2, len, "]").toString();
            }
            else  //有效区间 front-->capacity以及0-->front
            {
                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.replace(len-2, len, "]").toString();
            }
        }
    }
}

小结:实际上循环队列所做的只不过是在普通队列的基础上加上了front/rear到达capacity就回到0

 

队列的链式存储:(链队列)

(不用担心溢出)

public class LinkQueue<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 front; //保存该链队列的头结点
    private Node rear; //保存该链队列的尾节点
    private int size; //保存链队列中的节点个数
    
    public LinkQueue() //构建一个空队列
    {
        front = null;
        rear = null;
    }
    public LinkQueue(T element) //创建一个只有一个元素的链队列
    {
        front = new Node(element,null);
        rear = front;
        size++;
    }
    
    public int length()
    {
        return size;
    }
    public void add(T element)//入列,在rear后面加上一个节点
    {
        if(front==null)
        {
            front = new Node(element,null);
            rear = front;
        }
        else
        {
            Node newNode = new Node(element,null);
            rear.next = newNode;
            rear = newNode;
        }
        size++;
    }
    
    public T remove() //删除队列front端的元素
    {
        Node oldNode = front;
        front = front.next;
        oldNode.next = null;
        size--;
        return oldNode.data; //oldNode是局部变量不用主动置为null
    }
    
    public boolean isEmpty() //判断队列是否为空
    {
        return size==0;
    }
    public void clear() //清空队列
    {
        front = null;
        rear = null;
        size = 0;
    }
    
    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.replace(len-2, len, "]").toString();
        }
    }
    
}

小结:

链队列解决了很多问题。

顺序表一定要解决“假溢出”问题,方法之一就是“循环列表”,即超过了capacity就置为0.(当然队列的范围也做了相应的改变)

 

JDK中的队列:

Queue接口

N/A

抛异常

返回特殊值

插入

add(e)

offer(e)

移除

remove()

poll()

检查

element()

peek()

 

Queue接口的实现类:(下面列举的均是线程安全的)

ArrayBlockingQueue      ------高并发下,性能好

LinkedBlockingQueue    ------吞吐量大

ConcurrentLinkedQueue  -----wait-free队列算法实现

 

双向队列:Deque

(两端均可进行插入或者删除-----可以当做队列,一端插入一端删除;可以当做,插入删除均在一边)

技术分享

LinkedList既可以当做队列,也可以当做栈、还可以当做线性表,所以功能强大。(线程不安全,需要外同步)

对于大部分程序而言: ArrayList、ArrayDeque的性能还是好过 LinkedList

 

外同步:

       List list = Collections.synchronizedList(new LinkedList(...));

 


栈和队列

标签:

原文地址:http://www.cnblogs.com/bluechip/p/4176196.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!