用C实现链表是使用了指针,但是JAVA并没有指针这个名词。指针究竟是什么?链表的指针又代表了什么含义?这就是解题的具体思路。 在java中,用引用来代替指针的功能,不过 区别就是指针在指向目标地址的同时本身也占有内存,而引用就是单纯的指向一块内存。

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域(储存的对象),另一个是存储下一个结点地址的指针域(对下一个节点的引用)。 相比于线性表顺序结构,链表比较方便插入和删除操作。贴一张图大家可能更容易理解

          

Java 邻接链表 java链表详解_java

数组的特点是:寻址容易,插入和删除困难;
链表的特点是:寻址困难,插入和删除容易。

从逻辑结构来看:
数组:数组必须事先定义固定的长度(元素个数),不能动态地增减数据。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费;
           数组可以根据下标直接存取。
           数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);
链表:链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。
            链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但查找的时间复杂度很大,达O(N)。

从内存存储来看:
(静态)数组从栈中分配空间, 对于程序员方便快速,但是自由度小。
链表从堆中分配空间, 自由度大但是申请管理比较麻烦 。

如果需要快速访问数据,很少或不插入和删除元素,就应该用数组;相反, 如果需要经常插入和删除元素就需要用链表数据结构了。

一、单向链表

单向链表的结点的数据都是一样,代码为

public class Node {
    Object data;
    Node next;

    public Node(Object obj) {
        this.data = obj;
    }
}



1.1、单链表

Java 邻接链表 java链表详解_数据结构_02

insertHead:在表头插入一个新的链接点,时间复杂度为O(1)

deleteHead:删除表头的链接点,时间复杂度为O(1)有了这两个方法,就可以用单链表来实现一个栈

public class LinkedList {
    public Node head; //定义头结点
    private int pos = 0; //节点的位置

    public LinkedList() {
        this.head = null;
    }

    /**
     * 添加一个头结点
     */
    public void insertHead(Object data) {
        Node node = new Node(data);
        node.next = head;
        head = node;
    }

    /**
     * 在任意位置插入节点:在index的后面插入
     */
    public void insertByIndex(int index, int data) {
        Node node = new Node(data);
        Node current = head;
        Node previous = head;  //以前的

        while (pos != index) {
            previous = current;
            current = current.next;
            pos++;
        }

        node.next = current;
        previous.next = node;
        pos = 0;
    }

    /**
     * 删除一个头结点
     */
    public Object deleteHead() throws Exception {
        if(head == null)
            throw new Exception("LinkedList is empty!");

        Node tempNode = head;
        head = tempNode.next;
        return tempNode.data;
    }

    /**
     * 删除任意位置的节点
     */
    public Object deleteByPos(int index) throws Exception {
        if(head == null)
            throw new Exception("LinkedList is empty!");

        Node current = head;
        Node previous = head;
        while (pos != index) {
            previous = current;
            current = current.next;
            pos++;
        }
        pos = 0;
        if (current == head) {
            head = head.next;
        } else {
            previous.next = current.next;
        }

        return current.data;
    }

    /**
     * 根据节点的data删除节点(仅仅删除第一个)
     */
    public Object deleteByData(Object data) throws Exception {
        if(head == null)
            throw new Exception("LinkedList is empty!");

        Node current = head;
        Node previous = head;  //记住上一个节点

        while (!current.data.equals(data)) {
            if (current.next == null) {
                return null;
            }
            previous = current;
            current = current.next;
        }

        if (current == head) {
            head = head.next;
        } else {
            previous.next = current.next;
        }

        return current.data;
    }

    /**
     * 根据位置查找节点信息
     */
    public Object findByPos(int index) throws Exception {
        if(head == null)
            throw new Exception("LinkedList is empty!");

        Node current = head;
        if (pos != index) {
            current = current.next;
            pos++;
        }
        pos = 0;
        return current.data;
    }

    /**
     * 根据数据查找节点信息
     */
    public int findByData(Object data) throws Exception {
        if(head == null)
            throw new Exception("LinkedList is empty!");

        Node current = head;
        while (!current.data.equals(data)) {
            if (current.next == null) {
                throw new Exception("not this value!");
            }
            current = current.next;
            pos++;
        }
        return pos;
    }

    /**
     * 非递归打印元素的方法
     */
    public void display(){
        if(head == null)
            System.out.println("empty");
        Node cur = head;
        while(cur != null){
            System.out.print(cur.data.toString() + " -> ");
            cur = cur.next;
        }
        System.out.print("\n");
    }

}

测试:

@Test
    public void test() throws Exception {
        LinkedList ll = new LinkedList();
        ll.insertHead(4);
        ll.insertHead(3);
        ll.insertHead(2);
        ll.insertHead(1);
        ll.insertByIndex(2,5);
        ll.display();

        ll.deleteHead();
        ll.display();

        ll.deleteByData(3);
        ll.display();

        ll.deleteByPos(2);
        ll.display();
        System.out.println(ll.findByPos(0));
        System.out.println(ll.findByData(5));
    }

1 -> 2 -> 5 -> 3 -> 4 ->


2 -> 5 -> 3 -> 4 ->


2 -> 5 -> 4 ->


2 -> 5 ->


2


1


1.2、双端链表(不是双向链表)

与单向链表的不同之处在保存有对最后一个链接点的引用(last)

Java 邻接链表 java链表详解_链表_03

insertHead:在表头插入一个新的链接点,时间复杂度O(1)

insertRear:在表尾插入一个新的链接点,时间复杂度O(1)

deleteHead:删除表头的链接点,时间复杂度O(1)

deleteRear::删除表尾的链接点,由于只保存了表尾的链接点,没有保存表尾的前一个链接点(这里体现出双向链表的优势),所以在删除表尾链接点时需要遍历以找到表尾链接点的前一个链接点,查找N-1次,也就是O(N)

有了这insertRear和deleteHead就可以用双端链表来实现一个队列

public class FirstLastList {
    private Node head;
    private Node rear;

    public FirstLastList() {
        head = null;
        rear = null;
    }

    public void insertHead(Object obj) {
        Node node  = new Node(obj);
        if (head == null) {
            rear = node; //这里要想清楚
        }
        node.next = head;
        head = node;
    }

    public void insertRear(Object obj) {
        Node node = new Node(obj);
        if (head == null) {
            head = node; //这里要想清楚

        } else {
            //这里要想清楚
            rear.next = node;
        }

        rear = node;
    }

    public Object deleteHead() throws Exception {
        if (head == null)
            throw new Exception("empty");

        if (head.next == null)
            rear = null;

        Node temp = head;
        head = head.next;
        return temp.data;
    }

    public Object deleteRear() throws Exception {
        if (head == null)
            throw new Exception("empty");

        Node temp = head;
        if (head.next == null){
            head = null;
            rear = null;

        } else {
            while(temp.next != null){
                if(temp.next == rear){
                    rear = temp;
                    rear.next = null;
                    break;
                }
                temp = temp.next;
            }
        }

        return temp.data;
    }
    
    public void display() {
        if (head == null) {
            System.out.println("empty!");
        }

        Node cur = head;
        while (cur != null) {
            System.out.print(cur.data.toString() + " -> ");
            cur = cur.next;
        }
        System.out.print("\n");
    }
}

测试:

@Test
    public void test2() throws Exception {
        FirstLastList fll = new FirstLastList();
        fll.insertHead(1);
        fll.insertHead(2);
        fll.display();
        fll.insertRear(3);
        fll.display();
        fll.deleteHead();
        fll.display();
        fll.deleteRear();
        fll.display();
    }

2 -> 1 ->


2 -> 1 -> 3 ->


1 -> 3 ->


1 ->


1.3、有序链表

链表中的数据按从小到大排列。

表的插入和删除平均需要比较N/2次,即O(N),但是获取最小数据项只需O(1),因为其始终处于表头,对频繁操作最小数据项的应用,可以考虑使用有序链表实现,如:优先级队列 

public class SortedList {
    private Node head;

    public SortedList() {
        head = null;
    }

    public void insertHead(Object obj){
        Node node = new Node(obj);
        Node pre = null;
        Node cur = head;

        //循环结束得到的cur结点的值是比要插入的大
        while(cur != null &&
                (Integer.valueOf(node.data.toString()) > Integer.valueOf(cur.data.toString()))){
            pre = cur;
            cur = cur.next;
        }
        if(pre == null)
            head = node;
        else
            pre.next = node;

        node.next = cur;
    }
    public void display() { 
      //方法体同单链表的方法 
    } 
    public Object deleteHead() { 
       //方法体同单链表的方法 
    }
}




测试:

@Test
    public void test3(){
        SortedList sl = new SortedList();
        sl.insertHead(80);
        sl.insertHead(2);
        sl.insertHead(100);
        sl.display();
        System.out.println(sl.deleteHead());
        sl.insertHead(33);
        sl.display();
        sl.insertHead(99);
        sl.display();
    }

2 -> 80 -> 100 ->


2


33 -> 80 -> 100 ->


33 -> 80 -> 99 -> 100 ->


二、双向链表

Java 邻接链表 java链表详解_Java 邻接链表_04

双向链表的结点的数据结构为:

public class Node {
    public Node prior;  //前结点指针
    public Object value;  //结点值
    public Node next;     //后结点指针

    public Node(Object value) {
        this.value = value;
    }
}

具体实现:

public class DoublyLinkList {
    private Node head;  //链表头
    private Node rear;  //链表尾

    public DoublyLinkList(){
        head = null;
        rear = null;
    }

    public void insertHead(Object obj){
        Node node = new Node(obj);

        if(head == null){
            rear = node;
        }else{
            node.next = head;
            head.prior = node;
        }
        head = node;
    }

    public void insertRear(Object obj){
        Node node = new Node(obj);

        if(head == null){
            head = node;
        }else{
            rear.next = node;
            node.prior = rear;
        }
        rear = node;
    }

    public boolean insertAfter(Object target,Object obj){
        Node node = new Node(obj);
        Node cur = head;
        while(cur != null){
            if(cur.value.equals(target)){
                node.next = cur.next;
                node.prior = cur;
                if(cur == rear)
                    rear = node;
                else
                    cur.next.prior = node;
                cur.next = node;
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    public Object deleteHead() throws Exception{
        if(head == null)
            throw new Exception("empty!");
        Node temp = head;
        if(head.next == null){
            head = null;
            rear = null;
        }else{
            head.next.prior = null;
            head = head.next;
        }
        return temp;
    }

    public Object deleteRear() throws Exception{
        if(head == null)
            throw new Exception("empty!");
        Node temp = rear;
        if(head.next == null){
            head = null;
            rear = null;
        }else{
            rear.prior.next = null;
            rear = rear.prior;
        }
        return temp;
    }

    public Object delete(Object obj) throws Exception{
        if(head == null)
            throw new Exception("empty!");
        Node cur = head;
        while(cur != null){
            if(cur.value.equals(obj)){
                if(cur == rear)
                    rear = cur.prior;
                else
                    cur.next.prior = cur.prior;
                if(cur == head)
                    head = cur.next;
                else
                    cur.prior.next = cur.next;
                return obj;
            }
            cur = cur.next;
        }
        return null;
    }

    public void display(){
        System.out.print("head -> rear : ");
        Node node = head;
        while(node != null){
            System.out.print(node.value.toString() + " -> ");
            node = node.next;
        }
        System.out.print("\n");
    }
}

测试:

@Test
    public void test() throws Exception {
        DoublyLinkList dll = new DoublyLinkList();
        dll.insertHead(1);
        dll.insertRear(3);
        dll.display();
        dll.insertAfter(1, 2);
        dll.insertAfter(3, 4);
        dll.display();

        dll.deleteHead();
        dll.display();
        dll.deleteRear();
        dll.display();
        dll.delete(3);
        dll.display();

    }

head -> rear : 1 -> 3 ->


head -> rear : 1 -> 2 -> 3 -> 4 ->


head -> rear : 2 -> 3 -> 4 ->


head -> rear : 2 -> 3 ->


head -> rear : 2 ->