Java链表的使用

Java链表的使用

前言

说明:

  • 语言:Java
  • 环境:IntelliJ IDEA
  • JDK版本:1.8
  • 源码:GitHub
  • 链表的插入、查询、排序通常涉及算法,本文重点是探究链表,并非算法,因此代码只是以最通俗易懂的方式编写

在学习Java链表的使用之前,需要先了解Java引用类型的使用

int a = 10;
int b = a;
b++;
System.out.println(a);

上面这段代码的运行结果为:10。在int b = a;这个操作中,实际是将a的值赋给b,所以b++;不会影响a的值,这是基本数据类型的特性。

//Employee是描述员工信息的实体类
Employee employee1 = new Employee(1,"xiaoming",30);
Employee employee2 = employee1;
employee2.seteName("liumei");
System.out.println(employee1.toString());

上面这段代码的运行结果为:Employee{eId=1, eName='liumei', eAge=30}。这就说明,在Employee employee2 = employee1;操作中,使得employee1和employee2共享相同的数据,因此两个变量任意一个改变数据,都会影响对方。

实际上,new是在内存中划分一个空间来存储数据,而接收new的employee1变量只是一个指针,指向这个内存空间,当把这个变量赋给变量employee2时,employee2也将是指向和employee1相同内存空间的指针,因此操作这两个变量,实际上都是对同一内存空间的操作。

单链表

Java链表的使用_第1张图片

特点:

  • 拥有next指针,指向下一个节点
  • 只能单向的从头结点为起点操作链表
  • 在链表首部插入节点效率高,在链表尾部插入节点效率低

插入节点:

     /**
     * 在链表尾部插入数据
     */
    public boolean addLast(Employee employee){
        Node node = new Node(employee); //创建要插入的节点
        Node temp = this.head;  //创建辅助指针指向head节点
        while (true){   //移动辅助指针到当前链表最后一个节点
            if(temp.next==null){break;}
            temp = temp.next;
        }
        temp.next = node;   //将要插入的节点插入到链表尾部
        this.length++;
        return true;
    }

    /**
     * 在链表首部插入数据
     */
    public boolean addFirst(Employee employee){
        Node node = new Node(employee); //创建要插入的节点
        Node temp = this.head;  //创建辅助指针指向head节点
        if(isEmpty()){    //链表为空情况
            this.head.next = node;  //直接在head后插入节点
        }else{  //链表不为空情况
            temp = temp.next;   //移动temp到第一个有效节点
            this.head.next = node;  //将要插入节点插入到head节点后
            node.next = temp;   //将其他节点连接到刚插入的节点上
        }
        this.length++;
        return true;
    }

    /**
     * 在index节点之后插入节点,索引从0开始
     */
    public boolean addIndex(Employee employee,int index){
        if(isEmpty()||index>=this.length||index<0){ return false;}//空链表、索引格式范围不正确则插入失败
        Node node = new Node(employee); //创建要插入的节点
        Node temp = this.head;  //创建辅助指针指向head节点
        for (int i = 0;i<=index;i++){   //将辅助指针移动到目标节点
            temp = temp.next;
        }
        node.next = temp.next;  //让新节点的next指向目标节点之后的节点
        temp.next = node;   //让辅助指针指向新节点
        this.length++;
        return true;
    }

删除节点:

     /**
     * 删除最后一个节点
     */
    public boolean deleteLast(){
        if(isEmpty()){return false;}
        Node temp = this.head;  //创建辅助指针指向head节点
        while (true){   //移动辅助指针到当前链表最后一个节点的前一个节点
            if(temp.next.next==null){break;}
            temp = temp.next;
        }
        temp.next = null;   //断开与最后一个节点的的连接
        this.length--;
        return true;
    }

    /**
     * 删除第一个节点
     */
    public boolean deleteFirst(){
        if(isEmpty()){return false;}
        Node temp = this.head.next;  //创建辅助指针指向第一个有效节点
        this.head.next = temp.next; //将头节点连接到第一个有效节点之后的节点
        this.length--;
        return true;
    }

    /**
     * 删除指定索引节点,索引从0开始
     */
    public boolean deleteIndex(int index){
        if(isEmpty()||index>=this.length||index<0){ return false;}//空链表、索引格式范围不正确则删除失败
        Node temp = this.head;  //创建辅助指针指向头结点
        for (int i = 0;i

查询节点:

    /**
     * 查看index位置的数据,索引从0开始
     */
    public Employee getEmployee(int index){
        if(isEmpty()||index>=this.length||index<0){ return null;}//空链表、索引格式范围不正确则查询失败
        Node temp = this.head;  //创建辅助指针指向头结点
        for (int i = 0;i<=index;i++){    //将辅助指针移动到目标节点
            temp = temp.next;
        }
        return temp.data;   //返回数据
    }

修改节点:与查询类似,需要定制规则

双向链表

Java链表的使用_第2张图片

特点:

  • 拥有next指针和previous指针,next指针指向下一个节点,previous指针指向前一个节点
  • 允许双向操作链表
  • 双向链表通常拥有头结点和尾结点,这样从双端插入数据效率都很高
  • 由于允许双向操作链表,所以查询效率高于单链表

插入节点:

    /**
     * 在链表尾部插入数据
     */
    public boolean addLast(Employee employee){
        Node node = new Node(employee); //创建要插入的节点
        if(isEmpty()){  //如果链表为空
            this.head.next = node;  //将head和last节点连接到node
            this.last.pre = node;
            node.next = this.last;  //将node的next和pre连接到last和head
            node.pre = this.head;
        }else{  //如果链表不为空(注意顺序)
            node.next = this.last;  //将node连接到last和当前最后一个节点
            node.pre = this.last.pre;
            this.last.pre.next = node;  //将last和当前最后一个节点连接到node
            this.last.pre = node;
        }
        this.length++;
        return true;
    }

    /**
     * 在链表首部插入数据
     */
    public boolean addFirst(Employee employee){
        Node node = new Node(employee); //创建要插入的节点
        if(isEmpty()){  //如果链表为空
            this.head.next = node;  //将head和last节点连接到node
            this.last.pre = node;
            node.next = this.last;  //将node的next和pre连接到last和head
            node.pre = this.head;
        }else{  //如果链表不为空(注意顺序)
            node.next = this.head.next;  //将node连接到head和当前第一个节点
            node.pre = this.head;
            this.head.next.pre = node;  //将head和当前第一个节点连接到node
            this.head.next = node;
        }
        this.length++;
        return true;
    }

    /**
     * 在index节点之后插入节点,索引从0开始
     */
    public boolean addIndex(Employee employee,int index){
        if(isEmpty()||index>=this.length||index<0){return false;}//空链表、索引格式范围不正确则插入失败
        Node node = new Node(employee);;
        Node temp;
        if((index+1)<=this.length/2){   //如果要插入的位置小于链表总长度一半,则在前半段插入
            temp = this.head;
            for (int i = 0;i<=index;i++){
                temp = temp.next;
            }
            node.next = temp.next;
            node.pre = temp;
            temp.next.pre = node;
            temp.next = node;
        }else{  //如果要插入的位置大于链表总长度的一半,则在后半段插入
            temp = this.last;
            for (int i = 0;i<=this.length-(index+1);i++){
                temp = temp.pre;
            }
            node.next = temp.next;
            node.pre = temp;
            temp.next.pre = node;
            temp.next = node;
        }
        this.length++;
        return true;
    }

删除节点:

    /**
     * 删除最后一个节点
     */
    public boolean deleteLast(){
        if(isEmpty()){return false;}
        this.last.pre.pre.next = this.last; //将最后一个节点的前一个节点的next指向last
        this.last.pre = this.last.pre.pre;//将last的pre指向最后一个节点的前一个节点
        this.length--;
        return true;
    }

    /**
     * 删除第一个节点
     */
    public boolean deleteFirst(){
        if(isEmpty()){return false;}
        this.head.next.next.pre = this.head;//将第一个节点的后一个节点的pre指向head
        this.head.next = this.head.next.next;//将head的next指向第一个节点的后一个节点
        this.length--;
        return true;
    }

    /**
     * 删除指定索引节点,索引从0开始
     */
    public boolean deleteIndex(int index){
        if(isEmpty()||index>=this.length||index<0){return false;}//空链表、索引格式范围不正确则删除失败
        Node temp;
        if((index+1)<=this.length/2){   //如果要插入的位置小于链表总长度一半,则在前半段删除
            temp = this.head;
            for (int i = 0;i<=index;i++){
                temp = temp.next;
            }
            temp.pre.next = temp.next;
            temp.next.pre = temp.pre;
        }else{  //如果要插入的位置大于链表总长度的一半,则在后半段删除
            temp = this.last;
            for (int i = 0;i<=this.length-(index+1);i++){
                temp = temp.pre;
            }
            temp.pre.next = temp.next;
            temp.next.pre = temp.pre;
        }
        this.length--;
        return true;
    }

查找节点:

    /**
     * 查看index位置的数据,索引从0开始
     */
    public Employee getEmployee(int index){
        if(isEmpty()||index>=this.length||index<0){return null;}//空链表、索引格式范围不正确则删除失败
        Node temp;
        if((index+1)<=this.length/2){   //如果要插入的位置小于链表总长度一半,则在前半段查找
            temp = this.head;
            for (int i = 0;i<=index;i++){
                temp = temp.next;
            }
            return temp.data;
        }else{  //如果要插入的位置大于链表总长度的一半,则在后半段查找
            temp = this.last;
            for (int i = 0;i<=this.length-(index+1);i++){
                temp = temp.pre;
            }
            return temp.data;
        }
    }

修改节点:与查询类似,需要定制规则

循环链表

Java链表的使用_第3张图片

特点:

  • 拥有next指针,指向下一个节点
  • 链表的最后一个节点的next指向第一个节点,形成循环
  • 为了实现环的完整性,循环链表通常不需要头节点
  • 只能进行一个方向的循环

循环链表的增删改查需要根据需求确定,普通实现可类比将最后一个节点的next连接到首节点的单链表。实际上需要根据需求定制在环的什么位置插入,在什么位置删除,以及判定循环一圈和循环终止的条件

双向循环链表

Java链表的使用_第4张图片

特点:

  • 拥有next指针和previous指针,next指针指向下一个节点,previous指针指向前一个节点
  • 链表的最后一个节点的next指向第一个节点,第一个节点的previous指正指向最后一个节点,形成循环
  • 为了实现环的完整性,循环链表通常不需要头节点
  • 可以进行双向的循环

双向循环链表的增删改查需要根据需求确定,普通实现可类比将最后一个节点的next连接到首节点,首节点的previous连接在最后一个节点的双向链表。实际上需要根据需求定制在环的什么位置插入,在什么位置删除,以及判定循环一圈和循环终止的条件

转载于:https://my.oschina.net/rawlins/blog/3102322

你可能感兴趣的:(Java链表的使用)