排序与递归
好像差不多了, 这是链表的最后一讲了. 这一讲主要讲链表的排序, 要知道, 链表中也存了数据, 既然存了数据, 就无法避免地要面临排序的问题. 那么, 堆排序? 插入排序? 还是归并排序? 还是快速排序呢? 让我们直接看看效果好了!
链表排序
插入排序法
public ListNode sortList(ListNode head) {
ListNode dummy = new ListNode(Integer.MIN_VALUE);
dummy.next= head;
ListNode tail = dummy;
while (head != null) {
if (head.val >= tail.val) {
tail = head;
}
else {
ListNode tmp = dummy.next;
ListNode prev = dummy;
while (tmp != null && tmp.val < head.val ) {
prev = tmp;
tmp = tmp.next;
}
prev.next = head;
ListNode next = head.next;
head.next = tmp;
tail.next = next;
head = tail;
}
head = head.next;
}
return dummy.next;
}
效果: 奇差
归并排序
// 找到中间元素, 并将其截断
private ListNode findMiddle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
ListNode prev = null;
while (fast != null && fast.next != null) {
fast = fast.next.next;
prev = slow;
slow = slow.next;
}
if (prev != null) prev.next = null;
return slow;
}
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode middle = findMiddle(head);
ListNode left = sortList(head);
ListNode right = sortList(middle);
if (right.val < left.val) {
head = right;
right = right.next;
}
else {
head = left;
left = left.next;
}
ListNode tmp = head;
while (right != null && left != null) {
if (right.val < left.val) {
tmp.next = right;
right = right.next;
tmp = tmp.next;
}
else {
tmp.next = left;
left = left.next;
tmp = tmp.next;
}
}
if (right != null) {
tmp.next = right;
}
if (left != null) {
tmp.next = left;
}
return head;
}
效果: 不错, 推荐使用
流氓解法 (请勿模仿)
private int[] vals;
private int getSize(ListNode head) {
int i = 0;
ListNode tmp = head;
while(tmp != null) {
tmp = tmp.next;
i++;
}
return i;
}
public ListNode sortList(ListNode head) {
if (head == null) return null;
int size = getSize(head);
vals = new int[size];
ListNode tmp = head;
for (int i = 0 ;i < size ; i++) {
vals[i] = tmp.val;
tmp = tmp.next;
}
Arrays.sort(vals);
tmp = head;
for (int i = 0 ; i < size; i++) {
tmp.val = vals[i];
tmp = tmp.next;
}
return head;
}
效果: 也不错, 这个故事告诉我们改变了val有时候和改变指针的效果是相同的.
递归
最后, 还是要提一下, 几个解决链表问题的思路. 首先, 有reverse, 很多时候, 逆序一下有意想不到的结果. 其次, 用空间换时间, 可以new ListNode[getSize(head)] 把所有节点存起来快速访问; 有时候, 使用递归的效果也不错, 因为我们知道head.next还是ListNode, 这是典型的递归结构. 有时候, 可以通过递归, 实现链表的逆序访问, 这个和深度优先遍历其实是一个意思. 参考题目: 有序链表转二叉树.