文章目录
1 单链表
链表是有序的列表,但是它在内存中并不一定是连续存储的:
链表是以节点的方式来存储的,每个节点包括data域和next域,链表分为带头节点链表 和 不带头节点链表(根据实际需求确定)
1.1 单链表的创建和遍历(无序)
将每一个节点增加到单链表的尾部,不考虑节点内部的顺序
头节点作为基准,不能移动
定义一个temp作为临时节点来控制增加和遍历
/**
* @author 雫
* @date 2021/3/15 - 11:50
* @function 单链表
* 将新加入的节点存到当前链表尾部
*/
@SuppressWarnings("all")
public class MySinglyLinkedList {
//先初始化一个头节点,头节点不能改动
private Node head;
//辅助节点,用来控制链表
private Node temp;
public MySinglyLinkedList() {
this.head = new Node(0, "", "");
this.temp = head;
}
/*添加节点,每次将节点加到最后*/
public void add(Node node) {
temp.next = node;
temp = node;
}
/*遍历链表,先判断链表是否为空
* 除了头节点,剩下的节点全部打印一次*/
public void list() {
if(head.next == null) {
throw new RuntimeException("当前链表为空");
}
temp = head;
while(true) {
if(temp.next == null) {
break;
}
temp = temp.next;
System.out.println(temp);
}
}
}
但是这样加入节点,每次加入的节点都会被放到链表的尾部,如果加入节点的编号是1,3,2,7,6,那么在链表中的顺序也是1,3,2,7,6,现在想要根据节点的顺序,将节点有序地插入链表,即插入顺序是1,3,2,7,6,需要链表中的顺序是1,2,3,6,7
1.2 单链表的创建和遍历(有序)
添加节点时,根据节点内部的编号有序地加入链表
先通过遍历找到新节点的位置
再将新节点插入到链表中
/**
* @author 雫
* @date 2021/3/15 - 13:51
* @function 单链表
* 根据新节点的编号将其有序地插入到链表
*/
@SuppressWarnings("all")
public class MySinglyLinkedList2 {
private Node head;
private Node temp;
public MySinglyLinkedList2() {
this.head = new Node(0, "", "");
this.temp = head;
}
/*根据节点的no来有序地插入链表
* 链表中某个节点A的no比no大,则将新节点加入到A之前
* 定义一个flag用来表示链表中的节点的no是否和新节点的no重复*/
public void add(Node node) {
boolean flag = false;
while (true) {
if (temp.next == null) { //已经遍历到了链尾
break;
} else if(temp.next.no > node.no) { //找到了应该插入的位置
break;
} else if(temp.next.no == node.no) { //发现重复,更改flag报错
flag = true;
break;
}
temp = temp.next; //让temp后移
}
if(flag) {
throw new RuntimeException("要插入的节点的no在链表中已重复");
}
node.next = temp.next;
temp.next = node;
}
/*遍历链表*/
public void list() {
if(head.next == null) {
throw new RuntimeException("当前链表为空");
}
temp = head;
while(true) {
if(temp.next == null) {
break;
}
temp = temp.next;
System.out.println(temp);
}
}
}
1.3 根据编号修改节点
/*修改节点,根据node的no来修改
* 遍历链表,找到相等的no,替换掉该节点除了no和next的其它值*/
public void update(Node node) {
if(head.next == null) {
throw new RuntimeException("当前链表为空");
}
while (true) {
if(temp.next == null) {
throw new RuntimeException("要修改的节点不存在");
}
if(temp.no == node.no) {
temp.name = node.name;
temp.nick = node.nick;
break;
}
temp = temp.next;
}
}
1.4 根据编号删除节点
被删除的节点被链表排除出去,会被GC回收
/*删除节点,先遍历根据no找到待删除的节点的前一个节点A
* 让A.next = A.next.next即可*/
public void remove(Node node) {
if(head.next == null) {
throw new RuntimeException("当前节点为空");
}
temp = head;
while (true) {
if(temp.next == null) {
throw new RuntimeException("被删除的节点不存在");
}
if(temp.next.no == node.no) {
temp.next = temp.next.next;
break;
}
temp = temp.next;
}
}
1.5 单链表习题
1.5.1 求单链表中节点的个数
遍历单链表即可,每遍历一个有效节点,value++
/*获取单链表中有效元素个数*/
public int getCount() {
if (head.next == null) {
return 0;
}
int value = 0;
temp = head;
while (true) {
if(temp.next == null) {
break;
}
temp = temp.next;
value++;
}
return value;
}
1.5.2 查找单链表中的倒数第K个节点
将倒数的第k个节点转换成正向的第m个节点,遍历整个链表,每经过一个有效节点,value++,直到value等于m,即是需要找到的倒数第K个节点
m = (有效节点总数 - k + 1)
/*获取倒数第K个节点*/
public Node getNodeByReverseK(int k) {
if(head.next == null) {
System.out.println("当前链表为空");
return null;
}
int value = 0;
int count = (getLength() - k + 1);
Node result = null;
temp = head;
while (true) {
temp = temp.next;
value++;
if(value == count) {
result = temp;
break;
}
if(temp.next == null) {
break;
}
}
return temp;
}
1.5.3 单链表的反转
/*反转单链表*/
public void reverse() {
if(this.head.next == null || this.head.next.next == null) {
System.out.println("当前链表无需反转");
return;
}
temp = head.next; //temp用来遍历原单链表
Node next = null; //记录原单链表当前temp节点的下一个节点
Node reverseHead = new Node(0, "", ""); //作为反转后单链表的头节点
while (temp != null) {
next = temp.next; //保存当前节点的下一个节点
temp.next = reverseHead.next; //将节点逆序连接
reverseHead.next = temp; //将temp所指的节点连接到新链表上
temp = next; //temp后移
}
head.next = reverseHead.next;
}
1.5.4 反向遍历单链表
有两种方法:
方式一:先将单链表反转,再打印
但这样会破坏原先单链表的结构,不可取
方式二:利用栈,将各个节点顺序入栈
用栈的先进后出,就完成了逆序打印
/*反向打印单链表,使用栈的先进后出完成*/
public void reversePrint() {
if(head.next == null) {
System.out.println("当前栈为空");
return;
}
Stack<Node> stack = new Stack();
temp = head;
while (true) {
if(temp.next == null) {
break;
}
temp = temp.next;
stack.push(temp);
}
int size = stack.size();
for(int i = 0; i < size; i++) {
System.out.println(stack.pop());
}
}
2 双向链表
单链表有两个明显缺点:
1,单链表查找方向只能是一个方向
2,单链表不能自我删除,需要辅助节点(temp)
为此可以在节点中再增加一个pre指向该节点的前一个节点,来构成更方便的双向链表
2.1 双向链表的创建和遍历(无序)
双向链表的节点:
/**
* @author 雫
* @date 2021/3/15 - 11:50
* @function 双向链表中的节点
*/
@SuppressWarnings("all")
public class Node {
public int no;
public String name;
public String nick;
public Node next;
public Node pre;
public Node(int no, String name, String nick) {
this.no = no;
this.name = name;
this.nick = nick;
}
@Override
public String toString() {
return "Node{" +
"no=" + no +
", name='" + name + '\'' +
", nick='" + nick + '\'' +
'}';
}
}
双向链表:
/**
* @author 雫
* @date 2021/3/16 - 8:34
* @function 双向链表
*/
@SuppressWarnings("all")
public class DoubleLinkedList {
private Node head;
private Node temp;
public DoubleLinkedList() {
this.head = new Node(0, "", "");
this.temp = head;
}
public Node getHead() {
return head;
}
public void add(Node node) {
temp = head;
while (true) {
if(temp.next == null) {
break;
}
temp = temp.next;
}
temp.next = node;
node.pre = temp;
}
public void list() {
temp = head;
while (true) {
if(temp.next == null) {
break;
}
temp = temp.next;
System.out.println(temp);
}
}
public void update(Node node) {
if(head.next == null) {
System.out.println("当前链表为空!");
return;
}
temp = head;
while (true) {
if(temp.next == null) {
throw new RuntimeException("需要修改的节点不存在");
}
if(temp.no == node.no) {
temp.name = node.name;
temp.nick = node.nick;
break;
}
temp = temp.next;
}
}
public void remove(Node node) {
if(head.next == null) {
System.out.println("当前链表为空");
return;
}
temp = head.next;
while (true) {
if(temp == null) {
throw new RuntimeException("要删除的节点不存在");
}
if(temp.no == node.no) {
temp.pre.next = temp.next;
if(temp.next != null) { //删除最后一个节点时应该控制,避免空指针异常
temp.next.pre = temp.pre;
break;
}
break;
}
temp = temp.next;
}
}
}
2.2 双向链表的创建和遍历(有序)
只需要更改add方法即可,找到比要插入的节点的no值大的目标节点,并将插入节点插入到目标节点之前
public void addBySort(No de node) {
temp = head;
while (true) {
if(temp.no == node.no) {
throw new RuntimeException("要增加的节点的编号在链表中已重复");
}
if(temp.no > node.no) {
temp.pre.next = node;
node.pre = temp.pre;
node.next = temp;
temp.pre = node;
break;
}
if(temp.next == null) {
temp.next = node;
node.pre = temp;
break;
}
temp = temp.next;
}
}