排序专题 -----(2)归并排序
一、以顺序表为存储结构的归并排序
- 自顶向下递归分治 (排序地基本逻辑略过,代码有详细注释)
// 伪代码
template <typename T>
void _mergeSort(T arr[], int l, int r)
{
if(l >= r) return;
int mid = l + (r-l)/2; // 分治点
_mergeSort(arr, l, mid); // 左侧递归结果为有序序列
_mergeSort(arr, mid+1, r); // 右侧递归结果为有序序列
if(arr[mid] > arr[mid+1]) // 若左侧的最大值 < 右侧的最小值, 不进行合并操作
_merge(arr, l, mid, r); // 将arr[l...mid]和arr[mid+1...r]进行归并
}
T aux[l-r+1]; // 申请辅助数组, 可以malloc动态申请,也可以用vector
void _merge(T arr[], int l, int mid, int r)
{
int i, j, k; // i、j分别为aux左、右侧数组游标, k为arr游标
for(k = l; k <= r; ++k)
aux[k] = arr[k];
for(i = l, j = mid+1, k = i; i <= mid && j <= high; ++k)
{
if(aux[i] <= aux[j]) // 比较aux的左右两段中的元素
arr[k] = aux[i++]; // 较小的复制到原数组arr, 下同
else
arr[k] = aux[j++];
}
/* 下列这两个while为同一操作, 故只会执行其中一个, 均为剩余数填充 */
while(i <= mid) // 左侧表未检测完
arr[k++] = aux[i++];
while(j <= high) // 右侧表未检测完
arr[k++] = aux[j++];
}
- 使用循环进行自底向上的归并排序
template <typename T>
void mergeSortBu(T arr[], int n)
{
for(int sz = 1; sz <= n; sz += sz) // sz作为归并划分的步长
{
/* 四点说明:
1、对arr[i...i+sz-1]和arr[i+sz...i+sz+sz-1]进行归并
2、i+sz < n是为了防止前一段的右端点i+sz-1不会越界
3、每次归并之后的子表长为2*sz, 故for子循环自加2*sz
4、min在merge中作用是, 防止后一段的右端点i+2*sz-1不会越界 */
for(int i = 1;i+sz < n; i += 2*sz)
if(a[i+sz-1] > arr[i+sz]) // 和自顶向下相同, 都优化限制合并条件
__merge(arr, i, i+sz-1, min(i+2*sz-1, n-1));
}
}
二、以链表为存储结构的归并排序
- 对链表自顶向下归并排序的过程如下。
(1)、找到链表的中点,以中点为分界,将链表拆分成两个子链表。寻找链表的中点可以使用快慢指针的做法,快指针每次移动 2 步,慢指针每次移动 1 步,当快指针到达链表末尾时,慢指针指向的链表节点即为链表的中点。【Leetcode 876. 链表的中间结点】
(2)、对两个子链表分别排序,递归到最后只有一个节点或者为空,比较两个不为空节点大小即可得到一个有序子链。
(3)、递归合并链表。【Leetcode 21. 合并两个有序链表】
// 以下为Leetcode官方给出的题解,用重载函数转了接口
class Solution
{
public:
ListNode* sortList(ListNode* head)
{
return sortList(head, nullptr);
}
ListNode* sortList(ListNode* head, ListNode* tail)
{
if(nullptr == head) // 空表直接返回
return head;
if(head->next == tail) // 子表只有两个节点直接拆左右
{
head->next = nullptr;
return head;
}
ListNode* slow = head, *fast = head; // 快慢指针的方法确定中间位置
while(fast != tail)
{
slow = slow -> next;
fast = fast -> next;
if(fast != tail)
fast = fast -> next; // 快指针速度是慢指针两倍
}
ListNode* mid = slow;
return merge(sortList(head, mid), sortList(mid, tail));
}
ListNode* merge(ListNode* head1, ListNode* head2)
{
ListNode* dummyHead = new ListNode(0); // 创建哑节点,同一链表操作
ListNode* temp = dummyHead, *temp1 = head1, *temp2 = head2;
while(temp1 != nullptr && temp2 != nullptr)
{
if(temp1->val <= temp2->val) // 取值小的挂到temp上
{
temp -> next = temp1;
temp1 = temp1 -> next;
}
else
{
temp -> next = temp2;
temp2 = temp2 -> next;
}
temp = temp -> next; // 更新temp
}
if(temp1 != nullptr) // 值大的表一定有剩余, 挂到temp上
temp -> next = temp1;
else if (temp2 != nullptr)
temp -> next = temp2;
return dummyHead->next;
}
};