zoukankan      html  css  js  c++  java
  • java栈和队列


        可变长数组实现
        链表实现
        数组与链表的对比
    队列
        链表实现

    下压栈(简称栈)是一种基于后进后出(LIFO)策略的集合类型。这里学习分别用数组和链表这两种基础数据结构来实现栈。
    栈支持的基本操作有push,pop。

    可变长数组实现

    要用数组实现栈,可以声明一个int型的标记,这个标记指向的位置即为栈顶,push操作时,将值放在数据中标记的位置,同时将标记+1,pop时,返回数组在标记位置的值,同时标记-1。
    但java中的数组在声明的时候其长度就已经固定了,所以栈使用的空间只能是这个数组最大容量的一部分。为了能容纳更多的数据而声明一个特别大的数组会非常浪费空间,那如何解决这个问题,达到既不会浪费数组空间也不会超出数组范围呢?
    可以采用动态调整数组大小的方式,在push操作导致栈已满时,重新创建一个数组,其容量为原数组的两倍。同理在pop操作使数组的闲置空间达到一定程度时,重新创建一个容量更小的数组。但闲置的判断标准不能为一半1/2,否则会造成在1/2的临界点处push和pop操作时发生“抖动”,即频繁地进行数组扩容、缩容操作,这会极大地降低栈的性能。所以通常的做法是在减少到数组容量的1/4时缩容为1/2。

    实现可变长数组的代码如下:

    public class StackResizeArray<Item>  {
        private Item[] a; // array of items
        private int n; // number of elements on stack
    
        public StackResizeArray() {
            a = (Item[]) new Object[2];
            n = 0;
        }
    
        public boolean isEmpty() {
            return n == 0;
        }
    
        public int size() {
            return n;
        }
    
        private void resize(int capacity) {
            assert capacity >= n;
    
            Item[] temp = (Item[]) new Object[capacity];
            for (int i = 0; i < n; i++) {
                temp[i] = a[i];
            }
            a = temp;
        }
    
        public void push(Item item) {
            if (n == a.length)
                resize(2 * a.length); // double size of array if necessary
            a[n++] = item; // add item
        }
    
        public Item pop() {
            if (isEmpty())
                throw new NoSuchElementException("Stack underflow");
            Item item = a[n - 1];
            a[n - 1] = null; // to avoid loitering
            n--;
            // shrink size of array if necessary
            if (n > 0 && n == a.length / 4)
                resize(a.length / 2);
            return item;
        }
    }

    pop方法中,a[n - 1] = null将数组中已经出栈的位置设为null,是为了避免对象游离,否则这个位置的对象虽然已经不在栈中,但还被数组引用着,导致GC无法对其回收。

    链表实现

    栈的另外一种实现方式是采用链表,链表是一种递归的数据结构,它或者为空,或者指向一个结点的引用,该结点含有一个泛型的元素和一个指向另一条链表的引用。 构成链表的基本结点可以为:

    private static class Node<Item> {
        public Item item;
        public Node next;
    }

    其中泛型的item变量存放数据,同样是Node类型的next变量用来指向下一个结点。将链表的头部作为栈顶,push相当于在表头插入元素,pop是从表头删除元素。 代码如下:

    public class StackLinkedList<Item> {
        private static class Node<Item> {
            public Item item;
            public Node next;
        }
    
        private Node<Item> first;
        private int N;
    
        private boolean isEmpty() {
            return first == null;
        }
    
        public int size() {
            return N;
        }
    
        public void push(Item item) {
            Node<Item> oldFirst = first;
            first = new Node<Item>();
            first.item = item;
            first.next = oldFirst;
            N++;
        }
    
        public Item pop() {
            if (isEmpty())
                throw new NoSuchElementException("Stack underflow");
            Item item = first.item;
            first = first.next;
            N--;
            return item;
        }
    }
    数组与链表的对比

    数组与链表的区别决定了两种栈实现的区别:

    • 存取方式上,数组可以顺序存取或者随机存取,而链表只能顺序存取; 
    • 存储位置上,数组逻辑上相邻的元素在物理存储位置上也相邻,而链表不一定; 
    • 存储空间上,链表由于带有指针域,存储密度不如数组大; 
    • 按序号查找时,数组可以随机访问,需要的时间是常数,而链表不支持随机访问,需要的时间与数据规模成线性关系。
    • 按值查找时,若数组无序,数组和链表时间复杂度均为O(n),但是当数组有序时,可以采用折半查找将时间复杂度降为O(logn);
    • 插入和删除时,数组平均需要移动n/2个元素,而链表只需修改指针即可; 
    • 空间分配方面: 虽然可变长数组可以扩充,但需要移动大量元素,导致操作效率降低,而且如果内存中没有更大块连续存储空间将导致分配失败; 链表存储的节点空间只在需要的时候申请分配,只要内存中有空间就可以分配,操作比较灵活高效;

    队列

    链表实现

    先进先出队列(简称队列)是一种基于先进先出(FIFO)策略的集合类型。这里只关注队列的链表实现。与链表实现的栈类似,用结点包含泛型的item变量和next变量,前者存放数据,后者用来指向下一个结点。不同之处在于链表的头部、尾部都会被操作,从链表的一端入队(enqueue),从另一端出队(dequeue) 代码:

    public class Queue<Item> {
        private static class Node<Item> {
            public Item item;
            public Node next;
        }
    
        private Node<Item> first;
        private Node<Item> last;
        private int N;
    
        public void enqueue(Item item) {
            Node<Item> oldLast = last;
            last = new Node<Item>();
            last.item = item;
            if (isEmpty()) {
                first = last;
            } else {
                oldLast.next = last;
            }
            N++;
        }
    
        public Item dequeue() {
            if (isEmpty()) {
                return null;
            }
            Item result = first.item;
            first = first.next;
            if (isEmpty()) {
                last = null;
            }
            N--;
            return result;
        }
    
        public boolean isEmpty() {
            return N == 0;
        }
    
        public int size() {
            return N;
        }
    }
  • 相关阅读:
    pandas Dataframe filter
    process xlsx with pandas
    data manipulate in excel with easyExcel class
    modify registry in user environment
    add number line in vim
    java import webservice
    ctypes MessageBoxA
    music 163 lyrics
    【python实例】自动贩卖机
    【python基础】sys模块(库)方法汇总
  • 原文地址:https://www.cnblogs.com/qzhc/p/11652944.html
Copyright © 2011-2022 走看看