算法系列笔记(二)利用链表实现栈和队列

2018-07-17  本文已影响0人  shaclow

引言

上一次我们实现了泛型栈,并在最后粗略带过栈的迭代器的功能的实现。然而我们注意到我们是用数列去存储数据,但我们实现不定容的栈其实是和数组定容的特性是不搭的,导致我们就被迫建立方法resize,并在resize中使用大规模的数组遍历复制。这明显是比较消耗性能的。所以我们今天采用另外一种方式代替数组来存储数据,这就是链表

链表的介绍

链表你可以想象作一根线上有一颗颗珠子,老实说不是一根线是一段段线,连接相邻的两颗珠子。这样就能将一个个离散的珠子(离散的数据)联系在一起。是的,珠子应该是存储数据的实体,但珠子应该知道它相邻的珠子是谁,否则线没法将他们联系起来,所以就还必须存储这个相邻珠子的具体身份或地址(由于我这里是构建单向链表,所以珠子只需要知道它前一个珠子就行(当然你也可以构建双向的))

由于我觉得珠子其实就是一个个离散的点,所以我们这里将珠子叫作point

综上,可以归结出链表的实现必须依靠一个个point,而point的实现有两点要求

1.存储数据
2.存储上一个point的身份标识(地址)

具体实现

public class Listforstack<T> {
    private point first;
    private int n;
    private class point{
        T data;
        point last;            //存储上一个point的地址
    }
    public boolean isempty(){
        return n==0;
    }
    public int size(){
        return n;
    }
    public void push(T d){
        point oldpoint=first;
        first=new point();
        first.data=d;
        first.last=oldpoint;
        n++;
    }
    public T pop(){
       T d=first.data;
       first=first.last;
       n--;
       return d;
    }
}

这里point中的point last本来我是想着弄指针的,后来想起这是java不是c++,本身就是指针操作。
其实倘若想实现双向列表的话,只需要在point的定义中添加一个point next存下一个point的地址就行

队列和背包

下面实现队列,简单介绍一下

栈我们知道是数据先进后出
队列和栈差不多不过是先进先出,就好像排队一样,先排的肯定是最先受到服务之类的

下面是利用单向链表的队列的实现

    public class Listforqueue<T> {
        private point last;
        private point first;
        private int n;
        private class point{
            T data;
            point next;             //存储下一个point的地址
            public point(T d) {
                data=d;
            }
        }
        public boolean isempty(){
            return n==0;
        }
        public int size(){
            return n;
        }
        public void add(T d){
            if(n==0){                         //对n=0时特判
                last=new point(d);
                last.next=null;
                first=last;
                n++;
            }
            else{
            point oldlast=last;
            last=new point(d);
            oldlast.next=last;
            last.next=null;
            n++;
            }
        }
        public T pop(){             //这是清除并返回第一个元素
            if(isempty()){
                return null ;
            }
            T d=first.data;
            first=first.next;           //不怕越界出错,最多是变成null而已
            n--;
            return d;
        }
    }

注意这个pop是弹出第一个元素

对了别忘了我们之前的迭代器的实现,其实和我们之前的数组实现栈的方式差不多,下面我就写一个队列的迭代器版本吧

import java.util.Iterator;

public class ListforqueueIter<T> implements Iterable<T>{
    private point last;
    private point first;
    private int n;
    private class point{
        T data;
        point next;             //存储下一个point的地址
        public point(T d) {
            data=d;
        }
    }
    public boolean isempty(){
        return n==0;
    }
    public int size(){
        return n;
    }
    public void add(T d){
        if(n==0){                         //对n=0时特判
            last=new point(d);
            last.next=null;
            first=last;
            n++;
        }
        else{
        point oldlast=last;
        last=new point(d);
        oldlast.next=last;
        last.next=null;
        n++;
        }
    }
    public T pop(){             //这是清除并返回第一个元素
        if(isempty()){
            return null ;
        }
        T d=first.data;
        first=first.next;           //不怕越界出错,最多是变成null而已
        n--;
        return d;
    }
    
    //直到为止现在和上面的实现没有任何区别
    
    public Iterator<T> iterator() {
        return new Iter();
    }
    private class Iter implements Iterator<T>{
        private point cur=first;      //cur表示迭代器当前位置
        public boolean hasNext(){
            return cur.next!=null;
        }
        public void remove(){           //这个是删除当前位置的下一个元素
            point newnext=cur.next.next;   
            cur.next.next=null;
            cur.next=newnext;
        }
        public T next(){                //返回当前迭代器元素,迭代器往下移动*1
            T d =cur.data;
            cur=cur.next;
            return d;
        }
    }
}

注意这里的remove是删除当前迭代器位置的下一个元素,要删除迭代器位置的元素需要依靠双向链表。

上一篇 下一篇

猜你喜欢

热点阅读