[******] 链表问题:将单向链表按某值划分成左边小、中间相等、右边大的形式
问题描述
普通问题:
给定一个单向链表的头节点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; } } }