利用手摇算法实现原地归并排序

归并排序很基础,通常的实现需要O(n)的空间复杂度,思路简单,不赘述。

今年的百度笔试题考了一个原地归并排序的归并算法:

要求用O(1)的空间复杂度,合并2个已经有序的子数组a[0,mid-1] 和 a[mid,num-1]。按照升序排列。

手摇算法:使用3次reverse操作实现相邻区间的交换位置

void exchange(int a[], int size, int n) {
    reverse(a, n);
    reverse(a + n, size - n);
    reverse(a, size);
}

原理很简单,不多说了。

假设数组a[p...m]和a[m+1...r]是两个相邻的有序数组。
对于原地归并,我们定义两个下标i和j,保持下列循环不变式:
a[p...i-1],a[i...j-1],a[j...r]都是有序

i和j所指向的元素就是待归并的两个数组的第一个元素。
我们比较a[i]和a[j]
1. 如果a[i]比较小,我们就++i,显然循环不变式得到保持。
2. 如果a[j]比较小,令old_j = j,然后我们从j开始向后顺序搜索,直到a[j]>a[i],然后我们利用手摇法来交换a[i..old_j-1]和a[old_j...j-1],更新i的值。

重复上面的步骤,直到i == j 或者 j == r + 1

然后就是实现之~

//
//  Merge.c
//  O(1) : space complexity
//

#include <stdio.h>

void display(int a[], int num);
void swap(int *a, int *b);
void reverse(int a[], int size);
void exchange(int a[], int size, int n);
void merge(int a[], int mid, int num);
void merge_sort(int a[], int size);

int main()
{
    int a[6] = {2,5,8,1,3,7};
    //merge(a, 3, 6);
    
    merge_sort(a, 6);
    return 0;
}

void display(int a[], int num)
{
    for (int i = 0; i < num; i++)
        printf("%d ", a[i]);
    printf("\n");
}

void swap(int *a, int *b)
{
    int t = *a;
    *a = *b;
    *b = t;
}
/*
 space: O(1)
 reverse
 */
void reverse(int a[], int size)
{
    int s = 0, e = size - 1;
    while (s < e && s < size && e > 0) {
        swap(&a[s++], &a[e--]);
    }
}

void exchange(int a[], int size, int n)
{
    reverse(a, n);
    reverse(a + n, size - n);
    reverse(a, size);
}
/*
 a[0, mid-1] and a[mid, num-1] are sorted
 */
void merge(int a[], int mid, int num)
{
    int fir = 0, sec = mid;
    while (fir < sec && sec < num) {
        while (fir < sec && a[fir] <= a[sec]) {
            fir++;
        }
        int maxmove = 0;
        while (sec < num && a[fir] > a[sec]) {
            maxmove++,sec++;
        }
        exchange(&a[fir], sec - fir, sec - fir - maxmove);
        fir += maxmove;
        
        display(a, num);
    }

}

void merge_sort(int a[], int num)
{
    if (num <= 1) {
        return;
    }
    merge_sort(a, num/2);
    merge_sort(a + num/2, num - num/2);
    merge(a, num/2, num);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值