版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cjh_android/article/details/84098912
题目
将倒数K个节点放在链表头部
举例
Input 1->2->3->4->5->NULL and k = 2,
Output 4->5->1->2->3->NULL.
分析
不管使用何种方法,基于链表的特性,必须要找出三个节点:
- 输入链表的head节点,这个节点会变成下半部分的head节点
- 设链表长度为 len,那么需要找出 (len - k) 处的节点,该节点将成为新链表的head节点
- 输入链表的tail节点,该节点需要与 (len -k) 节点链接起来,成为输出链表
代码
方法一的思路见文章后面的图解部分;
方法二的思路简单,简述了一下。
package algorithm012;
import algorithm006.ListNode;
public class Algorithm012 {
public static void main(String[] args) {
ListNode test = getListNode();
System.out.println(test.toString());
test = reverseKNodesByLength(test, 2);
System.out.println(test.toString());
}
/**方法一
* 思路:快慢指针,见文章图解部分
* 优点:时间复杂度 O(n) 空间复杂度 O(1)
*
* @param listNode
* @param k
* @return
*/
public static ListNode reverseKNodesBy2Pointers(ListNode listNode, int k) {
if(k <=0 || null == listNode)
return listNode;
ListNode preFastNode = null;
ListNode fastNode = listNode;
ListNode slowNode = null;
int pos = 1;
while(fastNode != null) {
if(pos > k) {
if(null == slowNode)
slowNode = listNode;
else
slowNode = slowNode.next;
}
preFastNode = fastNode;
fastNode = fastNode.next;
pos++;
}
if(pos <= k)
return listNode;
preFastNode.next = listNode;
ListNode headNode = slowNode.next;
slowNode.next = null;
return headNode;
}
/**方法二
* 思路:通过计算链表的长度,遍历指定的长度,寻找需要的节点
* 优点:时间复杂度 O(2n) 空间复杂度 O(1) 思路直白简单
*
* @param listNode
* @param k
* @return
*/
public static ListNode reverseKNodesByLength(ListNode listNode, int k) {
int length = getListNodeLength(listNode);
if(length <= k)
return listNode;
int pos = 1;
int dValue = length - k;
ListNode originalHeadNode = listNode;
ListNode preNode = null;
ListNode dValueNode = null;
while(listNode != null) {
if(pos <= dValue) {
dValueNode = listNode;
pos++;
}
preNode = listNode;
listNode = listNode.next;
}
preNode.next = originalHeadNode;
ListNode headNode = dValueNode.next;
dValueNode.next = null;
return headNode;
}
public static int getListNodeLength(ListNode listNode) {
int length = 0;
ListNode headNode = listNode;
while(headNode != null) {
length++;
headNode = headNode.next;
}
return length;
}
public static ListNode getListNode() {
ListNode listNode2 = new ListNode(0);
ListNode listNode4 = new ListNode(1);
ListNode listNode6 = new ListNode(2);
ListNode listNode8 = new ListNode(3);
ListNode listNode10 = new ListNode(4);
ListNode listNode11 = new ListNode(5);
ListNode listNode12 = new ListNode(6);
ListNode listNode13 = new ListNode(7);
listNode2.next = listNode4;
listNode4.next = listNode6;
listNode6.next = listNode8;
listNode8.next = listNode10;
listNode10.next = listNode11;
listNode11.next = listNode12;
listNode12.next = listNode13;
return listNode2;
}
}
图解方法一
有题: 将倒数K个节点放在链表头部;于是,把链表分成两个部分:
那么如果使用两个节点,该怎么考虑呢?
结合上面的分析,我们需要知道三个节点的位置,head、(len -k)、tail。
head 和 tail 总是很容易就能知道的,那么怎么让指针指向 (len -k)的位置呢?
现在已知的条件只有 链表 和 K,如果反过来想呢?
如果一个指针fastNode
前进到了节点K
,那么接下来,再继续遍历直到null
,走过的路程其实就是len -k
,如果在fastNode
到了节点K
的时候,有一个slowNode
开始从head
节点走,当fastNode
变为null
停下来的时候,slowNode
也走了len-k
:
至此!所需要的三个节点全部有了出处!