:链表 -- 链表介绍单链表

Posted CodeJiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了:链表 -- 链表介绍单链表相关的知识,希望对你有一定的参考价值。

1. 链表介绍

好好学链表,链表是图和树的基础。


1.1 为什么会出现链式存储?

顺序存储虽然是一种很有用的数据结构,但它具有如下局限性:

  • 若要为线性表扩充存储空间,则需要重新创建一个地址连续的更大的存储空间,并把原有的数据都复制到心的存储空间中。
  • 因为顺序表要求逻辑上相邻的数据元素,在物理存储位置上也是相邻的,这就使得要增删改查数据元素会引起约一半的数据元素的移动。

所以:

顺序表适合存储“静态”线性表,即线性表一旦形成后,就很少进行插入与删除操作。对于需要
频繁执行插入和删除操作的“动态”线性表,通常采用链式存储结构,链式存储结构不要求逻辑
上相邻的数据元素物理元素也相邻,它是用一组地址任意的存储单元来存放数据元素的值。
因此,链式存储结构没有顺序存储结构所具有的在某些操作上面的局限性,但却失去了可以
随机存取的特点(可以插在任意位置,只要数组下标不越界),在链式存储结构上只能进行顺序存取(存储在next域)。

1.2 单链表的定义


1.3 链表在内存中的存储结构(真实的物理存储结构)

链表是有序的列表,但是它在内存中是存储如下


1.4 单链表(带头结点) 逻辑结构示意图如下

由图可知,单链表是通过指向后继结点的指针把它的一串结点连接成一个链。
头指针:线性表中第一个元素的存储地址。
一个单链表就是由它的头指针head来唯一标识它。
为了操作方法,在第一个结点之前虚加一个“头结点”,头结点的数据域一般不存放数据,
指针域存放指向第一个结点(也叫做“首结点”)的指针。
若线性表为空表,则头结点的指针域为“空”。
单链表的最后一个结点(尾结点)的指针域为“空”。

1.5 单链表(带头结点)实现的思路分析

1.5.1 初始状态:


1.5.2 添加结点

现在在s1和s2之间插入一个s3

为了方便对比,现在演示不带头结点的单链表表头位置进行插入

  1. 插入前
  2. 插入后

1.5.4 添加结点补充

添加结点补充:

  • 头插法:每次添加结点添加在头结点的next域
  • 尾插法:每次添加结点添加到当前单链表的表尾

1.5.3 删除结点

  1. 删除前
  2. 删除后(删除s3)

被删除的节点,将不会有其它引用指向,会被垃圾回收机制回收

补充:上面的删除思路有点问题 因为实际我并不知道S3后面是S2。所以最好的删除语句是:

 s1.next = s1.next.next

1.6 单链表示例

根据带有头部的单链表,实现商品增删改查,并且也可以针对商品已编号进行排序,完成排行榜。

GoodsNode.java

package data_structure;

public class GoodsNode 

    public int id;

    public String name;

    public double price;

    public GoodsNode next;

    public GoodsNode(int id, String name, double price) 
        this.id = id;
        this.name = name;
        this.price = price;
    

    @Override
    public String toString() 
        return "GoodsNode" +
                "id=" + id +
                ", name='" + name + '\\'' +
                ", price=" + price +
                '';
    

DLLinkedList.java

package data_structure;

public class DLLinkedList 

    // 头结点
    private final GoodsNode node = new GoodsNode(0, "", 0.0);


    /**
     * 往链表中添加结点
     */
    public void add(GoodsNode goodsNode) 
        GoodsNode temp = node;
        while (temp.next != null) 
            temp = temp.next;
        

        temp.next = goodsNode;

    

    /**
     * 按照商品编号id值进行添加,从小到大的顺序添加
     */
    public void addOrder(GoodsNode goodsNode) 
        GoodsNode temp = node;
        boolean flg = false;
        while (true) 
            if (temp.next == null) 
                break;
            
            if (temp.next.id > goodsNode.id) 
                break;
             else if (temp.next.id == goodsNode.id) 
                flg = true;
                break;
            

            temp = temp.next;
        

        if (flg) 
            System.out.println("已经存在了该商品,不能添加重复元素");
         else 
            goodsNode.next = temp.next;
            temp.next = goodsNode;
        

    

    /**
     * 修改结点
     * 1.先找到链表中目标结点,
     * 2.根据新的数据修改
     * 3.根据商品编号进行查找,
     */
    public void updateNode(GoodsNode goodsNode) 
        /*
         * 如果链表空
         */
        if (node.next == null) 
            System.out.println("链表为空...");
            return;
        

        GoodsNode temp = node.next;

        //标识符,表示找到了结点
        boolean flg = false;
        while (true) 
            if (temp == null) 
                break;
            

            if (temp.id == goodsNode.id) 
                flg = true;
                break;
            

            temp = temp.next;
        

        if (flg) 
            //真正的修改结点
            temp.name = goodsNode.name;
            temp.price = goodsNode.price;
         else 
            System.out.println("在整个链表中未找到目标节点...");
        
    

    /**
     * 结点删除功能
     * 条件:根据结点的编号删除
     */
    public void delNode(int id) 

        GoodsNode temp = node;

        boolean flg = false;
        while (true) 
            if (temp.next == null) 
                break;
            
            if (temp.next.id == id) 
                flg = true;
                break;
            
            temp = temp.next;
        

        if (flg) 
            temp.next = temp.next.next;
         else 
            System.out.println("未找到删除的结点...");
        

    

    /**
     * 定义查看链表中每一个结点元素
     */
    public void list() 
        if (node.next == null) 
            System.out.println("空链表");
            return;
        

        GoodsNode temp = node.next;
        while (temp != null) 
            System.out.println(temp);

            temp = temp.next;
        
    

Test.java

package data_structure;

public class Test 
    public static void main(String[] args) 
        GoodsNode goodsNode1 = new GoodsNode(1, "1号装备", 100);
        GoodsNode goodsNode2 = new GoodsNode(2, "2号装备", 200);
        GoodsNode goodsNode3 = new GoodsNode(3, "3号装备", 300);
        GoodsNode goodsNode4 = new GoodsNode(4, "4号装备", 400);

        DLLinkedList list = new DLLinkedList();
/*        list.add(goodsNode1);
        list.add(goodsNode2);
        list.add(goodsNode3);
        list.add(goodsNode4);*/
        list.addOrder(goodsNode3);
        list.addOrder(goodsNode1);
        list.addOrder(goodsNode4);
        list.addOrder(goodsNode2);
        list.list();
        System.out.println();
        list.updateNode(new GoodsNode(1, "最好的商品", 10000));
        list.list();
    

运行结果:


1.7 补充:面试题,统计单链表中结点个数

下面的代码是上面DLLinkedList类里面新增的一个方法。

    /**
     * 面试题
     * 计算单链表中存在的节点个数
     * 不统计头结点
     */
    public int getLength() 
        if (node.next == null) 
            System.out.println("空链表");
            return 0;
        

        GoodsNode temp = node.next;
        int length = 0;
        while (temp != null) 
            //结点个数
            length++;

            temp = temp.next;
        
        return length;
    


以上是关于:链表 -- 链表介绍单链表的主要内容,如果未能解决你的问题,请参考以下文章

数据结构-链表链表的相关算法

同时创建两条单链表,头插法插入节点,遍历,查找,删除,求长度,冒泡排序,反转,2条有序链表链接成一条链表后依然有序

单链表

数据结构学习链表

数据结构 Java 版玩转链表链表面试题及个人题解

线性表结构-链表