W. :利用合并排序和二分查找实现习题2.3-7

来源:互联网 发布:python最好的书籍推荐 编辑:程序博客网 时间:2024/06/14 04:01

//author: W.
//利用合并排序和二分查找实现习题2.3-7。
//时间复杂度:O(nlgn)

#include <stdio.h>
#include <stdlib.h>
void merge(int a[], int p, int q, int r)
{
    int n1 = q - p + 1;
    int n2 = r - q;
    int* L = (int*)malloc(sizeof(int) * n1);
    int* R = (int*)malloc(sizeof(int) * n2);
    int i;
    int j;
    int k;
    for(i = 0; i < n1; ++i)
    {
        L[i] = a[p+i];
    }
    for(j = 0; j < n2; ++j)
    {
        R[j] = a[q+1+j];
    }
    i = 0; //循环不变式初始化:L[i]和R[i]分别是L与R中最小值,新的a中加入的元素个数为0,即加入的元素是L和R中的最小元素(还没有元素呢)
    j = 0;
    for(k = p; k <= r; ++k)//保持:L[i]和R[i]分别是L与R中最小值,新的a中加入的元素是L和R中的最小元素,即a中已加入元素都是小于L和R中的元素,在完成++k后,a[p]~a[k-1]为已排序
    {
        //使用该方式的好处是在循环不变式的主循环中会在循环结束后得到结果,即对循环不变式的终止情况判断清晰
        //但这种方式会导致加入L或R剩余的元素时,每次都要多一次i == n1或 j == n2的判断
        if(i == n1) //使用在主循环中处理完所有操作,注意由于n1 + n2 == r - p + 1,所以这里无需判断j < n2
        {
            a[k] = R[j];
            ++j;
            continue;
        }
        else if(j == n2)
        {
            a[k] = L[i];
            ++i;
            continue;
        }
        if(L[i] <= R[j])
        {
            a[k] = L[i];
            ++i;
        }
        else
        {
            a[k] = R[j];
            ++j;
        }
    }
    //无论采用哪种方式,此时循环不变式到此终止,即k此时为r+1,则a[p]~a[r]已排序完毕
    free(L);
    free(R);
}

void merge_sort(int a[], int p, int r)
{
    //分治法:
    int q;
    if(p < r)
    {
        q = (p + r) / 2; //向下取整  //分解:分解成子问题,即分解成2部分子序列
        merge_sort(a, p, q); //解决:递归地解各个子问题。
        merge_sort(a, q+1, r);
        merge(a, p, q, r); //合并:将子问题的结果合并成,即将2部分子序列通过merge函数进行合并。
    }
    //当p >= r时,此时a[p] ~ a[r]至多只有一个元素,则处于已排序状态   //解决:若子问题足够小,直接求解。即这里已排序,无需做任何操作
}

//正常的二分查找
int binary_search(int a[], int begin, int end, int key)
{
    int mid;
    while(begin <= end)
    {
        mid = (begin + end) / 2;
        if(a[mid] < key)
        {
            begin = mid + 1;
        }
        else if(a[mid] > key)
        {
            end = mid - 1;
        }
        else
        {
            return mid;
        }
    }
    return -1;
}

//返回值:找到了返回真,没找到返回假
//参数a和b分别返回找到的值.如果没找到则忽略该值
int find_x_from_S(int S[], int length, int x, int* a, int* b)
{
    if(length < 2)
    {
        return 0;
    }
    merge_sort(S, 0, length-1);//先利用合并排序对S进行排序,时间复杂度为O(nlgn)
    int i;
    int search_index;
    for(i = 0; i < length; ++i)//总的循环时间复杂度为O(nlgn)
    {
        if((search_index = binary_search(S, i+1, length-1, x-S[i])) >= 0) //在循环内部进行二分查找,范围在S[i+1]~S[length-1]中查找,查找值为x-S[i]。二分查找的时间复杂度是O(lgn)
        {
            *a = S[i];
            *b = S[search_index]; //为了检验找到的索引正确性,否则直接返回x-*a即可。
            return 1;
        }
    }
    return 0;//排序+查找的时间为nlgn + nlgn,则时间复杂度依然是O(nlgn)
}

void test_find_x_from_S()
{
    int S[] = {4, 6, 3, 10, 6, 9, 8};
    int i;
    for(i = 0; i < sizeof(S)/sizeof(int); ++i)
    {  
        printf("%d ", S[i]);
    }  
    printf("/n");

    int a;
    int b;
    int x;
    for(x = 0; x < 20; ++x)
    {
        if(find_x_from_S(S, sizeof(S)/sizeof(int), x, &a, &b))
        {
            printf("x:%d = %d + %d/n", x, a, b);
        }
        else
        {
            printf("x:%d can not find/n", x);
        }
    }
}


int main(int argc, char** argv)
{
    test_find_x_from_S();
    return 0; 
}

//输出:
//4 6 3 10 6 9 8
//x:0 can not find
//x:1 can not find
//x:2 can not find
//x:3 can not find
//x:4 can not find
//x:5 can not find
//x:6 can not find
//x:7 = 3 + 4
//x:8 can not find
//x:9 = 3 + 6
//x:10 = 4 + 6
//x:11 = 3 + 8
//x:12 = 3 + 9
//x:13 = 3 + 10
//x:14 = 4 + 10
//x:15 = 6 + 9
//x:16 = 6 + 10
//x:17 = 8 + 9
//x:18 = 8 + 10
//x:19 = 9 + 10

原创粉丝点击