1. 题目:链表的中间结点
给你单链表的头结点 head ,请你找出并返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
力扣
1.1 分析
1.1.1 第一想法,使用计数器先统计处所有长度,然后再来个计数器让指针移动到中间位置时停止,此时就可以获取中间链表了。
public ListNode middleNode(ListNode head) {
ListNode current = head;
int length = 0;
while(current!=null) {
current = current.next;
length++;
}
int count = 0;
while(count < length/2){
head=head.next;
count++;
}
return head;
}
运行截图
时间复杂度:O(n)里面两次遍历使用计数器
空间复杂度: O(1)
总结:思路还是很清晰的,使用两个计数器
1.1.2 很显然,这个专题是双指针,肯定是双指针来的快,那么就要思考如何使用双指针,我可以定义一个快慢指针,快指针走两个,慢指针走一个,最后快指针结束了,那么中间节点位置就是慢指针的位置
public ListNode middleNode(ListNode head) {
ListNode slow =head;
ListNode fast = head;
while(fast!=null && fast.next!=null){
slow=slow.next;
fast=fast.next.next;
}
return slow;
}
运行截图
时间复杂度:O(n),遍历一次链表
空间复杂度: O(1)
总结:很明显,快慢指针时间空间上性能都很高,这里不需要考虑是否慢指针需要因为奇数偶数而再向后移动,因为条件里面的快指针就是条件,只要满足,最后慢指针就是结果
2. 题目: 链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
力扣
2.1 分析
2.1.1 采用快慢指针,先让快指针走k步,让快慢指针里面相差k步,然后两个指针同时走,直到快指针为null,那么慢指针就走到了倒数第k个节点,那么这个节点就是需要的节点。
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode slow = head;
ListNode fast =head;
// 快指针走k步,与慢指针相差k
while(fast!=null && k>0){
fast=fast.next;
k--;
}
// 慢指针开始移动,快指针也继续移动
while(fast!=null){
slow=slow.next;
fast=fast.next;
}
return slow;
}
运行截图:
时间复杂度:O(n),遍历一次链表
空间复杂度: O(1)
总结:当有这种k个距离的时候,采用快慢指针,速度一般是很快的,一开始想法是将链表反转,然后通过k元素的位置节点,然后再反转一次就是所求结果,但是很显然较为复杂,还是需要好好掌握快慢指针。
2. 旋转链表
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
力扣
2.2 分析
2.2.1 使用快慢指针找到k的位置,不过先要判断一下k是否为空和节点是否存在,还要判断当前链表长度和k的大小,这里采用取余,因为k可能超过链表长度,如果取余后结果为0,意味着不需要移动,但是如果取余大于0,快指针就需要移动一格直到k为0,然后快慢指针一起移动,等快指针为null的时候,k就在慢指针的位置,然后将快指针的next指向链表的头部,然后慢指针后面断开。
public ListNode rotateRight(ListNode head, int k) {
if(head == null || k == 0){
return head;
}
ListNode temp = head;
ListNode slow = head;
ListNode fast = head;
int len = 0;
// 计算链表长度
while(head!=null){
len++;
head=head.next;
}
// 当k就是链表的长度
if(k%len == 0){
return temp;
}
while(k%len >0){
fast=fast.next;
k--;
}
while(fast.next!=null){
fast=fast.next;
slow=slow.next;
}
ListNode res = slow.next;
slow.next = null;
fast.next = temp;
return res;
}
时间复杂度: O(n)链表长度
空间复杂度: O(1)
总结:需要注意的是一些临界值,比如k是否为0,或者取余是0,因为k不一定就是链表的长度以内。
2.2.2 看到这个题目初始印象就是先分割链表,然后就是合并链表,但是实际写下来发现和上面快慢指针差不多,也是先找到位置分割,然后将第二个链表的最后一个指向原链表的头部,还是不如双指针简单。
总结
对于这种题目说了第k的元素之类,优先考虑一下双指针。