数组作为数据存储结构有一定的缺陷。在无序数组中,搜索性能差,在有序数组中,插入效率又很低(插入位置后面的元素需要集体后移),而且这两种数组的删除效率(集体前移)都很低,并且数组在创建后,其大小是固定了,设置的过大会造成内存的浪费,过小又不能满足数据量的存储。

数组是一种通用的数据结构,能用来实现栈、队列等很多数据结构。而链表也是一种使用广泛的通用数据结构,它也可以用来作为实现栈、队列等数据结构的基础,基本上除非需要频繁的通过下标来随机访问各个数据,否则很多使用数组的地方都可以用链表来代替。

注意:链表是不能解决数据存储的所有问题的,它也有它的优点和缺点。本篇博客我们介绍几种常见的链表,分别是单向链表、双端链表、有序链表、双向链表以及有迭代器的链表。并且会讲解一下抽象数据类型(ADT)的思想,如何用 ADT 描述栈和队列,如何用链表代替数组来实现栈和队列。

头指针一定指向链表的第一个结点,有头结点就指向头结点,没有则指向第一个元素结点。

头结点,指向第一个元素结点,data域可为空,也可存放一些链表的信息,作用就是表示单链表的头

链表通常由一串节点组成,每个节点包含任意的实例数据域(data fields)和一个或两个来指向上一个或下一个节点的位置的指针域(next/prev fields)。链表节点的存储位置可能不连续。

链表分带头结点的链表和不带头结点的链表,根据实际需求来确定。

1.单向链表

单向链表只可向一个方向遍历,一般查找一个节点的时候需要从第一个节点开始每次访问下一个节点,一直访问到需要的位置。而插入一个节点,对于单向链表,我们提供在链表头插入或链表尾插入,只需要将当前插入的节点设置为头节点指向的节点,next指向原头节点指向的节点即可。删除一个节点,我们将该节点的上一个节点的next指向该节点的下一个节点。

尾结点的指针域指向null。

举例:

使用带头结点head的单向链表实现--水浒英雄排行榜管理

1)完成对英雄人物的增删改查操作;

2)第一种方式,直接添加到链表的尾部;

3)第二种方式,根据排名将英雄插入到指定位置(如果该排名已有人,则添加失败,并给出提示)

思路:

添加(创建)

1.先创建一个head头结点,作用就是表示单链表的头

2.后面我们每添加一个节点,就直接加入到链表的最后(尾插法)

遍历:

通过一个辅助遍历:帮助遍历整个链表

public class DemoSingleLinkedList {public static void main(String[] args) {// 测试// 先创建节点Node hero1 = new Node(1,"宋江","及时雨");Node hero2 = new Node(2,"卢俊义","玉麒麟");Node hero3 = new Node(3,"吴用","智多星");Node hero4 = new Node(4,"公孙胜","入云龙");// 创建一个链表SingleLinkedList singleLinkedList = new SingleLinkedList();singleLinkedList.addRear(hero1);singleLinkedList.addRear(hero4); // 尾插法打乱顺序后,影响排列。不符合题目要求singleLinkedList.addRear(hero2);singleLinkedList.addRear(hero3);// 显示singleLinkedList.show();}
}// 定义SingleLinkedList 单链表 管理英雄
class SingleLinkedList{// 先初始化一个头结点,头结点不要动private Node head = new Node(0,"","");private Node rear; // 本质是赋值地址// 构造器public SingleLinkedList() {// 初始化head.next = null; //指针域问题rear = head;}// 尾插法添加节点到单向链表public void addRear(Node node){// 尾插法node.next = null;rear.next = node; // 插入尾结点rear = node; // 尾指针后移}// 头插法添加节点到单向链表public void addHead(Node node){// 头插法,Node temp = head.next; // 暂存地址head.next = node;node.next = temp;}// 显示链表(遍历)public void show(){// 先判断链表是否为空if(head.next == null) System.out.println("链表为空");// 因为头结点不能动,需要一个辅助变量来遍历Node temp = head.next;while(temp != null){ // 链表不为空System.out.println(temp.toString()); // 输出节点信息temp = temp.next; // 指针后移}}
}
// 定义node节点,每个node对象就是一个节点
class Node{public int id; // 编号public String name; // 姓名public String nickName; // 英雄名// 因为一个节点就是这个Node类的实例对象,那么它存储的就是该对象的地址值。// 而指针域的作用就是指向下一个节点的地址,所以类型就是Nodepublic Node next; // 指向下一个节点// 构造器public Node(int id, String name, String nickName) {this.id = id;this.name = name;this.nickName = nickName;}// 显示方便,重写toString方法@Overridepublic String toString() {return "Node{" +"id=" + id +", name='" + name + '\'' +", nickName='" + nickName + '\'' +// ", next=" + next +'}';}
}

可以注意到,尾插法需要输入节点有序,如果输入节点无序,那么链表就也不会按排位来,接下来介绍一种中插法:按排名来添加,(如果该排名节点已存在,则添加失败,给出提示)

思路:

1.首先找到新添加节点的位置;

2.新的节点的next域指向下一个,上一个节点的next域指向新节点。

public class DemoSingleLinkedList {public static void main(String[] args) {// 测试// 先创建节点Node hero1 = new Node(1,"宋江","及时雨");Node hero2 = new Node(2,"卢俊义","玉麒麟");Node hero3 = new Node(3,"吴用","智多星");Node hero4 = new Node(4,"公孙胜","入云龙");// 创建一个链表SingleLinkedList singleLinkedList = new SingleLinkedList();singleLinkedList.addByOrder(hero1);singleLinkedList.addByOrder(hero4); // 尾插法打乱顺序后,影响排列。不符合题目要求singleLinkedList.addByOrder(hero3);singleLinkedList.addByOrder(hero2);singleLinkedList.addByOrder(hero4);// 显示singleLinkedList.show();}
}// 定义SingleLinkedList 单链表 管理英雄
class SingleLinkedList{// 先初始化一个头结点,头结点不要动private Node head = new Node(0,"","");private Node rear; // 本质是赋值地址// 构造器public SingleLinkedList() {// 初始化head.next = null;rear = head;}// 尾插法添加节点到单向链表public void addRear(Node node){// 尾插法node.next = null;rear.next = node; // 插入尾结点rear = node; // 尾指针后移}// 头插法添加节点到单向链表public void addHead(Node node){// 头插法,Node temp = head.next; // 暂存地址head.next = node;node.next = temp;}// 有序中插法public void addByOrder(Node node){// 遍历找到该位置,所以需要辅助指针Node temp = head;while(true){if(temp.id == node.id) { // 已存在排名 // 注意这个if要写在三个if的最前面,否则陷入死循环System.out.println("该排名英雄已存在!");break;}if(temp.next == null){// 链表为空或遍历到最后了,插在尾部addRear(node);break;}if(temp.id < node.id && temp.next.id > node.id){node.next = temp.next;temp.next = node;break;}temp = temp.next;}}// 显示链表(遍历)public void show(){// 先判断链表是否为空if(head.next == null) System.out.println("链表为空");// 因为头结点不能动,需要一个辅助变量来遍历Node temp = head.next;while(temp != null){ // 链表不为空System.out.println(temp.toString()); // 输出节点信息temp = temp.next; // 指针后移}}
}
// 定义node节点,每个node对象就是一个节点
class Node{public int id; // 编号public String name; // 姓名public String nickName; // 英雄名// 因为一个节点就是这个Node类的实例对象,那么它存储的就是该对象的地址值。// 而指针域的作用就是指向下一个节点的地址,所以类型就是Nodepublic Node next; // 指向下一个节点// 构造器public Node(int id, String name, String nickName) {this.id = id;this.name = name;this.nickName = nickName;}// 显示方便,重写toString方法@Overridepublic String toString() {return "Node{" +"id=" + id +", name='" + name + '\'' +", nickName='" + nickName + '\'' +// ", next=" + next +'}';}
}

运行截图:

可以看到,虽然重复插入的节点是最后插入的,但提示该英雄存在的信息是最先打印的,是因为,节点类本身就是递归嵌套定义的的,所以打印第一个是需要先找到最后一个的,而找到最后一个就会打印这句话。

单线链表的修改:

仍旧以这个例子,修改是指修改名字和昵称,编号不可修改(会打乱顺序)

    public static void main(String[] args) {// 测试// 先创建节点Node hero1 = new Node(1,"宋江","及时雨");Node hero2 = new Node(2,"卢俊义","玉麒麟");Node hero3 = new Node(3,"吴用","智多星");Node hero4 = new Node(4,"公孙胜","入云龙");// 创建一个链表SingleLinkedList singleLinkedList = new SingleLinkedList();singleLinkedList.addByOrder(hero1);singleLinkedList.addByOrder(hero4); // 尾插法打乱顺序后,影响排列。不符合题目要求singleLinkedList.addByOrder(hero3);singleLinkedList.addByOrder(hero2);singleLinkedList.addByOrder(hero4);// 显示System.out.println("原链表:");singleLinkedList.show();System.out.println("---------------");System.out.println("修改后的链表:");singleLinkedList.update(new Node(3,"小鹿","牛年大吉"));singleLinkedList.show();}
}// 定义SingleLinkedList 单链表 管理英雄
class SingleLinkedList{// 先初始化一个头结点,头结点不要动private Node head = new Node(0,"","");private Node rear; // 本质是赋值地址// 构造器public SingleLinkedList() {// 初始化head.next = null;rear = head;}// 尾插法添加节点到单向链表public void addRear(Node node){// 尾插法node.next = null;rear.next = node; // 插入尾结点rear = node; // 尾指针后移}// 头插法添加节点到单向链表public void addHead(Node node){// 头插法,Node temp = head.next; // 暂存地址head.next = node;node.next = temp;}// 有序中插法public void addByOrder(Node node){// 遍历找到该位置,所以需要辅助指针Node temp = head;while(true){if(temp.id == node.id) { // 已存在排名System.out.println("该排名英雄已存在!");break;}if(temp.next == null){// 链表为空或遍历到最后了,插在尾部addRear(node);break;}if(temp.id < node.id && temp.next.id > node.id){node.next = temp.next;temp.next = node;break;}temp = temp.next;}}// 修改节点信息,根据编号修改。/*说明根据node的id来修改*/public void update(Node node){// 判断链表是否为空if(head.next == null) System.out.println("链表为空");// 找到需要修改的节点// 定义一个辅助变量Node temp = head;boolean flag = false;while(true){if(temp.next == null) {// 链表为空,或遍历到最后一个节点了break;}if(temp.id == node.id){ // 找到节点flag = true;break;}temp = temp.next;}if(flag){temp.name = node.name;temp.nickName = node.nickName;}else System.out.println("没有找到该编号英雄!");}// 显示链表(遍历)public void show(){// 先判断链表是否为空if(head.next == null) System.out.println("链表为空");// 因为头结点不能动,需要一个辅助变量来遍历Node temp = head.next;while(temp != null){ // 链表不为空System.out.println(temp.toString()); // 输出节点信息temp = temp.next; // 指针后移}}
}
// 定义node节点,每个node对象就是一个节点
class Node{public int id; // 编号public String name; // 姓名public String nickName; // 英雄名// 因为一个节点就是这个Node类的实例对象,那么它存储的就是该对象的地址值。// 而指针域的作用就是指向下一个节点的地址,所以类型就是Nodepublic Node next; // 指向下一个节点// 构造器public Node(int id, String name, String nickName) {this.id = id;this.name = name;this.nickName = nickName;}// 显示方便,重写toString方法@Overridepublic String toString() {return "Node{" +"id=" + id +", name='" + name + '\'' +", nickName='" + nickName + '\'' +// ", next=" + next +'}';}

运行截图:

删除节点:

// 删除节点
public void deleteNode(Node node){Node temp = head; // 遍历指针boolean flag = false;while(true) {if (temp.next == null) { // 链表为空或遍历到最后了break;}if (temp.next.id == node.id) { // 找到该节点的前驱节点flag = true;break;}temp = temp.next;}if(flag){temp.next = temp.next.next;}else{ // 遍历到最后也没找到System.out.println("链表中不存在该节点");}
}
System.out.println("删除节点后的链表:");
singleLinkedList.deleteNode(hero4);
singleLinkedList.show();

运行截图:

根据id查找结点:

// 根据id查找结点
public Node get(int id){Node temp = head; // 遍历指针Node result = null;boolean flag = false;while(true){if(temp.id == id){ // 这个if要放在判空if前面,避免是最后一个节点result = temp;flag = true;break;}else if(temp.next == null){ // 链表为空,或找到最后一个节点了  break;}temp = temp.next;}if(flag) return result;else throw new NullPointerException("该节点不存在!");
}
System.out.println("根据id查找的链表:");
System.out.println(singleLinkedList.get(1).toString());
System.out.println("---------------");

运行截图:

参考博客:

https://www.cnblogs.com/ysocean/p/7928988.html#_label3

单链表LinkedList的增删改查相关推荐

  1. 详细记录->使用Maven+SSM框架,实现单表简单的增删改查

    话不多说,ssm框架整合小栗子 步骤 1.创建web Maven项目 2.创建数据库配置文件:jdbc.properties 3.项目总体目录: 4.添加spring配置文件:applicationC ...

  2. C语言:构建一个二级链表并完成增删改查

    构建一个下图所示的链表,并完成增.删.改.查 示例代码: #include <stdio.h> #include <stdlib.h> #include <string. ...

  3. c语言实现双链表的基本操作—增删改查

    //初始化 Node*InitList() {Node*head=(Node*)malloc(sizeof(Node));if(NULL==head){printf("内存分配失败!&quo ...

  4. Django---ORM简介丶单表操作丶增删改查

    一丶ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人 ...

  5. day 68 增删改查 语法

    1 普通正则 2 分组正则 url(r'/blog/(\d+)/(\d+)',views.blog)     blog(request,arq1,arq2) 按照位置传参 3 分组命名 url(r'/ ...

  6. SpringMVC表单数据增删改查简易梳理(含实例代码)

    使用SpringMVC创建表单进行数据的增删改查是javaEE开发的基本功,本人根据自己最近开发的基于jeecms框架的网站平台来梳理数据增删改查的思路. 首先根据所需表单页面设计数据库,定义不同字段 ...

  7. 数据结构----单链表增删改查

    单链表的增删改查 一.链表(Linked List) 链表是有序列表,以节点的方式来存储的,链式存储: 每个节点包含data域,next域:指向下一节点: 链表的各个节点不一定是连续存储: 链表分为带 ...

  8. 三、单链表增删改查原理和代码实现

    单链表 1.简单介绍 (1)单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素.链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素 ...

  9. (数据结构与算法)单链表与双链表增删改查的实现。

    文章目录 链表介绍 1. 单链表应用实例 1.1 实现思路 1.2 代码实现 2.单链表常见面试题 2.1 求单链表中有效节点的个数 2.2 查找单链表中倒数第K个节点 2.3 单链表的反转 2.4 ...

最新文章

  1. 推荐系统遇上深度学习,9篇阿里推荐论文汇总!
  2. 英特尔联合Facebook研发AI芯片:CPU老厂能在AI时代打好翻身仗吗
  3. RISC-V生态未来的三种可能~
  4. 数据结构源码笔记(C语言):二分查找
  5. Linux下启动、停止J2SE程序(脚本)
  6. linux用户配额管理,—linux 磁盘配额按用户管理(quota)
  7. Mugeda(木疙瘩)H5案例课—交互视频类H5-岑远科-专题视频课程
  8. 一键分享java英文_bShare RESTful分享API
  9. typecho添加html5视频播放器,DPlayer-Typecho视频播放插件
  10. 如何计算均值、标准差和标准误差
  11. LeetCode-347. Top K Frequent Elements [C++][Java]
  12. vb.net产生随机数
  13. JMeter性能测试之使用CSV文件参数化
  14. python geany是什么_Geany
  15. 半导体物理-固体晶格结构
  16. html插入swf自动播放,如何在HTML页面中嵌入SWF文件?
  17. 计算机毕业设计ssm基于ssm流浪宠物领养系统8xg84系统+程序+源码+lw+远程部署
  18. 关于电商商品数据API接口列表,你想知道的(详情页、Sku信息、商品描述、评论问答列表)
  19. 加了@CrossOrigin ,仍然报跨域错误
  20. 服务器内存已满,如何解决

热门文章

  1. 频域中求解零状态响应,频率响应(自主学习复习傅里叶变换)
  2. windows系统下超级好用的欧路词典,足以秒杀有道!
  3. jpg格式转换成pdf注册码
  4. 戴尔Optiplex-7080装ubuntu16.04双系统时遇到的一些坑
  5. 深入浅出最优化(2) 步长的计算方法
  6. 爱因斯坦五五问题(zebra问题)斑马问题
  7. 关于黑苹果clover写入EFI后Deepin无法正常引导的解决方法
  8. v-cut改善案例_改善开发人员的工作环境-仅分三个阶段
  9. qsv如何转换成AVI flv格式 ,最新转换方法
  10. es6转换es5 babel配置