【数据结构】java实现双向链表
2022-12-09 11:45:13 TwelveT 138
一、链表基础链表是一种常见的数据结构,其中运用到了结构体指针,链表可以实现动态存储分配,换而言之,链表是一个功能强大的数组,可以在某个节点定义多种数据类型,可以实现任意的添加,删除,插入节点等。链表分为两类,一种是有头节点的链表,另一种是无头节点的链表。头节点没有数据域的,其他每个节点都有两部分,一是数据域,二是指针域...
一、双向链表
- 双向链表的定义:在单链表的每个结点里再增加一个指向其直接前驱的指针域prior,这样链表中就形成了有两个方向不同的链,故称为双向链表。
- 优点:双向链表可以克服单链表查找链表中某结点不方便的缺点。
- 双向循环链表:和单链的循环表类似,双向链表也可以有循环表。让头结点的前驱指针指向链表的最后一个结点,让最后一个结点的后继指针指向头结点。
二、双向链表的特点与算法操作
- 双向链表结构的对称性:设指针P指向某一结点,侧有:p->next->prior=p=p->prior->next
- 在双向链表中有些操作(如: ListLength、GetElem等) ,因仅涉及一个方向的指针,故它们的算法与线性链表的相同。但在插入、删除时,则需同时修改两个方向上的指针,则与单链表不相同。

三、代码实现
1.创建链表节点
/**
* 定义HerNode,每个HeroNode对象就是一个节点
*/
class DoubleHeroNode {
public int no;
public String name;
public String nickname;
// 指向下一个节点,默认null
public DoubleHeroNode next;
// 指向前一个节点,默认null
public DoubleHeroNode pre;
public DoubleHeroNode(int hNo, String hName, String hNikeName) {
this.no = hNo;
this.name = hName;
this.nickname = hNikeName;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
2.链表实现
/**
* 双向链表
*/
public class DoubleLinkedList {
private static final Logger log = LoggerFactory.getLogger(DoubleLinkedList.class);
private final DoubleHeroNode head = new DoubleHeroNode(0, "", "");
public DoubleHeroNode getHead() {
return head;
}
/**
* 添加节点到单项链表
* 当不考虑编号顺序时
* 找到当前链表的最后节点
* 将最后的这个节点的next,指向新的节点
*
* @param heroNode HeroNode
*/
public void add(DoubleHeroNode heroNode) {
// 因为head节点不能动,因此我们需要一个辅助变量遍历temp
DoubleHeroNode temp = head;
// 遍历链表,找到最后
while (temp.next != null) {
// 找到链表最后
// 如果没有找到最后,将temp后移
temp = temp.next;
}
// 当退出while循环时,temp就指向了链表的最后
// 形成一个双向链表
temp.next = heroNode;
heroNode.pre = temp;
}
/**
* 修改节点的信息,根据no标号来修改,即no编号不能改
*
* @param newHeroNode HeroNode
*/
public void update(DoubleHeroNode newHeroNode) {
if (head.next == null) {
log.error("链表为空");
return;
}
// 找到需要修改的接地那,根据no标号
// 定义一个辅助变量
DoubleHeroNode 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 {
log.error("没有找到编号为{}的节点,不能被修改\n", newHeroNode.no);
}
}
/**
* 删除节点
* 对于双向链表,我们可以直接找到要删除的这个节点
* 找到后,自我删除即可
*/
public void delete(int no) {
// 判断当前链表是否为空
if (head.next == null) {
log.error("当前链表为空,无法删除");
return;
}
DoubleHeroNode temp = head.next;
// 标记是否找到待删除节点
boolean flag = false;
while (true) {
if (temp == null) {
// 已经找到脸部的最后
break;
}
if (temp.no == no) {
// 找到的待删除节点的前一个节点temp
flag = true;
break;
}
// temp 后移,遍历
temp = temp.next;
}
// 判断flag
if (flag) {
// 可以删除
temp.pre.next = temp.next;
// 最后一个节点不需要执行,否则空指针
if (temp.next != null) {
temp.next.pre = temp.pre;
}
} else {
log.error("要删除的{}节点不存在", no);
}
}
/**
* 第二种方式在天津英雄时,根据排名将英雄插入到指定位置
* 如果有这个排名,则天津失败,并给出提示
*/
public void addOrder(DoubleHeroNode heroNode) {
// 因为头节点不能动,因此我们任然通过一个辅助指针(变量)
// 因为单链表,找的temp是位于添加位置的前一个节点,否则插入不了
DoubleHeroNode temp = head;
// flag标志添加的编号是否存在,默认为false
boolean flag = false;
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.no > heroNode.no) {
// 位置找到,就在temp的后面char
break;
} else if (temp.next.no == heroNode.no) {
// 说明希望添加的heroNode的编号已然存在
flag = true;
break;
}
// 后移,遍历当前链表
temp = temp.next;
}
// 不能移动,说明编号存在
if (flag) {
log.error("准备插入的英雄编号:{}已经存在了,不能加入\n", heroNode.no);
} else {
// 插入到链表中,temp的后面
heroNode.next = temp.next;
temp.next = heroNode;
}
}
/**
* 遍历双向链表
*/
public void list() {
// 判断是否为空
if (head.next == null) {
log.error("链表为空");
return;
}
// 因为头节点,不能动,因此我们需要一个辅助变量来遍历
DoubleHeroNode temp = head.next;
while (temp != null) {
// 判断是否到链表最后
log.info("{}", temp);
// 将temp后移,一定小心
temp = temp.next;
}
}
}
免责声明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络收集整理,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。如果您喜欢该程序和内容,请支持正版,购买注册,得到更好的正版服务。我们非常重视版权问题,如有侵权请邮件与我们联系处理。敬请谅解!
![]() |
- 上一篇:【数据结构】约瑟夫链表-环形链表
- 下一篇:【数据结构】java实现单向链表
相关文章
-
12-09【数据结构】约瑟夫链表-环形链表
-
12-06【数据结构】java实现单向链表