链表结构的基本概念和操作

链表结构

什么是链表结构

在这里插入图片描述

如图所示,链表中的每个阶段都应包括如下部分:

  • 数据部分:保存的是该结点的实际数据
  • 地址部分:保存的是下一个结点的地址
    由于采用了引用来指示下一个数据的地址,因此在链表结构中。逻辑上相邻的节点在内存中并不一定相邻,逻辑相邻关系通过地址部分的引用变量来实现

使用链表的优缺点

优点

  • 结点之间不要求连续存放,因此在保存大量数据时,不需要分配一块连续的存储空间。用户可以用new函数动态分配结点的存储空间,当删除某个结点时,给该节点赋值null,释放其占用的内存空间。

缺点

  • 那就是浪费存储空间。因此,对于每个节点数据,都要额外保存一个引用变量。但是,在某些场合,链表结构所带来的好处还是大于其缺点的。

  • 对于链表的访问只能从表头逐个查找,即通过head头引用找到第一个结点, 再从第一个结点找到第二个节点……这样逐个比较一直到找到需要的结点为止,而不能像顺序表那样进行随机访问。

    链式存储是最常用的存储方式之一 ,它不仅可用来表示线性表,而且可用来表示各种非线性数据结构。

链表结构还可以细分为如下几类:

  • 单链表:同上面的链式结构-一样,每个结点中只包含一个引用。

  • 双向链表:若每个结点包含两个引用,一个指向下一个结点, 另一个指向上一个结点,说是双向链表。

  • 单循环链表:在单链表中,将终端节点的引用域null改为指向表头节点或开始节点即可构成单循环链表

  • 多重链的循环链表:如果将表中结点链在多个环上,将构成多重链的循环链表。

1.准备数据

有了前面的理论知识后,开始链表结构的程序设计。准备链表操作中需要用到的变量及类等。示例代码如下:

class DATA2 {
    //节点的关键字
    String key;
    String name;
    int age;
}

/**
 * 定义链表结构
 */

class CLType {
    DATA2 nodeData = new DATA2();
    CLType nextNode;
}

上述代码定义了链表数据元素的类DATA2及链表的类CLType.结点的具体数据保存在一个类DATA2中,而引用nextNode用来指向下一个结点。
其实,可以认为该链表是一个班级学生的记录,其中, key为学号,name 为学生的名称,age 为年龄。和顺序表所完成的工作类似。

2.追加结点

追加结点即在链表末尾增加一个结点。表尾结点的地址部分原来保存的是空地址null, 此时需将其设置为新增结点的地址(即原表尾结点指向新增结点),然后将新增结点的地址部分设置为空地址null,即新增结点成为表尾。
由于般情况下,链表只有个头引用head,要在末尾添加结点就需要从头引用head开始逐个检查,直到找到最后一个结点(即表尾)。
典型的追加结点的过程如图2-4所示。追加结点的操作步骤如下:

在这里插入图片描述

(1)首先分配内存空间,保存新增的结点。
(2)从头引用head开始逐个检查,直到找到最后一个结点(即表尾)。
(3)将表尾结点的地址部分设置为新增结点的地址。
(4)将新增结点的地址部分设置为空地址null,即新增结点成为表尾。

实例代码如下:

CLType CLAddEnd(CLType head, DATA2 nodeData) {
    CLType node, htemp;
    node = new CLType();
    if (node == null) {
        System.out.println("申请内存失败!");
        return null;
    } else {
        node.nodeData = nodeData;
        node.nextNode = null;
        if (head == null) {
            head = node;
            return head;
        }
        htemp = head;
        //查找表的末尾
        while (htemp.nextNode != null) {
            htemp = htemp.nextNode;
        }
        htemp.nextNode = node;
        return head;
    }
3.插入头节点

在这里插入图片描述

(1)首先分配内存空间,保存新增的结点。
(2)使新增节点指向头引用head所指向的节点
(3)使用头引用head指向新增节点

代码实例如下:

CLType CLAddFirst(CLType head, DATA2 nodeData) {
    CLType node;
    node = new CLType();
    if (node == null) {
        System.out.println("申请内存失败!");
        return null;
    } else {
        node.nodeData = nodeData;
        node.nextNode = null;
        head = node;
        return head;
    }
}
4.查找节点
CLType CLFindNode(CLType head, String key) {
    CLType htemp;
    htemp = head;
    while (htemp != null) {
        if (htemp.nodeData.key.compareTo(key) == 0) {
            return htemp;
        }
        htemp = htemp.nextNode;
    }
    return null;
}
5.插入节点

插入节点就是在链表中间部分的指定位置增加入结点。插入节点的操作步骤如下:
(1)分配内存空间,保存新增的结点。
(2)找到要插入的逻辑位置,也就是位于哪两个结点之间。
(3)修改插入位置结点的引用,使其指向新增结点,而使新增节点指向原插入位置所指向的节点。

在这里插入图片描述

CLType CLInsertNode(CLType head, String findkey, DATA2 nodeData) {
    CLType node, nodetemp;
    node = new CLType();
    if (node == null) {
        System.out.println("申请内存失败!");
        return null;
    }
    node.nodeData = nodeData;
    nodetemp = CLFindNode(head, findkey);
    if (nodetemp != null) {
        node.nextNode = nodetemp.nextNode;
        nodetemp.nextNode = node;
    } else {
        System.out.println("未找到正确的位置!");
    }
    return head;
}
6/删除节点

删除节点的操作步骤如下:
(1)查找需要删除的节点。
(2)使前一节点指向当前节点的下一节点
(3)删除节点

在这里插入图片描述

int CLDeleteNode(CLType head, String key) {
    //node保存删除节点的前一节点
    CLType node, htemp;
    htemp = head;
    node = head;
    while (htemp != null) {
        if (head.nodeData.key.compareTo(key) == 0) {
            node.nextNode = htemp.nextNode;
            //释放内存
            htemp = null;
            return 1;
        } else {
            node = htemp;
            htemp = htemp.nextNode;
        }
    }
    return 0;

}
7.计算链表长度
int CLLength(CLType head) {
    CLType htemp;
    int Len = 0;
    htemp = head;
    while (htemp != null) {
        Len++;
        htemp = htemp.nextNode;
    }
    return Len;
}
8.显示所有节点
void CLAllNode(CLType head) {
    CLType htemp;
    DATA2 nodeData;
    htemp = head;
    System.out.println("当前链表共有" + CLLength(head) + "个节点。链表所有的数据如下:");
    while (htemp != null) {
        nodeData = htemp.nodeData;
        System.out.println("节点:" + nodeData.key + " " + nodeData.name + " " + nodeData.age);
        htemp = htemp.nextNode;
    }
}

链表操作实例

import java.util.Scanner;

class DATA2 {
    //节点的关键字
    String key;
    String name;
    int age;
}

/**
 * 定义链表结构
 */

class CLType {
    DATA2 nodeData = new DATA2();
    CLType nextNode;


    CLType CLAddEnd(CLType head, DATA2 nodeData) {
        CLType node, htemp;
        node = new CLType();
        if (node == null) {
            System.out.println("申请内存失败!");
            return null;
        } else {
            node.nodeData = nodeData;
            node.nextNode = null;
            if (head == null) {
                head = node;
                return head;
            }
            htemp = head;
            //查找表的末尾
            while (htemp.nextNode != null) {
                htemp = htemp.nextNode;
            }
            htemp.nextNode = node;
            return head;
        }

    }

    CLType CLAddFirst(CLType head, DATA2 nodeData) {
        CLType node;
        node = new CLType();
        if (node == null) {
            System.out.println("申请内存失败!");
            return null;
        } else {
            node.nodeData = nodeData;
            node.nextNode = null;
            head = node;
            return head;
        }
    }

    CLType CLFindNode(CLType head, String key) {
        CLType htemp;
        htemp = head;
        while (htemp != null) {
            if (htemp.nodeData.key.compareTo(key) == 0) {
                return htemp;
            }
            htemp = htemp.nextNode;
        }
        return null;
    }

    CLType CLInsertNode(CLType head, String findkey, DATA2 nodeData) {
        CLType node, nodetemp;
        node = new CLType();
        if (node == null) {
            System.out.println("申请内存失败!");
            return null;
        }
        node.nodeData = nodeData;
        nodetemp = CLFindNode(head, findkey);
        if (nodetemp != null) {
            node.nextNode = nodetemp.nextNode;
            nodetemp.nextNode = node;
        } else {
            System.out.println("未找到正确的位置!");
        }
        return head;
    }

    int CLDeleteNode(CLType head, String key) {
        //node保存删除节点的前一节点
        CLType node, htemp;
        htemp = head;
        node = head;
        while (htemp != null) {
            if (htemp.nodeData.key.compareTo(key) == 0) {
                node.nextNode = htemp.nextNode;
                htemp = null;
                return 1;
            } else {
                node = htemp;
                htemp = htemp.nextNode;
            }
        }
        return 0;

    }

    int CLLength(CLType head) {
        CLType htemp;
        int Len = 0;
        htemp = head;
        while (htemp != null) {
            Len++;
            htemp = htemp.nextNode;
        }
        return Len;
    }

    void CLAllNode(CLType head) {
        CLType htemp;
        DATA2 nodeData;
        htemp = head;
        System.out.println("当前链表共有" + CLLength(head) + "个节点。链表所有的数据如下:");
        while (htemp != null) {
            nodeData = htemp.nodeData;
            System.out.println("节点:" + nodeData.key + " " + nodeData.name + " " + nodeData.age);
            htemp = htemp.nextNode;
        }
    }
}

public class LinkedList {
    public static void main(String[] args) {
        CLType node, head = null;
        CLType CL = new CLType();
        String key, findkey;
        Scanner input = new Scanner(System.in);
        System.out.println("链表测试。先输入链表中的数据,格式为:关键字 姓名 年龄");
        do {
            DATA2 nodeData = new DATA2();
            nodeData.key = input.next();
            if (nodeData.key.equals("0")) {
                break;
            } else {
                nodeData.name = input.next();
                nodeData.age = input.nextInt();
                //在链表尾部添加节点
                head = CL.CLAddEnd(head, nodeData);
            }
        } while (true);
        CL.CLAllNode(head);


        System.out.println("演示插入节点,输入插入位置的关键字:");
        findkey = input.next();
        System.out.println("输入插入节点的数据(关键字 姓名 年龄):");
        DATA2 nodeData = new DATA2();
        nodeData.key = input.next();
        nodeData.name = input.next();
        nodeData.age = input.nextInt();
        head = CL.CLInsertNode(head, findkey, nodeData);
        CL.CLAllNode(head);

        System.out.println("演示删除节点,输入删除位置的关键字:");
        key = input.next();
        CL.CLDeleteNode(head, key);
        CL.CLAllNode(head);

        System.out.println("演示在链表中查找,输入查找关键字:");
        key = input.next();
        node = CL.CLFindNode(head, key);
        if (node != null) {
            nodeData = node.nodeData;
            System.out.println("关键字" + key + "对应的节点为" + nodeData.key + " " + nodeData.name + " " + nodeData.age);
        } else {
            System.out.println("在链表中未找到关键字为" + key + "的节点");
        }
    }
}

执行结果

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值