本篇内容介绍了“什么是双链表”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
逻辑上它们均是线性表的链式实现,主要的区别是节点结构上的构造有所区别,这个区别从而引起操作的一些差异。 单链表:
单链表的一个节点,有储存数据的data
,还有后驱节点next
(指针)。也就是这个单链表想要一些遍历的操作都得通过前节点—>后节点。
双链表:
双链表的一个节点,有存储数据的data
,也有后驱节点next
(指针),这和单链表是一样的,但它还有一个前驱节点pre
(指针)。
上文讲单链表的时候,我们当时设计的是一个带头结点的链表就错过了不带头结点操作方式,这里双链表咱们就不带头结点设计实现。并且上文单链表实现的时候是没有尾指针tail的,在这里我们设计的双链表带尾指针。
所以我们构造的这个双链表是:不带头节点、带尾指针(tail)、双向链表。
对于node节点:
class node<t> {
T data;
node<t> pre;
node<t> next;
public node() {
}
public node(T data) {
this.data = data;
}
}
对于链表:
public class doubleList<t> {
private node<t> head;// 头节点
private node<t> tail;// 尾节点
private int length;
//各种方法
}
对于一个链表主要的操作还是增删。增删的话不光需要考虑链表是否带头节点,还有头插、尾插、中间插等多种插入删除形式,其中的一些细节处理也是比较重要的(防止链表崩掉出错),如果你对这块理解不够深入很容易写错也很难排查出来。当然,链表的查找、按位修改操作相比增删操作还是容易很多。
双链表在最初的时候头指针指向为null
。那么对于这个不带头节点的双链表而言。它的head
始终指向第一个真实有效的节点。tail
也指向最后一个有效的节点。在最初的时候head=null
,并且tail=head
,此时链表为空,等待节点插入。
public doubleList() {
head = null;
tail = head;
length = 0;
}
空链表插入
对于空链表来说。增加第一个元素可以特殊考虑。因为在链表为空的时候head
和tail
均为null。但head和tail又需要实实在在指向链表中的真实数据(带头指针就不需要考虑)。所以这时候就新建一个node
让head、tail等于它。
node<t> teamNode = new node(data);
if (isEmpty()) {
head = teamNode;
tail = teamNode;
}
头插
>对于头插入来说。步骤很简单,只需考虑head节点的变化。
新建插入节点node
head前驱指向node
node后驱指向head
head指向node。(这时候head只是表示第二个节点,而head需要表示第一个节点故改变指向)
尾插:
>对于尾插入来说。只需考虑尾节点tail节点的变化。
新建插入节点node
node前驱指向tail
tail后驱指向node
tail指向node。(这时候tail只是表示倒数第二个节点,而tail需要表示最后节点故指向node)
按编号插入
>对于编号插入来说。要考虑查找和插入两部,而插入既和head无关也和tail无关。
1 新建插入节点node
2 找到欲插入node的前一个节点preNode
。和后一个节点nextNode
3 node后驱指向nextNode,nextNode前驱指向node(此时node和后面与链表已经联立,但是和前面处理分离状态)
4 preNode后驱指向node。node前驱指向preNode(此时插入完整操作完毕)
整个流程的动态图为:
只有单个节点删除
>无论头删还是尾删,遇到单节点删除的需要将链表从新初始化!
if (length == 1)// 只有一个元素
{
head = null;
tail = head;
length--;
}
头删除
>头删除需要注意的就是删除不为空时候头删除只和head节点有关
流程大致分为:
1 head节点的后驱节点的前指针pre改为null。(head后面节点本指向head但是要删除第一个先让后面那个和head断绝关系
)
2 head节点指向head.next(这样head就指向我们需要的第一个节点了,前面节点就被删除成功,如果有c++等语言第一个被孤立的节点删除释放即可,但Java会自动释放)
尾删除
>尾删除需要注意的就是删除不为空时候尾删除只和tail节点有关。记得在普通链表中,我们删除尾节点需要找到尾节点的前驱节点。需要遍历整个表,而双向链表可以直接从尾节点遍历到前面。
尾删除就是删除双向链表中的最后一个节点,也就是尾指针所指向的那个节点,思想和头删除的思想一致,具体步骤为:
tail.pre.next=null
尾节点的前一个节点(pre)的后驱节点等于null
tail=tail.pre
尾节点指向它的前驱节点,此时尾节点由于步骤1
next已经为null。完成删除
普通删除
>普通删除需要重点掌握,普通删除要妥善处理好待删除节点的前后关系,具体流程如下:
1:找打待删除节点node的前驱节点prenode(prenode.next是要删除的节点)
2 : prenode.next.next.pre=prenode
.(将待删除node的后驱节点aftnode的pre指针指向prenode,等价于aftnode.pre=prenode
)
3: prenode.next=prenode.next.next;
此时node被跳过成功删除。
完成删除整个流程的动态图为:
通过上面的思路简单的实现一下双链表,当然有些地方命名不太规范,实现效率有待提升,主要目的还是带着大家理解。
代码:
/*
* 不带头节点的
*/
public class doubleList<t> {
class node<t> {
T data;
node<t> pre;
node<t> next;
public node() {
}
public node(T data) {
this.data = data;
}
}
private node<t> head;// 头节点
private node<t> tail;// 尾节点
private int length;
public doubleList() {
head = null;
tail = head;
length = 0;
}
boolean isEmpty() {
return length == 0 ? true : false;
}
void addFirst(T data) {
node<t> teamNode = new node(data);
if (isEmpty()) {
head = teamNode;
tail = teamNode;
} else {
teamNode.next = head;
head = teamNode;
}
length++;
}
void add(T data)// 默认尾节点插入
{
node<t> teamNode = new node(data);
if (isEmpty()) {
head = teamNode;
tail = teamNode;
} else {
tail.next = teamNode;
teamNode.pre=tail;
tail = teamNode;
}
length++;
}
int length()
{
return length;
}
T getElum(int index)//为了简单统一从头找
{
node<t> team=head;
for(int i=0;i<index;i++) 不带头节点 遍历次数-1 { team="team.next;" } return team.data; void add(int index, t data) 编号插入 if (index="=" 0) addfirst(data); else length) add(data); 重头戏 node teampre="teampre.next;" 为插入的前驱 无头节点,index-1位置找到前驱节点 for (int i < index -1; i++) node<t> team = new node(data);// a c 中插入B 找打a
team.next = teampre.next;// B.next=c
teampre.next.pre = team;// c.pre=B
team.pre = teampre;// 关联a B
teampre.next = team;
length++;
}
}
void deleteFirst()// 头部删除
{
if (length == 1)// 只有一个元素
{
head = null;
tail = head;
length--;
} else {
head = head.next;
length--;
}
}
void deleteLast() {
if(length==1)
{
head=null;
tail=head;
length--;
}
else {
tail.pre.next=null;
tail=tail.pre;
length--;
}
}
void delete(int index)
{
if(index==0)deleteFirst();
else if (index==length-1) {
deleteLast();
}
else {//删除 为了理解统一从头找那个节点
node<t>team=head;
for(int i=0;i<index-1;i++) { team } 此时为要删除的前节点 a c 插入b a为team team.next.next.pre="team;//c的前驱变成a" team.next="team.next.next;//a的后驱变成c" length--; void set(int index,t data) node<t>team=head;
for(int i=0;i<index-1;i++) { team="team.next;" } team.data="data;" @override public string tostring() node<t> team = head;
String vaString = "";
while (team != null) {
vaString += team.data + " ";
team = team.next;
}
return vaString;
}
}
测试:
“什么是双链表”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/u/4160637/blog/5048023