1,单向链表
1.1,单项链表基本介绍
- 链表是以节点的方式存储
- 链表的每个节点都包括
data
域和next
域,指向下一个节点 - 链表的各个节点不一定都是连续存储的
- 链表分为带头结点的链表和不带头结点的链表
1.2,单向链表示意图
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/323395c3238b6ae6445ace15677a27c1.png)
1.3,单向链表代码实现
package com.self.datastructure.linked;
import lombok.Data;
public class MyLinkedList {
public static void main(String[] args) {
SimpleLinkedList linkedList = new SimpleLinkedList();
linkedList.addByOrder(new LinkedNode(2, "名称", "昵称"));
linkedList.addByOrder(new LinkedNode(1, "名称", "昵称"));
linkedList.addByOrder(new LinkedNode(4, "名称", "昵称"));
linkedList.addByOrder(new LinkedNode(3, "名称", "昵称"));
linkedList.update(new LinkedNode(1, "覆盖", "覆盖"));
linkedList.delete(new LinkedNode(3, "", ""));
linkedList.showDetails();
}
}
class SimpleLinkedList {
private LinkedNode head = new LinkedNode(-1, "", "", null);
public void add(LinkedNode linkedNode) {
LinkedNode temp = head;
while (null != temp.getNext()) {
temp = temp.getNext();
}
temp.setNext(linkedNode);
}
public void addByOrder(LinkedNode linkedNode) {
LinkedNode temp = head;
while (null != temp.getNext()) {
LinkedNode nextNode = temp.getNext();
if (nextNode.getNo() > linkedNode.getNo()) {
temp.setNext(linkedNode);
linkedNode.setNext(nextNode);
return;
} else if (nextNode.getNo() == linkedNode.getNo()) {
temp.setNext(linkedNode);
linkedNode.setNext(nextNode.getNext());
nextNode.setNext(null);
return;
}
temp = nextNode;
}
temp.setNext(linkedNode);
}
public void update(LinkedNode linkedNode) {
LinkedNode temp = head;
while (null != temp) {
LinkedNode next = temp.getNext();
if (null != next && next.getNo() == linkedNode.getNo()) {
temp.setNext(linkedNode);
linkedNode.setNext(next.getNext());
next.setNext(null);
return;
}
temp = next;
}
}
public void delete(LinkedNode linkedNode) {
LinkedNode temp = head;
while (null != temp.getNext()) {
LinkedNode next = temp.getNext();
if (next.getNo() == linkedNode.getNo()) {
temp.setNext(next.getNext());
next.setNext(null);
return;
}
temp = next;
}
}
public void showDetails() {
LinkedNode next = head.getNext();
while (next != null) {
System.out.println(next);
next = next.getNext();
}
}
}
@Data
class LinkedNode {
private int no;
private String name;
private String nickName;
private LinkedNode next;
public LinkedNode(int no, String name, String nickName) {
this(no, name, nickName, null);
}
public LinkedNode(int no, String name, String nickName, LinkedNode next) {
this.no = no;
this.name = name;
this.nickName = nickName;
this.next = next;
}
@Override
public String toString() {
return "[LinkedNode: no = " + no + ", name = " + name + ", nickName = " + nickName + "]";
}
}
1.4,单向链表相关面试题
1.4.1,获取单链表的节点个数
public static int getNodeLength(LinkedNode head, boolean isNeedHead) {
if (null == head) {
return 0;
}
int count = isNeedHead ? 1 : 0;
LinkedNode temp = head;
for (;null != temp.getNext();) {
count++;
temp = temp.getNext();
}
return count;
}
1.4.2,查找单向链表中的倒数第K个节点:即获取正数第(count - K)个节点的数据
public static LinkedNode getIndexDataDesc(LinkedNode head, boolean isNeedHead, int descCount) {
int count = getNodeLength(head, isNeedHead);
if (count < descCount) {
throw new IndexOutOfBoundsException("索引越界");
}
int index = count - descCount;
LinkedNode temp = isNeedHead ? head : head.getNext();
for (int i = 0; i < index; i++, temp = temp.getNext());
return temp;
}
1.4.3,单链表反转
public static LinkedNode reverseLinked(LinkedNode head, boolean isNeedHead) {
LinkedNode realData = isNeedHead ? head : head.getNext();
LinkedNode reverseLinked = isNeedHead ? null : new LinkedNode(head);
for (; null != realData ;) {
LinkedNode newNode = new LinkedNode(realData);
if (null == reverseLinked) {
reverseLinked = newNode;
} else {
if (isNeedHead) {
newNode.setNext(reverseLinked);
reverseLinked = newNode;
} else {
LinkedNode nextNode = reverseLinked.getNext();
newNode.setNext(nextNode);
reverseLinked.setNext(newNode);
}
}
realData = realData.getNext();
}
return reverseLinked;
}
1.4.4,反向打印链表
public static void showDetailsReverse(LinkedNode linkedNode, boolean isNeedHead) {
Stack<LinkedNode> stack = new Stack<>();
LinkedNode realNode = isNeedHead ? linkedNode : linkedNode.getNext();
if (null == realNode) {
return;
}
for (; null != realNode; ) {
stack.push(realNode);
realNode = realNode.getNext();
}
LinkedNode currNode = null;
for (;stack.size() > 0; ) {
System.out.println(stack.pop());
}
}
1.4.5,合并两个有序的单链表,合并之后的链表依然有序
public static LinkedNode mergeOrderNode(LinkedNode firstNode, LinkedNode secondNode) {
firstNode = firstNode.getNext();
secondNode = secondNode.getNext();
if (null == firstNode || null == secondNode) {
return null == firstNode ? secondNode : firstNode;
}
return firstNode.getNo() > secondNode.getNo()
? doMergeOrderNode(secondNode, firstNode)
: doMergeOrderNode(firstNode, secondNode);
}
public static LinkedNode doMergeOrderNode(LinkedNode firstNode, LinkedNode secondNode) {
SimpleLinkedList simpleLinkedList = new SimpleLinkedList();
for (;null != firstNode;) {
for (;secondNode != null && firstNode.getNo() > secondNode.getNo();) {
simpleLinkedList.add(new LinkedNode(secondNode));
secondNode = secondNode.getNext();
}
simpleLinkedList.add(new LinkedNode(firstNode));
firstNode = firstNode.getNext();
}
for (;null != secondNode;) {
simpleLinkedList.add(new LinkedNode(secondNode));
secondNode = secondNode.getNext();
}
return simpleLinkedList.getHead();
}
2,双向链表
2.1,双向链表介绍
- 单向链表查找方向是能是一个方向;双向链表可以向前或者向后查找
- 单向链表不能自我删除,需要靠辅助节点即前一个节点;双向链表可以进行自删除,因为同时持有上一个节点和下一个节点的地址
- 遍历:遍历方式与单向链表基本一致,不过双向链表可以直接从后往前遍历
- 添加:
- 先找到需要插入到双向链表的目标节点
- 讲节点和对应前置节点与后置节点的
pre
和next
属性进行修改
- 删除:
- 双向链表可以实现单个节点的自删除,直接遍历到需要删除的当前节点
- 将当前节点前置节点的
next
属性指向当前节点的后置节点 - 将当前节点后置节点的
pre
属性指向当前节点的前置节点 - 处理之后,当前节点在链表中会挂空,也就是从链表中删除
2.2,代码演示
package com.self.datastructure.linked;
import lombok.Data;
public class DoubleLinkedList {
public static void main(String[] args) {
DLinkedList dLinkedList = new DLinkedList();
dLinkedList.addByOrder(new DoubleNode(3, "名称", "昵称"));
dLinkedList.addByOrder(new DoubleNode(2, "名称", "昵称"));
dLinkedList.addByOrder(new DoubleNode(4, "名称", "昵称"));
dLinkedList.addByOrder(new DoubleNode(1, "名称", "昵称"));
dLinkedList.showDetailsFromHead();
}
}
class DLinkedList {
private DoubleNode head = new DoubleNode(-1, "", "");
private DoubleNode tail = new DoubleNode(-2, "", "");
public DLinkedList() {
head.setNext(tail);
tail.setPre(head);
}
public void addByOrder(DoubleNode doubleNode) {
DoubleNode realData = head.getNext();
for (;null != realData;) {
if (realData == tail) {
add(doubleNode);
return;
}
if (realData.getNo() > doubleNode.getNo()) {
DoubleNode pre = realData.getPre();
pre.setNext(doubleNode);
doubleNode.setPre(pre);
doubleNode.setNext(realData);
realData.setPre(doubleNode);
return;
}
realData = realData.getNext();
}
}
public void add(DoubleNode doubleNode) {
DoubleNode preNode = tail.getPre();
preNode.setNext(doubleNode);
doubleNode.setPre(preNode);
doubleNode.setNext(tail);
tail.setPre(doubleNode);
}
public void remove(int no) {
DoubleNode realData = head.getNext();
for (;null != realData && tail != realData;) {
if (realData.getNo() == no) {
DoubleNode pre = realData.getPre();
DoubleNode next = realData.getNext();
pre.setNext(next);
next.setPre(pre);
realData = null;
return;
}
realData = realData.getNext();
}
}
public void showDetailsFromHead() {
DoubleNode realData = head.getNext();
for (;null != realData && tail != realData;) {
System.out.println(realData);
realData = realData.getNext();
}
}
public void showDetailsFromTail() {
DoubleNode realData = tail.getPre();
for (;null != realData && realData != head;) {
System.out.println(realData);
realData = realData.getPre();
}
}
public DoubleNode getHead() {
return head;
}
public DoubleNode getTail() {
return tail;
}
}
@Data
class DoubleNode {
private DoubleNode pre;
private DoubleNode next;
private int no;
private String name;
private String nickName;
public DoubleNode(DoubleNode doubleNode) {
this(doubleNode.getNo(), doubleNode.getName(), doubleNode.getNickName());
}
public DoubleNode(int no, String name, String nickName) {
this(no, name, nickName, null, null);
}
public DoubleNode(int no, String name, String nickName, DoubleNode pre, DoubleNode next) {
this.no = no;
this.name = name;
this.nickName = nickName;
this.pre = pre;
this.next = next;
}
@Override
public String toString() {
return "[DoubleNode: no = " + no + ", name = " + name + ", nickName = " + nickName + "]";
}
}
3,单向环形链表:约瑟夫问题(Josephu)
3.1,Josephu(约瑟夫)问题介绍
- 设编号为1,2 … n的n个人围成一个圈,并约定编号为K(1<=K<=n)的人从1开始报数,数到M的那个人出列,他的下一位又从1开始数,依次类推,直到所有人出列
- 通过不带头节点的单向链表来处理
3.2,Josephu(约瑟夫)问题实现代码
package com.self.datastructure.linked;
import lombok.Data;
import java.util.Scanner;
public class JosephuQuestion {
public static void main(String[] args) {
JosephuLinkedList linkedList = new JosephuLinkedList();
for (int i = 0; i < 10; i++) {
linkedList.add(new Node(i));
}
linkedList.startGame();
}
}
class JosephuLinkedList {
private Node head = null;
private int totalCount = 0;
public void startGame() {
Scanner scanner = new Scanner(System.in);
Node startNode = head;
for(;;) {
if (totalCount == 0) {
System.out.println("编号全部移除, 游戏结束...");
return;
}
System.out.println("当前游戏人数: " + totalCount);
String input = scanner.nextLine();
switch (input) {
case "s":
showDetails();
break;
case "r":
System.out.println("输入跳过的人数: M");
int runCount = scanner.nextInt();
Node targetPreNode = startNode;
for (int i = 0; i < runCount - 1; i++, targetPreNode = targetPreNode.getNext());
startNode = targetPreNode.getNext().getNext();
System.out.println("移除完成..., 移除编号: " + targetPreNode.getNext().getNo() + ", 下次开始编号: " + startNode.getNo());
targetPreNode.setNext(targetPreNode.getNext().getNext());
totalCount--;
break;
case "e":
return;
default:
break;
}
}
}
public void add(Node node) {
if (null == head) {
totalCount++;
head = node;
head.setNext(head);
return;
}
Node temp = head;
for (;null != temp;) {
Node next = temp.getNext();
if (head == next) {
totalCount++;
temp.setNext(node);
node.setNext(head);
return;
}
temp = temp.getNext();
}
}
public void remove(int no) {
if (null == head) return;
if (head.getNo() == no) {
if (head == head.getNext()) {
head = null;
} else {
Node preHead = head;
head = head.getNext();
Node temp = head;
for (; null != temp; ) {
if (preHead == temp.getNext()) {
temp.setNext(head);
break;
}
temp = temp.getNext();
}
}
} else {
Node temp = head;
for (;null != temp;) {
Node next = temp.getNext();
if (next == head) {
return;
}
if (next.getNo() == no) {
temp.setNext(next.getNext());
break;
}
temp = temp.getNext();
}
}
totalCount--;
}
public void showDetails() {
if (null == head) {
System.out.println("数据为空...");
return;
}
Node temp = head;
boolean flag = false;
for (;null != temp;) {
if (flag && temp == head) {
System.out.println("遍历完成...");
return;
}
flag = true;
System.out.println(temp);
temp = temp.getNext();
}
}
public Node getHead() {
return head;
}
}
@Data
class Node {
private int no;
private Node next;
public Node(int no) {
this.no = no;
}
@Override
public String toString() {
return "[Node: no = " + no + "]";
}
}