数据结构学习笔记——表、栈、队列的Java实现

数据结构学习笔记——表、栈、队列的Java实现

首先介绍抽象数据类型(Abstract data type, ADT)是带有一组操作的一些对象的集合。举个例子,假设“空调”这个ADT必须要有“制冷”、“干燥”这两个功能(操作),但是我们并不关心A品牌空调和B品牌空调是如何实现这两个功能,以及C品牌可能拥有额外的“制热”功能,但我们关注的是只要是“空调”这个ADT,就必须要有“制冷”、“干燥”这两个功能(操作)。

概念介绍

  • 表(List):可分为ArrayList和LinkedList。

    • ArrayList:使用数组储存元素,通过数组的下标对表内元素进行增删改查等系列操作,并记录表中元素个数;
    • LinkedList:使用嵌套类Node来储存元素,通过用pre和next把两个元素连接起来,从而使元素的排列形成一条链,可对元素进行增删改查等操作,并记录表中元素的个数和链的头尾两个节点或其一。链表可分为单链表和双链表。
    • 可调用方法包括:add(插入)、get(查找)、set(改值)、remove(删除)、clear(初始化)、isEmpty(判断表是否为空)
    • 优缺点:由于数组的下标存在,使得ArrayList的get(查找)和set(改值)花费常数时间O(1),但是add(插入)和remove(删除)花销很大,原因是插入和删除的同时需要把数组中其他位置的元素移动位置,时间复杂度为O(N);LinkedList却刚好相反,插入和删除一个元素的花销很小O(1),因为不会影响到表中其他的元素(除了与之相连的两个元素),但是由于只储存了头尾两个节点,对于链中的任意元素,都需要从头节点或者尾节点出发去查找,因此get和set的花销较大,时间复杂度为O(N)。
  • 栈(Stack):栈是一个特殊的表,具有Last in first out(后进先出)的特点,只能查找栈顶的元素,进栈时元素压入栈底,出栈时返回栈顶的元素,并弹出该元素。栈也可分为ArrayStack和LinkedStack。

    • 可调用方法包括:push(进栈)、pop(出栈)、top(查找顶栈元素)、isEmpty(判断栈是否为空)
    • 优缺点:由于栈默认只能操作栈顶的元素,因此优点是运行速度快,不管是push、top还是pop,时间复杂度都为O(1),缺点是看不到栈内的元素,必须等栈顶的元素出栈后才能看到下一个栈内的元素。
  • 队列(Queue):队列是一个特殊的表,具有First in first out(先进先出)的特点,入队在队尾插入元素,出队在队头删除元素,记录队列中的元素个数。队列也可分为ArrayQueue和LinkedQueue。

    • 可调用方法包括:enQueue(入队)、deQueue(出队)、isEmpty(判断队列是否为空)
    • 优缺点:队列的优点也是运行速度快,无论是enQueue(入队)还是deQueue(出队),花销都很小,时间复杂度为O(1)。

应用场景

  • 表(List):一般现实生活中可以用表格记录的内容都可以用数据结构表来表示,如学校班级学生名单、实验结果记录等

  • 栈(Stack):如某仓库的某个库位的货物竖直堆起来存放,则该货位的储存情况可以用栈来表示,首先进栈的是堆放在最底下的货物,最后进栈的是堆放在最顶上的货物;想要把最底下的货物取出,必须先把堆放在上方的货物依次清空。

  • 队列(Queue):如打印机处理任务的序列可用队列来表示,先提交的任务先处理,后提交的任务后处理,符合先进先出的特点。

代码实现

ArrayList的Java实现:

public class myArrayList<AnyType> implements Iterable<AnyType>{
    private static final int DEFAULT_CAPACITY=100;//默认表的大小
    private int theSize;//表的大小
    private AnyType[] theItems;//存放在表里的数据类型

    public myArrayList(){
        doClear();
    }
    public void clear(){
        doClear();
    }
    private void doClear(){
        theSize=0;
        ensureCapacity(DEFAULT_CAPACITY);
    }
    private void ensureCapacity(int newCapacity){
        if(newCapacity<theSize)
            return;
        AnyType[] old=theItems;
        theItems=(AnyType[]) new Object[newCapacity];
        for(int i=0;i<size();i++)
            theItems[i]=old[i];
    }
    public int size(){
        return theSize;
    }
    public boolean isEmpty(){
        return size()==0;
    }
    public AnyType get(int idx){//查找
        if(idx<0||idx>=size())
            throw new ArrayIndexOutOfBoundsException();
        return theItems[idx];
    }
    public void set(int idx,AnyType newVal){//改值
        if(idx<0||idx>=size())
            throw new ArrayIndexOutOfBoundsException();
        theItems[idx]=newVal;
    }
    public boolean addAfter(AnyType x){//在表尾插入
        add(size(),x);
        return true;
    }
    public void add(int idx,AnyType x){//指定位置插入
        if(theItems.length==size())
            ensureCapacity(size()*2+1);
        for(int i=size();i>idx;i--){
            theItems[i]=theItems[i-1];
        }
        theItems[idx]=x;
        theSize++;
    }
    public AnyType remove(int idx){//删除
        AnyType removeItem=theItems[idx];
        for(int i=idx;i<size()-1;i++){
            theItems[i]=theItems[i+1];
        }
        theSize--;
        return removeItem;
    }

    public java.util.Iterator<AnyType> iterator(){
        return new ArrayListIterator();
    }

    private class ArrayListIterator implements java.util.Iterator<AnyType>{
        private int current=0;

         @Override
         public boolean hasNext() {
             return current<size();
         }
         public AnyType next(){
             return theItems[current++];
         }

         @Override
         public void remove() {
             myArrayList.this.remove(--current);
         }
    }
}

LinkedList的Java实现:

public class myLinkedList<AnyType> implements Iterable<AnyType> {
    public static class Node<AnyType>{
        public AnyType data;//存放在表里的数据类型
        public Node<AnyType> pre;
        public Node<AnyType> next;

        public Node(AnyType d,Node<AnyType> p,Node<AnyType> n){
            data=d;
            pre=p;
            next=n;
        }
    }

    public myLinkedList(){
        doClear();
    }
    public void clear(){
        doClear();
    }
    private void doClear(){
        beginMarker=new Node<AnyType>(null,null,null);
        endMarker=new Node<AnyType>(null,beginMarker,null);
        beginMarker.next=endMarker;

        int theSize=0;
    }
    public int size(){
        return theSize;
    }
    public boolean isEmpty(){
        return size()==0;
    }
    public AnyType get(int idx){//查找
        return getNode(idx).data;
    }
    private Node<AnyType> getNode(int idx){
        Node<AnyType> p;
        p=beginMarker.next;
        for (int i=0;i<idx;i++){
            p=p.next;
        }
        return p;
    }
    public void addAfter(AnyType x){//在表尾插入
        add(size(),x);
    }
    public void add(int idx,AnyType x){//在指定位置插入
        addBefore(getNode(idx),x);
    }
    private void addBefore(Node<AnyType> p,AnyType val){
        Node<AnyType> newNode=new Node<>(val,p.pre,p);
        p.pre.next=newNode;
        p.pre=newNode;
        theSize++;
    }
    public void remove(int idx){//删除
        remove(getNode(idx));
    }
    private void remove(Node<AnyType> p){
        p.pre.next=p.next;
        p.next.pre=p.pre;
        theSize--;
    }
    public void set(int idx,AnyType val){//改值
        getNode(idx).data=val;
    }

    private int theSize;//表的大小
    private Node<AnyType> beginMarker;
    private Node<AnyType> endMarker;

    public java.util.Iterator<AnyType> iterator(){
        return new LinkedListIterator();
    }
    private class LinkedListIterator implements java.util.Iterator<AnyType>{
        private Node<AnyType> current=beginMarker.next;

        public boolean hasNext(){
            return current!=endMarker;
        }
        public AnyType next(){
            if(!hasNext())
                throw new java.util.NoSuchElementException();
            AnyType nextItem=current.data;
            current=current.next;
            return nextItem;
        }
    }
}

由于ArrayStack、LinkedStack和ArrayQueue、LinkedQueue两者的实现大同小异,因此这里只选择展示ArrayStack和LinkedQueue的代码实现。

ArrayStack的Java实现:

public class myArrayStack<AnyType> extends myArrayList {
    private int topOfStack;//栈顶的下标
    private int theSize;//栈的大小
    private AnyType[] theArray;//存放在栈里的数据类型
    private int DEFAULT_CAPACITY=100;默认栈的大小

    public myArrayStack(){//新建一个myStack
        doClear();
    }
    public void clear(){
        doClear();
    }
    private void doClear(){
        theSize=0;
        topOfStack=-1;
        ensureCapacity(DEFAULT_CAPACITY);
    }
    private void ensureCapacity(int newCapacity){
        if(newCapacity<theSize)
            return;
        AnyType[] old=theArray;
        theArray=(AnyType[]) new Object[newCapacity];
        for(int i=0;i<size();i++)
            theArray[i]=old[i];
    }
    public int size(){
        return theSize;
    }
    public boolean isEmpty(){
        return theSize==0;
    }
    public void push(AnyType val){//进栈
        if(theArray.length==size()){
            ensureCapacity(size()*2+1);
        }
        topOfStack++;
        theArray[topOfStack]=val;
        theSize++;
    }
    public AnyType pop(){//出栈
        AnyType popItem=theArray[topOfStack];
        topOfStack--;
        theSize--;
        return popItem;
    }
    public AnyType top(){//读取栈顶的元素
        return theArray[topOfStack];
    }
}

LinkedQueue的Java实现:

public class myLinkedQueue<AnyType> extends myLinkedList {
    private int currentSize;
    private Node<AnyType> beginMarker;
    private Node<AnyType> endMarker;
    
    public myLinkedQueue(){
        doClear();
    }
    public void clear(){
        doClear();
    }
    private void doClear(){
        beginMarker=new Node(null,null,null);
        endMarker=new Node(null,beginMarker,null);
        beginMarker.next=endMarker;
        currentSize=0;
    }
    public boolean isEmpty(){
        return currentSize==0;
    }
    public void enQueue(AnyType val){//入队
        addBefore(endMarker,val);
    }
    private void addBefore(Node<AnyType> p,AnyType val){
        Node<AnyType> newNode=new Node(val,p.pre,p);
        p.pre.next=newNode;
        p.pre=newNode;
        currentSize++;
    }
    public AnyType deQueue(){//出队
        return remove(beginMarker.next);
    }
    private AnyType remove(Node<AnyType> p){
        p.next.pre=p.pre;
        p.pre.next=p.next;
        currentSize--;
        return p.data;
    }
    public AnyType peek(){//查找队头元素
        return beginMarker.next.data;
    }
}

测试结果

使用System.nanoTime()来记录运行时间,测试ArrayList、LinkedList、ArrayStack、LinkedStack、ArrayQueue和LinkedQueue这六种数据结构增删改查四种操作各100次,记录其运行时间,每一项操作均记录了5次,得到下面的测试结果:
表1 测试结果
从表中数据对比可以看出,ArrayList的add和remove操作的运行时间对比LinkedList花销非常大,几乎相差了一个数量级,但是get和set两项操作的花销对比LinkedList又非常小,两者也是几乎相差了一个数量级。充分说明了ArrayList的优点在于get和set的运行速度快,原因是ArrayList使用数组来储存元素,因此运用数组自带的下标来获取元素的花销是十分小的,但是add和remove操作由于需要调整其他元素的位置,因此花销巨大;而LinkedList却恰恰相反,其运用链式结构把相邻的元素关联起来,对于add和remove操作,都只会影响到相邻元素,而对链条上的其他元素没有太大的影响,因此花销较小,但是对于get和set操作,由于没有下标可以寻找,因此需要从头节点或尾节点出发,找到相应的元素,因此花销不小。
Stack和Queue都是一种特殊的List,两者的不同点在于Stack具有后进先出的特点,而Queue具有先进先出的特点,两者都是只能操作处于List最外端的元素(栈顶/队头/队尾),因此增删的操作花销都并不大,运行速度较快。由于两者的特点不同,其对应的应用也不尽相同。

以上博文仅基于博主根据教材《数据结构与算法分析 Java语言描述》(Mark Allen Weiss)第3版中的内容以自己的理解加以转化和记录,若有理解错误或者不到位的地方,欢迎大家指正,多相互交流,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值