前言:
大家好,我是良辰丫,上一篇文章我和大家一起去探索了单链表的知识世界,今天我们去接触双向链表,什么?没听错,就是双向链表,比单链表更炫酷的一种链表,首先祝大家新年快乐,新年新气象,希望大家能够在新的一年心想事成。然后请跟随我的脚步,去学习了解双向链表。。
个人主页:良辰针不戳
所属专栏:java数据结构
励志语句:生活也许会让我们遍体鳞伤,但最终这些伤口会成为我们一辈子的财富。
期待大家三连,关注,点赞,收藏。
作者能力有限,可能也会出错,欢迎大家指正。
愿与君为伴,共探Java汪洋大海。
在双向链表中一个数据域会分散出两个指针域,一个pre指针指向上一个数据的地址,一个next指针指向下一个数据的地址。
下面为一个双向链表结构图,可能画的图有些抽象哈哈,是每个指针指向它所指的节点。
首先申请一个节点(基本套路),头结点为空时,说明双向链表中没有数据,那么头指针和尾指针都指向新节点。
头结点不为空时,进行头插操作,我们前面说过后绑思想,node节点先与头结点进行绑定。双向链表头插法比较特殊,两个指针,我们可以不按照后绑思想(还是建议大家就使用后绑思想,不容易出错),绑定完成后,不要忘了头指针前移。
public void addFirst(int data){
ListNode node = new ListNode(data);
if(head == null) {
head = node;
last = node;
}else {
node.next = head;
head.prev = node;
head = node;
}
}
- 双向链表尾插法时间复杂度为O(1),因为双向链表有尾指针,不用通过遍历寻找尾巴。
- 头结点为空时,直接让头指针和尾指针指向头结点。
- 然后通过尾指针进行连接操作。
- 最后尾指针后移。
//尾插法 O(1)
public void addLast(int data){
ListNode node = new ListNode(data);
if(head == null) {
head = node;
last = node;
}else {
last.next = node;
node.prev = last;
last = node;
}
}
- 插入位置不合法直接退出。
- 在第一个位置插入直接调用头插法。
- 在最后一个位置插入直接调用尾插法。
- 其它情况找到要插入的位置下标,还是采用后绑法,分为四步进行插入,大家灵活掌握哦!
//任意位置插入
public void addIndex(int index,int data){
if(index < 0 || index > size()) {
return ;
}
if(index == 0) {
addFirst(data);
return;
}
if(index == size()) {
addLast(data);
return;
}
ListNode cur = findIndex(index);
//
ListNode node = new ListNode(data);
node.next = cur;
cur.prev.next = node;
node.prev = cur.prev;
cur.prev = node;
}
private ListNode findIndex(int index) {
ListNode cur = head;
while (index != 0) {
cur = cur.next;
index--;
}
return cur;
}
查找关键词其实和单链表一样,遍历一次找到返回true,找不到返回false。
public boolean contains(int key){
ListNode cur = head;
while (cur != null) {
if(cur.val == key) {
return true;
}
cur = cur.next;
}
return false;
}
public void remove(int key){
ListNode cur = head;
while (cur != null) {
//开始删除了
if(cur.val == key) {
//1. 删除的是头节点
if(cur == head) {
head = head.next;
//只有一个节点
if(head != null) {
head.prev = null;
}
}else {
//中间 尾巴
cur.prev.next = cur.next;
//不是尾巴节点
if(cur.next != null) {
cur.next.prev = cur.prev;
}else {
//是尾巴节点
last = last.prev;
}
}
return;
}
cur = cur.next;
}
}
//删除所有值为key的节点
public void removeAllKey(int key){
ListNode cur = head;
while (cur != null) {
//开始删除了
if(cur.val == key) {
//1. 删除的是头节点
if(cur == head) {
head = head.next;
//只有一个节点
if(head != null) {
head.prev = null;
}
}else {
//中间 尾巴
cur.prev.next = cur.next;
//不是尾巴节点
if(cur.next != null) {
cur.next.prev = cur.prev;
}else {
//是尾巴节点
last = last.prev;
}
}
}
cur = cur.next;
}
}
和单链表一样,遍历一次,记录元素个数。
public int size(){
int len = 0;
ListNode cur = head;
while (cur != null) {
len++;
cur = cur.next;
}
return len;
}
遍历一次双向链表,依次打印数据。双向链表有前驱指针,因此也可以倒着打印。
public void display(){
ListNode cur = head;
while (cur != null) {
System.out.print(cur.val+" ");
cur = cur.next;
}
System.out.println();
}
遍历一次链表,释放所有指针指向,注意释放前要保存next域,防止找不到下一个位置。当然了,也可以通过粗鲁的释放,直接释放首尾节点。
public void clear(){
ListNode cur = head;
while (cur != null) {
ListNode curNext = cur.next;
cur.prev = null;
cur.next = null;
cur = curNext;
}
head = null;
last = null;
}
后序:
双向链表的内容和单链表很相似,但是这里的任意位置插入
刚开始学总会觉得有些奇怪,很正常的表现,其实没有大家想的那么难,多去思考,多去敲代码,你会有很好的收获!!!