星之所在

静心学习 莫问前程

【算法导论】分治法及归并排序

有很多算法,在结构上他们是递归的:为了解决一个已经给定的问题,算法要一次或多次的递归调用其自身来解决相关的子问题,这类算法通常采用分治策略。

分治法的基本思想:

(1).原问题==(划分)== >>若干个更小规模的子问题。

(2).要求子问题的求解方法与原问题相同。

分治法的求解步骤:

(1)分解(Divide):将当前问题划分成若干子问题

(2)解决(Conquer):递归解子问题,如果子问题足够小则直接解

(3)合并(Combine):将子问题的解合并长原问题


分治法示例,归并排序:

/*
《Introduction to Algorithms(second edition)》
 chapter2,MERGE_SORT()
date:2014-9-18
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#define MAX 50

typedef struct
{
	int arr[MAX+1];
	int length;
}SortArr;

SortArr *CreateSortArr()
{
	int i = 0;
	char buf[4*MAX] = "";
	char *ptr = NULL;
	SortArr *sortArr = (SortArr *)malloc(sizeof(SortArr));
	memset(sortArr, 0, sizeof(SortArr));

	printf("请输入待排序数据,以逗号分隔,以分号结束\n"
		"例:23,12,65,36,35;\n"
		"input:");
	scanf("%s", buf);

	ptr = buf;
	sortArr->arr[i] = 0;	//arr[0]不存值用作哨兵单元
	i = 1;
	while(*ptr != ';')
	{
		sortArr->arr[i] = atoi(ptr);
		i++;

		ptr = strstr(ptr, ",");
		if(!ptr)
		{
			break;
		}
		ptr++;
	}
	sortArr->length = (i - 1);

	return sortArr;
}

int merge(int arr[], int p, int q, int r)
{
    int i = 0;
    int j = 0;
    int k = 0;
    int n1 = 0;
    int n2 = 0;
    int *leftArr = NULL;
    int *rightArr = NULL;

    n1 = q - p + 1;
    n2 = r - q;

    //为了和arr[]下标保持一致,leftArr[0]和rightArr[0]不用
    leftArr = (int *)malloc((n1 + 2) * sizeof(int));
    rightArr = (int *)malloc((n2 + 2) * sizeof(int));

    for(i = 1; i <= n1; i++)
    {
        leftArr[i] = arr[p+i-1];
    }
    for(j = 0; j <= n2; j++)
    {
        rightArr[j] = arr[q+j];
    }

    i = 1;
    j = 1;

    //将最后一个数设置为哨兵
    leftArr[n1+1] = INT_MAX;
    rightArr[n2+1] = INT_MAX;

    for(k = p; k <= r; k++)
    {
        if(leftArr[i] <= rightArr[j])
        {
            arr[k] = leftArr[i];
            i++;
        }
        else
        {
            arr[k] = rightArr[j];
            j++;
        }
    }

    free(leftArr);
    free(rightArr);
    return 0;
}

int mergeSort(int arr[], int p, int r)
{
    int q = 0;

    if(p < r)
    {
        q = (p + r) / 2;
        mergeSort(arr, p, q);
        mergeSort(arr, (q+1), r);
        merge(arr, p, q, r);
    }

    return 0;
}

int MergingSortRecursion(SortArr *sortArr)
{
    mergeSort(sortArr, 1, sortArr->length);
    return 0;
}

int PrintArray(SortArr sortArr)
{
	int i = 0;

	for(i = 1; i <= sortArr.length; i++)
	{
		printf("%d,", sortArr.arr[i]);
	}
	printf("\b;\n");

	return 0;
}

int main()
{
	SortArr *sortArr = NULL;
	sortArr = CreateSortArr();

	printf("\nBefore Sort:\t");
	PrintArray(*sortArr);

	MergingSortRecursion(sortArr);

	printf("Sorted Array:\t");
	PrintArray(*sortArr);

	free(sortArr);

	return 0;
}
时间复杂度分析:

在merge()函数中,第70行~77行的的for()循环所需要的时间O(n1+n2)=O(n),第86行~98行的for循环共有n轮迭代,其中每一轮迭代所需的时间都是常量,所以merge()函数的时间复杂度为O(n)。

阅读更多
个人分类: 算法导论
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭