🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 1. 题目描述 给定一个单链表,做链表中的数据的三类划分,小于`number`放置在链表左边,等于`nubmer`的数放在中间,大于`number`的数放在右边。 ## 1.1 分析 ### 解法1 这个题在快速排序算法拓展中做过。那么我们可以将链表中的数据装入数组,然后再在数组中做一次三部分的数据划分。代码如下: ~~~ /** * 划分链表为三个部分,小于、等于和大于 * @param list 链表 * @param number 比较数字 * @return 链表 */ public Node partitionList(Node list, int number){ if(list == null || list.next == null) return list; List<Node> datas = new ArrayList<>(); Node ptr = list; while(ptr != null){ datas.add(ptr); ptr = ptr.next; } Node[] objects = new Node[datas.size()]; for (int i = 0; i < datas.size(); i++) { objects[i] = datas.get(i); } int left = 0, right = objects.length - 1, k = 0; while(k <= right){ if(objects[k].val < number){ swap(objects, left, k); if(left == k) k++; left++; } else if(objects[k].val > number){ swap(objects, right, k); right--; }else{ k++; } } Node dummyNode = new Node(-1); ptr = dummyNode; for (Node object : objects) { ptr.next = object; ptr = ptr.next; } ptr.next = null; return dummyNode.next; } private void swap(Node[] objects, int left, int i) { Node temp = objects[left]; objects[left] = objects[i]; objects[i] = temp; } ~~~ 测试用例: ~~~ static class Node{ int val; Node next; public Node(int val){ this.val = val; } public static Node createLinkedList(int[] arr){ if(arr == null || arr.length == 0) return null; Node dummyNode = new Node(-1); Node ptr = dummyNode; for (int val : arr) { ptr.next = new Node(val); ptr = ptr.next; } ptr.next = null; return dummyNode.next; } public static void printLinkedList(Node head){ Node ptr = head; while(ptr != null){ System.out.print(ptr.val+"\t"); ptr = ptr.next; } System.out.println(); } } public static void main(String[] args) { int[] arr = new int[]{1, 5, 4, 8, 3, 1, 4}; Node linkedList = Node.createLinkedList(arr); Node.printLinkedList(linkedList); Node node = new ListDemo().partitionList(linkedList, 4); Node.printLinkedList(node); } ~~~ 结果: <img src="http://weizu_cool.gitee.io/gif-source/images/屏幕截图 2022-01-08 171132.png"/> 因为这个部分的逻辑就来源于快排的扩展部分,这里就不再赘述。 ### 解法2 当然有更加简单做法,也就是直接整三个链表,然后分别根据值来添加其位置,代码如下: ~~~ public Node partitionList_2(Node list, int number){ if(list == null || list.next == null) return list; Node leftPartStart = new Node(-1); leftPartStart.next = null; Node midPartStart = new Node(-1); midPartStart.next = null; Node rightPartStart = new Node(-1); rightPartStart.next = null; Node ptr = list; while(ptr != null){ Node temp = ptr.next; if(ptr.val < number){ ptr.next = leftPartStart.next; leftPartStart.next = ptr; } else if(ptr.val > number){ ptr.next = rightPartStart.next; rightPartStart.next = ptr; } else{ ptr.next = midPartStart.next; midPartStart.next = ptr; } ptr = temp; } Node temp = leftPartStart.next; while(temp!= null && temp.next != null){ temp = temp.next; } Node midTemp = midPartStart.next; while(midTemp != null && midTemp.next != null){ midTemp = midTemp.next; } if(temp != null){ temp.next = midPartStart.next; } if(midTemp != null){ midTemp.next = rightPartStart.next; } return leftPartStart.next; } ~~~ 结果: <img src="http://weizu_cool.gitee.io/gif-source/images/屏幕截图 2022-01-08 194239.png"/> ### 解法3 注意到上面的解法中最终还是需要遍历链表,如果我们可以直接记录左边、中间和右边链表各自的头尾,那么就可以直接链接成链,而不用遍历。解法如下: ~~~ /** * 更节省空间的做法 * @param list 链表 * @param number 划分数据 * @return 划分后的链表 */ public Node partitionList_3(Node list, int number) { if (list == null || list.next == null) return list; Node leftPartStart = new Node(-1); Node leftPartEnd = new Node(-1); leftPartStart.next = leftPartEnd; leftPartEnd.next = null; Node midPartStart = new Node(-1); Node midPartEnd = new Node(-1); midPartStart.next = midPartEnd; midPartEnd.next = null; Node rightPartStart = new Node(-1); Node rightPartEnd = new Node(-1); rightPartStart.next = rightPartEnd; rightPartEnd.next = null; Node ptr = list, leftP = leftPartStart, midP = midPartStart, rightP = rightPartStart; while(ptr != null){ Node temp = ptr.next; if(ptr.val < number){ ptr.next = leftP.next; leftP.next = ptr; leftP = leftP.next; } else if(ptr.val == number){ ptr.next = midP.next; midP.next = ptr; midP = midP.next; }else{ ptr.next = rightP.next; rightP.next = ptr; rightP = rightP.next; } ptr = temp; } if(midPartStart.next != midPartEnd){ leftP.next = midPartStart.next; } if(rightPartStart.next != rightPartEnd){ midP.next = rightPartStart.next; } rightP.next = null; return leftPartStart.next; } ~~~ 且因为在这个解法中,一直是按照链接到各自链表尾部的做法,所以对于相同元素而言会按照其出现位置连在对应子链表的后面,所以这个算法是稳定的。