美文网首页
仿源码自定义LinkedList

仿源码自定义LinkedList

作者: 四喜汤圆 | 来源:发表于2018-09-04 11:07 被阅读0次

一、基本操作

LinkedList基于双链表实现。

  • 链表无容量限制,但双链表本身占用空间
  • 按下标访问元素需要遍历一半链表移动到位
  • 插入、删除时:还是要遍历部分链表才能移动到链表所指向的位置


二、常用API

三、实现了上述基本操作的LinkedList

1,按照普适思路编写;2,注意判空

/**
     * 插入单个节点
     * 从双链表的尾部插入
     * @param e
     * @return
     */
    public boolean add(E e){
        linkLast(e);
        return true;
    }
    
    /**
     * 将节点连接到双链表尾部
     * @param e
     */
    private void linkLast(E e){
        Node<E> l=last;
        // 新节点指向前一节点
        Node<E> newNode=new Node<E>(l,e,null);
        // 更改last指针
        last=newNode;
        // 原来的last节点指向新节点(需考虑原来的last节点为null的情况)
        if(l==null){
            first=newNode;
        }else{
            l.next=newNode;
        }
        // size++
        size++;
        
    }

/**
     * 从index
     * @param index
     * @param e
     * @return
     */
    public void add(int index, E e){
        // index要在[0,size]的闭区间内
        if(index==size){
            // 插入到链表尾部
            linkLast(e);
        }else{
            // 将新节点插入到"第index节点"之前
            linkBefore(e,node(index));
        }
    }

1,按照普适思路编写;2,注意判空

/**
     * 删除指定索引处的元素,并将删除的元素值返回
     * @param index
     * @return
     */
    public E remove(int index){
        checkElementIndex(index);
        return unlink(node(index));
    }

/**
     * 删除指定元素
     * 需要遍历链表
     * @param o
     * @return
     */
    public boolean remove(Object o){
        if(o==null){
            Node<E> f=first;
            while(f!=null){
                // 遍历链表
                if(f.item==null){
                    unlink(f);
                    return true;
                }
                f=f.next;
            }
        }else{
            Node<E> f=first;
            while(f!=null){
                // 遍历链表
                if(o.equals(f.item)){
                    unlink(f);
                    return true;
                }
                f=f.next;
            }
        }
        return false;
    }

注意其中找出第index节点的方法

/**
     * 修改
     * @param index
     * @param element
     * @return
     */
    public E set(int index,E element){
        // index在[0,size-1]范围内则合法,否则抛出IndexOutOfBoundException
        checkElementIndex(index);
        Node<E> x=node(index);
        E oldVal=x.item;
        x.item=element;
        return oldVal;
    }

/**
     * 找到索引为index的Node节点
     * @param index
     * @return
     */
    private Node<E> node(int index) {
        if(index<(size>>1)){
            // 从first节点开始遍历
            Node<E> x=first;
            for(int i=0;i<index;i++){
                x=x.next;
            }
            return x;
        }else{
            Node<E> x=last;
            for(int i=size-1;i>index;i--){
                x=x.prev;
            }
            return x;
        }
    }

4. 查

/**
     * 查:得到索引处的值
     * @param index
     * @return
     */
    public E get(int index){
        checkElementIndex(index);
        return node(index).item;
    }

源码

import java.util.Collection;
/**
 * 仿源码,自定义LinkedList
* <p>Description: </p>  
* @author 杨丽金  
* @date 2018-9-4  
* @version 1.0
 */
public class MyLinkedList<E> {
    static class Node<E> {
        E item;
        Node<E> prev;
        Node<E> next;

        Node(Node<E> prev, E element, Node<E> next) {
            this.prev = prev;
            this.item = element;
            this.next = next;
        }
    }

    int size;// 元素个数(元素索引从0开始)
    Node<E> first;// 首元素
    Node<E> last;// 尾元素
    
    // 构造函数
    public MyLinkedList(){}
    
    public MyLinkedList(Collection<? extends E> c){
        this();
        addAll(c);
    }
    
    /**
     * 插入单个节点
     * 从双链表的尾部插入
     * @param e
     * @return
     */
    public boolean add(E e){
        linkLast(e);
        return true;
    }
    
    /**
     * 将节点连接到双链表尾部
     * @param e
     */
    private void linkLast(E e){
        Node<E> l=last;
        // 新节点指向前一节点
        Node<E> newNode=new Node<E>(l,e,null);
        // 更改last指针
        last=newNode;
        // 原来的last节点指向新节点(需考虑原来的last节点为null的情况)
        if(l==null){
            first=newNode;
        }else{
            l.next=newNode;
        }
        // size++
        size++;
        
    }
    
    /**
     * 将节点连接到双链表首部
     * @param e
     */
    private  void LinkFirst(E e){
        Node<E> f=first;
        // 新节点指向原来的first节点
        Node<E> newNode=new Node<E>(null,e,f);
        // first指针指向新节点
        first=newNode;
        // 原来的first节点指向新节点
        if(f==null){
            last=newNode;
        }else{
            f.prev=newNode;
        }
        size++;
        
    }
    /**
     * 从index
     * @param index
     * @param e
     * @return
     */
    public void add(int index, E e){
        // index要在[0,size]的闭区间内
        if(index==size){
            // 插入到链表尾部
            linkLast(e);
        }else{
            // 将新节点插入到"第index节点"之前
            linkBefore(e,node(index));
        }
    }
    
    /**
     * 将e插入到node之前
     * @param e
     * @param node
     */
    private void linkBefore(E e, Node<E> succ) {
        // succ的前一节点
        Node<E> p=succ.prev;
        Node<E> newNode=new Node<E>(p,e,succ);
        // succ的prev指向newNode
        succ.prev=newNode;
        // p的next指向newNode(但考虑p为null的情况)
        if(p==null){
            first=newNode;
        }else{
            p.next=newNode;
        }
        size++;
    }

    public boolean addAll(Collection<? extends E> c){
        return addAll(size,c);
    }
    
    /**
     * 批量插入数组:
     * for循环遍历数组,依次插入
     * @param index
     * @param c
     * @return
     */
    public boolean addAll(int index,Collection<? extends E> c){
        // index要在[0,size]的闭区间内
        
        // 转化成数组
        Object[] a=c.toArray();
        int newNum=a.length;
        if(newNum==0){
            // 搞笑的!都没有数据还有必要往里面插入吗?!
            return false;
        }
        
        // 那么遍历c,依次插入各个元素
        // 在遍历之前要准备好pred,succ(要插入范围的开始节点、结束节点)
        Node<E> pred,succ;
        if(index==size){
            // 插入到尾部
            pred=last;
            succ=null;
        }else{
            // 插入到中间某一位置
            succ=node(index);
            pred=succ.prev;
        }
        
        // 遍历集合,将元素依次插入
        for(Object o:a){
            // 强转
            @SuppressWarnings("unchecked")E e=(E)o;
            Node<E> newNode=new Node<E>(pred,e,null);
            if(pred==null){
                // 要考虑pred为空的情况
                first=newNode;
            }else{
                pred.next=newNode;
            }
            // 变量的改变
            pred=newNode;
            
        }
        
        // 遍历完后,将succ节点连接到链表上
        pred.next=succ;
        if(succ==null){
            last=pred;
        }else{
            succ.prev=pred;
        }
        
        size+=newNum;
        
        return true;
    }
    
    /**
     * 删除指定索引处的元素,并将删除的元素值返回
     * @param index
     * @return
     */
    public E remove(int index){
        checkElementIndex(index);
        return unlink(node(index));
    }

    /**
     * 删除一个节点,需要修改它的前驱节点、以及后继节点
     * @param node
     * @return
     */
    private E unlink(Node<E> node) {
        final E element=node.item;
        final Node<E> pred=node.prev;
        final Node<E> succ=node.next;
        if(pred==null){
            first=succ;
        }else{
            pred.next=succ;
        }
        if(succ==null){
            last=pred;
        }else{
            succ.prev=pred;
        }
        // 防止内存泄漏:把node节点占用的资源释放掉
        node.prev=null;
        node.next=null;
        node.item=null;
        
        size--;
        return node.item;
    }
    
    /**
     * 删除指定元素
     * 需要遍历链表
     * @param o
     * @return
     */
    public boolean remove(Object o){
        if(o==null){
            Node<E> f=first;
            while(f!=null){
                // 遍历链表
                if(f.item==null){
                    unlink(f);
                    return true;
                }
                f=f.next;
            }
        }else{
            Node<E> f=first;
            while(f!=null){
                // 遍历链表
                if(o.equals(f.item)){
                    unlink(f);
                    return true;
                }
                f=f.next;
            }
        }
        return false;
    }
    
    /**
     * 修改
     * @param index
     * @param element
     * @return
     */
    public E set(int index,E element){
        // index在[0,size-1]范围内则合法,否则抛出IndexOutOfBoundException
        checkElementIndex(index);
        Node<E> x=node(index);
        E oldVal=x.item;
        x.item=element;
        return oldVal;
    }

    private void checkElementIndex(int index) {
        if(!(index>=0 && index<size) ){
            // 如果下标不在范围内:抛出异常
            throw new IndexOutOfBoundsException(index+"");
        }
    }

    /**
     * 找到索引为index的Node节点
     * @param index
     * @return
     */
    private Node<E> node(int index) {
        if(index<(size>>1)){
            // 从first节点开始遍历
            Node<E> x=first;
            for(int i=0;i<index;i++){
                x=x.next;
            }
            return x;
        }else{
            Node<E> x=last;
            for(int i=size-1;i>index;i--){
                x=x.prev;
            }
            return x;
        }
    }
    
    /**
     * 查:得到索引处的值
     * @param index
     * @return
     */
    public E get(int index){
        checkElementIndex(index);
        return node(index).item;
    }
    
    
    
    public int size(){
        return size;
    }
    
}

测试:

public class Test {
    public static void main(String[] args) {
        MyLinkedList<Integer> list = new MyLinkedList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        for (int i = 0; i < list.size; i++) {
            System.out.println(list.get(i));
        }
        list.remove(0);
        list.set(1, 5);
        for (int i = 0; i < list.size; i++) {
            System.out.println(list.get(i));
        }
    }
}

相关文章

网友评论

      本文标题:仿源码自定义LinkedList

      本文链接:https://www.haomeiwen.com/subject/csliwftx.html