数据结构-链表 (手写双向非循环链表)

接口

public interface MyLinkedList<E> {
    //添加元素--尾插法
    void add(E e);  //参数为任意类型
    //添加元素--头插法
    void  addFirst(E e); //参数为任意类型
    //获得元素的个数
    int size(); //返回值类型为int
    //获得元素
    E   get2(int ge); //参数类型为int,返回值类型为任意类型
    E   get(int ge);  //参数类型为int,返回值类型为任意类型
    //修改
    boolean set(int index,E e);  //参数类型为int和任意类型,返回值类型为boolean
    //删除
    boolean remove(int index); //参数类型为int,返回值类型为boolean
}

实现类

public class MyLinkedListImpl<E> implements  MyLinkedList<E> {

    private   Node<E>  first;//头节点
    private   Node<E>   last;//尾结点
    private  int        size;//代表链表的长度  也就是说有几个节点


    //添加元素-尾插法
    @Override
    public void add(E e) {
        //获取当前尾节点对象
        Node<E> oldLastNode=this.last;  //第一次赋值尾节点是null,将它赋值给oldLastNode;第二次赋值oldLastNode地址为0x530
        //创建新的节点,因为我们现在是尾插 所以现在新的节点就是最后一个节点
        Node<E> newNode=new Node<>(oldLastNode,e,null); //第一次创建新的链表为null,1,null;第二次创建新的链表为0x530,2,null,并且头部指向第一次创建节点的尾部
        //把当前新建的节点指定成尾节点
        this.last=newNode; //第一次创建的链表对象赋值给尾节点为null,第二次创建链表对象的尾节点0x532

        if (oldLastNode==null){ //代表是第一次进行节点的插入,将第一次创建的地址值赋值给头节点
            this.first=newNode;
        }else {
            oldLastNode.next=newNode; //把旧尾节点next指向新节点对象,将第二次创建的地址值赋值给第一次尾部节点
        }
        size++; //添加完成节点 整体加一
    }
    //元素添加-头插法
    @Override
    public void addFirst(E e) {
        //获得目前的头节点元素
        Node<E>  oldFirstNode=this.first;
        //创建新节点
        Node<E>  newNode=new Node<>(null,e,oldFirstNode);
        //把当前新节点设置为头节点
        this.first=newNode;

        if(oldFirstNode==null){ //代表是第一次添加节点
            this.last=newNode;
        }else{ //新的元素对象赋值给 之前旧头节点的pre
            oldFirstNode.pre=newNode;
        }

        size++;//总体长度加一

    }

    //返回链表的长度
    @Override
    public int size() {
        return size;
    }

    //获得元素实现
    @Override
    public E get2(int ge) {

         checkedIndex(ge);//判断下标是否越界

        Node<E>  fis=this.first; //获得链表的头节点

        //一定注意ge 不可以等于 如果是等于这个时候就取到了目标元素下一个
        for (int i = 0; i <ge; i++) {
               fis=fis.next;  //获取下一个地址值
        }
        //循环结束 当前fis对象就是指定目标元素对象
        return fis.item;//返回目标对象的item  item就是节点中保存的值
}

    //查询的升级版本
    @Override
    public E get(int ge) {

          checkedIndex(ge);//判断下标是否越界
          Node<E> node = getNode(ge);//根据下标 查询到指定节点对象,并返回给对象node
        return  node.item;  //返回给中间值
    }

    //封装查询节点对象方法
    private    Node<E>   getNode(int  index){

        if(index < (size>>1)){ //如果用户查询的元素是小于长度的1/2 这个时候正着查询元素
            Node<E>  fis=this.first; //获得链表的头节点
            //一定注意ge 不可以等于 如果是等于这个时候就取到了目标元素下一个
            for (int i = 0; i <index; i++) {
                fis=fis.next;
            }
            return  fis;

        }else{
            //如果大于使用元素反正查询即可
            Node<E> las=this.last;
            for (int i=size-1;i>index;i--){
                las=las.pre;
            }
            return  las;
        }
    }


    //修改的实现
    @Override
    public boolean set(int index, E e) {

        try {
            checkedIndex(index);//判断用户输入的下标是否越界

            Node<E> node = getNode(index);//根据下标获得指定节点对象

            node.item=e;//把用户传入的值 赋值给目标节点中item
            return  true;
        } catch (Exception ex) {
           return  false;
        }


    }

    //删除的实现
    /*
     * 1.删除的即为头结点又为尾结点
     *   将头结点设为为null ---first
     *   将尾结点设置为null ---last
     *   将被删除的头结点设为为null
     *
     * 2.删除的为头结点
     *   将删除头结点的下一个结点的上一个结点的地址设置为null
     *   将删除头结点的下一个结点, 设置为新的头结点
     *   将被删除的头结点设为为null
     *
     * 3.删除的为尾结点
     *   将删除尾结点的上一个结点的下一个结点的地址设置为null
     *   将删除结点的上一个结点, 设置为新的尾结点
     *   将被删除的尾结点设置为null
     *
     * 4.删除非头尾结点
     *   将删除结点的上一个结点指向下一个结点的地址变换为删除结点的下一个结点的地址
     *   将删除结点的下一个结点指向上一个结点的地址变换为删除结点的上一个结点的地址
     *   将被删除的姐点设置为null
     * 1 , 2, 3, 4 都需要进行size--
     * */
    @Override
    public boolean remove(int index) {

         checkedIndex(index);//检查下标是否合法

        try {
            Node<E> node = getNode(index);//获得删除的节点
            Node<E> p = node.pre; //获得删除节点前一个地址
            Node<E> n = node.next;//获得删除节点后一个地址

            if(p==null&&n==null){  //删除节点及时头节点 又是尾结点
                this.first=null;
                this.last=null;
            }else if(p==null){ //删除的是首节点元素
                n.pre=null; // 将删除头结点的下一个结点的上一个结点的地址设置为null
                this.first=n;//将删除头结点的下一个结点, 设置为新的头结点

            }else if(n==null){ //删除是尾结点
                 p.next=null;//将删除尾结点的上一个结点的下一个结点的地址设置为null
                 this.last=p;//将删除结点的上一个结点, 设置为新的尾结点

            }else{ //删除是中间的元素
                  p.next=n;//将删除结点的上一个结点指向下一个结点的地址变换为删除结点的下一个结点的地址
                  n.pre=p;//将删除结点的下一个结点指向上一个结点的地址变换为删除结点的上一个结点的地址
            }
            node=null;
            size--;//删除完成整体长度减一

            return  true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }



    }


    private   void   checkedIndex(int  ge){
        if(ge<0||ge>=size){
            throw   new ArrayIndexOutOfBoundsException("数组下标越界");
        }
    }


    //新建内部类 内部类就是节点类
     private class  Node<E>{

          private  Node<E>  pre; //前一个节点对象
          private  E        item;//中间保存的值
          private  Node<E>  next;//下一个节点对象

        public Node(){

        }
        public Node(Node<E> pre, E item, Node<E> next) {  //有参构造,参数分别为: 前一个节点对象,中间保存的值,下一个节点对象
            this.pre = pre;  //将用户传过来的参数赋值给当前属性pre
            this.item = item; //将用户传过来的参数赋值给当前类属性item
            this.next = next; //将用户传过来的参数赋值给当前类属性next
        }
    }

}

测试类

public class TestA {

    public static void main(String[] args) {

          MyLinkedList<Integer>  list=new MyLinkedListImpl<>();
          //list.add(1);
          //list.add(2);
           list.add(100);
           list.add(200);
           list.add(300);
           list.add(400);
           list.add(500);
           list.add(600);
           list.add(700);
           list.add(800);
           list.add(900);
           list.add(1000);

       // System.out.println(list.get(9));

        //list.set(1,8888);

        list.remove(9);

        System.out.println("-----------------数组的遍历--------------------");

        for (int i = 0; i <list.size(); i++) {

            System.out.print(list.get(i)+"\t");
        }
      
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鲸叫我照顾大海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值