单链表Java代码实现,以水浒英雄链表为例
文章目录
单链表 :元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
┌───┬───┐
│data │next │
└───┴───┘
头结点不存放数据,但存放着下一个指针的地址,其他每个指针服从上面所示存储关系,最后一个指针指向null。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200618194338750.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1Nha3VyYUE2,size_16,color_FFFFFF,t_70)
定义一个英雄链表
/**
* 定义结点, 每个对象是一个结点
*
*/
class HeroNode {
public int no;
public String name;
public String nickname;
public HeroNode next; //指向下一个结点
/**
* 构造器
* @param no
* @param name
* @param nickname
*/
public HeroNode(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}
/**
* 为了显示方便,重写toString
* @return
*/
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='"+ nickname + "}";
}
定义一个SingleLinkedList 用于管理结点
初始化头结点以及添加结点到单链表
添加时要遍历到最后一个结点,然后将指针指向要添加的结点即heroNode
//初始化头结点,头结点不要动,不存放数据
private HeroNode head = new HeroNode(0, "", "");
/**
* 添加节点到单向列表
* @param heroNode
*/
public void add(HeroNode heroNode) {
// head不能动,需要辅助遍历temp
HeroNode temp = head;
while (true) {
// 找到链表最后
if (temp.next == null) {
break;
}
//如果没有找到最后,将temp后移
temp = temp.next;
}
//退出while时,temp就指向了链表的最后
// 将最后这个节点的next指向新的结点
temp.next = heroNode;
}
显示链表
只要结点的next不指向空,那么就输出这个结点的信息,否则输出最后一个结点的信息然后结束。
/**
*
* 显示链表
*/
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 = temp.next;
}
}
添加元素到指定位置
当添加元素到链表中间,涉及到如下操作,头一个结点指针指向新结点,新结点的指针指向原先的后一个结点。即
heroNode.next = temp.next;
temp.next = heroNode;
/**
* 添加元素到指定位置
* @param heroNode
*/
public void addByOrder(HeroNode heroNode) {
//因为是单链表,所以我们的temp是添加位置的前一个结点
HeroNode temp = head;
boolean flag = false; //标志添加的编号是否存在,默认为false
while (true) {
if (temp.next == null) { //temp已经位于链表的最后
break;
}
if (temp.next.no > heroNode.no) { //位置找到,在temp的后面插入
break;
} else if (temp.next.no == heroNode.no) { //说明希望添加的heroNode编号已然存在
flag = true;
break;
}
temp = temp.next; //后移,遍历当前链表
}
if (flag) {
System.out.printf("准备插入的元素编号%d 已经存在了,不能加入\n", heroNode.no);
} else {
heroNode.next = temp.next;
temp.next = heroNode;
}
}
修改结点信息
注意判断条件,修改信息只是简单的赋值语句
/**
* 修改结点的信息
* @param newHeroNode
*/
public void update(HeroNode newHeroNode) {
//判断是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
HeroNode temp = head.next;
boolean flag = false;
while (true) {
if (temp == null) {
break; //已经遍历完了链表
}
if (temp.no == newHeroNode.no) {
//找到
flag = true;
break;
}
temp = temp.next;
}
//根据flag 判断是否找到要修改的结点
if (flag) {
temp.name = newHeroNode.name;
temp.nickname = newHeroNode.nickname;
} else { //没有找到
System.out.println("没有找到需要修改的结点的信息");
}
}
删除元素
例如我们要把刚才添加的元素删除,
只需把next重新指向原先的后一个元素
/*
* 删除元素
* @param no
*
*/
public void del(int no) {
HeroNode temp = head;
boolean flag = false;
while (true) {
if (temp.next == null) { //已到链表最后
break;
}
if (temp.next.no == no) {
//找到待删除结点的前一个结点temp
flag = true;
break;
}
temp = temp.next; //temp后移
}
if(flag) { //找到
temp.next = temp.next.next;
} else{
System.out.printf("要删除的%d 结点不存在\n",no);
}
}
测试
//测试
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.addByOrder(hero1);
singleLinkedList.addByOrder(hero2);
singleLinkedList.addByOrder(hero3);
singleLinkedList.addByOrder(hero4);
//显示
singleLinkedList.list();
//修改测试
HeroNode newHeroNode = new HeroNode(2, "小卢", "王齐麟");
singleLinkedList.update(newHeroNode);
System.out.println("修改后的链表情况");
singleLinkedList.del(3);
//显示
singleLinkedList.list();
结果
HeroNode{no=1, name='宋江', nickname='及时雨}
HeroNode{no=2, name='卢俊义', nickname='王齐麟}
HeroNode{no=3, name='吴用', nickname='智多星}
HeroNode{no=4, name='林冲', nickname='豹子头}
修改后的链表情况
HeroNode{no=1, name='宋江', nickname='及时雨}
HeroNode{no=2, name='小卢', nickname='王齐麟}
HeroNode{no=4, name='林冲', nickname='豹子头}
完整代码
public class LinkedList {
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.addByOrder(hero1);
singleLinkedList.addByOrder(hero2);
singleLinkedList.addByOrder(hero3);
singleLinkedList.addByOrder(hero4);
//显示
singleLinkedList.list();
//修改测试
HeroNode newHeroNode = new HeroNode(2, "小卢", "王齐麟");
singleLinkedList.update(newHeroNode);
System.out.println("修改后的链表情况");
singleLinkedList.del(3);
//显示
singleLinkedList.list();
}
}
// 定义SingleLinkedList 管理英雄
class SingleLinkedList {
//初始化头结点,头结点不要动,不存放数据
private HeroNode head = new HeroNode(0, "", "");
/**
* 添加节点到单向列表
* @param heroNode
*/
public void add(HeroNode heroNode) {
// head不能动,需要辅助遍历temp
HeroNode temp = head;
while (true) {
// 找到链表最后
if (temp.next == null) {
break;
}
//如果没有找到最后,将temp后移
temp = temp.next;
}
//退出while时,temp就指向了链表的最后
// 将最后这个节点的next指向新的结点
temp.next = heroNode;
}
/**
* 添加元素到指定位置
* @param heroNode
*/
public void addByOrder(HeroNode heroNode) {
//因为是单链表,所以我们的temp是添加位置的前一个结点
HeroNode temp = head;
boolean flag = false; //标志添加的编号是否存在,默认为false
while (true) {
if (temp.next == null) { //temp已经位于链表的最后
break;
}
if (temp.next.no > heroNode.no) { //位置找到,在temp的后面插入
break;
} else if (temp.next.no == heroNode.no) { //说明希望添加的heroNode编号已然存在
flag = true;
break;
}
temp = temp.next; //后移,遍历当前链表
}
if (flag) {
System.out.printf("准备插入的元素编号%d 已经存在了,不能加入\n", heroNode.no);
} else {
heroNode.next = temp.next;
temp.next = heroNode;
}
}
/**
* 修改结点的信息
* @param newHeroNode
*/
public void update(HeroNode newHeroNode) {
//判断是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
HeroNode temp = head.next;
boolean flag = false;
while (true) {
if (temp == null) {
break; //已经遍历完了链表
}
if (temp.no == newHeroNode.no) {
//找到
flag = true;
break;
}
temp = temp.next;
}
//根据flag 判断是否找到要修改的结点
if (flag) {
temp.name = newHeroNode.name;
temp.nickname = newHeroNode.nickname;
} else { //没有找到
System.out.println("没有找到需要修改的结点的信息");
}
}
/*
* 删除元素
* @param no
*
*/
public void del(int no) {
HeroNode temp = head;
boolean flag = false;
while (true) {
if (temp.next == null) { //已到链表最后
break;
}
if (temp.next.no == no) {
//找到待删除结点的前一个结点temp
flag = true;
break;
}
temp = temp.next; //temp后移
}
if(flag) { //找到
temp.next = temp.next.next;
} else{
System.out.printf("要删除的%d 结点不存在\n",no);
}
}
/**
*
* 显示链表
*/
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 = temp.next;
}
}
}
/**
* 定义结点, 每个对象是一个结点
*
*/
class HeroNode {
public int no;
public String name;
public String nickname;
public HeroNode next; //指向下一个结点
/**
* 构造器
* @param no
* @param name
* @param nickname
*/
public HeroNode(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}
/**
* 为了显示方便,重写toString
* @return
*/
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='"+ nickname + "}";
}
}
双向链表
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。
定义节点,注意,此时要添加一个pre信息
/*
定义结点, 每个对象是一个结点
*/
class HeroNode2 {
public int no;
public String name;
public String nickname;
public HeroNode2 next; //指向下一个结点
public HeroNode2 pre;
/*
* 构造器
*/
public HeroNode2(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}
/**
* 为了显示方便,重写toString
*
* @return
*/
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + "}";
}
}
初始化头结点以及遍历双向链表
//初始化一个头结点,不存放具体数据
private HeroNode2 head = new HeroNode2(0, "", "");
/*
返回头结点
*/
public HeroNode2 getHead() {
return head;
}
/*
遍历双向链表
*/
public void list() {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
//头节点不能动,需要辅助变量遍历
HeroNode2 temp = head.next;
while (true) {
// 判断是否到链表最后
if (temp == null) {
break;
}
//输出结点信息
System.out.println(temp);
//temp后移
temp = temp.next;
}
}
向双向链表最后添加元素
将原先最后一元素的next指向新结点,同时该结点指向前一结点
/*
添加节点到双向列表的最后
*/
public void add(HeroNode2 heroNode) {
// head不能动,需要辅助遍历temp
HeroNode2 temp = head;
while (true) {
// 找到链表最后
if (temp.next == null) {
break;
}
//如果没有找到最后,将temp后移
temp = temp.next;
}
//退出while时,temp就指向了链表的最后
//形成一个双向链表
temp.next = heroNode;
heroNode.pre = temp;
}
修改一个结点的内容(和单链表相同)
/*
修改一个结点的内容,和单向链表一样
*/
public void update(HeroNode2 newHeroNode) {
//判断是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
HeroNode2 temp = head.next;
boolean flag = false;
while (true) {
if (temp == null) {
break; //已经遍历完了链表
}
if (temp.no == newHeroNode.no) {
//找到
flag = true;
break;
}
temp = temp.next;
}
//根据flag 判断是否找到要修改的结点
if (flag) {
temp.name = newHeroNode.name;
temp.nickname = newHeroNode.nickname;
} else { //没有找到
System.out.println("没有找到需要修改的结点的信息");
}
}
删除一个结点
删除节点需要如图所示的指针转换操作,同时要用判断语句判断是否删除的是最后一个元素,如果是则不需要改变下一个结点的pre指针,因为下一个结点为空。
/*
删除一个结点
*/
public void del(int no) {
//判断当前链表是否为空
if (head.next == null) {
System.out.println("链表为空,无法删除");
return;
}
HeroNode2 temp = head.next; //辅助变量(指针)
boolean flag = false;
while (true) {
if (temp == null) { //已到链表最后
break;
}
if (temp.no == no) {
//找到待删除结点的前一个结点temp
flag = true;
break;
}
temp = temp.next; //temp后移
}
if (flag) { //找到
temp.pre.next = temp.next;
//避免出现最后一个元素出现空指针异常,所以加上判断语句
if (temp.next != null) {
temp.next.pre = temp.pre;
} else {
System.out.printf("要删除的%d 结点不存在\n", no);
}
}
}
完整代码
public class LinkedList2 {
public static void main(String[] args) {
//test
System.out.println("双向链表测试");
//创建节点
HeroNode2 hero1 = new HeroNode2(1, "宋江", "及时雨");
HeroNode2 hero2 = new HeroNode2(2, "卢俊义", "王齐麟");
HeroNode2 hero3 = new HeroNode2(3, "吴用", "智多星");
HeroNode2 hero4 = new HeroNode2(4, "林冲", "豹子头");
//创建一个双向链表
doublelinkedlist doublelinkedlist = new doublelinkedlist();
doublelinkedlist.add(hero1);
doublelinkedlist.add(hero2);
doublelinkedlist.add(hero3);
doublelinkedlist.add(hero4);
doublelinkedlist.list();
//修改
HeroNode2 newHeroNode = new HeroNode2(2,"公孙胜","入云龙");
System.out.println("修改后~~~~");
doublelinkedlist.list();
//删除
doublelinkedlist.del(3);
System.out.println("删除后~~~~");
doublelinkedlist.list();
}
}
/*
定义一个双向链表
*/
class doublelinkedlist {
//初始化一个头结点,不存放具体数据
private HeroNode2 head = new HeroNode2(0, "", "");
/*
返回头结点
*/
public HeroNode2 getHead() {
return head;
}
/*
遍历双向链表
*/
public void list() {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
//头节点不能动,需要辅助变量遍历
HeroNode2 temp = head.next;
while (true) {
// 判断是否到链表最后
if (temp == null) {
break;
}
//输出结点信息
System.out.println(temp);
//temp后移
temp = temp.next;
}
}
/*
添加节点到双向列表的最后
*/
public void add(HeroNode2 heroNode) {
// head不能动,需要辅助遍历temp
HeroNode2 temp = head;
while (true) {
// 找到链表最后
if (temp.next == null) {
break;
}
//如果没有找到最后,将temp后移
temp = temp.next;
}
//退出while时,temp就指向了链表的最后
//形成一个双向链表
temp.next = heroNode;
heroNode.pre = temp;
}
/*
修改一个结点的内容,和单向链表一样
*/
public void update(HeroNode2 newHeroNode) {
//判断是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
HeroNode2 temp = head.next;
boolean flag = false;
while (true) {
if (temp == null) {
break; //已经遍历完了链表
}
if (temp.no == newHeroNode.no) {
//找到
flag = true;
break;
}
temp = temp.next;
}
//根据flag 判断是否找到要修改的结点
if (flag) {
temp.name = newHeroNode.name;
temp.nickname = newHeroNode.nickname;
} else { //没有找到
System.out.println("没有找到需要修改的结点的信息");
}
}
/*
删除一个结点
*/
public void del(int no) {
//判断当前链表是否为空
if (head.next == null) {
System.out.println("链表为空,无法删除");
return;
}
HeroNode2 temp = head.next; //辅助变量(指针)
boolean flag = false;
while (true) {
if (temp == null) { //已到链表最后
break;
}
if (temp.no == no) {
//找到待删除结点的前一个结点temp
flag = true;
break;
}
temp = temp.next; //temp后移
}
if (flag) { //找到
temp.pre.next = temp.next;
//避免出现最后一个元素出现空指针异常,所以加上判断语句
if (temp.next != null) {
temp.next.pre = temp.pre;
} else {
System.out.printf("要删除的%d 结点不存在\n", no);
}
}
}
}
/*
定义结点, 每个对象是一个结点
*/
class HeroNode2 {
public int no;
public String name;
public String nickname;
public HeroNode2 next; //指向下一个结点
public HeroNode2 pre;
/*
* 构造器
*/
public HeroNode2(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}
/**
* 为了显示方便,重写toString
*
* @return
*/
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + "}";
}
}
执行结果
双向链表测试
HeroNode{no=1, name='宋江', nickname='及时雨}
HeroNode{no=2, name='卢俊义', nickname='王齐麟}
HeroNode{no=3, name='吴用', nickname='智多星}
HeroNode{no=4, name='林冲', nickname='豹子头}
修改后~~~~
HeroNode{no=1, name='宋江', nickname='及时雨}
HeroNode{no=2, name='卢俊义', nickname='王齐麟}
HeroNode{no=3, name='吴用', nickname='智多星}
HeroNode{no=4, name='林冲', nickname='豹子头}
删除后~~~~
HeroNode{no=1, name='宋江', nickname='及时雨}
HeroNode{no=2, name='卢俊义', nickname='王齐麟}
HeroNode{no=4, name='林冲', nickname='豹子头}
Process finished with exit code 0