您的位置:首页 > 理论基础 > 数据结构算法

Java数据结构与算法------双向链表

2020-07-07 19:04 148 查看

单链表的每个结点包含一个数据域和一个指向下一结点的指针域
相对于单链表,双向链表多了一个指向前一个结点的指针

创建结点

class HeroNode2{
public int no;//英雄的编号
public String name;//英雄的名字
public String nickname;//英雄的昵称
public HeroNode2 next;//指向下一个结点
public HeroNode2 pre;//指向前一个结点
//构造器
public HeroNode2(int hno,String hname,String hnickname) {
this.no=hno;
this.name=hname;
this.nickname=hnickname;
}

//为了显示方法,重写toString
@Override
public String toString() {
return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
}

}

插入结点(插入到链表尾部)

  1. 依然是先找到最后一个结点
  2. 使这个结点的next指向新的结点
  3. 使新结点的pre指向这个结点
public void add(HeroNode2 heronode) {
//因为head结点不能动,所以需要一个辅助遍历的temp
HeroNode2 temp = head;
//遍历链表,找到最后的结点
while(true) {
if(temp.next == null) {
break;
}
//如果没有没有找到最后结点
temp = temp.next;
}
//当退出循环时,temp就指向了链表的最后
//将最后这个结点的next 指向新的结点
temp.next=heronode;
heronode.pre=temp;
}

插入结点(考虑顺序的插入)

  1. 此时插入 ,当我们找到相应的位置后(temp)
  2. 先把新的结点的next指向temp.next
  3. 接着把temp.next.pre指向新的结点(这里因为可能该位置是最后一个结点,所以temp.next.pre无法执行,需要一个判断)
  4. 再把新结点的pre指向temp
  5. 最后把temp的next指向新的结点
public void addByOrder(HeroNode2 heronode) {
//还是头结点不能动,所以定义一个辅助的temp,帮助找到要添加的位置
//因为是单链表,因此temp要找到 位于添加位置的前一个结点,否则添加不了
HeroNode2 temp= head;
boolean flag = false;//标识添加的编号是否存在,默认为false
while(true) {
if(temp.next==null) {//说明此事temp已经在链表的最后
break;
}
if(temp.next.no>heronode.no) {//位置就在temp的后面插入(因为如果此时条件成立,那么说明要加入的结点排名就在temp与temp.next之间)
break;
}else if(temp.next.no==heronode.no) {//如果此时条件成立,说明此时新加入的结点的编号已经存在
flag = true;//说明编号存在
break;
}
temp=temp.next;

}
//判断flag的值
if(flag) {//此时说明要加入的结点的编号已经存在,因此不能加入
System.out.printf("准备插入的英雄编号%d已经存在,不能加入\n",heronode.no);
}
else {
//插入到链表中,temp的后面
heronode.next=temp.next;
if(temp.next!=null) {
temp.next.pre=heronode;
}
heronode.pre=temp;

temp.next=heronode;
}
}

修改结点
此处与单链表一致`

public void update(HeroNode2 newheronode) {//根据这个新结点的no来修改
//判断是否为空
if(head.next==null) {
System.out.println("链表为空");
return;
}
//找到需要修改的结点   还是先定义一个辅助结点
HeroNode2 temp = head;
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 {
System.out.printf("没有找到编号%d的结点",newheronode.no);
}
}

删除结点

  1. 在单链表中我们删除一个结点需要找到这个结点的上一个结点,而这个双链表删除结点只用找到这个结点就行
  2. 当我们找到这个结点temp后执行:temp.pre.next=temp.next 即把该结点的上一个结点的next指向该结点的下一个结点
  3. 接着执行:temp.next.pre=temp.pre(这里同样原因,需要一个判断),即把该结点的下一个结点的pre指向该结点的上一个结点
public void delete(int no) {
//判断是否为空
if(head.next==null) {
System.out.println("链表为空");
return;
}
HeroNode2 temp = head.next;
boolean flag = false;//判断是否找到需要删除结点的前一个结点
while(true) {
if(temp == null) {
break;//表示链表已经遍历结束
}
if(temp.no==no) {//找到了需要删除的结点
flag = true;
break;
}
temp=temp.next;
}
if(flag) {//temp为被删除结点的上一个结点,temp.next为被删除结点,temp.next.next为被删除结点的下一个结点
temp.pre.next=temp.next;
//这里有个问题:如果要删除的结点是最后一个结点,那么temp.next.pre则无法执行,所以要进行一个判断
if(temp.next!=null) {
temp.next.pre=temp.pre;
}
}else {
System.out.println("没有找到要删除的结点");
}
}

总代吗:

package Linkedlist;

public class doubleLinkList {

public static void main(String[] args) {
System.out.println("双向链表的测试");
//先创建几个结点
HeroNode2 heroNode = new HeroNode2(1,"宋江","及时雨");
HeroNode2 heroNode2 = new HeroNode2(2,"卢俊义","玉麒麟");
HeroNode2 heroNode3 = new HeroNode2(3,"吴用","智多星");
HeroNode2 heroNode4 = new HeroNode2(4,"林冲","豹子头");
//创建双向链表对象
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
//		doubleLinkedList.add(heroNode);
//		doubleLinkedList.add(heroNode2);
//		doubleLinkedList.add(heroNode3);
//		doubleLinkedList.add(heroNode4);

doubleLinkedList.addByOrder(heroNode4);
doubleLinkedList.addByOrder(heroNode2);
doubleLinkedList.addByOrder(heroNode3);
doubleLinkedList.addByOrder(heroNode);

doubleLinkedList.list();
System.out.println("修改后的链表情况");
//修改一个结点
HeroNode2 newheronode = new HeroNode2(3,"吴用","无量天尊");
doubleLinkedList.update(newheronode);
doubleLinkedList.list();

System.out.println("删除后的链表情况");
doubleLinkedList.delete(2);
doubleLinkedList.list();
}

}

class DoubleLinkedList{
//先初始化一个头结点,因为在遍历时,头结点在最前面,所以头结点不变,以防单链表找不到头结点
private static HeroNode2 head = new HeroNode2(0,"","");

public static HeroNode2 getHead() {
return head;
}

//显示链表
public void list() {
//先判断链表是否为空
if(head.next==null) {
System.out.println("链表为空");
return;
}
//因为头结点不能动,因此需要辅助变量遍历
HeroNode2 temp = head.next;
while(true) {
//判断是否到链表最后
if(temp==null) {
break;
}
//如果不为空,输出结点的信息
System.out.println(temp);
//将temp后移
temp=temp.next;
}
}
//添加一个结点内容
public void add(HeroNode2 heronode) {
//因为head结点不能动,所以需要一个辅助遍历的temp
HeroNode2 temp = head;
//遍历链表,找到最后的结点
while(true) {
if(temp.next == null) {
break;
}
//如果没有没有找到最后结点
temp = temp.next;
}
//当退出循环时,temp就指向了链表的最后
//将最后这个结点的next 指向新的结点
temp.next=heronode;
heronode.pre=temp;
}

//第二种添加方式,根据英雄的排名插入到指定位置(如果有这个排名则添加失败,即这名英雄已经在链表)
public void addByOrder(HeroNode2 heronode) {
//还是头结点不能动,所以定义一个辅助的temp,帮助找到要添加的位置
//因为是单链表,因此temp要找到 位于添加位置的前一个结点,否则添加不了
HeroNode2 temp= head;
boolean flag = false;//标识添加的编号是否存在,默认为false
while(true) {
if(temp.next==null) {//说明此事temp已经在链表的最后
break;
}
if(temp.next.no>heronode.no) {//位置就在temp的后面插入(因为如果此时条件成立,那么说明要加入的结点排名就在temp与temp.next之间)
break;
}else if(temp.next.no==heronode.no) {//如果此时条件成立,说明此时新加入的结点的编号已经存在
flag = true;//说明编号存在
break;
}
temp=temp.next;

}
//判断flag的值
if(flag) {//此时说明要加入的结点的编号已经存在,因此不能加入
System.out.printf("准备插入的英雄编号%d已经存在,不能加入\n",heronode.no);
}
else {
//插入到链表中,temp的后面
heronode.next=temp.next;
if(temp.next!=null) {
temp.next.pre=heronode;
}
heronode.pre=temp;

temp.next=heronode;
}
}

//根据编号修改结点信息,
public void update(HeroNode2 newheronode) {//根据这个新结点的no来修改
//判断是否为空
if(head.next==null) {
System.out.println("链表为空");
return;
}
//找到需要修改的结点   还是先定义一个辅助结点
HeroNode2 temp = head;
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 {
System.out.printf("没有找到编号%d的结点",newheronode.no);
}
}

//从双向链表中删除一个结点
//在单链表中需要找到要删除结点的前一个结点,而双向链表中可以直接找到要删除结点,找到后可自我删除
public void delete(int no) {
//判断是否为空
if(head.next==null) {
System.out.println("链表为空");
return;
}
HeroNode2 temp = head.next;
boolean flag = false;//判断是否找到需要删除结点的前一个结点
while(true) {
if(temp == null) {
break;//表示链表已经遍历结束
}
if(temp.no==no) {//找到了需要删除的结点
flag = true;
break;
}
temp=temp.next;
}
if(flag) {//temp为被删除结点的上一个结点,temp.next为被删除结点,temp.next.next为被删除结点的下一个结点
temp.pre.next=temp.next;
//这里有个问题:如果要删除的结点是最后一个结点,那么temp.next.pre则无法执行,所以要进行一个判断
if(temp.next!=null) {
temp.next.pre=temp.pre;
}
}else {
System.out.println("没有找到要删除的结点");
}
}

}

//创建一个结点
class HeroNode2{
public int no;//英雄的编号
public String name;//英雄的名字
public String nickname;//英雄的昵称
public HeroNode2 next;//指向下一个结点
public HeroNode2 pre;//指向前一个结点
//构造器
public HeroNode2(int hno,String hname,String hnickname) {
this.no=hno;
this.name=hname;
this.nickname=hnickname;
}

//为了显示方法,重写toString
@Override
public String toString() {
return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: