数据结构单链表的定义(Java)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/c_yejiajun/article/details/78410348

定义一个接口

public interface ILinarList<E> {
    public abstract boolean add(E item);          //添加元素
    public abstract boolean add(int i,E item);    //插入元素
    public abstract E remove(int i);              //删除元素
    public abstract int indexOf(E item);          //定位元素
    public abstract E get(int i);                 //取表元素 
    public abstract int size();                   //求线性表长度
    public abstract void clear();                 //清空线性表
    public abstract boolean isEmpty();            //判断线性表是否为空

}

定义一个结点类

/*
 * 单链表的的原理就在结点这里,在每一个结点中都包含一个下一个结点的地址,
 * 如果想要访问下一个结点,就要通过上一个结点来调用他的成员变量next,以此类推
 */
public class Node<E> {
    E item;//创建一个泛型类item
    Node<E> next;//创建一个结点类类型
    public Node(E element,Node <E> next) {
        this.item=element;
        this.next=next;
    }

}

创建一个SLinkList类来实现接口

public class SLinkList<E> implements ILinarList<E>{
    private Node<E> start;//1    新建一个空的带头节点
    int size;
    public SLinkList(){
        start=null;
        size=0;
    }
    /*一个节点中的成员变量指向一个新的Node,从实例对象的角度,整条链表中有实例化的对象为start,
     * 详见1、2,可以看出,private Node<E> start=new Node<E>(item,null),实例化了一个start
     * 接下来add方法中,Node<E> current=start 为引用了start 的地址,start 的成员变量next的类型是Node<E>的引用类型,
     * 里面包含一个E类型的item,和一个Node<E>类型的next
     * 也就是说每添加一个节点都是没有实例化对象的,直接从start 的next成员指向一个新的Node<E>,
     * 再添加就是从新的Node<E>中的next成员指向新的Node<E>
     * 如下面的语句
     */
    //添加元素到链表的末尾
    public boolean add(E item) {
        if(start==null)//如果带头节点为空,将新建一个带头节点
            start=new Node<E>(item,null);//2
        else {//如果带头节点不为空的话,新建一个current指向start的地址
            Node<E> current=start;
            while(current.next!=null){//依次访问每一个节点,当一个节点的next为空时,说明此节点为最后一个节点
                current=current.next;//注意,这里的赋值,每次都是赋予地址,并不是修改
            }
            current.next=new Node<E>(item,null);//引用最后一个节点的next,创建一个新的节点
        }
        size++;
        return true;
    }
    //插入元素到i索引位置前插入一个数据元素
    public boolean add(int i, E item) {
        if(i<0||i>size)//等于size时候即为在末尾调加一个元素
            throw new IndexOutOfBoundsException("Index:"+i+",Size:"+size);//抛出越界异常
        Node<E> newnode=new Node<E>(item, null);
        if(i==0){//第一个元素前插入一个元素
            newnode.next=start;//newnode.next指向start的地址,此时原头元素有了一个指向他的地址
            start=newnode;//start的地址指向newnode的地址,所以此时有两个实例对象指向带头元素,分别是start和newnode
            size++;
        }else{
             Node<E> current=start;//将current指向头元素的地址
             Node<E> previous=null;
             int number=0;//定义一个number用来计算访问节点的次数
             while(number<i){//从0开始自加,加到i时候,此时current指向i的索引位置,previous指向前一个
                 previous=current;//previous始终保存current前一个节点的地址
                 current=current.next;//循环访问current的next的地址
                 number++;//每访问一次number自加一次
             }
             previous.next=newnode;//将前一个节点的地址的next指向修改为newnode
             newnode.next=current;//将newnode的next指向当前的节点元素
             size++;
        }
        return true;
    }
    //清楚索引的元素
    public E remove(int i) {
        E oidValue=null;//先赋值为null,两个循环都没有进入时,此方法返回一个null
        if(i<0||i>size-1)
            throw new IndexOutOfBoundsException("Index:"+i+",Size:"+size);//抛出越界异常
        else if(!isEmpty()){//当整个链表不为空时,进去此语句
            if(i==0){//当要清楚头元素时
                oidValue=start.item;//将返回元素赋值
                start=start.next;
                size--;
            }else{//以下方法同add重写的第二次方法
                Node<E> previous=null;
                Node<E> current=start;
                int number=0;
                while(number<i){
                    previous=current;
                    current=current.next;
                    number++;
                }
                oidValue=current.item;
                previous.next=current.next;//将指定元素的前一个元素的next指向指定元素的下一个元素
                current.next=null;//释放当前元素的next
                current=null;//同上(个人觉得不做这两步好像也没有什么问题?)
                size--;
            }
        }
        return oidValue;
    }

    public int indexOf(E item) {
        int number=0;
        if(item==null){//需要单独对item==null的情况判定,因equals没有比较null,空指针错误
            for(Node<E> x=start;x!=null;x=x.next){
                if(x.item==null)
                    return number;
                number++;
            }
        }else{
            for(Node<E>x=start;x!=null;x=x.next){
                if(item.equals(x.item))//判定是否为所要的元素
                    return number;
                number++;
            }
        }
        return -1;//当遍历完整个链表之后没有所要的元素,说明此链表不存在此元素,返回-1
    }

    public E get(int i) {
        E item=null;
        if(i<0||i>size-1)
            throw new IndexOutOfBoundsException("Index:"+i+",Size:"+size);//抛出越界异常
        else if(!isEmpty()){
            Node<E> current=start;
            for(int j=0;j<i;j++)//当知道要取第i个元素的时候,用for循环执行i次
                current=current.next;
            item=current.item;
        }
        return item;
    }

    public int size() {
        return size;
    }

    public void clear() {
        for(Node<E> x=start;x!=null;){//当x为空时停止
            Node<E> next=x.next;//保存x.next的地址
            x.item=null;//将x中的元素清空
            x.next=null;//将x的指向清空,注意,x.next不代表是下一个节点,仅仅是指向下一个节点的地址
            x=next;
        }
        start =null;//start也是指向一片地址,如果没有清空的话,依然可以访问,只不过start.item和start.next都为空罢了
        size=0;
    }

    public boolean isEmpty() {
        return size==0;
    }
}

猜你喜欢

转载自blog.csdn.net/c_yejiajun/article/details/78410348