链表基础

142 阅读2分钟

1、基本概念

核心:一个结点只能有一个后继,但不代表一个结点只能有一个被指向。

节点:每个点都由值和指向下一个结点的地址组成的独立的单元,称为一个节点,链表一般由头节点与一般节点组成。

虚拟节点:

虚拟节点:

dummyNode,其next指针指向头节点,也就是dummyNode.next=head。

因此,如果我们在算法里使用了虚拟结点,则要注意如果要获得head结点,或者从方法(函数)里返回的时候,则应使用dummyNode.next。

dummyNode的val不会被使用,初始化为0或者-1等都是可以的。

虚拟节点存在的作用为了方便我们处理首部结点,否则我们需要在代码里单独处理首部节点的问题。在链表反转里,我们会看到该方式可以大大降低解题难度。

2、链表的创建

public class ListNode {

    public int val;

    public ListNode next;

    public ListNode(int val) {
        this.val = val;
        this.next = null;
    }
}

3、链表的遍历

注意:操作之后必须要能找到表头,不能只顾当前位置而将丢掉了标记表头的指针

public static int getListLength(ListNode head) {
    int length = 0;
    ListNode node = head;
    while(node != null){
        length++;
        node = node.next;
    }
    return length;
}

4、链表插入

1、链表头插入

注意:要将head指向链首元素

2、链表中间插入

我们应该找到目标节点的前驱节点,利用listNode.next来判断而不是listNode。否则我们将找不到前驱节点。

如果要在7的前面插入,当listNode.next=node(7)了就应该停下来,此时listNode.val=15。然后需要给

new前后接两根线,时只能先让new.next=node(15).next(图中虚线),然后node(15).next=new,而且顺序还不能错。

3、链表结尾插入(一般与链表中间插入一块写)

普通链表的插入

/**
 * 链表插入
 * @param head 头节点
 * @param nodeInsert 待插入的节点
 * @param position 待插入的位置,从1开始
 * @return 插入后得到的链表的头节点
 */
public static ListNode insertNode(ListNode head,ListNode nodeInsert,int position){
    if(head == null){
        //插入的节点就是链表的头节点
        return nodeInsert;
    }
    int size = getListLength(head);
    if(position > size + 1 || position < 1){
        System.out.println("参数越界");
        return head;
    }

    //链表头插入
    if(position == 1){
        nodeInsert.next = head;
        head = nodeInsert;
        return head;
    }

    //链表中间或者尾部插入
    int count = 1;
    ListNode node = head;
    while (count < position - 1){
        count ++;
        node = node.next;
    }
    nodeInsert.next = node.next;
    node.next = nodeInsert;

    return head;
}

有序链表的插入

/**
 * 有序链表的插入
 *
 * @param head       头节点
 * @param nodeInsert 待插入的节点
 * @return
 */
public static ListNode insertOrderedNode(ListNode head, ListNode nodeInsert) {
    if (head == null) {
        //插入的节点就是链表的头节点
        return nodeInsert;
    }

    //链表头插入
    if (head.val > nodeInsert.val) {
        nodeInsert.next = head;
        head = nodeInsert;
    }

    //链表中间或者尾部插入
    ListNode node = head;
    while (node.next != null && node.next.val < nodeInsert.val) {
        node = node.next;
    }

    nodeInsert.next = node.next;
    node.next = nodeInsert;

    return head;
}

5、链表的删除

1、删除链表头

直接head=head.next即可

2、删除链最后一个节点

找到要删除的节点的前驱节点,这里同样要在提前一个位置判断。

如需删除40,其前驱节点为7。遍历的时候需要判断cur.next是否为40,如果是,则只要执行cur.next=null即可。

3、删除中间节点

也需要提前一个位置判断

cur.next=cur.next.next

/**
 * 删除节点
 * @param head 链表头节点
 * @param position 删除节点的位置,取值从1开始
 * @return 删除后的链表的头节点
 */
public static ListNode deleteNode(ListNode head,int position){
    if(head == null){
        return null;
    }
    int size = getListLength(head);
    if(position > size || position < 1){
        System.out.println("参数越界");
        return head;
    }
    if(position == 1){
        head = head.next;
        return head;
    }

    int count = 1;
    ListNode node = head;
    while (count < position - 1) {
        count++;
        node = node.next;
    }
    node.next = node.next.next;

    return head;
}