相关题目
反转链表的原型
题目如下:
拆解反转的过程:
1->2->3->4->5
1 2->3->4->5
1<-2 3->4->5
1<-2<-3 4->5
1<-2<-3<-4 5
1<-2<-3<-4<-5
可以看出反转的过程中有两个链表:
- 已经反转好的链表
已经反转的链表okHead,初始化应该是null,然后逐个反转过程中,okHead一直更新。
- 剩下未反转的链表
剩下未反转链表remainHead,初始化就是原来的链表头节点head,随着节点逐个反转,remainHead也一直在更新。
具体反转过程看以下代码的注解
public ListNode reverseList(ListNode head) {
// 已经反转的链表okHead,初始化应该是null
ListNode okHead = null;
// 剩下未反转链表remainHead,初始化就是原来的链表头节点head
ListNode remainHead = head;
// 还有剩下的未反转的节点,就继续反转
while (remainHead != null) {
// 暂时保存下一个待反转的节点,防止找不到了(因为当前节点next要指向已经反转好的链表)
ListNode nextHead = remainHead.next;
// 当前节点要指向反转好的链表头,并把自己当作新的已反转链表头
remainHead.next = okHead;
okHead = remainHead;
// 把刚才暂时保存的节点当作未反转链表的头节点
remainHead = nextHead;
}
return okHead;
}
反转链表变形一
把反转链表当作一个已经拥有的功能,只要考虑怎么利用已有功能来达到题目想要的效果。
找到左边节点的前一个节点leftPre,和右边节点的下一个节点rightNext,以及反转后的头节点leftNode、尾节点rightNode,就可以把他们连接起来。
leftPre.next=rightNode;
leftNode.next=rightNext;
具体看代码
public ListNode reverseBetween(ListNode head, int left, int right) {
// 简化left=1这种类型,直接搞一个虚拟的头节点(链表中常用技巧)
ListNode headHead = new ListNode();
headHead.next = head;
ListNode cur = headHead;
ListNode leftPre = null;
ListNode leftNode = null;
ListNode rightNode = null;
ListNode rightNext = null;
for (int i = 0; i <= right; i++) {
if (i == left - 1) {
leftPre = cur;
leftNode = cur.next;
}
if (i == right) {
rightNode = cur;
rightNext = cur.next;
}
cur = cur.next;
}
// 先解开链子
leftPre.next = null; //这一步非必须,这里是为了好理解
rightNode.next = null; //这一步必须,因为要判断反转的最后一个节点
// 反转
reverseList(leftNode);
// 连接起来
leftPre.next = rightNode;
leftNode.next = rightNext;
return headHead.next;
}
反转链表变形二
注意审题,K个一组,不满k个的就保持原样。
把反转链表当作一个已经拥有的功能,只要考虑怎么利用已有功能来达到题目想要的效果。 这里面就有两个问题:
-
按组反转后怎么连接起来 首先要有一个变量kPre保存前一组链表的最后一个。
当前这组链表反转完之后返回的是newHead,连接到上一组链表中:kPre.next=newHead
那么当你要反转下一组链表的时候,要怎么更新kPre呢,很简单,在反转当前组之前,把第一个节点保存下来记作kLast,这个节点就是我们要更新成为的kPre。 -
不够k个的,怎么保持原顺序
每一组在反转之前,看下够不够k个元素,不够的话就不需要反转。这时候不影响时间复杂度,仍然是O(N)。
具体看代码注释
public ListNode reverseKGroup(ListNode head, int k) {
// 为了保持一致性,加一个虚拟的头结点
ListNode xNode = new ListNode(-1);
xNode.next = head;
//初始化kPre和kLast
ListNode kPre = xNode;
ListNode kLast = kPre.next;
while (kLast != null) { //如果下一个节点是空的就不需要反转了
// 往后走k步,如果不为空,就说明够k个元素
ListNode nextK = kPre;
for (int i = 0; i < k; i++) {
nextK = nextK.next;
if (nextK == null) {
break;
}
}
if (nextK != null) {
//够k个元素,执行标砖的反转
ListNode okHead = null;
ListNode remainHead = kPre.next;
for (int i = 0; i < k; i++) {
ListNode nextHead = remainHead.next;
remainHead.next = okHead;
okHead = remainHead;
remainHead = nextHead;
}
// 反转完毕之后 连接+更新kPre和kLast
kLast.next = remainHead; // 连接到下一个
kPre.next = okHead; // 上一组连接自己
kPre = kLast; // 更新下一组前一个节点
kLast = remainHead; // 更新下一组的最后一个节点
}else{
//不够k个,直接退出
break;
}
}
return xNode.next;
}