目录
1. 双向链表的实现既增删查改
2. 单向环形链表的实现
3. 约瑟夫环问题(小孩报数出圈)
一. 双向链表
1. 单向链表,查找的方向只能是一个方向,而双向链表可以向前或向后查找
2. 单向链表不能自我删除,需要靠辅助节点,而双向链表则可以自我删除,所以前面我们单链表删除节点时,总是找到temp,temp是待删除节点的前一个节点。
双向链表有两个指针:Pre指向前一个节点,Next指向后一个节点
遍历方法和单链表一样,只是可以向前,也可以向后查找
添加(默认添加到双向链表的最后)
先找到双向链表最后的这个节点
temp.next = newNode
newNode.pre = temp
修改:思路和单向链表一样
删除:
因为是双向链表,因此,我们可以实现自我删除某个节点直接找到要删除的这个节点,比如temp
temp.pre.next = temp.next
temp.next.pre = temp.pre
双向链表的创建
class DoubleLinkedList {
private Node2 head = new Node2(0, "", "");
public Node2 getHead() {
return head;
}
}
class Node2 {
public int no;
public String name;
public String nickname;
public Node2 next;
public Node2 pre;
public Node2(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}
@Override
public String toString() {
return "Node2{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
通过序号顺序添加
public void addByOrder(Node2 node) {
Node2 temp = head;
boolean flag = false;
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.no > node.no) {
break;
} else if (temp.next.no == node.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
System.out.printf("准备插入的节点的编号%d已经存在了,不能加入\n", node.no);
} else {
node.next = temp.next;
node.pre = temp;
temp.next = node;
}
}
删除节点
在双向链表中删除节点需要注意要将:
(例如要删除的节点为temp节点)
该节点后继的前躯指针指向该节点的前躯
temp.next.pre = temp.pre;
该节点的前躯的后继指针指向该节点的后继
temp.pre.next = temp.next;
public void delete(int no) {
if (head.next == null) {
System.out.println("链表为空,无法删除");
return;
}
Node2 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的节点", no);
}
}
遍历
public void list() {
if (head.next == null) {
System.out.println("链表为空");
return;
}
Node2 temp = head.next;
while (true) {
if (temp == null) {
break;
}
System.out.println(temp);
temp = temp.next;
}
}
修改
public void update(Node2 newnode) {
if (head.next == null) {
System.out.println("链表为空");
return;
}
Node2 temp = head.next;
boolean flag = false;
while (true) {
if (temp == null) {
break;
}
if (temp.no == newnode.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.name = newnode.name;
temp.nickname = newnode.nickname;
} else {
System.out.printf("没有找到编号为%d的节点", newnode.no);
}
}
整体代码
package com.lmh.sparsearray;
public class DoubleLinkedListDemo {
public static void main(String[] args) {
System.out.println("双向链表的测试====");
Node2 node1 = new Node2(1, "老大", "老大");
Node2 node2 = new Node2(2, "老二", "老二");
Node2 node3 = new Node2(3, "小三", "小三");
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
doubleLinkedList.addByOrder(node1);
doubleLinkedList.addByOrder(node3);
doubleLinkedList.addByOrder(node2);
doubleLinkedList.list();
Node2 newNode = new Node2(3,"老四","老四");
doubleLinkedList.update(newNode);
System.out.println("修改后的链表");
doubleLinkedList.list();
doubleLinkedList.delete(2);
System.out.println("删除后的链表");
doubleLinkedList.list();
}
}
class DoubleLinkedList {
private Node2 head = new Node2(0, "", "");
public Node2 getHead() {
return head;
}
public void addByOrder(Node2 node) {
Node2 temp = head;
boolean flag = false;
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.no > node.no) {
break;
} else if (temp.next.no == node.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
System.out.printf("准备插入的节点的编号%d已经存在了,不能加入\n", node.no);
} else {
node.next = temp.next;
node.pre = temp;
temp.next = node;
}
}
public void delete(int no) {
if (head.next == null) {
System.out.println("链表为空,无法删除");
return;
}
Node2 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的节点", no);
}
}
public void add(Node2 node) {
Node2 temp = head;
while (true) {
if (temp.next == null) {
break;
}
temp = temp.next;
}
temp.next = node;
node.pre = temp;
}
public void update(Node2 newnode) {
if (head.next == null) {
System.out.println("链表为空");
return;
}
Node2 temp = head.next;
boolean flag = false;
while (true) {
if (temp == null) {
break;
}
if (temp.no == newnode.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.name = newnode.name;
temp.nickname = newnode.nickname;
} else {
System.out.printf("没有找到编号为%d的节点", newnode.no);
}
}
public void list() {
if (head.next == null) {
System.out.println("链表为空");
return;
}
Node2 temp = head.next;
while (true) {
if (temp == null) {
break;
}
System.out.println(temp);
temp = temp.next;
}
}
}
class Node2 {
public int no;
public String name;
public String nickname;
public Node2 next;
public Node2 pre;
public Node2(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}
@Override
public String toString() {
return "Node2{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
二.单向环形链表
约瑟夫问题(约瑟夫环问题)
设编号为1,2,3,。。。。,n的n个人围坐一圈,约定编号为k(1<= k <=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,以此类推,直到所有人出列为止,由此产生一个出队编号的序列
n=5,既有5个人
k=1,从第1个人开始报数
m=2,数到2出列
![](https://i-blog.csdnimg.cn/blog_migrate/963930b12e6226ae5908cc2335077820.png)
首先我们测试构建和遍历单向环形链表:
package com.lmh.sparsearray;
public class Josepfu {
public static void main(String[] args) {
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.addBoy(5);
circleSingleLinkedList.showBoy();
}
}
class CircleSingleLinkedList{
private Boy first = new Boy(-1);
public void addBoy(int nums){
if (nums < 1){
System.out.println("nums的值不正确");
return;
}
Boy curBoy = null;
for (int i = 1; i < nums+1; 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();
}
}
}
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;
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/f184f995d57abd484491a18438d8f046.png)
注解:
1. 因为辅助指针变量是指向最后一个节点,所以当从第k个人开始报数之前我们应该要
让first指向要报数的也就是第k个小孩,让helper指针指向该小孩的前一个;
2. 当小孩报数时,我们让first和helper指针移动(m-1)次,因为这一个是在模拟报数的过程,
自己是1所以向后移动(m-1)次
3. 小孩出圈相当于链表的删除
first = first.next;
helper.next = first;
具体代码如下
package com.lmh.sparsearray;
public class Josepfu {
public static void main(String[] args) {
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.addBoy(5);
circleSingleLinkedList.showBoy();
circleSingleLinkedList.countBoy(1, 2, 5);
}
}
class CircleSingleLinkedList {
private Boy first = new Boy(-1);
public void addBoy(int nums) {
if (nums < 1) {
System.out.println("nums的值不正确");
return;
}
Boy curBoy = null;
for (int i = 1; i < nums + 1; 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;
}
}