diff --git a/src/class05/Code04_DoubleLinkedListQuickSort.java b/src/class05/Code04_DoubleLinkedListQuickSort.java new file mode 100644 index 0000000..5a84484 --- /dev/null +++ b/src/class05/Code04_DoubleLinkedListQuickSort.java @@ -0,0 +1,305 @@ +package class05; + +import java.util.ArrayList; +import java.util.Comparator; + +// 双向链表的随机快速排序 +// 课上没有讲,因为这是群里同学问的问题 +// 作为补充放在这,有需要的同学可以看看 +// 和课上讲的数组的经典快速排序在算法上没有区别 +// 但是coding需要更小心 +public class Code04_DoubleLinkedListQuickSort { + + public static class Node { + public int value; + public Node last; + public Node next; + + public Node(int v) { + value = v; + } + } + + public static Node quickSort(Node h) { + if (h == null) { + return null; + } + int N = 0; + Node c = h; + Node e = null; + while (c != null) { + N++; + e = c; + c = c.next; + } + return process(h, e, N).h; + } + + public static class HeadTail { + public Node h; + public Node t; + + public HeadTail(Node head, Node tail) { + h = head; + t = tail; + } + } + + // L...R是一个双向链表的头和尾, + // L的last指针指向null,R的next指针指向null + // 也就是说L的左边没有,R的右边也没节点 + // 就是一个正常的双向链表,一共有N个节点 + // 将这一段用随机快排的方式排好序 + // 返回排好序之后的双向链表的头和尾(HeadTail) + public static HeadTail process(Node L, Node R, int N) { + if (L == null) { + return null; + } + if (L == R) { + return new HeadTail(L, R); + } + // L..R上不只一个节点 + // 随机得到一个随机下标 + int randomIndex = (int) (Math.random() * N); + // 根据随机下标得到随机节点 + Node randomNode = L; + while (randomIndex-- != 0) { + randomNode = randomNode.next; + } + // 把随机节点从原来的环境里分离出来 + // 比如 a(L) -> b -> c -> d(R), 如果randomNode = c,那么调整之后 + // a(L) -> b -> d(R), c会被挖出来,randomNode = c + if (randomNode == L || randomNode == R) { + if (randomNode == L) { + L = randomNode.next; + L.last = null; + } else { + randomNode.last.next = null; + } + } else { // randomNode一定是中间的节点 + randomNode.last.next = randomNode.next; + randomNode.next.last = randomNode.last; + } + randomNode.last = null; + randomNode.next = null; + Info info = partition(L, randomNode); + // randomNode的部分去排序 + HeadTail rht = process(info.rh, info.rt, info.rs); + // 左部分排好序、右部分排好序 + // 把它们串在一起 + if (lht != null) { + lht.t.next = info.eh; + info.eh.last = lht.t; + } + if (rht != null) { + info.et.next = rht.h; + rht.h.last = info.et; + } + // 返回排好序之后总的头和总的尾 + Node h = lht != null ? lht.h : info.eh; + Node t = rht != null ? rht.t : info.et; + return new HeadTail(h, t); + } + + public static class Info { + public Node lh; + public Node lt; + public int ls; + public Node rh; + public Node rt; + public int rs; + public Node eh; + public Node et; + + public Info(Node lH, Node lT, int lS, Node rH, Node rT, int rS, Node eH, Node eT) { + lh = lH; + lt = lT; + ls = lS; + rh = rH; + rt = rT; + rs = rS; + eh = eH; + et = eT; + } + } + + // (L....一直到空),是一个双向链表 + // pivot是一个不在(L....一直到空)的独立节点,它作为划分值 + // 根据荷兰国旗问题的划分方式,把(L....一直到空)划分成: + // pivot 三个部分,然后把pivot融进=pivot的部分 + // 比如 4(L)->6->7->1->5->0->9->null pivot=5(这个5和链表中的5,是不同的节点) + // 调整完成后: + // 4->1->0 小于的部分 + // 5->5 等于的部分 + // 6->7->9 大于的部分 + // 三个部分是断开的 + // 然后返回Info: + // 小于部分的头、尾、节点个数 : lh,lt,ls + // 大于部分的头、尾、节点个数 : rh,rt,rs + // 等于部分的头、尾 : eh,et + public static Info partition(Node L, Node pivot) { + Node lh = null; + Node lt = null; + int ls = 0; + Node rh = null; + Node rt = null; + int rs = 0; + Node eh = pivot; + Node et = pivot; + Node tmp = null; + while (L != null) { + tmp = L.next; + L.next = null; + L.last = null; + if (L.value < pivot.value) { + ls++; + if (lh == null) { + lh = L; + lt = L; + } else { + lt.next = L; + L.last = lt; + lt = L; + } + } else if (L.value > pivot.value) { + rs++; + if (rh == null) { + rh = L; + rt = L; + } else { + rt.next = L; + L.last = rt; + rt = L; + } + } else { + if (eh == null) { + eh = L; + et = L; + } else { + et.next = L; + L.last = et; + et = L; + } + } + L = tmp; + } + return new Info(lh, lt, ls, rh, rt, rs, eh, et); + } + + // 为了测试 + public static class NodeComp implements Comparator { + + @Override + public int compare(Node o1, Node o2) { + return o1.value - o2.value; + } + + } + + // 为了测试 + public static Node sort(Node head) { + if (head == null) { + return null; + } + ArrayList arr = new ArrayList<>(); + while (head != null) { + arr.add(head); + head = head.next; + } + arr.sort(new NodeComp()); + Node h = arr.get(0); + h.last = null; + Node p = h; + for (int i = 1; i < arr.size(); i++) { + Node c = arr.get(i); + p.next = c; + c.last = p; + c.next = null; + p = c; + } + return h; + } + + // 为了测试 + public static Node generateRandomDoubleLinkedList(int n, int v) { + if (n == 0) { + return null; + } + Node[] arr = new Node[n]; + for (int i = 0; i < n; i++) { + arr[i] = new Node((int) (Math.random() * v)); + } + Node head = arr[0]; + Node pre = head; + for (int i = 1; i < n; i++) { + pre.next = arr[i]; + arr[i].last = pre; + pre = arr[i]; + } + return head; + } + + // 为了测试 + public static Node cloneDoubleLinkedList(Node head) { + if (head == null) { + return null; + } + Node h = new Node(head.value); + Node p = h; + head = head.next; + while (head != null) { + Node c = new Node(head.value); + p.next = c; + c.last = p; + p = c; + head = head.next; + } + return h; + } + + // 为了测试 + public static boolean equal(Node h1, Node h2) { + return doubleLinkedListToString(h1).equals(doubleLinkedListToString(h2)); + } + + // 为了测试 + public static String doubleLinkedListToString(Node head) { + Node cur = head; + Node end = null; + StringBuilder builder = new StringBuilder(); + while (cur != null) { + builder.append(cur.value + " "); + end = cur; + cur = cur.next; + } + builder.append("| "); + while (end != null) { + builder.append(end.value + " "); + end = end.last; + } + return builder.toString(); + } + + // 为了测试 + public static void main(String[] args) { + int N = 500; + int V = 500; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int size = (int) (Math.random() * N); + Node head1 = generateRandomDoubleLinkedList(size, V); + Node head2 = cloneDoubleLinkedList(head1); + Node sort1 = quickSort(head1); + Node sort2 = sort(head2); + if (!equal(sort1, sort2)) { + System.out.println("出错了!"); + break; + } + } + System.out.println("测试结束"); + } + +}