《通关村第一关--链表青铜》

文章目录


一、链表是什么?

链表在物理上是非连续,非顺序的数据结构,由若干节点组成,每一个节点指向下一个节点(有且只有一个后续节点)。单向链表中每一个节点由数据和指向下一节点的指针next组成。

虚拟节点 

        虚拟节点通常位于链表的头部,作为一个占位符,起到辅助作用。

        在链表插入、删除等操作中,虚拟节点可以作为目标位置的前一个节点,使得代码逻辑更加统一。在插入新节点时,不需要单独处理链表为空或只有一个节点的情况,而是统一处理所有位置的插入操作。

        对于链表的搜索和遍历,虚拟节点也可以提供便利。它可以作为循环的起点,避免对链表为空的情况做特殊处理。

二、创建链表

1.创建节点

代码如下(示例):

        节点中包括存储的数据

        以及指向下一节点的指针

    /**
     * 节点
     */
    class Node{
        // 存放的数据
        int data;
        // 下一节点
        Node next;
        // 构造函数
        public Node(int data) {
            this.data = data;
            this.next = null;
        }
    }

 节点之间指向的示意图

 

2.链表的遍历

代码如下(示例):

    /**
     * 遍历链表的长度
     * @param head
     */
    public static int getListLength(Node head){
        int size = 0;
        Node temp = head;
        // 遍历链表
        while (temp != null){
            temp = temp.next;
            size ++;
        }
        return size;
    }

该处使用“中间"节点temp获取指向首节点的指针,进行遍历。

3.链表的初始化

代码如下(示例):

    /**
     * 初始化链表
     * @param arrays
     * @return
     */
    public static Node initLink(List<Integer> arrays){
        // 首节点
        Node head = null;
        // 后续节点
        Node cur = null;
        for (int i = 0; i < arrays.size(); i++) {
            // 创建当前节点
            Node newNode = new Node(arrays.get(i));
            if (i == 0){
                head = newNode;
                cur = newNode;
            }else {
                // 后续节点往后插入新节点
                cur.next = newNode;
                cur = cur.next;
            }
        }
        // 返回首节点
        return head;
    }

创建了两个节点,head节点用来固定首节点的位置用来返回。cur节点用来在后续新增新节点。有了head节点不用担心cur插入新节点找不到首节点的引用。

4.插入节点

        (1)插入首节点:将新节点newNode的next指向首节点head。再将首节点head的指针指向新节点newNode。

        (2)插入尾节点:将尾节点cur的next指向新节点newNode,再将cur的指针指向新节点newNode。

        (3)插入中间节点:遍历到当前要插入位置的前一个结点lastNode。将lastNode的next赋予新节点newNode的next。再将lastNode的next指向newNode新节点。(顺序一定不能颠倒,颠倒了会丢失原链表的后续节点)

代码如下(示例):  

    /**
     * 插入节点
     * @param head 首节点
     * @param data 插入数据
     * @param index 插入位置
     * @return
     */
    public static Node insertNode(Node head , int data , int index){
        // 创建新节点
        Node newNode = new Node(data);
        // 判断当前链表是否为空
        if (head == null){
            return newNode;
        }
        // 遍历当前链表有多少个元素
        int size = getListLength(head);
        // 判断元素是否超出链表范围
        if (index < 1 || index > size + 1){
            System.out.println("超出了当前链表的范围");
            return head;
        }
        // 判断是插入首节点
        if (index == 1){
            newNode.next = head;
            head = newNode;
        }else {
            // 这里不用判断是否插入尾节点
            // 插入中间节点已经包含了这个情况。是去插入节点的前一个在操作。
            Node temp = head;
            // 获取到前一个节点
            while (index > 2){  //找到插入位置的上一节点 等同于 int count = 1; count < index - 1;
                temp = temp.next;
                index --;
            }
            newNode.next = temp.next;
            temp.next = newNode;
        }

        return head;
    }

 使用虚拟节点进行插入

    /**
     * 使用虚拟节点插入新节点
     * @param head 头节点
     * @param data 插入数据
     * @param index 插入位置
     * @return
     */
    public static Node insertNodeDummy(Node head, int data,int index){
        // 创建虚拟节点
        Node dummyNode = new Node(0);
        dummyNode.next = head;
        // 获取当前链表长度
        int size = getListLength(head);
        // 判断链表范围界限
        if ( index < 1 || index > size + 1){
            return head;
        }
        Node newNode = new Node(data);
        Node temp = dummyNode;
        // 插入节点
        while ( index > 1){
            temp = temp.next;
            index --;
        }
        newNode.next = temp.next;
        temp.next = newNode;

        return dummyNode.next;
    }

 5.删除节点

        (1)删除首节点:将指向头节点head的指针指向head的next节点。此时原来的头节点后续会被jvm回收。

        (2)删除尾节点:将指向尾节点cur的指针指向cur的上一节点。将上一节点的next指向null。此时原来的尾节点后续会被jvm回收。

        (3)删除中间节点:找到删除节点的上一节点,将上一节点的next指向next节点的next。如图删除节点7。

代码如下(示例):  

    /**
     * 删除链表
     * @param head 头节点
     * @param index 删除节点的位置
     * @return
     */
    public static Node delNode(Node head,int index){
        Node temp = head;
        // 当前链表为空不操作
        if (head != null){
            // 判断index是否超出链表的范围
            int size = 0 ;
            while (temp != null){
                temp = temp.next;
                size ++;
            }
            if ( index < 1 || index > size){
                System.out.println("超出链表的范围!!!");
            }else {
                // 删除节点
                // 删除首节点
                if (index == 1){
                    head = head.next;
                }else {
                    // 删除中间节点,包含了删除尾节点的情况
                    //定位删除的节点的上一节点
                    temp = head;
                    int count = 1;
                    while ( count < index - 1){ // 找到删除节点的上一节点
                        temp = temp.next;
                        count ++;
                    }
                    temp.next = temp.next.next;
                }
            }
        }
        return head;
    }

使用虚拟节点:

     /**
     * 使用虚拟节点进行删除节点
     * @param head
     * @param index
     * @return
     */
    public static Node delNodeDummy(Node head, int index){
        // 创造虚拟节点
        Node dummyHead = new Node(0);
        dummyHead.next = head;
        // 链表为空不操作
        if (head != null){
            // 判断删除节点的位置是否超出链表范围
            int size = getListLength(head);
            if (index < 1 || index > size){
                System.out.println("超出链表范围不操作!!!");
            }else {
                // 删除中间节点
                // 找到删除节点的上一节点
                Node temp = dummyHead;
                while ( index > 1 ){   // 相当于 int count = 1 ; count < index
                    temp = temp.next;
                    index --;
                }
                // 进行节点删除
                temp.next = temp.next.next;
            }
        }
        return dummyHead.next;
    }

三、全部代码实现

/**
 * 创建链表
 */
public class CreateLink {

    public static void main(String[] args) {
        int[] ints = {1, 2, 3, 4, 5};
        Node node = initLink(ints);
        printNode(node);
        System.out.println("========节点插入=========");
        node = insertNode(node,6,6);
        printNode(node);
        node = insertNode(node,0,1);
        printNode(node);
        node = insertNode(node,9,3);
        printNode(node);

        System.out.println("========使用虚拟节点插入=========");
        // 虚拟节点插入
        node = insertNodeDummy(node,-1,1);
        printNode(node);
        node = insertNodeDummy(node,10,10);
        printNode(node);
        node = insertNodeDummy(node,11,6);
        printNode(node);

        System.out.println("========删除节点=========");
        node = delNode(node, 1);
        printNode(node);
        node = delNode(node, 10);
        printNode(node);
        node = delNode(node, 5);
        printNode(node);

        System.out.println("========使用虚拟节点删除=========");
        node = delNodeDummy(node,3);
        printNode(node);
        node = delNodeDummy(node,1);
        printNode(node);
        node = delNodeDummy(node,6);
        printNode(node);

    }

    /**
     * 插入节点
     * @param head 首节点
     * @param data 插入数据
     * @param index 插入位置
     * @return
     */
    public static Node insertNode(Node head , int data , int index){
        // 创建新节点
        Node newNode = new Node(data);
        // 判断当前链表是否为空
        if (head == null){
            return newNode;
        }
        // 遍历当前链表有多少个元素
        int size = getListLength(head);
        // 判断元素是否超出链表范围
        if (index < 1 || index > size + 1){
            System.out.println("超出了当前链表的范围");
            return head;
        }
        // 判断是插入首节点
        if (index == 1){
            newNode.next = head;
            head = newNode;
        }else {
            // 这里不用判断是否插入尾节点
            // 插入中间节点已经包含了这个情况。是去插入节点的前一个在操作。
            Node temp = head;
            // 获取到前一个节点
            while (index > 2){
                temp = temp.next;
                index --;
            }
            newNode.next = temp.next;
            temp.next = newNode;
        }

        return head;
    }

    /**
     * 使用虚拟节点插入新节点
     * @param head 头节点
     * @param data 插入数据
     * @param index 插入位置
     * @return
     */
    public static Node insertNodeDummy(Node head, int data,int index){
        // 创建虚拟节点
        Node dummyNode = new Node(0);
        dummyNode.next = head;
        // 获取当前链表长度
        int size = getListLength(head);
        // 判断链表范围界限
        if ( index < 1 || index > size + 1){
            return head;
        }
        Node newNode = new Node(data);
        Node temp = dummyNode;
        // 插入节点
        while (index > 1){
            temp = temp.next;
            index --;
        }
        newNode.next = temp.next;
        temp.next = newNode;

        return dummyNode.next;
    }

    /**
     * 删除链表
     * @param head 头节点
     * @param index 删除节点的位置
     * @return
     */
    public static Node delNode(Node head,int index){
        Node temp = head;
        // 当前链表为空不操作
        if (head != null){
            // 判断index是否超出链表的范围
            int size = 0 ;
            while (temp != null){
                temp = temp.next;
                size ++;
            }
            if ( index < 1 || index > size){
                System.out.println("超出链表的范围!!!");
            }else {
                // 删除节点
                // 删除首节点
                if (index == 1){
                    head = head.next;
                }else {
                    // 删除中间节点,包含了删除尾节点的情况
                    //定位删除的节点的上一节点
                    temp = head;
                    int count = 1;
                    while ( count < index - 1){ // 找到删除节点的上一节点
                        temp = temp.next;
                        count ++;
                    }
                    temp.next = temp.next.next;
                }
            }
        }
        return head;
    }

    /**
     * 使用虚拟节点进行删除节点
     * @param head
     * @param index
     * @return
     */
    public static Node delNodeDummy(Node head, int index){
        // 创造虚拟节点
        Node dummyHead = new Node(0);
        dummyHead.next = head;
        // 链表为空不操作
        if (head != null){
            // 判断删除节点的位置是否超出链表范围
            int size = getListLength(head);
            if (index < 1 || index > size){
                System.out.println("超出链表范围不操作!!!");
            }else {
                // 删除中间节点
                // 找到删除节点的上一节点
                Node temp = dummyHead;
                while ( index > 1 ){
                    temp = temp.next;
                    index --;
                }
                // 进行节点删除
                temp.next = temp.next.next;
            }
        }
        return dummyHead.next;
    }

    /**
     * 初始化链表
     * @param arrays
     * @return
     */
    public static Node initLink(int[] arrays){
        // 首节点
        Node head = null;
        // 后续节点
        Node cur = null;
        for (int i = 0; i < arrays.length; i++) {
            // 创建当前节点
            Node newNode = new Node(arrays[i]);
            if (i == 0){
                head = newNode;
                cur = newNode;
            }else {
                // 后续节点往后插入新节点
                cur.next = newNode;
                cur = cur.next;
            }
        }
        // 返回首节点
        return head;
    }

    /**
     * 遍历链表的长度
     * @param head
     */
    public static int getListLength(Node head){
        int size = 0;
        Node temp = head;
        // 遍历链表
        while (temp != null){
            temp = temp.next;
            size ++;
        }
        return size;
    }

    /**
     * 打印链表
     * @param head 首节点
     */
    public static void printNode(Node head){
        Node temp = head;
        StringBuilder str = new StringBuilder();
        while (temp != null){
            str.append(temp.data).append(" --> ");
            temp = temp.next;
        }
        System.out.println(str.toString());

    }


    /**
     * 节点
     */
    static class Node{
        // 存放的数据
        int data;
        // 下一节点
        Node next;

        public Node(int data) {
            this.data = data;
            this.next = null;
        }
    }
}

 


总结

        以上就是今天要讲的内容,本文仅仅简单介绍了普通单链表的创建,新增节点 以及删除节点。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值