美文网首页
仿源码自定义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