【链表-(单链表、循环链表、双向链表)】数据结构与算法03-java实现

本文详细介绍了链表的三种常见类型:单链表、循环链表和双向循环链表。通过示例代码展示了各类型链表的节点结构、常见操作(如插入、删除、遍历)以及它们的特点。单链表是最基础的,循环链表的最后一个节点指向第一个节点,双向循环链表每个节点都可以访问前后两个节点。
摘要由CSDN通过智能技术生成

链表

1. 简介

所谓链表,通俗的说就是用一条链把数据都串起来

前面我们提到的数组、栈和队列都是顺序存储结构,这里的链表属于链式存储结构

什么是链表呢,我们直接上图

在这里插入图片描述

我们可以看到:每个链表结点至少包含两个元素:数据data、地址next(指向下一个数据)

分类:
最基础的链表就是单链表,在单链表的基础上有循环链表、双向链表、双向循环链表。
下面会分别进行介绍和实现。

2. 单链表

你猜的没错,前面我们看到的链表图,其实就是单链表。
单链表除了最后一个结点以外,其他结点都由数据data和t指向下一结点的nex组成
而最后一个结点的next为null

常见操作(方法):
getData()—获取当前结点数据
next()—返回下一个结点
append()—结点追加-追加在链表最后
insertAfter()—结点插入
removeNode()—结点移除
show()—输出当前链表
isLast()—判断是否是最后一个结点

代码:

public class Node {
    //结点数据
    private int data;
    //指向下一个结点
    private Node next;
    //构造函数中对结点赋值
    public Node(int data){
        this.data=data;
    }
    //返回当前结点数据
    public int getData(){
        return this.data;
    }
    //追加结点
    public Node append(Node node){
        //点前结点
        Node currentNode=this;
        //从当前结点开始依次往后寻找为null的结点进行追加
        while(true){
            //如果下一个结点为空,进行追加结点并调出循环
            if(currentNode.next==null){
                currentNode.next=node;
                break;
            }
            //不是空,将下一个结点指向当前结点,接着向后寻找
            currentNode=currentNode.next;
        }
        //返回当前结点方便调用
        return this;
    }
    //获取下一个结点
    public Node next(){
        return this.next;
    }
    //判断是否最后一个结点
    public Boolean isLast(){
        return this.next==null;
    }
    //输出链表 (此方法只能输出当前结点及往后的元素结点内容)
    public void show(){
        //当前结点
        Node CurrentNode=this;
        while (true){
            System.out.print(CurrentNode.data);
            //如果没有下一个结点,结束
            if(CurrentNode.next==null){
                System.out.println();
                break;
            }else{
                System.out.print("->");
                //向后查找
                CurrentNode=CurrentNode.next;
            }
        }
    }

    //删除下一个结点
    public void removeNode(){
        //将下下个结点指向当前结点的下一个结点
        this.next=this.next.next;
    }
    //插入一个结点(当前结点后面)
    public void insetAfter(Node node){
        //保存下一个结点
        Node nextNode=this.next;
        //将插入的元素指向当前结点的下一个结点
        this.next=node;
        //最后将保存的结点指向刚刚插入结点的下一个结点
        node.next=nextNode;
    }
}

测试类

public class TestNode {
    public static void main(String[] args) {
        //创建三个结点,现在他们之间没有任何关联
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        //将node1和node2追加到node上面形成链表
        node1.append(node2).append(node3).append(node4);
        //通过node1获取node3的数据
        System.out.println(node1.next().next().getData());
        node1.show();
        //移除node2
        node1.removeNode();
        node1.show();
        //插入元素
        node1.insetAfter(new Node(100));
        node3.insetAfter(new Node(200));
        node1.show();
    }
}

结果:

3
1->2->3->4
1->3->4
1->100->3->200->4

3. 循环链表

在这里插入图片描述

循环链表就是将单链表最后一个结点指向第一个结点,这样让整个链表形成一个循环
循环链表由于没有尾结点,所以不能append结点

注意:循环链表只能插入结点,删除结点

在单链表基础上,我们将结点中的next初始化为this,也就是指向当前结点自己,就变成了循环链表,话不多说,下面直接上代码。

代码:

public class LoopNode {
    //结点数据
    private int data;
    //指向下一个结点
    private LoopNode next=this;
    //构造函数中对结点赋值
    public LoopNode(int data){
        this.data=data;
    }
    //返回当前结点数据
    public int getData(){
        return this.data;
    }

    //获取下一个结点
    public LoopNode next(){
        return this.next;
    }

    //删除下一个结点
    public void removeNode(){
        //将下下个结点指向当前结点的下一个结点
        this.next=this.next.next;
    }

    //插入一个结点(当前结点后面)
    public void insetAfter(LoopNode node){
        //保存下一个结点
        LoopNode nextNode=this.next;
        //将插入的元素指向当前结点的下一个结点
        this.next=node;
        //最后将保存的结点指向刚刚插入结点的下一个结点
        node.next=nextNode;
    }
}

测试类:

public class TestLoopNode {
    public static void main(String[] args) {
        //新增循环链表结点
        LoopNode loopNode1 = new LoopNode(1);
        LoopNode loopNode2 = new LoopNode(2);
        LoopNode loopNode3 = new LoopNode(3);
        //将loopNode2、loopNode3插入
        loopNode1.insetAfter(loopNode2);
        loopNode2.insetAfter(loopNode3);
        //通过next查看下一个结点的值
        System.out.println(loopNode1.next().getData());
        System.out.println(loopNode2.next().getData());
        //循环链表的特征
        //注意:这里通过loopNode3.next() 访问到loopNode1 
        System.out.println(loopNode3.next().getData());
    }
}

运行结果

2
3
1

4. 双向循环链表

双向循环链表:顾名思义就是每一个结点都可以访问其前后两个结点,并且是循环链表
在这里插入图片描述
特点:每一个结点都有pre和next分别指向相邻结点
特别的:第一个结点的pre指向最后一个结点,而最后一个结点的next指向第一个结点

每一个结点都可以访问到其前后两个结点所我们需要增加一个pre指向上一个结点
同时插入操作相比于单链表较为复杂,需要修改四个指向,见代码

代码:

public class DoubleNode {
    //结点数据、指向前一个结点、指向后一个结点
    private int data;
    //如果没有结点插入(一个结点)默认指向自己
    private DoubleNode pre=this;
    private DoubleNode next=this;
    public DoubleNode(int data){
        this.data=data;
    }
    //返回当前结点的上一个结点
    public DoubleNode pre(){
        return this.pre;
    }
    //返回当前结点的下一个结点
    public DoubleNode next(){
        return this.next;
    }
    //获取数据
    public int getData(){
        return this.data;
    }
    //插入元素
    public void insertAfter(DoubleNode doubleNode){
        //保存当前结点的下一个结点(简称保存结点)
        DoubleNode nextDoubleNode=this.next;
        //将插入的结点指向当前结点的下一个结点next
        this.next=doubleNode;
        //同时将当前结点指向插入结点的上一个结点pre
        doubleNode.pre=this;
        //将保存结点指向插入结点的下一结点next
        doubleNode.next=nextDoubleNode;
        //同时将插入结点指向保存结点的上一结点pre
        nextDoubleNode.pre=doubleNode;
    }
}

测试类:

public class TestDoubleNode {
    public static void main(String[] args) {
        //初始化双向循环链表
        DoubleNode doubleNode1 = new DoubleNode(1);
        DoubleNode doubleNode2 = new DoubleNode(2);
        DoubleNode doubleNode3 = new DoubleNode(3);
        //将doubleNode2、doubleNode3插入doubleNode1
        doubleNode1.insertAfter(doubleNode2);
        doubleNode2.insertAfter(doubleNode3);
        //输出检验
        System.out.println(doubleNode1.pre().getData());
        System.out.println(doubleNode1.next().getData());
        System.out.println(doubleNode2.pre().getData());
        System.out.println(doubleNode2.next().getData());
        System.out.println(doubleNode3.pre().getData());
        System.out.println(doubleNode3.next().getData());
    }
}

结果:

3
2
1
3
2
1
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Kygo Chen

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

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

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

打赏作者

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

抵扣说明:

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

余额充值