问题描述
普通问题:
给定一个单向链表的头节点head,节点的值类型是整数,再给定一个整数 pivot,实现一个调整链表的函数:
使得左半部分的值都是小于pivot的节点,中间部分都是等于pivot的节点,右边部分都是大于pivot的节点,对于左半部分、中间、右半部分内部的顺序没有要求。
进阶问题:
【要求】 调整后所有小于pivot的节点之间的相对顺序和调整前一样
【要求】 调整后所有等于pivot的节点之间的相对顺序和调整前一样
【要求】 调整后所有大于pivot的节点之间的相对顺序和调整前一样
【要求】 时间复杂度请达到O(N), 额外空间复杂度请达到O(1)。
思路描述
普通问题:变成数组,数组上partition(快排),然后再返回链表。 时间O(N),空间O(N)
进阶问题:想办法弄三个链表(小于,等于,大于),然后合并链表。时间O(N),空间O(1)
代码
普通问题
public class Main{
static class Node {
int value;
Node next;
public Node(int value){
this.value = value;
}
}
/**************************普通问题******************/
/**
* 普通问题
* 时间复杂度是 O(N),空间复杂度是 O(N)
* 首先遍历一遍链表,得到链表的长度,将链表元素依次放到数组中,然后利用类似于快速排序中的partition 思想进行分割
*/
public static Node listPartition(Node head, int pivot) {
if (head == null) {
return head;
}
//计算链表的长度
Node cur = head;
int len = 0;
while (cur != null) {
len++;
cur = cur.next;
}
//将链表变成数组:将链表中的每个元素依次放入到数组中
Node[] nodeArray = new Node[len];
int i = 0;
cur = head;
for (i = 0; i != nodeArray.length; i++) {
nodeArray[i] = cur;
cur = cur.next;
}
//将数组进行分区: 进行类似快速排序的partition
arrPartition(nodeArray, pivot);
//将数组变成链表:重新连接各个链表节点
for (i = 1; i != len; i++) {
nodeArray[i - 1].next = nodeArray[i];
}
nodeArray[i - 1].next = null;
//返回链表的head
return nodeArray[0];
}
/**进行类似快速排序的partition(补充,如果要稳定性,可以考虑插入排序,冒泡排序或者归并排序)*/
public static void arrPartition(Node[] nodeArray, int pivot) {
int small = -1;
int big = nodeArray.length;
int index = 0;
while (index != big) {
if (nodeArray[index].value < pivot) {//如果小于p,
swap(nodeArray, ++small, index++);//首先交换small和index的位置,然后各自+1,表示
} else if (nodeArray[index].value == pivot) {
index++;
} else {
swap(nodeArray, --big, index);
}
}
}
/**交换两个数*/
public static void swap(Node[] nodeArray, int a, int b) {
Node tmp = nodeArray[a];
nodeArray[a] = nodeArray[b];
nodeArray[b] = tmp;
}
/***测试**/
public static void main(String[] args) {
int[] array = {2,3,4,6,7,1,2,4,8,9};
int pivot = 4;
Node head = new Node(array[0]);
Node cur = head;
for (int i = 1; i < array.length; i++) {
cur.next = new Node(array[i]);
cur=cur.next;
}
head = listPartition(head, pivot);
cur = head;
while(cur!=null){
System.out.print(cur.value+" ");
cur = cur.next;
}
}
}
进阶问题
public class Main{
static class Node {
int value;
Node next;
public Node(int value){
this.value = value;
}
}
/**************************进阶问题******************/
/**
* 进阶问题: 保证各部分内部的顺序与原来链表中各个节点的顺序相同(稳定性)
* 时间复杂度是 O(N),空间复杂度是 O(1)
* 考察 利用有限的几个变量来调整链表的代码实现能力。
* 具体思路如下:
* 1.将链表分为三部分:small,equal,big
* small:1->2->null
* equal:5->5->null
* big:9->8->null
* 2.将small、equal、big三个链表重新串起来
* 3.整个过程需要特别注意对null节点的判断和处理
*/
public static Node listPartition2(Node head, int pivot){
// 将链表分为small、equal、big
Node sH = null;//small head
Node sT = null;//small tail
Node eH = null;//equal head
Node eT = null;//equal tail
Node bH = null;//big head
Node bT = null;//big tail
Node next = null;// 保存下一个节点
//将所有的节点依次保存在三个链表中
while (head != null){
// 将head节点独立出来
next = head.next;
head.next = null;
if (head.value < pivot){
if (sH == null){
sH = head;
}else {
sT.next = head;
}
sT = head;
}else if (head.value == pivot){
if (eH == null){
eH = head;
}else {
eT.next = head;
}
eT = head;
}else {
if (bH == null){
bH = head;
}else {
bT.next = head;
}
bT = head;
}
head = next;
}
// 小的和相等的重新连接
if (sT != null){
sT.next = eH;
if(eT==null){
eT=sT;
}
}
// 和大的重新连接
if (eT != null){
eT.next = bH;
}
// 判断头节点是否为空
if(sH!=null){
return sH;
}else{
if(eH!=null){
return eH;
}else{
return bH;
}
}
}
/***测试**/
public static void main(String[] args) {
int[] array = {2,3,4,6,7,1,2,4,8,9};
int pivot = 4;
Node head = new Node(array[0]);
Node cur = head;
for (int i = 1; i < array.length; i++) {
cur.next = new Node(array[i]);
cur=cur.next;
}
head = listPartition2(head, pivot);
cur = head;
while(cur!=null){
System.out.print(cur.value+" ");
cur = cur.next;
}
}
}
整合
public class Main{
static class Node {
int value;
Node next;
public Node(int value){
this.value = value;
}
}
/**************************普通问题******************/
/**
* 普通问题
* 时间复杂度是 O(N),空间复杂度是 O(N)
* 首先遍历一遍链表,得到链表的长度,将链表元素依次放到数组中,然后利用类似于快速排序中的partition 思想进行分割
*/
public static Node listPartition(Node head, int pivot) {
if (head == null) {
return head;
}
//计算链表的长度
Node cur = head;
int len = 0;
while (cur != null) {
len++;
cur = cur.next;
}
//将链表变成数组:将链表中的每个元素依次放入到数组中
Node[] nodeArray = new Node[len];
int i = 0;
cur = head;
for (i = 0; i != nodeArray.length; i++) {
nodeArray[i] = cur;
cur = cur.next;
}
//将数组进行分区: 进行类似快速排序的partition
arrPartition(nodeArray, pivot);
//将数组变成链表:重新连接各个链表节点
for (i = 1; i != len; i++) {
nodeArray[i - 1].next = nodeArray[i];
}
nodeArray[i - 1].next = null;
//返回链表的head
return nodeArray[0];
}
/**进行类似快速排序的partition(补充,如果要稳定性,可以考虑插入排序,冒泡排序或者归并排序)*/
public static void arrPartition(Node[] nodeArray, int pivot) {
int small = -1;
int big = nodeArray.length;
int index = 0;
while (index != big) {
if (nodeArray[index].value < pivot) {//如果小于p,
swap(nodeArray, ++small, index++);//首先交换small和index的位置,然后各自+1,表示
} else if (nodeArray[index].value == pivot) {
index++;
} else {
swap(nodeArray, --big, index);
}
}
}
/**交换两个数*/
public static void swap(Node[] nodeArray, int a, int b) {
Node tmp = nodeArray[a];
nodeArray[a] = nodeArray[b];
nodeArray[b] = tmp;
}
/**************************进阶问题******************/
/**
* 进阶问题: 保证各部分内部的顺序与原来链表中各个节点的顺序相同(稳定性)
* 时间复杂度是 O(N),空间复杂度是 O(1)
* 考察 利用有限的几个变量来调整链表的代码实现能力。
* 具体思路如下:
* 1.将链表分为三部分:small,equal,big
* small:1->2->null
* equal:5->5->null
* big:9->8->null
* 2.将small、equal、big三个链表重新串起来
* 3.整个过程需要特别注意对null节点的判断和处理
*/
public static Node listPartition2(Node head, int pivot){
// 将链表分为small、equal、big
Node sH = null;//small head
Node sT = null;//small tail
Node eH = null;//equal head
Node eT = null;//equal tail
Node bH = null;//big head
Node bT = null;//big tail
Node next = null;// 保存下一个节点
//将所有的节点依次保存在三个链表中
while (head != null){
// 将head节点独立出来
next = head.next;
head.next = null;
if (head.value < pivot){
if (sH == null){
sH = head;
}else {
sT.next = head;
}
sT = head;
}else if (head.value == pivot){
if (eH == null){
eH = head;
}else {
eT.next = head;
}
eT = head;
}else {
if (bH == null){
bH = head;
}else {
bT.next = head;
}
bT = head;
}
head = next;
}
// 小的和相等的重新连接
if (sT != null){
sT.next = eH;
if(eT==null){
eT=sT;
}
}
// 和大的重新连接
if (eT != null){
eT.next = bH;
}
// 判断头节点是否为空
if(sH!=null){
return sH;
}else{
if(eH!=null){
return eH;
}else{
return bH;
}
}
}
/***测试**/
public static void main(String[] args) {
int[] array = {2,3,4,6,7,1,2,4,8,9};
int pivot = 4;
Node head = new Node(array[0]);
Node cur = head;
for (int i = 1; i < array.length; i++) {
cur.next = new Node(array[i]);
cur=cur.next;
}
head = listPartition2(head, pivot);
cur = head;
while(cur!=null){
System.out.print(cur.value+" ");
cur = cur.next;
}
}
}