⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章
⭐作者主页:@逐梦苍穹
⭐所属专栏:数据结构。数据结构专栏主要是在讲解原理的基础上拿Java实现
⭐如果觉得文章写的不错,欢迎点个关注一键三连😉有写的不好的地方也欢迎指正,一同进步😁
目录
- 1、有效节点的个数
- 2、查找单链表中的倒数第k个结点
- 2.1、方式一
- 2.2、方式二
- 3、反转单链表
- 4、从尾到头打印单链表
- 4.1、方式一
- 4.2、方式二
- 4.3、⭐方式三
- 5、合并有序链表
- 5.1、方式一
- 5.2、方式二
- 6、所有代码
单链表知识请查看另外一篇文章,其中有详细的介绍,已经链表类和节点类的实现:
[Data structure]单链表 | 一文介绍线性数据结构之一的单链表(Java实现)
五道单链表常见算法题:
- 求单链表中有效节点的个数
- 查找单链表中的倒数第k个结点[新浪面试题]
- 单链表的反转[腾讯面试题]
- 从尾到头打印单链表,不破坏链表结构[百度面试题]
- 合并两个有序的单链表,合并之后的链表依然有序
五道题解法总览如下:
1、有效节点的个数
思路:传入的头节点为空节点,头节点的下一个节点才是链表的内容。如果一开始判断头节点没有下一个节点,则什么为空链表,直接返回0;不为空则进入while循环,每次指针后移一位,total就自增一次,直到指针下一个节点为空跳出循环。
2、查找单链表中的倒数第k个结点
2.1、方式一
按链表长度查找:传入参数为链表的头节点和需要的是倒数的第几个节点。
思路分析:链表除去头节点之外,其他存在的节点个数之和即为链表的有效节点个数(也可以说是链表的长度)。链表倒数第K个节点的位置为:(链表长度 - k的值 + 1)。想要获取到节点的地址,则需要遍历到目标节点的上一个节点
,即需要遍历到的位置是:链表长度 - k的值,也就是length - index。
2.2、方式二
双指针遍历:先创建两个节点,初始地址均指向头节点的下一个节点(第一个有效节点)。让temp1指针向前先移动n位(n的值取决与k的值是多少,即取决于传入的index整型参数的值
)。此时让temp1和temp2两个指针同时开始移动,每次移动一个节点,当前驱指针的下一个节点为null的时候,说明前驱指针temp1已经到达链表的尾部,那么此时后继指针temp2指向的就是"倒数第k个节点 的 前一个节点"。
3、反转单链表
此题的思路为:使用三个辅助节点(前驱节点prev、当前节点current、后继节点next),完成对链表的反转。
步骤如下:
- 先将前驱节点prev置为空,当前节点为第一个有效节点,后继节点为第二个有效节点
- 把当前节点的下一个节点地址,设置为prev,即为null;然后把后继节点next的下一个节点地址设置为当前节点(此时就完成了第二个有效数据排在了第一个有效数据之前的操作)
- 把前驱节点、当前节点、后继节点指针均往后移动一位,然后继续重复之前的操作
- 直到当前节点为null,说明节点遍历完成。
- 此时的前驱节点prev指向了原链表最后一个节点(即反转后的链表的第一个有效节点),此时只需要把原链表的头节点指向当前的prev,即完成了链表的反转
4、从尾到头打印单链表
三种实现方式:
4.1、方式一
获取有效个数的长度,从头到尾打印:这个算法没啥好说的,只能说 能跑。但是性能很低。
思路:获取有效个数长度,把节点依次存入数组,对数组反向遍历。
4.2、方式二
采用递归的方式,在每一次打印之前,都先调用自身,所以最后打印的顺序,就是从后往前
4.3、⭐方式三
栈是一种常见的数据结构,它具有“先进后出”(Last-In-First-Out,LIFO)的特点。栈是由一系列节点组成的集合,其中每个节点包含一个元素和一个指向下一个节点的指针。
栈的基本操作包括:入栈(push)、出栈(pop)、查看栈顶元素(peek)和判断栈是否为空(isEmpty)等。其中,入栈操作会在栈的顶部添加一个新的元素,出栈操作会从栈的顶部删除一个元素,并返回被删除的元素。查看栈顶元素操作则返回栈顶的元素,而判断栈是否为空则返回一个布尔值,表示栈中是否有元素。
这里采用栈的方式,先得到的对象,压入栈中,则后得到的对象,先弹出栈,所以就实现了逆序打印。
5、合并有序链表
两种方式实现:
先介绍第一种低性能的方式(仅限于 能跑),然后再介绍第二种双指针
的方式。
5.1、方式一
这种方法性能很低,比较原始,采用的是直接把两个有序链表的内容,存入数组,之后采用冒泡排序对数组内的链表节点对象进行排序。之后再通过遍历的方式,把每个节点的next域重新赋值。
5.2、方式二
双指针合并的实现步骤如下:
- 创建一个新的链表,用来存放合并后的结果
- 定义两个指针,分别指向两个链表的头结点
- 比较两个指针所指节点的值的大小,将较小的节点添加到新链表中,并将指针向后移动一位
- 重复上述步骤,直到有一个链表为空
- 将另一个链表中剩余的节点直接添加到新链表的末尾
- 返回新链表的头结点
6、所有代码
package linkedList.singleList;
import java.util.Stack;
/**
* @author 逐梦苍穹
* @date 2023/5/1 8:57
*/
public class AlgorithmQuestion {
/**
* 求单链表中有效节点的个数
*/
public static int getLength(Node headNode) {
int total = 0;
if (headNode.getNext() == null) return total;
while (headNode.getNext() != null) {
total++;
headNode = headNode.getNext();
}
return total;
}
/**
* 查找单链表中的倒数第K个节点
*/
//方式一:按链表长度寻找
public static Node findLastIndexNodeMethod1(Node head, int index) {
int temp = 0;
int length = getLength(head);
int nodeFontIndex = length - index;
while (true) {
if (temp == nodeFontIndex) {
break;
}
head = head.getNext();
temp++;
}
return head.getNext();
}
//方式二:双指针遍历
public static Node findLastIndexNodeMethod2(Node head, int index){
Node temp1 = head.getNext();
Node temp2 = head.getNext();
if (head.getNext() == null) return head;
for (int i = 0; i < index; i++) {
temp1 = temp1.getNext();
if (i == index - 1 && temp1 == null) return temp2;
if (temp1 == null){
System.out.println("链表长度不足");
return head;
}
}
while (temp1.getNext() != null){
temp1 = temp1.getNext();
temp2 = temp2.getNext();
}
return temp2.getNext();
}
/**
* 反转单链表
*/
public static void reverseLinkedList(Node headNode) {
if (headNode == null || headNode.getNext() == null) return;
Node prev = null;
Node current = headNode.getNext();
while (current != null) {
Node next = current.getNext();
current.setNext(prev);
prev = current;
current = next;
}
headNode.setNext(prev);
}
/**
* 从尾到头打印链表(不破坏链表结构)
*/
//方式一:获取有效个数的长度,从尾到头打印
public static void reversePrintMethod1(Node headNode) {
int length = getLength(headNode);
Node[] nodeArr = new Node[length];
if (length == 0) return;
if (length == 1) {
System.out.println(headNode.getNext());
return;
}
for (int i = 0; i < length; i++) {
nodeArr[i] = headNode.getNext();
headNode = headNode.getNext();
}
for (int j = length - 1; j >= 0; j--) {
System.out.println(nodeArr[j]);
}
}
//方式二:递归
public static void reversePrintMethod2(Node headNode) {
if (headNode == null || headNode.getNext() == null) return;
reversePrintMethod2(headNode.getNext());
System.out.println(headNode.getNext());
}
//方式三:栈
public static void reversePrintMethod3(Node headNode) {
if (headNode.getNext() == null) {
System.out.println("链表为空");
return;
}
Stack<Node> stack = new Stack<>();
Node temp = headNode.getNext();
while (temp != null) {
stack.push(temp);
temp = temp.getNext();
}
while (!stack.empty()) {
System.out.println(stack.pop());
}
}
/**
* 合并两个有序的单链表,合并之后的链表依然有序
*/
//方式一:存入数组再合并
public static SingleList mergeTwoSingleListMethod1(SingleList list1,SingleList list2){
SingleList singleList = new SingleList();
Node singleListHead = singleList.getHead();
Node list1HeadNode = list1.getHead();
int list1Length = getLength(list1.getHead());
Node list2HeadNode = list2.getHead();
int list2Length = getLength(list2.getHead());
Node[] mergeArray = new Node[list1Length + list2Length];
for (int i = 0; i < list1Length; i++) {
mergeArray[i] = list1HeadNode.getNext();
list1HeadNode = list1HeadNode.getNext();
}
for (int j = list1Length; j < list1Length + list2Length; j++) {
mergeArray[j] = list2HeadNode.getNext();
list2HeadNode = list2HeadNode.getNext();
}
for (int k = 0; k < mergeArray.length - 1; k++) {
for (int l = 0; l < mergeArray.length - 1 - k; l++) {
if (mergeArray[l].getId() > mergeArray[l + 1].getId()){
Node temp = mergeArray[l];
mergeArray[l] = mergeArray[l + 1];
mergeArray[l + 1] = temp;
}
}
}
for (int n = 0; n < mergeArray.length; n++) {
if (n != mergeArray.length - 1) {
singleListHead.setNext(mergeArray[n]);
singleListHead = singleListHead.getNext();
}
}
return singleList;
}
//方式二:双指针合并
public static Node mergeTwoSingleListMethod2(Node head1, Node head2) {
if (head1 == null) {
return head2;
}
if (head2 == null) {
return head1;
}
Node temp = new Node(0,0);
Node cur = temp;
head1 = head1.getNext();
head2 = head2.getNext();
while (head1 != null && head2 != null) {
if (head1.id <= head2.id) {
cur.setNext(head1);
head1 = head1.getNext();
} else {
cur.setNext(head2);
head2 = head2.getNext();
}
cur = cur.getNext();
}
cur.setNext ((head1 == null) ? head2 : head1);
return temp;
}
}