数据结构与算法之单链表
1、链表介绍
链表是有序的列表,但是它在内存中是存储如下
小结上图:
-
链表是以节点的方式来存储,是链式存储
-
每个节点包含 data 域, next 域:指向下一个节点.
-
如图:发现链表的各个节点不一定是连续存储.
-
链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定
单链表(带头结点)逻辑结构示意图如下
2、单链表的应用实例
使用带 head 头的单向链表实现 –水浒英雄排行榜管理完成对英雄人物的增删改查操作, 注: 删除和修改,查找
(1) 第一种方法在添加英雄时,直接添加到链表的尾部
思路分析示意图:
(2) 第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
思路的分析示意图:
(3) 修改节点功能
思路(1) 先找到该节点,通过遍历,(2) temp.name = newHeroNode.name ; temp.nickname= newHeroNode.nickname
(4) 删除节点
思路分析的示意图:
3、代码实现
package com.datastrucate.linkedlist;
/**
* ClassName:SingleLinkedList
* Package:com.datastrucate.linkedlist
* Description:
* 用水浒传英雄模拟单链表的CRUD
*
* @Date:2021/2/25 17:01
* @Author:hm
*/
public class SingleLinkedListDemo {
public static void main(String[] args) {
//先创建节点
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
//创建链表
SingleLinkedList singleLinkedList = new SingleLinkedList();
// singleLinkedList.add(hero1);
//
// singleLinkedList.add(hero4);
//
// singleLinkedList.add(hero2);
//
// singleLinkedList.add(hero3);
singleLinkedList.addOrder(hero1);
singleLinkedList.addOrder(hero4);
singleLinkedList.addOrder(hero2);
singleLinkedList.addOrder(hero3);
//显示链表
singleLinkedList.list();
singleLinkedList.update(new HeroNode(1,"宋小江","及时雨"));
System.out.println("更新后");
singleLinkedList.list();
singleLinkedList.delete(1);
System.out.println("删除后");
singleLinkedList.list();
}
}
//单链表类
class SingleLinkedList{
private HeroNode head = new HeroNode(0,"","");//头结点,不带数据,不可移动
/**
* 添加方法一:不带顺序的添加,添加到末尾
*/
public void add(HeroNode heroNode){
HeroNode temp = head;//临时节点,用于避免移动头结点
//遍历到节点末尾
while(true){
if (temp.next == null){//空链表,可以添加
break;
}
//没找到,指针后移
temp = temp.next;
}
//退出循环代表,此时temp指针在最后面
//添加新节点到最后面
temp.next = heroNode;
}
/**
* 第二种添加:
* 带顺序添加
*/
public void addOrder(HeroNode heroNode){
//因为单链表,因为我们找的 temp 是位于 添加位置的前一个节点,否则插入不了
HeroNode temp = head;//临时节点,用于避免移动头结点
boolean flag = true ;//用于标记不可以添加
//遍历
while (true){
if (temp.next == null){//空链表
break;
}
if (temp.next.no > heroNode.no){//序号小于temp,能添加
break;
}else if (temp.next.no == heroNode.no){//序号存在,不能添加
flag = false;
break;
}
//指针后移
temp = temp.next;
}
//判断flag
if (flag){//可以添加
heroNode.next = temp.next;
temp.next = heroNode;
}else {//不能添加
System.out.println("链表已经存在该英雄,不能添加");
}
}
/**
* 更新节点
*
*/
public void update(HeroNode heroNode){
if (head.next == null){
System.out.println("链表为空");
}
HeroNode temp = head.next;//头节点的后一节点
boolean flag = false;//用于标记是否更新
//遍历
while (true){
if (temp == null){//空链表
break;
}
if (temp.no == heroNode.no){//更新
flag = true;
break;
}
//指针后移
temp = temp.next;
}
//更新
if (flag){
temp.name = heroNode.name;
temp.nickname = heroNode.nickname;
}else {
System.out.println("没有找到编号为"+heroNode.no+"的英雄");
}
}
/**
* 删除节点
* 思路
* 1. head 不能动,因此我们需要一个 temp 辅助节点找到待删除节点的前一个节点
* 2. 说明我们在比较时,是 temp.next.no 和 需要删除的节点的 no 比较
*/
public void delete(int no){
if (head.next == null){
System.out.println("链表为空");
}
HeroNode temp = head;
Boolean flag = false;//用于判断是否可删除
while(true){
if (temp.next == null){
break;
}
if (temp.next.no == no ){
flag = true;
break;
}
//节点指针后移
temp = temp.next;
}
//删除
if (flag){
temp.next = temp.next.next;
}else {
System.out.println("不存在该节点,不能删除");
}
}
/**
* 显示所有节点信息
*/
public void list(){
if (head.next == null){//链表为空
System.out.println("链表为空");
return;
}
HeroNode temp = head.next;//临时节点,用于记录头结点后一节点
//遍历显示
while(true){
if (temp == null){//链表为空
break;
}
System.out.println("链表信息为:"+ temp);
//后移指针
temp = temp.next;
}
}
}
//实体类
class HeroNode{
public int no;//英雄编号
public String name;//姓名
public String nickname;//昵称
public HeroNode next;//指向下一节点的指针
public HeroNode(int no,String name, String nickname){
this.no = no;
this.name = name;
this.nickname = nickname;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}