生活中,有一种单线联络的方式;即该工作者只有一个上级和一个下级,不与其他人发生工作联系。这种方式在计算机中也有很好的体现,例如数据结构中的链表。
链表
链表是一种在物理上非连续,非顺序的数据结构,是以若干个节点组成的链式存储形式。
上图是两种形式的链表:单向链表和双向链表,下面主要介绍单向链表。
单向链表
单向链表的每一个节点包含两部分:一部分为存储数据的变量Date,一部分是指向下一个节点的指针next,链表的第一个节点称为头结点,最后一个节点称为尾节点,尾节点的next指针指向空。
与数组按照下标来寻找元素不同,链表只能通过从头结点开始遍历,然后通过next指针来找到下一个节点。
单向链表的基本操作
1.查找节点
链表在查找节点元素的时候,只能从头节点开始一个一个节点遍历查找。
2.更新节点
先通过遍历查找到该节点,然后直接对该节点的数据进行替换即可完成节点的更新。
3.插入节点
- 尾部插入:直接把之后一个节点的next指针指向心插入的节点即可。
- 中间插入:新节点的next指针,指向插入位置的节点;插入位置前置节点的next指针,指向新节点。
- 头部插入:把新节点的next指向原链表的头节点;将新节点变成链表的头节点。
4.删除节点
- 尾部删除:将尾部节点的前置节点的next指针指向null即可。
- 中间删除:将需要删除的节点的前置节点的next指针,直接指向需要删除节点的后置节点即可。
- 头部删除:将链表的头部直接设置为原链表头节点的next指针即可。
以上是链表操作的基本思路,在链表操作中,一般分为两种情况:第一种是头节点不存储数据;第二种是头节点存储数据。但是整体上的操作思路不会有大的变化。
以下为头节点不存储有效数据的一些功能实现
/**
* 定义水浒英雄节点类
*/
class HeroNode{
public int rank;//排名
public String name;//姓名
public String nickname;//昵称
public HeroNode next = null;//指针: 指向下一个节点
/**
* 构造方法:用于构建英雄对象
* @param rank 排名
* @param name 姓名
* @param nickname 昵称
*/
public HeroNode(int rank, String name,String nickname){
this.rank = rank;
this.name = name;
this.nickname = nickname;
}
@Override
public String toString() {
return "HeroNode{" +
"rank=" + rank +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
/**
* 单向链表实现: 头结点不存储有效数据
*/
class SingleLinkedList{
//初始化头结点 头结点不存储有效数据
HeroNode head = new HeroNode(0,"","");
/**
* 遍历链表的有效数据
*/
public void showNode(){
//头节点的next指针指向空,表明该链表没有有效数据
if(head.next == null){
System.out.println(" 链表为空... ");
return;
}
//通过辅助指针temp操作数据
HeroNode temp = head.next;
while(true){
if(temp == null){//如果节点为空,表明链表已遍历完毕
return;
}
System.out.println(temp);//打印节点数据
temp = temp.next;//移动辅助指针指向下一个节点
}
}
/**
* 在链表末尾添加节点
* 遍历链表,通过当前节点的next指针指向是否为空来确定当前指针是否是尾节点
* 若当前指针是尾节点,则将当前节点的next指针指向要添加的节点
* 若当前指针不是尾节点,则继续遍历,直到找到尾节点
* @param node
*/
public void addNode(HeroNode node){
//定义辅助指针 指向头节点
HeroNode temp = head;
while(true){
if(temp.next == null){//如果当前节点的next指针指向空,表明为尾节点
temp.next = node;//将尾节点的next指向新节点,实现尾部插入。
return;
}
temp = temp.next;//移动辅助指针指向下一个节点
}
}
/**
* 根据排名将英雄插入指定位置(英雄排名从 1 开始增加)
* 遍历链表,当前节点的next指针指向的下一个节点的英雄排名大于要添加的英雄,则表名找到了添加位置
* 当前节点的next指针指向了null,则表明当前节点是尾节点,要添加的英雄排名数字比链表中任何的一个都大,所以直接添加到末尾即可
* 当前节点的next指针指向节点的英雄排名等于要添加的英雄,则表名该排名已存在英雄,做出相应提示
*/
public void addNodeByOrder(HeroNode heroNode){
//定义辅助指针 指向头节点
HeroNode temp = head;
while(true){
//在链表中没找到该节点的插入位置,将该节点插入到尾部
if(temp.next == null){//如果当前节点的next指针指向空,表明为尾节点
temp.next = heroNode;//将尾节点的next指向新节点,实现尾部插入。
return;
}
//在链表中找到了该节点的插入位置
if(temp.next.rank > heroNode.rank){
heroNode.next = temp.next;
temp.next = heroNode;
return;
}
if(temp.next.rank == heroNode.rank){
System.out.println("该排名已经存在,不能添加");
return;
}
temp = temp.next;
}
}
/**
* 通过英雄排名 修改节点信息
* 遍历链表,若当前节点为空,则表明该链表中没有该英雄 无法完成删除
* 若当前节点的英雄排名等于要修改的英雄排名,直接对英雄信息做出修改即可
*
* @param heroNode
*/
public void updateNode(HeroNode heroNode){
//定义辅助指针 指向头节点
HeroNode temp = head;
while(true){
if (temp != null){//当前节点不为空, 则判断当前节点是否为要修改的节点
if(temp.rank == heroNode.rank){//是需要修改的节点
//修改节点信息
temp.name = heroNode.name;
temp.nickname = heroNode.nickname;
return;
}
//不是需要修改的节点 则移动辅助指针指向下一个节点
temp = temp.next;
}else {//当前节点为空,表明链表中不存在该需要修改的节点
System.out.println("没有该排名的英雄,无法完成修改");
return;
}
}
}
/**
* 根据排名删除节点
* 遍历链表,判断当前节点的next指针指向的英雄是否为要删除的英雄
* 若是: 则将当前节点的next指针直接指向当前节点的next指针指向的节点的next指针的节点
* 若不是:继续遍历,当当前节点的next指针指向了空,则表明当前节点为尾节点,链表中没有要删除的那个节点
* @param rank
*/
public void deleteNode(int rank){
//定义辅助指针 指向头节点
HeroNode temp = head;
while (true){
//遍历寻找要删除节点的前置节点
if(temp.next != null){
if(temp.next.rank == rank){
temp.next = temp.next.next;
return;
}
//不是要删除的节点 则移动辅助指针指向下一个节点
temp = temp.next;
}else {
System.out.println("没有该节点,无法完成删除");
return;
}
}
}
}
/**
* 测试单向链表
*/
public class SingleLinkedListDemo {
public static void main(String[] args) {
//创建链表对象
SingleLinkedList singleLinkedList = new SingleLinkedList();
//创建节点对象
HeroNode hero1 = new HeroNode(1,"宋江","及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "小星星");
HeroNode hero4 = new HeroNode(6, "林冲", "豹子头");
//将节点添加到链表中
singleLinkedList.addNode(hero1);
singleLinkedList.addNode(hero2);
singleLinkedList.addNode(hero3);
singleLinkedList.addNode(hero4);
System.out.println("--------添加英雄到链表-----------");
//遍历链表
singleLinkedList.showNode();
System.out.println("-------------------");
System.out.println("----------按照排名添加英雄到链表---------");
HeroNode hero5 = new HeroNode(4, "公孙胜", "入云龙");
HeroNode hero6 = new HeroNode(5, "关胜", "大刀");
//按照排名添加英雄
singleLinkedList.addNodeByOrder(hero5);
singleLinkedList.addNodeByOrder(hero6);
//遍历链表
singleLinkedList.showNode();
System.out.println("-------------------");
System.out.println("---------修改英雄信息----------");
//修改英雄信息
hero3 = new HeroNode(3, "吴用", "智多星");
singleLinkedList.updateNode(hero3);
//遍历链表
singleLinkedList.showNode();
System.out.println("-------------------");
System.out.println("---------删除英雄----------");
//删除英雄
singleLinkedList.deleteNode(4);
//遍历链表
singleLinkedList.showNode();
System.out.println("-------------------");
}
}
当头节点存储有效数据时,通过分析主要修改的是插入和删除,还有遍历会有点小的变动。修改单链表实现类如下:
/**
* 单向链表实现: 头结点存储有效数据
*/
class SingleLinkedList{
//初始化头结点
HeroNode head;
boolean flag = true;
/**
* 遍历链表的有效数据
*/
public void showNode(){
if(head.next == null){
return;
}
//头结点不能移动 所以需要辅助节点
HeroNode temp = head;
while(true){
if(temp == null){
return;
}
System.out.println(temp);
temp = temp.next;
}
}
/**
* 在链表末尾添加节点
* @param node
*/
public void addNode(HeroNode node){
if(flag){
head = node;
flag = false;
return;
}
//头结点不能移动 所以需要辅助节点
HeroNode temp = head;
while(true){
if(temp.next == null){
temp.next = node;
return;
}
temp = temp.next;
}
}
/**
* 根据排名将英雄插入指定位置
*/
public void addNodeByOrder(HeroNode heroNode){
//判断头节点为空时
if(flag){
head = heroNode;
flag = false;
return;
}
HeroNode temp = head;
if(head != null && heroNode.rank < head.rank){
heroNode.next = temp;
head = heroNode;
return;
}
while(true){
if(temp.next == null){
temp.next = heroNode;
return;
}
if(temp.next.rank > heroNode.rank){
heroNode.next = temp.next;
temp.next = heroNode;
return;
}
if(temp.next.rank == heroNode.rank){
System.out.println("该排名已经存在,不能添加");
return;
}
temp = temp.next;
}
}
/**
* 通过英雄排名 修改节点信息
* @param heroNode
*/
public void updateNode(HeroNode heroNode){
HeroNode temp = head;
while(true){
if (temp != null){
if(temp.rank == heroNode.rank){
temp.name = heroNode.name;
temp.nickname = heroNode.nickname;
return;
}
temp = temp.next;
}else {
System.out.println("没有该排名的英雄,无法完成修改");
return;
}
}
}
/**
* 根据排名删除节点
* @param rank
*/
public void deleteNode(int rank){
//删除头节点
if(rank == head.rank){
head = head.next;
return;
}
//删除其他节点
HeroNode temp = head;
while (true){
if(temp.next != null){
if(temp.next.rank == rank){
temp.next = temp.next.next;
return;
}
temp = temp.next;
}else {
System.out.println("没有该节点,无法完成删除");
return;
}
}
}
}
以上主要是通过一个标志位实现的头节点存储有效数据,其实实现方式多种多样,欢迎大家给我提供各种思路来实现,同时帮助我查出我的代码错误之处,谢谢。