排序链表

129 阅读2分钟

考察内容:链表,递归,二分

时间:2020-09-09 星期三

作者:guuzaa

掘金主页:🌏

题目描述 📃

题目链接 🔗,题目来自 leetcode。题目要求很简单,用 Nlog(N) 的时间复杂度和常数空间复杂度排序链表。

分析 💻

先实现一下数组的归并排序。

void mergeSort(vector<int> &nums, int left, int right) {
	if (left < right) {  // 递归出口
        int mid = left + (right - left) >> 1;
        mergeSort(nums, left, mid);
        mergeSort(nums, mid+1, right);
        
        merge(nums, left, mid, right);
    }
}


void merge(vector<int> &nums, int left, int mid, int right) {
    int h1 = left;
    int h2 = mid + 1;
    
    vector<int> tab;
    while (h1 <= mid && h2 <= right) {
        if (nums[h1] < nums[h2]) {
            tab.emplace_back(nums[h1++]);
        } else {
            tab.emplace_back(nums[h2++]);
        }
    }
    
    while (h1 <= mid) tab.emplace_back(nums[h1++]);
    
    while (h2 < right) tab.emplace_back(nums[h2++]);
    
    for (int i = 0; i < (int) tab.size(); i++) {
        nums[left + i] = tab[i];
    }
}

这道题目不过就是将数组换成了链表而已,大致思想没有变化。首先将链表不停分成两半,对左右链表递归调用归并排序,直到可以直接得到结果为止(链表长度为 1)。最后将左右有序链表归并为一个有序链表。

先实现链表的归并操作。

ListNode* merge(ListNode *head1, ListNode *head2) {
    auto left = head1;
    auto right = head2;
    ListNode dummy(-1);
    auto cur = &dummy;
    
    while (left && right) {
        if (left->val < right->val) {
            cur->next = left;
            left = left->next;
        } else {
            cur->next = right;
            right = right->next;
        }
        cur = cur->next;
    }
    cur->next = head1 ? head1 : head2;
    
    return dummy.next;
}

接着仿照数组的归并排序实现链表的。

ListNode* mergeSort(ListNode* head) {
    // 递归出口
    if (head == nullptr || head1->next == nullptr) return head;
    // 将链表折半 就是找链表的中间节点(快慢指针)
    ListNode *slow = head;
    ListNode *fast = head->next;
    
    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
    }
    // 这时中间节点是slow 进行断链操作,将fast指向右侧链表。
    fast = slow->next;
    slow->next = nullptr;
    // head 和 fast 是原链表折半后的左右链表
    
    auto left = mergeSort(head);
    auto right= mergeSort(fast);
    
    return merge(left, right);
    
}

可以稍微简化一下代码,leftright 是没有必要定义的,直接写在 merge 即可,代码如下。

ListNode* mergeSort(ListNode* head) {
    
    if (head == nullptr || head1->next == nullptr) return head;
    
    ListNode *slow = head;
    ListNode *fast = head->next;
    
    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
    }
    
    fast = slow->next;
    slow->next = nullptr;
    return merge(mergeSort(head), mergeSort(fast));
    
}

总结 📕:

  • 如果这个题目考察的是用 Nlog(N) 的时间复杂度和常数空间复杂度排序数组,我相信我是能做出来的。但是题目剑走偏锋,让我们排序链表。这就把我难住了。
  • 归并链表操作、找链表中间节点代码有待加强。
  • 研究生开学快乐 😄

全文完