栈和队列

【1】栈的定义

  • 栈是仅仅限定在表尾进行插入和删除操作的线性表;运行插入和删除元素的一端叫栈顶,另外一端叫栈底,栈元素遵循先进先出的规则。栈的插入操作叫进栈,删除操作叫出栈,如下图所示:    

        

  • 顺序栈:我们知道栈也是一种特殊的线性表,那么顺序栈就是顺序线性表的一种简化,其Java代码实现如下:
  • /**
     * Created by shifeifei on 2017/6/11.
     */
    public class MyStack<T> {
    
        protected Object[] elementData;
    
        public MyStack() {
            elementData = new Object[]{};
        }
    
        //入栈
        public T push(T e) {
            int len = capacity();
            elementData[len - 1] = e;
            return e;
        }
    
        //出栈
        public T pop() {
            int len = size();
            if (len == 0) {
                throw new RuntimeException("空栈");
            }
            //删除栈顶元素
            int index = len - 1;
            if (index < 0) {
                throw new RuntimeException("空栈");
            }
            
            T e = (T) elementData[index];
            Object[] newElements = new Object[index];
            for (int i = 0; i < index; i++) {
                newElements[i] = elementData[i];
            }
            elementData = newElements;
            return e;
        }
    
        public int size() {
            return elementData.length;
        }
    
        private int capacity() {
            elementData = Arrays.copyOf(elementData, elementData.length + 1);
            return elementData.length;
        }
    
        public void print() {
            for (Object o : elementData) {
                System.out.print(o + "\n");
            }
        }
    
        public static void main(String[] args) {
            MyStack<String> stack = new MyStack<String>();
            stack.push("a");
            stack.push("b");
            stack.push("c");
            stack.push("d");
            stack.push("f");
    
            stack.print();
            System.out.println("--------");
            stack.pop();
    
            stack.print();
    
            stack.pop();
            System.out.println("--------");
            stack.print();
        }
    }
              
  • 链式栈:无非是使用单链表来实现栈,栈只允许在栈顶进行删除和插入操作,那么为了操作方便,我们肯定会把栈顶放链表的头部,对于链栈来说,不存在栈满的情况,除非物理内存不够用了。
    • 链栈的定义
    • /**
       * Created by shifeifei on 2017/6/11.
       */
      public class MyLinkedStack<T> {
      
          private Node<T> top;
      
          private int size;
      
          public void MyLinkedStack() {
          }
      
          private static class Node<T> {
              private T element;
              private Node<T> next;
      
              Node(T element, Node<T> next) {
                  this.element = element;
                  this.next = next;
              }
          }
      }
    • 链栈的入栈操作 
    •  如图所示,只需要将top向栈顶移动即可;Java代码如下所示: 
    •  
      //入栈
      public void push(T e) {
          top = new Node<T>(e, top);
          size++;
      }
    • 链栈的出栈栈操作
    • //出栈
      public void pop() {
          if (size == 0) {
              throw new RuntimeException("空栈");
          }
      
          Node old = top;
          top = top.next;
          //释放引用
          old.next = null;
          size--;
      }
    •  测试
    • public String print() {
          StringBuffer sb = new StringBuffer();
          Node temp = top;
          while (null != temp) {
              sb.append(temp.element).append("\t");
              temp = temp.next;
          }
          return sb.toString();
      }
      
      public static void main(String[] args) {
          MyLinkedStack<String> linkedStack = new MyLinkedStack<String>();
          linkedStack.push("a");
          linkedStack.push("b");
          linkedStack.push("c");
          linkedStack.push("d");
      
          System.out.println(linkedStack.print());
          System.out.println("---------------");
      
          linkedStack.pop();
          System.out.println(linkedStack.print());
      }
                   
  •  队 :只允许在一段进行插入操作,在另一端进行删除操作的线性表,是一种先进先出的线性表,运行插入的一段称之为队尾,运行删除的一段称为队头。              
    • 顺序存储的队列:假如有n个元素的顺序队列,那我们需要建立一个长度大于n数组,并不所有元素存储在数组的前n个单元里,数组下标为0的是队头,入队操作就是在队尾添加一个元素,此时的时间复杂度为O(1);
    • 入队操作:

                

                出队操作:

                

         出队操作是在队头进行,所以删除队头元素后,队列中的所有元素都要向前移动,意味着时间复杂度是O(n)。

  • 循环队列:把这种头尾相接的顺序存储的队列称之为循环队列
    • 我们再想想,出队的时候,队列元素可以不往前移动吗?也就是说队头元素不一定是下标为0的元素,如下图所示:
    • 为了避免队列只有一个元素的时,队头和队尾重合,我们可以让front指向队头元素,rear指向队尾元素的下一个位置,当front等于rear时,就表示是空队列。现在假设长度为5的数组,初始状态入下图左图所示,front和rear均指向0的位置,然后插入4个元素,rear指向下标为4的元素位置,如下右图所示:
    • 现在假设前两个元素出栈,则如下图所示:
    • 我们发现,rear明显已经指向队尾之外的地方,如果再在队尾添加元素,可以回出现数组越界的情况,但是实际上我们的队列还有空间的,那就是下标是0和1的地方;我们想想可不可以让rear指向0的位置呢?答案是肯定的,这样的话就可以继续向队列中插入元素了,假如此时我们再以此插入两个元素,情况如下图所示:
    • 当再插入两个元素之后,到底如何判断队列是空还是满呢?现在做如下规定:当front == rear时,为空队列;那么要讨论的就是如何判断队满的情况?当队列满时,可以让front和rear之间差一个元素,即队列数组中有一个空闲单元,如下图所示:
    • 假设队列的最大长度是size,由图4-12-8可以推出队列满的条件是:(rear+1)%size ==front;而计算队列的当前长度公式为:(rear-front+size)%size;同时也可以得到队头的位置:(front-1)%size;
  • 循环队列的顺序存储结构
    • 顺序存储结构的定义
    • /**
       * Created by shifeifei on 2017/6/15.
       * 队列的线性实现
       */
      public class MyQueue<T> {
      
          //存储队列元素
          T[] elements;
      
          //指向队头
          private int head;
      
          //指向队尾
          private int tail;
      
          public MyQueue() {
              elements = (T[]) new Object[5];
              tail = head = 0;
          }   
      }
    • 队尾添加元素
    • /**
       * 入队,队尾添加元素
       *
       * @param e
       */
      public void addLast(T e) {
          if (e == null)
              throw new RuntimeException("插入元素不能为空");
          elements[tail] = e;
          //指向队列尾部
          tail = (tail + 1) % elements.length;
          //判断队列是否满了,满了则动态扩容
          if (tail == head)
              allocateSpace();
      }
    • 队头删除元素
    • /**
       * 出队,队头删除元素
       *
       * @param e
       */
      public Object removeFirst(T e) {
          if (e == null)
              throw new RuntimeException("插入元素不能为空");
      
          Object temp = elements[head];
          elements[head] = null;
      
          //向后移动队头下标
          head = (head + 1) % elements.length;
          return temp;
      }
    • 其它方法
    • /**
       * 动态扩容
       */
      public void allocateSpace() {
          int p = head;
          int size = elements.length;
          //队列数组右侧的元素个数
          int r = size - p;
          int newCapacity = size << 1;// size值的二进制数的最后一位上加个0
          if (newCapacity < 0)
              throw new RuntimeException("队列扩容出错");
      
          Object[] a = new Object[newCapacity];
          //复制head下标右侧元素:即从elements数组的head开始r长度元素复制到a数组0位置开始
          System.arraycopy(elements, p, a, 0, r);
          //复制head下标左侧元素:即从elements数组的0开始p长度元素复制到a数组r位置开始
          System.arraycopy(elements, 0, a, r, p);
      
          elements = (T[]) a;
          head = 0;
          tail = size; //指向队尾
      }
      
      /**
       * 队列长度
       *
       * @return
       */
      public int size() {
          return (tail - head + elements.length) % elements.length;
      }
      
      public static void main(String[] args) {
          MyQueue<String> myQueue = new MyQueue<String>();
          myQueue.addLast("a");
          myQueue.addLast("b");
          myQueue.addLast("c");
          myQueue.addLast("d");
          myQueue.addLast("f");
          myQueue.addLast("g");
          myQueue.addLast("h");
          myQueue.addLast("i");
      
          System.out.println(Arrays.toString(myQueue.elements) + ",size=" + myQueue.size());
      
          System.out.println(myQueue.removeFirst("a"));
      
          System.out.println(Arrays.toString(myQueue.elements) + ",size=" + myQueue.size());
      }
  • 队列的链式存储结构:其实它就是线性单链表,只不过是要去尾进头出而已;我们可以将头指针指向链队列的头结点,而队尾指针指向终端结点;如下图所示:
  •  

                

 

 

转载于:https://my.oschina.net/sxshifeifei/blog/918454

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值