❣博主主页: 33的博客❣
▶️文章专栏分类:数据结构◀️
🚚我的代码仓库: 33的代码仓库🚚
🫵🫵🫵关注我带你学更多数据结构知识
目录
- 1.前言
- 2.删除值为key的所有结点
- 3.反转链表
- 4.返回中间结点
- 5.输出倒数第k个结点
- 6.链表分割
- 7.链表回文
- 8.相交链表
- 9.环形链表
- 9.1是否为环形链表
- 9.2入环第一个节点
- 10.总结
1.前言
在上一篇文章中博主已经介绍了链表的基础知识,什么是链表,如何实现一个链表,以及LinkedList的操作方法,那么在这篇文章中通过一些链表的经典习题来练习吧。
2.删除值为key的所有结点
删除值为val的所有结点:OJ链接
解题思路:
1.遍历链表。
2.如果某个结点A的值等于key,那么A的前一个结点B的next值直接等于A的next。
3,继续遍历,遇到等于值等于key的重复上诉操作,直到全部遍历完成。
public void removeAllKey(int key) {
if (head==null){
return;
}
Node node=head;
Node cur=head.next;
while (cur!=null){
if(cur.val==key){
node.next=cur.next;
cur=cur.next;
}else {
node=node.next;
cur=cur.next;
}
}
if(head.val==key){
head=head.next;
}
}
3.反转链表
反转链表:OJ链接
public ListNode reverseList(ListNode head) {
if(head==null||head.next==null){
return head;
}
ListNode cur=head.next;
head.next=null;
while (cur!=null){
ListNode curNext=cur.next;
cur.next=head;
head=cur;
cur=curNext;
}
return head;
}
4.返回中间结点
返回中间结点:OJ链接
方式一:
解题思路:
1.求链表的大小size
2.中间结点为size/2
遍历链表,走到size/2的位置,返回该节点
class Solution {
//求链表长度
public int size(ListNode head) {
ListNode node=head;
int count=0;
while (node!=null){
count++;
node=node.next;
}
return count;
}
public ListNode middleNode(ListNode head) {
if(head==null||head.next==null){
return head;
}
int Size=size(head);
int middle=Size/2;
ListNode node=head;
for (int i=0;i<middle;i++){
node=node.next;
}
return node;
}
}
方式二:
解题思路:
1.设置一个快结点:fast,设置一个慢结点:slow。
2.我们试想:如果fast的速度是slow的两倍,那么当fast到达路程的终点时,此时slow就走了路程的一半。
3.所以我们让fast每次走两步,slow每次走一步,当fast=null或者fast.next=null的时候,slow就是中间结点
public Node middleNode2() {
Node fast=head;
Node slow=head;
while (fast!=null||fast.next!=null){
fast=fast.next.next;
slow=slow.next;
}
return slow;
}
5.输出倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点:OJ链接
解题思路:
1。让fast先走k-1步数
2.再同时走,当fast到达终点时,那么它们就相差k
3.此时的slow就是倒数第k个结点
ublic Node FindKthToTail (int k) {
Node fast=head;
Node slow=head;
if(k<=0||head==null){
return null;
}
while (k-1>0){
fast=fast.next;
k--;
}
while (fast.next!=null){
fast=fast.next;
slow=slow.next;
}
return slow;
}
6.链表分割
以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前:OJ链接
解题思路:
1.首先,遍历链表
2.再创建两个链表,一个用于存放小于x的值,另一个用于存放大于x的值
3.再把第一个链表的最后一个结点与第二个链表的头节点串起来。
public Node partition(int x) {
if (head==null){
return null;
}
Node as=null;
Node ae=null;
Node bs=null;
Node be=null;
Node cur=head;
while (cur!=null){
if (cur.val<x){
if(as==null){
//插入第一个节点
as=cur;
ae=cur;
cur=cur.next;
} else {
ae.next=cur;
ae=ae.next;
cur=cur.next;
}
}else {
//cur.val>x
if(bs==null){
//插入第一个节点
bs=cur;
be=cur;
cur=cur.next;
} else {
be.next=cur;
be=be.next;
cur=cur.next;
}
}
}
//如果第一个链表为空就直接返回第二个链表的头节点
if(as==null){
return bs;
}
ae.next=bs;
if(bs != null) {
//第2个区间有数据
be.next = null;
}
head=as;
return head;
}
7.链表回文
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构:OJ链接
解题思路:
1.查看链表是否回文,是指从前向后遍历,或者从后向前遍历,输出的之都相同。
2.我们可以先找到中间位置,再从中间位置进行翻转,使它同时从两头向中间遍历
3.遍历的时候如果如果链表有偶数个的情况,和有奇数个的情况是不一样的,当有奇数个时,走到相同的位置就停止,当有偶数个时,当下再走一步就相遇时就停止。
public boolean chkPalindrome() {
if(head == null || head.next == null) {
return true;
}
//按照中间位置翻转链表
//1.找到中间位置(middle已经在4中写过)
Node middle=middleNode2();
//2.翻转
Node ehead=middle;
Node cur=ehead.next;
while (cur!=null){
Node curNext=cur.next;
cur.next=ehead;
ehead=cur;
cur=curNext;
}
//从两头开始遍历
Node snode=head;
Node enode=ehead;
while (snode!=enode){
if (snode.val!=enode.val){
return false;
}
if (snode.next==enode){
return true;
}
snode=snode.next;
enode=enode.next;
}
return true;
}
8.相交链表
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点:OJ链接
解题思路:
1.求两个链表的长度,再求长度差值
2.定义节点fast代表长的链表头节点,slow代表短的链表的头节点。先让长的链表先走差值步,再同时走。
3.两个链表相遇时就是相交节点。
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode nodeA=headA;
ListNode nodeB=headB;
int lenA=size(headA);
int lenB=size(headB);
ListNode fast=headA;
ListNode slow=headB;
int len=lenA-lenB;
if(len<0){
len=lenB-lenA;
fast=headB;
slow=headA;
}
while(len>0){
fast=fast.next;
len--;
}
while(fast!=slow&&fast!=null){
fast=fast.next;
slow=slow.next;
}
return fast;
}
9.环形链表
9.1是否为环形链表
给你一个链表的头节点 head ,判断链表中是否有环:OJ链接
解题思路:
1.定义节点fast代表长的链表头节点,slow代表短的链表的头节点.
2.每次让fast走两步,slow走一步,如果是环形链表,那么它们一定会在环中的某一个位置相遇,否则不是环形链表
为什么每次让fast走两步,slow走一步?不可以fast走3步,4步吗?假设环中只有两个元素A,B,当slow进入环时在A的节点的位置,此时fast在B节点的位置,slow走一步就到B的位置,fast走3步就到A的位置,那么它们永远都不会相遇。
只有每次让fast走两步,slow走一步,换的长度为1,那么肯定会相遇。
public boolean hasCycle(ListNode head) {
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null){
//fast.next!=null&&fast!=null,不能这样写
fast=fast.next.next;
slow=slow.next;
if(fast==slow){
return true;
}
}
return false;
}
9.2入环第一个节点
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null:OJ链接
解题思路:
让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。
证明如下:
public class Solution {
ListNode hasCycle(ListNode head) {
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null){
//fast.next!=null&&fast!=null,不能这样写
fast=fast.next.next;
slow=slow.next;
if(fast==slow){
return fast;
}
}
return null;
}
public ListNode detectCycle(ListNode head) {
ListNode fast=hasCycle(head);
if(fast==null){
return null;
}
ListNode slow=head;
while(fast!=slow){
fast=fast.next;
slow=slow.next;
}
return fast;
}
}
10.总结
本篇博客主要对链表的经典习题进行了讲解,同学们要理解解题的一些思想做到融会贯通,如果有疑惑的地方欢迎大家来和博主沟通,交流。
下期预告:栈