双向链表
管理单向链表的缺点分析:
单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找。单向链表不能自我删除,需要靠辅助节点,而双向链表,则可以自我删除,所以前面我们单链表删除时节点,总是找到temp,temp 是待删除节点的前一个节点(认真体会).
双向链表应用实例(增删改)
双向链表的操作分析和实现: 使用带head 头的双向链表实现–水浒英雄排行榜 对上图的说明: 分析双向链表的遍历,添加,修改,删除的操作思路===》代码实现
遍历:方和单链表一样,只是可以向前,也可以向后查找
添加(默认添加到双向链表的最后): 第一种无序: (1) 先找到双向链表的最后这个节点 (2) temp.next = newHeroNode (3) newHeroNode.pre = temp;(和单链表比改变的地方) 第二种有序: (1)新的节点.next = temp.next(重新连接,使断开数据1,4的连接线并且连接数据2,4) (2) 将temp.next = 新的节点(连接数据1和2) (3)newHeroNode.pre = temp;(和单链表比改变的地方)
修改思路和原来的单向链表一样.
删除:(如图所示) (1) 因为是双向链表,因此,我们可以实现自我删除某个节点 (2) 直接找到要删除的这个节点,比如temp (3) temp.pre.next = temp.next (4) temp.next.pre = temp.pre;
双向链表的代码实现
public static void main(String
[] args
) {
System
.out
.println("双向链表的测试");
HeroNode2 hero1
= new HeroNode2(1, "宋江", "及时雨");
HeroNode2 hero2
= new HeroNode2(2, "卢俊义", "玉麒麟");
HeroNode2 hero3
= new HeroNode2(3, "吴用", "智多星");
HeroNode2 hero4
= new HeroNode2(4, "林冲", "豹子头");
DoubleLinkedList doubleLinkedList
= new DoubleLinkedList();
doubleLinkedList
.addByOrder(hero1
);
doubleLinkedList
.addByOrder(hero4
);
doubleLinkedList
.addByOrder(hero2
);
doubleLinkedList
.addByOrder(hero3
);
doubleLinkedList
.list();
HeroNode2 newHeroNode
= new HeroNode2(4, "公孙胜", "入云龙");
doubleLinkedList
.update(newHeroNode
);
System
.out
.println("修改后的链表情况");
doubleLinkedList
.list();
doubleLinkedList
.del(3);
System
.out
.println("删除后的链表情况~~");
doubleLinkedList
.list();
}
}
class DoubleLinkedList {
private HeroNode2 head
= new HeroNode2(0, "", "");
public 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
.next
;
}
}
public void add(HeroNode2 heroNode
) {
HeroNode2 temp
= head
;
while (true) {
if (temp
.next
== null
) {
break;
}
temp
= temp
.next
;
}
temp
.next
= heroNode
;
heroNode
.pre
= temp
;
}
public void addByOrder(HeroNode2 heroNode
) {
HeroNode2 temp
= head
;
boolean flag
= false;
while (true) {
if (temp
.next
== null
) {
break;
}
if (temp
.next
.no
> heroNode
.no
) {
break;
} else if (temp
.next
.no
== heroNode
.no
) {
flag
= true;
break;
}
temp
= temp
.next
;
}
if (flag
) {
System
.out
.printf("准备插入的英雄的编号 %d 已经存在了, 不能加入\n", heroNode
.no
);
} else {
heroNode
.next
= temp
.next
;
temp
.next
= heroNode
;
heroNode
.pre
= temp
;
}
}
public void update(HeroNode2 newHeroNode
) {
if (head
.next
== null
) {
System
.out
.println("链表为空~");
return;
}
HeroNode2 temp
= head
.next
;
boolean flag
= false;
while (true) {
if (temp
== null
) {
break;
}
if (temp
.no
== newHeroNode
.no
) {
flag
= true;
break;
}
temp
= temp
.next
;
}
if (flag
) {
temp
.name
= newHeroNode
.name
;
temp
.nickname
= newHeroNode
.nickname
;
} else {
System
.out
.printf("没有找到 编号 %d 的节点,不能修改\n", newHeroNode
.no
);
}
}
public void del(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
.pre
.next
= temp
.next
;
if (temp
.next
!= null
) {
temp
.next
.pre
= temp
.pre
;
}
} else {
System
.out
.printf("要删除的 %d 节点不存在\n", no
);
}
}
}
class HeroNode2 {
public int no
;
public String name
;
public String nickname
;
public HeroNode2 next
;
public HeroNode2 pre
;
public HeroNode2(int no
, String name
, String nickname
) {
this.no
= no
;
this.name
= name
;
this.nickname
= nickname
;
}
@Override
public String
toString() {
return "HeroNode [no=" + no
+ ", name=" + name
+ ", nickname=" + nickname
+ "]";
}
}
单向环形链表
单向环形链表介绍
单向环形链表应用场景
Josephu(约瑟夫、约瑟夫环) 问题
Josephu 问题为:设编号为1,2,… n 的n 个人围坐一圈,约定编号为k(1<=k<=n)的人从1 开始报数,数到m 的那个人出列,它的下一位又从1 开始报数,数到m 的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
约瑟夫问题的示意图
Josephu 问题为:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。 n = 5 , 即有5个人 k = 1, 从第一个人开始报数 m = 2, 数2下 出圈的顺序: 2->4->1->5->3
提示:
用一个不带头结点的循环链表来处理Josephu 问题:先构成一个有n 个结点的单循环链表,然后由k 结点起从1 开始计数,计到m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1 开始计数,直到最后一个结点从链表中删除算法结束。
约瑟夫问题-创建环形链表的思路图解(添加,遍历)
构建一个单向的环形链表思路
先创建第一个节点, 让 first 指向该节点,并形成环形 (First指头不能动,因为遍历和创建的时候都需要尾指头所以不能动)后面当我们每创建一个新的节点,就把该节点,加入到已有的环形链表中即可. 代码:
if (i
== 1) {
first
= boy
;
first
.setNext(first
);
curBoy
= first
;
} else {
curBoy
.setNext(boy
);
boy
.setNext(first
);
curBoy
= boy
;
}
遍历环形链表
先让一个辅助指针(变量) curBoy,指向first节点然后通过一个while循环遍历 该环形链表即可 curBoy.next == first 结束
约瑟夫问题-小孩出圈的思路分析图
根据用户的输入,生成一个小孩出圈的顺序 n = 5 , 即有5个人 k = 1, 从第一个人开始报数 m = 2, 数2下
需求创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点. 补充: 小孩报数前,先让 first 和 helper 移动 k - 1次 (从第几个开始数,因为本来就在1了,所以k-1)当小孩报数时,让first 和 helper 指针同时 的移动 m - 1 次 (自己先喊了一次使用移动少一次)这时就可以将first 指向的小孩节点 出圈 first = first .next helper.next = first 原来first 指向的节点就没有任何引用,就会被回收 出圈的顺序:2->4->1->5->3
代码实现
public class Josepfu {
public static void main(String
[] args
) {
CircleSingleLinkedList circleSingleLinkedList
= new CircleSingleLinkedList();
circleSingleLinkedList
.addBoy(125);
circleSingleLinkedList
.showBoy();
circleSingleLinkedList
.countBoy(10, 20, 125);
}
}
class CircleSingleLinkedList {
private Boy first
= null
;
public void addBoy(int nums
) {
if (nums
< 1) {
System
.out
.println("nums的值不正确");
return;
}
Boy curBoy
= null
;
for (int i
= 1; i
<= nums
; i
++) {
Boy boy
= new Boy(i
);
if (i
== 1) {
first
= boy
;
first
.setNext(first
);
curBoy
= first
;
} else {
curBoy
.setNext(boy
);
boy
.setNext(first
);
curBoy
= boy
;
}
}
}
public void showBoy() {
if (first
== null
) {
System
.out
.println("没有任何小孩~~");
return;
}
Boy curBoy
= first
;
while (true) {
System
.out
.printf("小孩的编号 %d \n", curBoy
.getNo());
if (curBoy
.getNext() == first
) {
break;
}
curBoy
= curBoy
.getNext();
}
}
public void countBoy(int startNo
, int countNum
, int nums
) {
if (first
== null
|| startNo
< 1 || startNo
> nums
) {
System
.out
.println("参数输入有误, 请重新输入");
return;
}
Boy helper
= first
;
while (true) {
if (helper
.getNext() == first
) {
break;
}
helper
= helper
.getNext();
}
for(int j
= 0; j
< startNo
- 1; j
++) {
first
= first
.getNext();
helper
= helper
.getNext();
}
while(true) {
if(helper
== first
) {
break;
}
for(int j
= 0; j
< countNum
- 1; j
++) {
first
= first
.getNext();
helper
= helper
.getNext();
}
System
.out
.printf("小孩%d出圈\n", first
.getNo());
first
= first
.getNext();
helper
.setNext(first
);
}
System
.out
.printf("最后留在圈中的小孩编号%d \n", first
.getNo());
}
}
class Boy {
private int no
;
private Boy next
;
public Boy(int no
) {
this.no
= no
;
}
public int getNo() {
return no
;
}
public void setNo(int no
) {
this.no
= no
;
}
public Boy
getNext() {
return next
;
}
public void setNext(Boy next
) {
this.next
= next
;
}
}