题目描述
给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。
请尝试使用原地算法完成。你的算法的空间复杂度应为O(1)
,时间复杂度应为O(nodes)
,nodes
为节点总数。
示例1:
输入: head = [1, 2, 3, 4, 5]
输出: [1, 3, 5, 2, 4]
示例2:
输入: head = [2, 1, 3, 5, 6, 4, 7]
输出: [2, 3, 6, 7, 1, 5, 4]
说明:
- 应保持奇数节点和偶数节点的相对顺序;
- 链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推;
思路分析
我们通过简单的思考,可以想出通过对链表进行“奇数分区”和“偶数分区”进行节点区分,此时我们不免会想到,奇数分区需要一个指针指向奇数分区尾节点oddTail
以便后续节点的插入;同样地, 偶数分区也需要一个指针指向偶数分区尾节点evenTail
;同时,我们遍历还需要一个遍历指针p
。
此时我们对遍历、插入的准备工作进行了简单讨论,这里我们通过代码实现时,会发现我们需要对p
遍历时对节点的 “奇偶性”进行判断 ,因为这里是下标的奇偶性,故我们可以通过设置一个布尔常量来进行标识。
再进一步思考,因为链表之被分为两个分区,在代码运行过程中链表一般可被表示为三个区域:a. 偶数分区;b. 奇数分区;c. 待遍历分区。如图1所示:
图1
这里我们可以看到,在p
遍历的时候,会已知保持与指针evenTail
的前后关系,则我们这里去掉p
指针,则现在我们需要的就是两个分区指针oddTail, evenTail
和一个布尔变量进行功能实现。这里我们直接给出解题步骤与解题代码:
解题步骤
- 初始化两个分区指针
oddTail, evenTail
和布尔变量isOddNode
; - 遍历原始链表,根据
isOddNode
判断evenTail.next
是否插入奇数分区; - 当
evenTail.next == null
时停止并返回head
.
解题代码
public static ListNode solution(ListNode head) {
if (head == null || head.next == null || head.next.next == null) {
return head;
}
/* Step1: Init. pointers */
ListNode oddTail = head;
ListNode evenTail = head.next;
boolean isOddNode = true;
/* Step2: go through the head-list
and
split nodes according the odevity(奇偶性) of index
*/
while(evenTail.next != null){
ListNode node = evenTail.next;
// oddNode: directly insert after oddTail
if(isOddNode){
evenTail.next = evenTail.next.next;
node.next = oddTail.next;
oddTail.next = node;
oddTail = node;
isOddNode = false;
}else{ // evenNode: just move forward
evenTail = evenTail.next;
isOddNode = true;
}
}
/* Step3: return head */
return head;
}
复杂度分析
时间复杂度:我们对原始链表进行了一次遍历,容易理解时间复杂度为O(N)
;
空间复杂度:我们这里没有设置辅助容器,只需要两个指针和一个布尔量,故空间复杂度为O(1)
.
GitHub源码
完整可运行文件请访问GitHub。