题目: 给定一个单链表的头节点head,节点的值类型是整型,再给定一个整数pivot,实现一个调整链表的函数,将链表调整为左部分都是值小于pivot的节点,中间部分都是值等于pivot的节点,右部分都是值大于pivot的节点。
进阶-在实现原问题功能的基础上增加如下的要求:
- 调整后所有小于pivot的节点之间的相对顺序和调整前一样
- 调整后所有等于pivot的节点之间的相对顺序和调整前一样
- 调整后所有大于pivot的节点之间的相对顺序和调整前一样
- 时间复杂度请达到O(N),额外空间复杂度请达到O(1)
法一:借助辅助空间
思路
把链表中的所有节点放入一个额外的数组中,然后统一调整位置的办法。
具体过程如下:
- 1、先遍历一遍链表,为了得到链表的长度,假设长度为N。
- 2、生成长度为N的Node类型的数组nodeArr,然后遍历一次链表,将节点依次放进nodeArr中。
- 3、在nodeArr中把小于pivot的节点放在左边,把相等的放中间,把大于的放在右边。也就是改进了快速排序中partition的调整过程。
- 即如下代码中的arrPartition方法。实现的具体解释请参看"数组类似partition的调整"问题,这里不再详述。
- 4、经过步骤3的调整后,nodeArr是满足题目要求的节点顺序,只要把nodeArr中的节点依次重连起来即可,整个过程结束。
复杂度
时间复杂度为O(N),额外空间复杂度为O(N)
法二: 不借助辅助空间,链表拆分成三个小链表
思路
将原链表拆分成小于,等于,大于区域的小链表,然后去遍历原列表,将大于,小于,等于数分别放到对应链表中。最后将三个链表连接起来就可以了。注意连接三个小链表时注意边界情况的处理。
eg:第一次找到第一个小于参考值的数。将它放进小于区域,这样就保证了在前面小于参考值的数先进入小于区域,然后整体就保证了相对位置不变。
举个例子
假设现在链表为[4,6,3,5,8,5,2],pivot为5。
- 1.小于pivot的区域的小链表的首尾分别用sH、sT来表示,等于pivot的区域的小链表的首尾分别用eH、eT来表示,大于pivot的区域的小链表的首尾分别用mH、mT来表示,遍历链表之前,sH、sT、eH、eT、mH、mT均为null
- 2.遍历到第一个节点时,sH、sT均指向4这个节点
- 3.遍历到第二个节点时,mH、mT均指向6这个节点
- 4.遍历到第三个节点时,sT尾部接上这个节点(也就是sT.next = cur),然后sT指向这个节点(也就是sT = cur)
- 5.遍历到第四个节点时,eH、eT均指向5这个节点
- 6.遍历到第五个节点时,mT尾部接上这个节点(也就是mT.next = cur),然后mT指向这个节点(也就是mT = cur)
- 7.遍历到第六个节点时,eT尾部接上这个节点(也就是eT.next = cur),然后eT指向这个节点(也就是eT = cur)
- 8.遍历到第七个节点时,sT尾部接上这个节点(也就是sT.next = cur),然后sT指向这个节点(也就是sT = cur)
- 9.连接
复杂度
时间复杂度O(N),额外空间复杂度O(1)
public static Node listPartition2(Node head, int pivot){ Node sH = null; // small head Node sT = null; // small tail Node eH = null; // equal head Node eT = null; // equal tail Node mH = null; // big head Node mT = null; // big tail Node next = null; // save next node // every node distributed to three lists while(head != null){ next = head.next; head.next = null; if(head.value < pivot){ if(sH == null){ sH = head; sT = head; }else{ sT.next = head; sT = head; } }else if(head.value == pivot){ if(eH == null){ eH = head; eT = head; }else{ eT.next = head; eT = head; } }else(head.value > pivot){ if(mH == null){ mH = head; mT = head; }else{ mT.next = head; mT = head; } } head = next; } // small and equal reconnect if(sT != null){ sT.next = eH; eT = eT == null ? sT : eT; // 下一步,谁去连大于区域的头,谁就变成eT } // 上面的if,不管跑了没有,et // all reconnect if(eT != null){ // 如果小于区域和等于区域,不是都没有 eT.next = mH; } return sH != null ? sH : (eH != null ? eH : mH); }