今天补一下昨天没写的博客。
今天是记录leetcode刷题的第六天,今天的问题是一个经常被问到的面试题--链表反转。
我们循序渐进,从易->难,对今天做的题及逆行一个排序:
- 反转链表
- 指定区间反转链表
- 链表每k个一组反转
反转链表
要求如下
给定一个单链表的头结点pHead,长度为n,反转该链表后,返回新链表的表头。
作为最简单的反转链表,只要思路对了就很容易实现,而思路就是双指针,
1、我们每次都记住第i个指针和第i+1个指针,然后翻转,
2、再将双指针后移一位,
3、循环前面的步骤,直到i+1指向的指针为空结束。
这个实现起来比较简单,并没有什么难点,所以直接放代码了
public ListNode ReverseList(ListNode head) {
// 双指针 ,一个指针指向头节点,一个指针指向头节点的下一节点
// null
if(head == null || head.next == null) {
return head;
}
ListNode pre = head;
ListNode next = head.next;
//断开
pre.next = null;
while (next != null){
ListNode temp = next.next;
// 断开连接,并反向 1 --> 2 --> 3 --> 4 ==> null <-- 1 <-- 2
next.next = pre;
pre = next;
next = temp;
}
return pre;
}
static class ListNode {
int val;
ListNode next;
public ListNode(int x) {
val = x;
}
}
指定区间
要求如下
将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转,要求时间复杂度O(n),空间复杂度 O(1)。
例如:
给出的链表为 1 → 2 → 3 → 4 → 5 → NULL, m=2,n=4
返回 1 → 4 → 3 → 2 → 5 → NULL.
指定区间后,我们就需要找到起点和终点,然后将此链表“孤立”,然后反转此链表,然后再把链表拼接起来,这样的话,我们就需要起点的前置节点和终点的后置节点。
画个图更容易明白
1、找到起点和终点,前置节点和后置节点。
2、断开连接
3、翻转
4、连接
注意,在实际编程过程中,不完全按照上图的规则,我们需要随机应变,画图只是为了好理解,需要时刻注意节点的移动。
代码如下:
public ListNode reverseBetween(ListNode head, int m, int n) {
if (head == null || head.next == null || m == n) {
return head;
}
//找到头和前置节点
ListNode pre = null;
ListNode left = head;
while (--m > 0) {
pre = left;
left = pre.next;
}
//找到尾和后置节点
ListNode right = head;
while (--n > 0) {
right = right.next;
}
ListNode next = right.next;
// 断开与后面相连的部分,也是孤立前面的链表,这样就形成了反转前面的链表,
right.next = null;
// 翻转 left -> right,同时拼接最后面的部分
ListNode cur = left.next;
// 断开,而因为此时left为末尾,下一个就是next
left.next = next;
while (cur != null) {
ListNode temp = cur.next;
cur.next = left;
left = cur;
cur = temp;
}
// 拼接
if(pre == null){
// pre 为null,则left为头,直接返回
return left;
}
// pre不为null,则代表有前置节点,则拼接起来
pre.next = left;
return head;
}
static class ListNode {
int val;
ListNode next;
public ListNode(int x) {
val = x;
}
}
K个一组
有了上面做铺垫,相信这道题对你来说已经不算很难了,只需要对上述代码稍加处理就可以完成。
代码如下:
public ListNode reverseKGroup(ListNode head, int k) {
// write code here
int size = getSize(head);
// 记录当前旋转的是第几组
int start = 0, end = k - 1;
while (end < size){
// 旋转
head = reverseBetween(head,start,end);
// 下一组
start = start + k;
end = end + k;
}
return head;
}
private ListNode reverseBetween(ListNode node, int start, int end) {
// K
int k = end - start;
// 找到头和前置节点
ListNode pre = null;
ListNode head = node;
while(start-- > 0){
pre = head;
head = pre.next;
}
// 找到尾
ListNode tail = head;
while (k-- > 0){
tail = tail.next;
}
// 后置节点
ListNode next = tail.next;
// 断开
tail.next = null;
// 翻转 head -> tail
ListNode cur = head.next;
head.next = next;
while (cur != null){
ListNode temp = cur.next;
cur.next = head;
head = cur;
cur = temp;
}
// pre -> head
if(pre == null){
return head;
}
pre.next = tail;
return node;
}
private int getSize(ListNode head) {
int size = 0;
while (head != null) {
size++;
head = head.next;
}
return size;
}
static class ListNode {
int val;
ListNode next;
public ListNode(int x) {
val = x;
}
}
传送门
多加练习,未来更好。