1、链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

那么为什么需要链表呢?
相比数组而言:无序数组查找效率低,有序数组插入效率低,且数组创建后,大小固定,扩容相对麻烦,数组创建过大则浪费空间。
链表则相对改进了数组的缺点,充分利用了内存空间,但其付出的代价为:链表必须按序一个一个往下查找,意味着其查询效率并不是那么高,因而链表不适合于频繁查询的场景。

2、单链表

java链表题怎么定义节点_头结点


单链表为链表里最简单的结构,每个节点包括一个数据域,一个指向下一个节点的指针域(虽然Java中不存在指针说法,其next域存放的为下一个对象的地址)。

2.1不带头结点的单链表

即意味着每个链表第一个节点就开始存储数据,当第一个节点不存在的时候,链表也不存在,插入时需要注意插入的位置,相对于有节点的单链表,更容易产生BUG。

2.2带头结点的单链表

带头街点的单链表,以付出一个头结点内存的代价,可以相对简化相关头结点的插入删除等操作。

首先我们定义一个单链表节点:

class Node{
        int val;
        Node next;
        Node(int val){
        this.val = val;
        }
    }

单链表完成的基本功能

/**
 * get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
 * addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
 * addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
 * addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。
 * deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
 */
class MyLinkedList {
    class Node{
        int val;
        Node next;
        Node(int val){
            this.val = val;
        }
    }

    Node head = new Node(0);
    int size = 0;
    /** Initialize your data structure here. */
    public MyLinkedList() {

    }

    /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
    public int get(int index) {
        Node temp = head;
        if(index <0 || index >=size){
            return  -1;
        }else{
            for(int i = 0;i<=index;i++){
                temp = temp.next;
            }
            return temp.val;
        }
    }

    /** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
    public void addAtHead(int val) {
        Node node = new Node(val);
        node.next = head.next;
        head.next = node;
        size++;
    }

    /** Append a node of value val to the last element of the linked list. */
    public void addAtTail(int val) {
        Node node = new Node(val);
        Node temp = head;
        while(temp.next != null){
            temp = temp.next;
        }
        temp.next = node;
        size++;
    }

    /** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
    public void addAtIndex(int index, int val) {
        Node temp = head;
        Node node = new Node(val);
        if(index <0 || index >size){
            return ;
        }
        if(index == size ){
            while(temp.next != null) temp = temp.next;
            temp.next = node;
            size++;
            return ;
        }
        for(int i = 0;i <index;i++ ){
            temp = temp.next;
        }

        node.next = temp.next;
        temp.next = node;
        size++;
    }

    /** Delete the index-th node in the linked list, if the index is valid. */
    public void deleteAtIndex(int index) {
        Node temp = head;
        if(index <0 || index >=size){
            return ;
        }
        for(int i = 0;i < index;i++ ){
            temp = temp.next;
        }
        if(temp != null)
            temp.next = temp.next.next;
       	    size--;
    }
    /**依次遍历单链表**/
    public void show(){
        Node temp = head;
        while(temp.next != null){
            System.out.print(temp.next.val + " ");
            temp = temp.next;
        }
    }
}

单链表操作中需要使用到一个temp的中间节点来操作,否则head结点往后移动之后,head就不再为头结点了!

3、双向链表

单链表中数据访问方向是唯一的,为了改进这样的缺点,就产生了双向链表。

java链表题怎么定义节点_数据结构_02


相比于单链表,双向链表只是多了一个指向前节点的指针

class Node{
        int val;
        Node prev;
        Node next;
        Node(int val){
            this.val = val;
        }
    }

同样参照单链表功能,双向链表实现如下:

package DateStructure.list;


/**
 * get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
 * addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
 * addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
 * addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。
 * deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
 */
class MyTwoLinkedList {
    class Node{
        int val;
        Node prev;
        Node next;
        Node(int val){
            this.val = val;
        }
    }

    Node head = new Node(0);
    int size = 0;
    /** Initialize your data structure here. */
    public MyTwoLinkedList() {

    }

    /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
    public int get(int index) {
        if(index < 0 || index >= size){
            return -1;
        }
        Node temp = head;
        while(index >= 0){
            temp = temp.next;
            index--;
        }
        if(temp != null){
            return temp.val;
        }else {
            return -1;
        }

    }

    /** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
    public void addAtHead(int val) {
        Node node = new Node(val);
        node.next = head.next;
        head.next = node;
        size++;
    }

    /** Append a node of value val to the last element of the linked list. */
    public void addAtTail(int val) {
        Node node = new Node(val);
        Node temp = head;
        while(temp.next != null){
            temp = temp.next;
        }
        temp.next = node;
        node.prev = temp;
        size++;
    }

    /** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
    public void addAtIndex(int index, int val) {
        Node temp = head;
        Node node = new Node(val);
        if(index <0 || index >size){
            return ;
        }
        if(index == size ){
            while(temp.next != null) temp = temp.next;
            temp.next = node;
            size++;
            return ;
        }
        for(int i = 0;i <index;i++ ){
            temp = temp.next;
        }

        node.next = temp.next;
        node.prev = temp;
        temp.next = node;
        node.next.prev = node;
        size++;
    }

    /** Delete the index-th node in the linked list, if the index is valid. */
    public void deleteAtIndex(int index) {
        Node temp = head;
        if(index <0 || index >=size){
            return ;
        }
        for(int i = 0;i < index;i++ ){
            temp = temp.next;
        }
        if(temp != null && temp.next.next != null) {
            //需要先操作prev指针
            temp.next.next.prev = temp;
            temp.next = temp.next.next;
        }else if(temp != null && temp.next.next == null){
            //temp.next.next不存在则说明要删除的为表尾
            temp.next = null;
        }
        size--;
    }
    public void show(){
        Node temp = head;
        while(temp.next != null){
            System.out.print(temp.next.val + " ");
            temp = temp.next;
        }
    }

}

4、总结

以上为单链表和双向链表的简单实现,其不同之处只在于节点指针。当然链表中还有循环链表(尾节点指向头结点)、双端链表(记录头结点的同时还记录尾节点),可以针对不同的需要选择使用。利用链表,可以让我们更高效率进行插入和删除操作。Java集合中,LinkedList即采用了双向链表实现。