双向链表是对于单向链表的增强,建议先查看单向链表,掌握单向链表后再学习双向链表事半功倍
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。
说人话,每个节点都有两个变量,一个指向前一个节点,一个指向后一个节点。
使用带head头的单向链表实现输入学生信息,查询时根据学生id顺序显示
添加节点:根据学生id将学生信息插入到指定位置(若id存在则提醒添加失败)
添加(创建)
1.创建一个head头节点,作为单链表的头部
2.找到新添加节点的位置,可以通过辅助变量(指针)完成,用temp保存(新节点的前一个节点)
3.新节点.next = temp.next
4.temp.next.pre = 新节点
5.temp.next = 新节点
6.新节点.pre = temp
==注意:第三步与第五步顺序不能反,若先将当前节点的next指向新节点,则原链表截断后的元素则丢失。==
修改节点
1.找到将要修改的节点
2.将信息给原节点
删除节点
1.找到需要删除的节点。存为temp
2.temp.pre.next = temp.next
3.temp.next.pre = temp.pre;
查看节点
通过遍历进行查询
全部代码:
import java.util.Stack;
/**
* 双向链表
* @author 张家宝
* @date 2020/4/9
*/
public class DoublyLinkedListDemo {
public static void main(String[] args) {
//乱序添加
StudentNodeDoubly node1 = new StudentNodeDoubly(1,"唐僧");
StudentNodeDoubly node3 = new StudentNodeDoubly(3,"猪八戒");
StudentNodeDoubly node2 = new StudentNodeDoubly(2,"孙悟空");
StudentNodeDoubly node4 = new StudentNodeDoubly(4,"沙僧");
//创建链表
DoublyLinkedList doublyLinkedList = new DoublyLinkedList();
System.out.println("创建后查询:");
doublyLinkedList.show();
//将节点添加到链表中
doublyLinkedList.add(node1);
doublyLinkedList.add(node3);
doublyLinkedList.add(node2);
doublyLinkedList.add(node4);
System.out.println("添加后查询:");
doublyLinkedList.show();
//修改节点
StudentNodeDoubly node5 = new StudentNodeDoubly(4,"沙和尚");
doublyLinkedList.update(node5);
System.out.println("修改后查询:");
doublyLinkedList.show();
//删除节点
doublyLinkedList.delete(2);
System.out.println("删除后查询:");
doublyLinkedList.show();
//统计当前链表有效节点数
System.out.println("当前链表有效节点数为:"+doublyLinkedList.size());
//查找倒数第n个节点
System.out.println("倒数第1个节点为:"+doublyLinkedList.findLastIndexNode(1));
// //反转当前链表
// System.out.println("反转后查询:");
// doublyLinkedList.reverseList();
// doublyLinkedList.show();
//反转打印当前链表
System.out.println("反转打印:");
doublyLinkedList.reverseShow();
//合并链表
//创建第二个链表
DoublyLinkedList doublyLinkedList2 = new DoublyLinkedList();
StudentNodeDoubly node11 = new StudentNodeDoubly(6,"白骨精");
StudentNodeDoubly node12 = new StudentNodeDoubly(7,"牛魔王");
StudentNodeDoubly node13 = new StudentNodeDoubly(2,"孙悟空");
doublyLinkedList2.add(node11);
doublyLinkedList2.add(node12);
doublyLinkedList2.add(node13);
//进行合并
doublyLinkedList.addAll(doublyLinkedList2);
System.out.println("合并后打印:");
doublyLinkedList.show();
}
}
/**
* 双向链表
*/
class DoublyLinkedList{
//初始化一个头节点,头节点不存放具体数据
private StudentNodeDoubly head = new StudentNodeDoubly(0,"");
/**
* 添加节点到单向链表
* 思路:
* 1.找到新添加节点的位置
* 2.新节点.next = temp.next
* 3.temp.next.pre = 新节点
* 4.temp.next = 新节点
* 5.新节点.pre = temp
* 注意:第二步与第四步顺序不能反,若先将当前节点的next指向新节点,则原链表截断后的节点则丢失。
* @param studentNode
*/
public void add(StudentNodeDoubly studentNode){
//因为头节点不能动,所有我们需要一个辅助指针(变量)来协助找到添加的位置
//单链表结构,我们找到的temp是位于 添加位置 的前一个节点。
StudentNodeDoubly temp = head;
//标志着添加编号是否存在,默认为false;
boolean flag = false;
while(true){
//说明temp已经是链表的最后一个
if(temp.next == null){
break;
}
//找到要添加的位置,在temp后边添加节点
if(temp.next.id > studentNode.id){
break;
}
//说明该id已存在,更改验证存在标识为true
else if(temp.next.id == studentNode.id){
flag = true;
break;
}
//指针后移,遍历当前链表
temp = temp.next;
}
//判断是否进行添加操作
if(flag){
System.out.printf("待添加的学生id %d 已被占用,不可添加\n",studentNode.id);
}else{
//将新节点的下一个节点指向到新节点的next中
studentNode.next = temp.next;
//需判断当前节点后是否存在节点,若存在节点则需将当前节点之后的节点的前指针指向新节点(细品)
if (temp.next != null) {
//将当前节点的next.pre指向新节点
temp.next.pre = studentNode;
}
//将当前节点的next指向到新节点
temp.next = studentNode;
//将新节点的pre 指向当前节点
studentNode.pre = temp;
// System.out.println("添加成功!");
}
}
/**
* 修改节点,根据id进行修改,因此id不可更改
* 思路:
* 1.找到将要修改的节点
* 2.将信息给原节点
* @param studentNode
*/
public void update(StudentNodeDoubly studentNode){
//判断当前链表是否为空
if(head.next == null){
System.out.println("链表为空 ~~~~~ ");
return;
}
//找到将要修改的节点
StudentNodeDoubly temp = head.next;
//标识是否找到该节点
boolean flag = false;
while(true){
//说明temp已经是链表的最后一个
if(temp == null){
break;
}
//将节点进行比较
if(temp.id == studentNode.id){
flag = true;
break;
}
temp = temp.next;
}
//判断是否可以进行修改操作
if(flag){
temp.name = studentNode.name;
}else {
System.out.printf("没有找到 id为 %d 的节点,不可修改\n",studentNode.id);
}
}
/**
* 删除节点 ,根据id删除节点
* 思路:
* 1.如果指针指向了要删除的节点则无法删除,所有需要找到要删除的节点的上一个节点
* 2.temp.next = temp.next.next
* @param id
*/
public void delete(int id){
//因为头节点不能动,所有我们需要一个辅助指针(变量)来协助找到添加的位置
//单链表结构,我们找到的temp是位于 头节点之后的第一个节点。
StudentNodeDoubly temp = head.next;
//标识是否找到待删除的节点
boolean flag = false;
while (true){
//说明temp已经是链表的最后一个
if(temp == null){
break;
}
//将节点进行比较
if(temp.id == id){
flag = true;
break;
}
temp = temp.next;
}
//判断是否可以进行删除操作
if(flag){
//当前节点的前一个节点的下一个节点指向当前节点的下一个节点
temp.pre.next = temp.next;
//判断当前节点后是否存在节点,若存在节点则需将 当前节点之后的节点的前指针指向当前节点的前一个节点(细品)
if(temp.next!=null){
temp.next.pre = temp.pre;
}
}else {
System.out.printf("没有找到 id为 %d 的节点,无法删除\n",id);
}
}
/**
* 显示链表中所有节点
*/
public void show(){
//判断当前链表是否为空
if(head.next == null){
System.out.println("链表为空 ~~~~~ ");
return;
}
System.out.println("-----------------显示链表开始-------------------");
//因为头节点不能动,所有我们需要一个辅助指针(变量)来协助找到添加的位置
//单链表结构,我们找到的temp是位于 添加位置 的前一个节点。
StudentNodeDoubly temp = head;
while (true){
//说明temp已经是链表的最后一个
if(temp == null){
break;
}
//将节点进行打印
System.out.println(temp);
temp = temp.next;
}
System.out.println("-----------------显示链表结束-------------------");
}
/*----------- 拓展 ------------*/
/**
* 获取当前链表有效节点数
* 思路:遍历链表中的所有节点
* @return
*/
public int size(){
//空链表
if(head.next == null){
return 0;
}
int size = 0;
//定义一个辅助指针(变量)
StudentNodeDoubly temp = head.next;
while (temp != null){
size++;
//遍历当前链表下的节点
temp = temp.next;
}
return size;
}
/**
* 找到单链表中倒数第index个节点
* 思路:
* 1.判断节点是否存在
* 2.从第一个节点开始遍历,遍历(size - index)个节点
* @param index
* @return
*/
public StudentNodeDoubly findLastIndexNode(int index){
//空链表
if(head.next == null){
return null;
}
//判断节点数是否存在
if(index<=0 || index > size()){
return null;
}
//遍历第size - index的位置,该位置就是我们要找的节点
StudentNodeDoubly temp = head.next;
for (int i = 0;i < size() - index;i++){
temp = temp.next;
}
return temp;
}
/**
* 反转当前链表
*/
public void reverseList(){
//空链表或只有一个节点直接返回
if(head.next == null || head.next.next == null){
return;
}
//定义一个辅助指针(变量)
StudentNodeDoubly temp = head.next;
//指向当前节点的下一个节点,避免节点丢失
StudentNodeDoubly next = null;
//创建临时链表头节点
StudentNodeDoubly reverseHead = new StudentNodeDoubly(0,"");
//遍历原链表中的每一个节点将其取出放在临时链表reverseHead的最前端
while (temp != null){
//暂时保存当前节点后的节点
next = temp.next;
//将temp的下一个链表指向新链表的最前端
temp.next = reverseHead.next;
//将temp连接到新链表上
reverseHead.next = temp;
//遍历当前链表下的节点(指针后移)
temp = next;
}
head.next = reverseHead.next;
}
/**
* 反向打印单链表
* 使用Stack栈,利用Stack栈先进后出原则进行反向打印,反向打印不影响源数据结构
*/
public void reverseShow(){
//空链表
if(head.next == null){
return;
}
//创建一个栈,将各个节点压入栈
Stack<StudentNodeDoubly> stack = new Stack<StudentNodeDoubly>();
StudentNodeDoubly temp = head.next;
while(temp != null){
stack.add(temp);
//指针后移
temp = temp.next;
}
while (stack.size()>0){
//打印节点信息
System.out.println(stack.pop());
}
}
/**
* 合并/添加一个有序的单链表,合并后依然有序
*/
public void addAll(DoublyLinkedList doublyLinkedList){
//判断待添加链表有效节点数的数量
if(doublyLinkedList.size()==0)
{
return;
}
//将待添加链表加入当前链表中
while (doublyLinkedList.size()>0){
StudentNodeDoubly temp = doublyLinkedList.findLastIndexNode(1);
doublyLinkedList.delete(temp.id);
this.add(temp);
}
}
}
/**
* 定义一个StudentNode, 每个StudentNode对象就是一个链表中的节点
*/
class StudentNodeDoubly{
public int id;
public String name;
public StudentNodeDoubly next;
public StudentNodeDoubly pre;
public StudentNodeDoubly(int id,String name){
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "StudentNode{id=" + id +", name='" + name + "'}";
}
}