目录
一、反转单链表
描述
?二、返回链表的中间节点
三、返回倒数第K个节点
描述
示例1
?四、合并两个已排序的链表
描述
五、分隔链表
六、判断一个链表是否为回文结构
描述
示例1
示例2
示例3
七、两个链表的第一个公共结点
描述
输入描述:
返回值描述:
示例1
示例2
八、?判断链表中是否有环
描述
九、?链表中环的入口结点
描述
输入描述:
返回值描述:
一、反转单链表
OJ链接:反转链表_牛客题霸_牛客网 (nowcoder.com)https://www.nowcoder.com/practice/75e878df47f24fdc9dc3e400ec6058ca?tpId=295&tqId=23286&ru=/exam/oj&qru=/ta/format-top101/question-ranking&sourceUrl=%2Fexam%2Foj
描述
给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。
????????数据范围:?0\leq n\leq10000≤n≤1000
????????要求:空间复杂度?O(1)O(1)?,时间复杂度?O(n)O(n)?。
????????如当输入链表{1,2,3}时,经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。
以上转换过程如下图所示:
题解:我们可以定义一个指针,指向第二个节点也就是head.next,再将head.next置为空,然后将定义的这个指针的next节点指向head,再将头更新为定义的指针,再将定义的指针向后移动重复操作直至遍历完
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
//1.处理特殊情况
if(head == null || head.next == null){
return head;
}
//2.开始反转
ListNode cur = head.next;
head.next = null;
while(cur != null){
ListNode next = cur.next;
cur.next = head;
head = cur;
cur = next;
}
return head;
}
}
?二、返回链表的中间节点
OJ链接:
OJ链接:2095. 删除链表的中间节点https://leetcode.cn/problems/delete-the-middle-node-of-a-linked-list/
难度中等30收藏分享切换为英文接收动态反馈
给你一个链表的头节点?head ?。删除?链表的?中间节点?,并返回修改后的链表的头节点?head ?。
长度为?n ?链表的中间节点是从头数起第??n / 2? ?个节点(下标从?0?开始),其中??x? ?表示小于或等于?x ?的最大整数。
- 对于?
n ?=?1 、2 、3 、4 ?和?5 ?的情况,中间节点的下标分别是?0 、1 、1 、2 ?和?2 ?。
示例 1:
输入:head = [1,3,4,7,1,2,6]
输出:[1,3,4,1,2,6]
解释:
上图表示给出的链表。节点的下标分别标注在每个节点的下方。
由于 n = 7 ,值为 7 的节点 3 是中间节点,用红色标注。
返回结果为移除节点后的新链表。
示例 2:
输入:head = [1,2,3,4]
输出:[1,2,4]
解释:
上图表示给出的链表。
对于 n = 4 ,值为 3 的节点 2 是中间节点,用红色标注
提示:
- 链表中节点的数目在范围?
[1, 105] ?内 1 <= Node.val <= 105
题解:
题目要求只遍历一次,所以不能使用计算节点个数的方法进行查找。有这样一个例子,A与B两个小车同时从一个地方出发,A的速度是B的二倍,两个人同时沿着同一路线到同一终点,当A到终点时此时B所在的位置就是中点,对于一个链表同理,定义两个指针,一个指针每次走两步叫做快指针,另一个指针每次走一步叫做慢指针。两个指针同时走,当快指针走到结尾时慢指针刚好就是中点
/**
* OJ2:找到链表的中间节点
* @return
*/
public Node LocateIntermediateNode(){
//1.处理特殊情况
if(head == null || head.next == null){
return head;
}
//2.快慢指针处理
Node fast = head;
Node slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
//3.返回慢指针
return slow;
}
三、返回倒数第K个节点
OJ链接
链表中倒数最后k个结点_牛客题霸_牛客网 (nowcoder.com)https://www.nowcoder.com/practice/886370fe658f41b498d40fb34ae76ff9?tpId=295&tqId=1377477&ru=/exam/oj&qru=/ta/format-top101/question-ranking&sourceUrl=%2Fexam%2Foj
描述
输入一个长度为 n 的链表,设链表中的元素的值为 ai?,返回该链表中倒数第k个节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。
数据范围:0 \leq n \leq 10^50≤n≤105,0 \leq a_i \leq 10^90≤ai?≤109,0 \leq k \leq 10^90≤k≤109
要求:空间复杂度?O(n)O(n),时间复杂度?O(n)O(n)
进阶:空间复杂度?O(1)O(1),时间复杂度?O(n)O(n)
如输入{1,2,3,4,5},2时,对应的链表结构如下图所示:
其中蓝色部分为该链表的最后2个结点,所以返回倒数第2个结点(也即结点值为4的结点)即可,系统会打印后面所有的节点来比较。
示例1
输入:
{1,2,3,4,5},2
返回值:
{4,5}
题解:
定义两个指针,让一个快指针先走k-1步,然后再一起走,快指针领先慢指针k-1步所以当快指针到最后的时候,慢指针所在的位置就是要求的位置
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* public ListNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pHead ListNode类
* @param k int整型
* @return ListNode类
*/
public ListNode FindKthToTail (ListNode pHead, int k) {
// write code here
//1.处理特殊情以及判断下标合法性
if(k < 0){
return null;
}
if(pHead == null){
return null;
}
//2.定以快慢指针
ListNode fast = pHead;
ListNode slow = pHead;
//3.快指针走k步
while(k-- != 1){
fast = fast.next;
if(fast == null){
return fast;
}
}
//4.快慢指针一起走
while(fast.next != null){
fast = fast.next;
slow = slow.next;
}
//5.返回慢指针
return slow;
}
}
?四、合并两个已排序的链表
OJ链接:
合并两个排序的链表_牛客题霸_牛客网 (nowcoder.com)https://www.nowcoder.com/practice/d8b6b4358f774294a89de2a6ac4d9337?tpId=295&tqId=23267&ru=/exam/oj&qru=/ta/format-top101/question-ranking&sourceUrl=%2Fexam%2Foj
描述
输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
数据范围:?0 \le n \le 10000≤n≤1000,-1000 \le 节点值 \le 1000?1000≤节点值≤1000 要求:空间复杂度?O(1)O(1),时间复杂度?O(n)O(n)
如输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6},所以对应的输出为{1,2,3,4,5,6},转换过程如下图所示:
或输入{-1,2,4},{1,3,4}时,合并后的链表为{-1,1,2,3,4,4},所以对应的输出为{-1,1,2,3,4,4},转换过程如下图所示:
题解:?
定义一个新的头节点,遍历两个有序链表的同时比较两个链表的当前节点哪个值更小,哪个小哪个就接到新节点后面,然后这个链表的当前节点向后走,依次类推,直到有一个链表遍历完成,此时找到没有遍历完成的链表,接到新链表后即可
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
//1.定义傀儡节点
ListNode newHead = new ListNode(-1);
ListNode tmp = newHead; //让tmp代替新节点去接节点
//2.遍历两个链表
while(list1 != null && list2 != null){
//比较找到更小的哪个接到tmp后面
if(list1.val < list2.val){
tmp.next = list1;
list1 = list1.next;
}else{
tmp.next = list2;
list2 = list2.next;
}
tmp = tmp.next;
}
//3.找到没有遍历完成的链表接到后面
if(list1 != null){
tmp.next = list1;
}
if(list2 != null){
tmp.next = list2;
}
//4.返回新的链表
return newHead.next;
}
}
五、分隔链表
OJ链接:
86. 分隔链表 - 力扣(LeetCode)https://leetcode.cn/problems/partition-list/
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置。
示例 1: 输入:head = [1,4,3,2,5,2], x = 3 输出:[1,2,2,4,3,5] 示例 2:
输入:head = [2,1], x = 2 输出:[1,2]
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/partition-list 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解:
定义两段链表一段用于接取小于x的节点另以段用于接取大于等于x的节点,最后将两段链表进行合并
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode partition(ListNode head, int x) {
//1.定义两段链表的头节点与尾节点
ListNode bs = null;
ListNode be = null;
ListNode as = null;
ListNode ae = null;
//2.遍历head
while(head != null){
if(head.val < x){
//此时创建第一段链表接取节点
if(bs == null){
bs = head;
be = head;
}else{
be.next = head;
be = be.next;
}
}else{
//此时创建第一段链表接取节点
if(as == null){
as = head;
ae = head;
}else{
ae.next = head;
ae = ae.next;
}
}
head = head.next;
}
//3.此时分情况将两段链表进行链接:1.前段没有节点 2.后段没有节点 3.都有
if(bs == null){
return as;
}
be.next = as;
if(as != null)
ae.next = null; //要注意将最后一段链表的最后一个节点的指针域置空不然链表将会循环
//4.返回
return bs;
}
}
六、判断一个链表是否为回文结构
OJ链接:
判断一个链表是否为回文结构_牛客题霸_牛客网 (nowcoder.com)https://www.nowcoder.com/practice/3fed228444e740c8be66232ce8b87c2f?tpId=295&tqId=1008769&ru=/exam/oj&qru=/ta/format-top101/question-ranking&sourceUrl=%2Fexam%2Foj
描述
给定一个链表,请判断该链表是否为回文结构。
回文是指该字符串正序逆序完全一致。
数据范围:?链表节点数?0 \le n \le 10^50≤n≤105,链表中每个节点的值满足?|val| \le 10^7∣val∣≤107
示例1
输入:
{1}
返回值:
true
示例2
输入:
{2,1}
返回值:
false
说明:
2->1
示例3
输入:
{1,2,2,1}
返回值:
true
说明:
1->2->2->1
题解:
这道题如果不借助其他数据结构只是操作链表本身,我们可先找到这个链表的中间节点,然后从中间节点开始反转,反转完成后,定义两个指针,一个slow从结尾开始一个cur从头开始一起走,便走遍判断直到相遇,这里要处理的是,如果有奇数个节点时会相遇,但是偶数个接单时则不相遇,如果当cur能满足cur.next == slow时就是偶数节点且回文
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
/**
*
* @param head ListNode类 the head
* @return bool布尔型
*/
public boolean isPail (ListNode head) {
// write code here
//1.处理特殊情况
if(head == null && head.next == null){
return true;
}
//2.找到中间节点
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
//3.开始反转
ListNode cur = slow.next;
while(cur != null){
ListNode next = cur.next;
cur.next = slow;
slow = cur;
cur = next;
}
//4,一起走
cur = head;
while(cur != slow){
if(cur.val != slow.val){
return false;
}
//处理偶数节点的情况
if(cur.next == slow){
return true;
}
cur = cur.next;
slow = slow.next;
}
//5.返回结果
return true;
}
}
七、两个链表的第一个公共结点
OJ链接:
两个链表的第一个公共结点_牛客题霸_牛客网 (nowcoder.com)https://www.nowcoder.com/practice/6ab1d9a29e88450685099d45c9e31e46?tpId=295&tqId=23257&ru=/exam/oj&qru=/ta/format-top101/question-ranking&sourceUrl=%2Fexam%2Foj
描述
输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
数据范围:?n \le 1000n≤1000 要求:空间复杂度?O(1)O(1),时间复杂度?O(n)O(n)
例如,输入{1,2,3},{4,5},{6,7}时,两个无环的单向链表的结构如下图所示:
可以看到它们的第一个公共结点的结点值为6,所以返回结点值为6的结点。
输入描述:
输入分为是3段,第一段是第一个链表的非公共部分,第二段是第二个链表的非公共部分,第三段是第一个链表和第二个链表的公共部分。 后台会将这3个参数组装为两个链表,并将这两个链表对应的头节点传入到函数FindFirstCommonNode里面,用户得到的输入只有pHead1和pHead2。
返回值描述:
返回传入的pHead1和pHead2的第一个公共结点,后台会打印以该节点为头节点的链表。
示例1
输入:
{1,2,3},{4,5},{6,7}
复制返回值:
{6,7}
复制说明:
第一个参数{1,2,3}代表是第一个链表非公共部分,第二个参数{4,5}代表是第二个链表非公共部分,最后的{6,7}表示的是2个链表的公共部分
这3个参数最后在后台会组装成为2个两个无环的单链表,且是有公共节点的
示例2
输入:
{1},{2,3},{}
返回值:
{}
说明:
2个链表没有公共节点 ,返回null,后台打印{}
?题解:
两个链表有公共节点说明这两个链表走到公共节点以后的所有节点都是一样的长度也是一样,因此我们可以分别计算两个链表的长度,然后让较长的哪个链表先走与较短链表的差值步,然后一起走,当节点地址相同时就是第一个公共节点
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
//计算长度
private int size(ListNode head){
int size = 0;
while(head != null){
size++;
head = head.next;
}
return size;
}
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
//1.计算长度
int len = size(pHead1) - size(pHead2);
ListNode cur1 = null; //代替长的链表
ListNode cur2 = null; //代替短的链表
if(len < 0){
//说明后面的更长
cur1 = pHead2;
cur2 = pHead1;
len = -1*len;
}else{
//前面的长
cur1 = pHead1;
cur2 = pHead2;
}
//2.走差值步
while(len-- != 0){
cur1 = cur1.next;
}
//3.一起走相遇时返回
while(cur1 != null && cur2 != null){
if(cur1 == cur2){
return cur1;
}
cur1 = cur1.next;
cur2 = cur2.next;
}
//4.此时没有交点
return null;
}
}
八、?判断链表中是否有环
OJ链接:
判断链表中是否有环_牛客题霸_牛客网 (nowcoder.com)https://www.nowcoder.com/practice/650474f313294468a4ded3ce0f7898b9?tpId=295&tqId=605&ru=/exam/oj&qru=/ta/format-top101/question-ranking&sourceUrl=%2Fexam%2Foj
描述
判断给定的链表中是否有环。如果有环则返回true,否则返回false。
数据范围:链表长度?0 \le n \le 100000≤n≤10000,链表中任意节点的值满足?|val| <= 100000∣val∣<=100000
要求:空间复杂度?O(1)O(1),时间复杂度?O(n)O(n)
输入分为两部分,第一部分为链表,第二部分代表是否有环,然后将组成的head头结点传入到函数里面。-1代表无环,其它的数字代表有环,这些参数解释仅仅是为了方便读者自测调试。实际在编程时读入的是链表的头节点。
例如输入{3,2,0,-4},1时,对应的链表结构如下图所示:
可以看出环的入口结点为从头结点开始的第1个结点(注:头结点为第0个结点),所以输出true。
题解:?
如果链表有环,则定义两个速度不同的指针一个每次走一步一个两步,两个指针进入环后由于速度不同终将会相遇。那么为啥是两步呢。最好情况下慢指针刚进入环时就相遇,最坏情况是快指针刚走到入口的下一个,而慢指针才入环,此时两指针同时走一次两个的距离就缩小一步,最坏情况下走一圈就能再次相遇。如果走3,4,5……步,假设快指针走3步,慢指针进环时如图:
?此时快指针走3步回到入口处然后慢指针走一步到了圈内,没有相遇,快指针再走三步慢指针走一步依旧没有相遇,就会可能存在套圈问题。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
//1.定义快慢指针
ListNode fast = head;
ListNode slow = head;
//3.一起走
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
return true;
}
}
//4.如果退出循环说明没环
return false;
}
}
九、?链表中环的入口结点
OJ链接:
链表中环的入口结点_牛客题霸_牛客网 (nowcoder.com)https://www.nowcoder.com/practice/253d2c59ec3e4bc68da16833f79a38e4?tpId=295&tqId=23449&ru=/exam/oj&qru=/ta/format-top101/question-ranking&sourceUrl=%2Fexam%2Foj
描述
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
数据范围:?n\le10000n≤10000,1<=结点值<=100001<=结点值<=10000
要求:空间复杂度?O(1)O(1),时间复杂度?O(n)O(n)
例如,输入{1,2},{3,4,5}时,对应的环形链表如下图所示:
可以看到环的入口结点的结点值为3,所以返回结点值为3的结点。
输入描述:
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台会根据第二段是否为空将这两段组装成一个无环或者有环单链表
返回值描述:
返回链表的环的入口结点即可,我们后台程序会打印这个结点对应的结点值;若没有,则返回对应编程语言的空结点即可。
题解:?
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode head) {
//1.定义快慢指针
ListNode fast = head;
ListNode slow = head;
//2.找相遇点
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
break;
}
}
if(fast == null || fast.next == null){
return null;
}
//3.一个从头一个从相遇点出发,再次相遇即为入口点
fast = head;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
?
|