反转部分单向链表
【题目】 给定一个单向链表的头节点 head,以及两个整数 from 和 to,在单向链表上把第 from 个节点到第 to 个节点这一部分进行反转。 例如: 1->2->3->4->5->null,from=2,to=4 调整结果为:1->4->3->2->5->null 再如: 1->2->3->null,from=1,to=3 调整结果为:3->2->1->null
【要求】 1.如果链表长度为 N,时间复杂度要求为 O(N),额外空间复杂度要求为 O(1)。 2.如果不满足 1<=from<=to<=N,则不用调整。
本题有可能存在换头的问题,比如题目的第二个例子,所以函数应该返回调整后的新头节点,整个处理过程如下: 1.先判断是否满足 1≤from≤to≤N,如果不满足,则直接返回原来的头节点。 2.找到第 from-1 个节点 fPre 和第 to+1 个节点 tPos。fPre 就是要反转部分的前一个节点,tPos 是反转部分的后一个节点。把反转的部分先反转,然后正确地连接 fPre 和 tPos。
例如:1->2->3->4->null,假设 fPre 为节点 1,tPos 为节点 4,要反转部分为 2->3。先反转成3->2,然后 fPre 连向节点 3,节点 2 连向 tPos,就变成了 1->3->2->4->null。 3.如果 fPre 为 null,说明反转部分是包含头节点的,则返回新的头节点,也就是没反转之前反转部分的最后一个节点,也是反转之后反转部分的第一个节点;如果 fPre 不为 null,则返回旧的头节点。 全部过程请参看如下代码中的 reversePart 方法。
public class ReversePart { class Node { private int value; private Node next; public Node(int data) { this.value = data; } } public Node reversePart(Node head, int from, int to) { Node node1 = head; // fPre指向要反转的 Node fPre = null; Node tPos = null; int n = 0; // 遍历链表,找出fPre节点和tPos节点,并得到链表长度 while (node1 != null) { n++; fPre = n == from - 1 ? node1 : fPre; tPos = n == to + 1 ? node1 : tPos; node1 = node1.next; } if (from > to || from < 1 || to > n) { return head; } /** * 解释: * 使node1指向需要反转的第一个节点 * 如果fPre为null,那么fPre的next属性就没有值,所以这里使用三目运算 */ node1 = fPre == null ? head : fPre.next; // node2指向node1后面一个节点 Node node2 = node1.next; // 反转第一个需要反转的节点,使node1的next指针指向tPos node1.next = tPos; // 申请一个变量用于保存当前node2的next指针所指向的位置 Node next = null; // 开始反转剩余部分节点 while (node2 != tPos) { // next指向node2的next指针所指向的位置 next = node2.next; // 反转node2,使node2的next属性指向前一个节点。这样node2的next属性就丢失了,所以要用next变量保存 node2.next = node1; // node1、node2节点向后移 node1 = node2; node2 = next; } // 如果fPre不为空表示,反转部分在链表中间,所以不需要换头,直接返回旧头节点即可 if (fPre != null) { // 使fPre指向反转之后反转部分的第一个节点 fPre.next = node1; return head; } else { // 如果fPre为空,那么反转之后反转部分的第一个节点就是头节点 return node1; } } }#链表#
笔者学习数据结构与算法的心得与经验。