Java数据结构:链表

1. 概述

链表是一种数据结构,在内存中通过节点记录内存地址而相互链接形成一条链的储存方式。链表的插入和删除都比较快,缺点是查找比较慢。除非需要频繁的通过下标来随机访问数据,否则在很多使用数组的地方都可以用链表代替。相比数组而言,链表在内存中不需要连续的区域,只需要每一个节点都能够记录下一个节点的内存地址,通过引用进行查找,因此链表增删操作时间消耗很小,而查找遍历时间消耗很大。

2. 链表的分类

链表常用的有 3 类: 单链表、双向链表、循环链表。

单链表:

单链表是链表中结构最简单的,是一种线性表,实际上是由节点(Node)组成的。一个单链表的节点(Node)分为两个部分,第一个部分(data)保存或者显示关于节点的信息,另一个部分存储下一个节点的地址。最后一个节点存储地址的部分指向空值。

单向链表的实现:

package cn.zzw.singlelink;


/**
 * @author 鹭岛猥琐男
 * @create 2019/8/13 5:48
 */
public class SingleLink<T> {
    private Node first;
    private Node last;
    private int size;

    private class Node<T> {
        private Node next;
        private T data;

        Node(T data, Node next) {
            this.data = data;
            this.next = next;
        }
    }

    /*
     *   向链表头部添加元素
     */
    public void push(T t) {
        Node f = first;
        Node newNode = new Node(t, f);
        //因为是向链表头部添加元素,所以第一个节点变成新的节点
        first = newNode;
        //如果原来的第一个节点是空,则原链表为空,添加后,链表仅有一个节点,最后一个节点也是新添加的节点
        if (f == null) {
            last = newNode;
        }
        size++;
    }

    /*
     *  向链表的尾部添加元素
     */
    public void add(T t) {
        Node l = last;
        Node newNode = new Node(t, null);
        last = newNode;
        if (l == null) {
            first = newNode;
        } else {
            l.next = newNode;
        }
        size++;
    }

    /**
     * 打印结点
     */
    public void printLink() {
        Node curNode = first;
        while (curNode != null) {
            System.out.print(curNode.data + " ");
            curNode = curNode.next;
        }
        System.out.println();
    }

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

    /*
     *  删除指定位置的节点
     */
    public void remove(int index) {
        //先判断index是否合法
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
        //当需要删除的是头节点,直接将头节点指向下一个节点
        if (index == 0) {
            Node removeNode = first;
            first = removeNode.next;
        } else {
            Node prev = first;
            for (int i = 0; i < index - 1; i++) {
                //prev此时指向的是被删除节点的前一个位置
                prev = prev.next;
            }
            //需要被删除的节点
            Node removeNode = prev.next;
            prev.next = removeNode.next;
            removeNode.next = null;
        }
        size--;
    }

    /*
     *  获取指定位置的节点的值
     */
    public T get(int index) {
        //先判断index是否合法
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
        Node<T> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x.data;
    }
}

测试类:

package cn.zzw.singlelink;
 
/**
 * @author 鹭岛猥琐男
 * @create 2019/8/13 5:48
 */
public class TestLink
{
    public static void main(String[] args)
    {
        SingleLink<Integer> singleLink =new SingleLink();
        singleLink.add(8);
        singleLink.add(9);
        singleLink.push(1);
        singleLink.push(2);
        System.out.println("链表的长度:"+singleLink.getSize());
        //按照上面的添加顺序,链表的排序应该是:2,1,8,9
        singleLink.printLink();
        //获取指定节点的值
        Integer index0 = singleLink.get(0);
        Integer index1 = singleLink.get(1);
        System.out.println("获取头节点的值:"+index0);
        System.out.println("获取节点1的值:"+index1);
        //删除指定位置的节点
        singleLink.remove(3);
        System.out.println("删除节点后的列表:");
        singleLink.printLink();
        System.out.println("删除后链表的长度:"+singleLink.getSize());
    }
}

测试结果:

链表的长度:4
2 1 8 9 
获取头节点的值:2
获取节点1的值:1
删除节点后的列表:
2 1 8 
删除后链表的长度:3

 

双向链表:

双向链表即是这样一个有序的结点序列,每个节点分为三个部分,一个部分(data)保存或者显示关于节点的信息,另一个部分存储下一个节点的地址,还有一部分存储上一个节点的地址。第一个节点的存储的上一个节点的地址为空值,最后一个节点存储的下一个节点的地址为空值。双向链表是可以从两个方向进行遍历的。

双向链表的实现:

package cn.zzw.doublelink;

public class DoubleLink<E> {
    private Node first;
    private Node last;
    private int size;

    /*
     *   与单向链表相比,多了指向前一个节点的prev
     */
    private class Node<E> {
        private E data;
        private Node<E> prev;
        private Node next;

        Node(E data, Node prev, Node next) {
            this.data = data;
            this.prev = prev;
            this.next = next;
        }
    }

    /*
     *  向链表的尾部添加元素
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }

    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<E>(e, l, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
    }

    /*
     *   向链表头部添加元素
     */
    public void push(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(e, null, f);
        //因为是向链表头部添加元素,所以第一个节点变成新的节点
        first = newNode;
        //如果原来的第一个节点是空,则原链表为空,添加后,链表仅有一个节点,最后一个节点也是新添加的节点
        if (f == null) {
            last = newNode;
        } else {
            f.prev = newNode;
        }
        size++;
    }

    /**
     * 打印结点
     */
    public void printLink() {
        Node curNode = first;
        while (curNode != null) {
            System.out.print(curNode.data + " ");
            curNode = curNode.next;
        }
        System.out.println();
    }

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

    /*
     *  删除指定位置的节点,并返回删除节点的值
     */
    public E remove(int index) {
        //先判断index是否合法
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
        return unlink(node(index));
    }

    /*
     *  查找指定位置的节点
     */
    Node<E> node(int index) {
        if (index < (size >> 1)) {
            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;
        }
    }

    E unlink(Node<E> x) {
        final E element = x.data;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.data = null;
        size--;
        return element;
    }

    /*
     *  获取指定位置的节点的值
     */
    public E get(int index) {
        //先判断index是否合法
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
        return node(index).data;
    }
}

 测试类:

package cn.zzw.doublelink;

import cn.zzw.singlelink.SingleLink;

/**
 * @author 鹭岛猥琐男
 * @create 2019/8/13 5:48
 */
public class TestDoubleLink
{
    public static void main(String[] args)
    {
        DoubleLink<Integer> doubleLink =new DoubleLink();
        doubleLink.add(8);
        doubleLink.add(9);
        doubleLink.push(1);
        doubleLink.push(2);
        System.out.println("链表的长度:"+doubleLink.getSize());
        //按照上面的添加顺序,链表的排序应该是:2,1,8,9
        doubleLink.printLink();
        //获取指定节点的值
        Integer index0 = doubleLink.get(0);
        Integer index1 = doubleLink.get(1);
        System.out.println("获取头节点的值:"+index0);
        System.out.println("获取节点1的值:"+index1);
        //删除指定位置的节点
        doubleLink.remove(3);
        System.out.println("删除节点后的列表:");
        doubleLink.printLink();
        System.out.println("删除后链表的长度:"+doubleLink.getSize());
    }
}

测试结果:

链表的长度:4
2 1 8 9 
获取头节点的值:2
获取节点1的值:1
删除节点后的列表:
2 1 8 
删除后链表的长度:3

Java自带的集合包中有实现双向链表,路径是:java.util.LinkedList,以上双向链表的实现,就是参考它的源码来实现的。

循环链表:

循环链表指的是在单向链表和双向链表的基础上,将两种链表的最后一个结点指向第一个结点从而实现循环。

循环列表分为单向循环链表和双向循环链表,循环链表和单向列表以及双向链表的区别在于,最后一个结点是否指向第一个结点,这里就不再展开了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值