链表?or 双向链表?

本文详细介绍了如何在Java中创建和操作链表,包括单链表和双向链表。首先讲解了栈区和堆区的概念,然后展示了两种创建链表节点的方法。接着,文章阐述了链表的遍历、插入(首部、中部、尾部)和删除(首部、尾部、中部)的操作。对于双向链表,文章特别提到了其结构和如何进行首尾插入、删除以及中间插入和删除。
摘要由CSDN通过智能技术生成

1. 单链表

1.1 创建链表

在JVM中,栈区存放的是引用(这里我理解为指针),堆区存放的才是创建的对象。

链表访问结构

使用Java创建链表结点的方式有两种,一种是根据面向对象思想去创建链表结点,还有一种则是违背面向对象思想,采用一种更精简的方式去创建链表结点

根据面向对象思想去创建链表结点:

public class LinkNode {
    private int data;
    private LinkNode next;

    // 构造方法
    public LinkNode(int data) {
        this.data = data;
    }

    public int getData() {
        return data;
    }
    public void setData(int data) {
        this.data = data;
    }
    public LinkNode getNext() {
        return next;
    }
    public void setNext(LinkNode next) {
        this.next = next;
    }
}

违背面向对象思想,采用一种更精简的方式去创建链表结点:

public class LinkNode {
    public int val;
    public LinkNode next;

    LinkNode(int x) {
        val = x;
        next = null; // 可以不写,写上更规范
    }
}

1.2 遍历链表

对于单链表,一定是从头开始逐个向后访问,所以操作之后一定要确保还能找到表头

/**
 * 获取链表长度
 * @param head 头结点
 * @return 链表长度
 */
public static int getListLength(Node head) {
    int length = 0;
    Node node = head;
    while (node != null) {
        length++;
        node = head.next;
    }
    return length;
}

1.3 链表插入

单链表的插入需要考虑到三种情况:首部、中部、尾部

(1)在链表的首部插入

需要注意从首部插入后要将head指向新插入的结点

在链表的首部插入

(2)在链表的中部插入

需要注意的是遍历时要在插入的前一个位置停下

在链表的中部插入

(3)在链表的尾部插入

只需要将尾结点指向新的结点就可以了。

在链表的尾部插入

(4)编码
/**
 * 链表插入
 * @param head 头结点
 * @param nodeInsert 要插入的结点
 * @param position 要插入的位置
 * @return 新链表的头结点
 */
public static Node insertNode(Node head, Node nodeInsert, int position) {
    // 1. 判断当前链表是否是一个空链表
    if (head == null) {
        return nodeInsert;
    }

    // 2. 判断要插入的位置是否越界
    int size = getListLength(head);
    if (position > size + 1 || position < 1) {
        System.out.println("位置参数越界");
        return head;
    }

    // 3. 插入结点
    // 当插入位置是头部的情况时
    if (position == 1) {
        nodeInsert.next = head;
        // 这里可以直接写 return nodeInsert; 也可以像下面这么写
        head = nodeInsert;
        return head;
    }

    Node pNode = head;
    int count = 1;

    while (count < position - 1) {
        pNode = pNode.next;
        count++;
    }
        
    nodeInsert.next = pNode.next;
    pNode.next = nodeInsert;
        
    return head;
}

1.4 链表删除

同样要考虑到三种情况:首部、中部、尾部

(1)删除表头结点

只要将head向前移动一个就行,JVM会自动回收到不可达的结点。

删除表头结点

(2)删除最后一个结点

删除最后一个结点

(3)删除中间的结点

删除中间的结点

(4)编码
/**
 * 链表删除
 * @param head 头结点
 * @param position 要删除结点的位置
 * @return 新链表的头结点
 */
public static Node deleteNode(Node head, int position) {
    // 1. 判断当前链表是否是一个空链表
    if (head == null) {
        return null;
    }

    // 2. 判断要删除的位置是否越界
    int size = getListLength(head);
    if (position > size || position < 1) {
        System.out.println("位置越界");
        return head;
    }

    // 3. 删除结点
    if (position == 1) {
        return head.next;
    } else {
        Node pNode = head;
        int count = 1;

        while (count < position - 1) {
            pNode = pNode.next;
            count++;
        }
        Node curNode = pNode.next;
        pNode.next = curNode.next;
    }
    return head;
}

2. 双向链表

2.1 基本概念

双向链表

2.2 创建、遍历链表

public class DoubleLink {
    // 双向链表的结构
    private DoubleNode first;
    private DoubleNode last;

    public DoubleLink() {
        first = null;
        last = first;
    }

    /**
     * 定义结点类
     */
    public static class DoubleNode {
        public int data;
        public DoubleNode next;
        public DoubleNode prev;

        public DoubleNode(int data) {
            this.data = data;
        }

        // 打印结点的数据域
        public void displayNode() {
            System.out.println("{" + data + "}");
        }
    }

    /**
     * 从头部遍历打印
     */
    public void displayForward() {
        System.out.println("List(first---->last): ");
        DoubleNode current = first;
        while (current != null) {
            current.displayNode();
            current = current.next;
        }
        System.out.println();
    }

    /**
     * 从尾部遍历打印
     */
    public void displayBackward() {
        System.out.println("List(last---->first): ");
        DoubleNode current = last;
        while (current != null) {
            current.displayNode();
            current = current.prev;
        }
        System.out.println();
    }
}

2.3 链表插入

首尾插入要比中间插入简单,中间插入的图像为:

双向链表中间插入结点

public class DoubleLink {
    // 双向链表的结构
    private DoubleNode first;
    private DoubleNode last;

    public DoubleLink() {
        first = null;
        last = first;
    }

    /**
     * 定义结点类
     */
    public static class DoubleNode {
        public int data;
        public DoubleNode next;
        public DoubleNode prev;

        public DoubleNode(int data) {
            this.data = data;
        }

        // 打印结点的数据域
        public void displayNode() {
            System.out.println("{" + data + "}");
        }
    }

    /**
     * 从头部遍历打印
     */
    public void displayForward() {
        System.out.println("List(first---->last): ");
        DoubleNode current = first;
        while (current != null) {
            current.displayNode();
            current = current.next;
        }
        System.out.println();
    }

    /**
     * 从尾部遍历打印
     */
    public void displayBackward() {
        System.out.println("List(last---->first): ");
        DoubleNode current = last;
        while (current != null) {
            current.displayNode();
            current = current.prev;
        }
        System.out.println();
    }

    /**
     * 判断链表是否为空
     * @return 布尔值
     */
    public boolean isEmpty() {
        return first == null || last == null;
    }

    /**
     * 在头部插入结点
     * @param data 结点数据
     */
    public void insertFirst(int data) {
        DoubleNode newDoubleNode = new DoubleNode(data);
        if (isEmpty()) {
            last = newDoubleNode;
        } else {
            first.prev = newDoubleNode;
        }
        newDoubleNode.next = first;
        first = newDoubleNode;
    }

    /**
     * 在尾部插入结点
     * @param data 插入的结点数据
     */
    public void insertLast(int data) {
        DoubleNode newDoubleNode = new DoubleNode(data);
        if (isEmpty()) {
            first = newDoubleNode;
        } else {
            newDoubleNode.prev = last;
            last.next = newDoubleNode;
        }
        last = newDoubleNode;
    }

    /**
     * 在中间插入结点
     * @param key 在对应数据为key的结点后面插入
     * @param data 要插入的结点的数据
     */
    public void insertAfter(int key, int data) {
        DoubleNode newDoubleNode = new DoubleNode(data);
        DoubleNode current = first;

        while (current != null && current.data != key) {
            current = current.next;
        }

        // 如果当前结点为null
        if (current == null) {
            // 判断是不是空链表
            if (isEmpty()) {
                first = newDoubleNode;
                last = newDoubleNode;
            } else {
                // 找不到key的值,就在最后插入一个结点
                newDoubleNode.prev = last;
                last.next = newDoubleNode;
                last = newDoubleNode;
            }
        } else {    // 找到了key的值,分情况讨论
            if (current == last) {
                // key的值和最后结点的data相等
                newDoubleNode.next = null;
                last = newDoubleNode;
            } else {
                // 在两个结点中间插入
                newDoubleNode.next = current.next;
                current.next.prev = newDoubleNode;
            }
            newDoubleNode.prev = current;
            current.next = newDoubleNode;
        }

    }
}

2.4 链表删除

同样的,首尾删除要比中间删除简单,中间删除的图像为:

双向链表中间删除结点

/**
     * 删除首结点
     * @return 删除的结点
     */
    public DoubleNode deleteFirst() {
        DoubleNode temp = first;
        // 如果链表只有一个结点
        if (first.next == null) {
            last = null;
        } else {
            // 两个以上的结点
            first.next.prev = null;
        }
        first = first.next;
        return temp;
    }

    /**
     * 删除尾结点
     * @return 删除的结点
     */
    public DoubleNode deleteLast() {
        DoubleNode temp = last;
        if (last.prev == null) {
            first = null;
        } else {
            last.prev.next = null;
        }
        last = last.prev;
        return temp;
    }

    /**
     * 删除中间结点
     * @param key 对应删除结点的key
     * @return 删除的结点
     */
    public DoubleNode deleteKey(int key) {
        DoubleNode current = first;
        while (current != null && current.data != key) {
            current = current.next;
        }

        // 如果current为null
        if (current == null) {
            return null;
        } else {
            // 如果current是第一个结点
            if (current == first) {
                first = current.next;
                current.next.prev = null;
            } else if (current == last) {
                // 如果current是最后一个结点
                last = current.prev;
                current.prev.next = null;
            } else {
                // current在中间
                current.prev.next = current.next;
                current.next.prev = current.prev;
            }
        }
        
        return current;
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张小yu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值