线性时间选择 5元组选中位数法

//
/*
	//  [8/7/2011] By Whisper
	对那些证明,O(X)我真的不太懂怎么证,数学学的不是太好。
	希望读到这篇代码的,懂的朋友可以留言帮助我理解一下,我只是简单的将代码实现了,需要的就来拿吧。
	在看这个选择算法的时候,应该对前面那个三位取中划分有些了解。
	其中有好多细节,我就不写了,代码能表述一切。

	http://blog.csdn.net/v_july_v/article/details/6431001 这里有一篇很不错的讲解。

	算法导论9.3节代码实现
	主要思想:
	1. 对输入的数组分成 N/5个5元组,最后一个元组个数为 n % 5
	2. 对 N/5 个5元组,进行排序,找出其中的中位数(就是第3个数),再存成一个有N/5个数的中位数数组
	3. 再对这N/5个中位数, 递归查找中位数。
	4. 按找到的这个数,进行左右划分
	5. 如果要查找的第K大个数与这个划分点位置相等,则返回,如果小于,则在左边,否则在右边。
	
*/
//
//test.cpp VC6 编译通过
//
#include 
#include 
#include "initFuncs.h"       //此文件包含了随机初始化函数 RndInitArray() 和 输出函数 Outprint()


//下面是initFuncs.h的辅助函数代码,可以自己组织一下。
		 
/*

#ifndef _FUNC_INIT_
#define _FUNC_INIT_
  
	#include 
	#include 
	#include 
	
#endif
	  
void RndINitArray(int nArray[], int nArraySize);
void OutPrint(int nArrany[], int nArraySize);

  
void RndINitArray(int nArray[], int nArraySize)
{
	srand(time(NULL));
	for (int i = 0; i < nArraySize; i++)
	{
		nArray[i] = rand() % 90 + 10;
	}
}
	
void OutPrint(int nArray[], int nArraySize)
{
  for (int i = 0; i < nArraySize; i++)
  {
	  printf("%3d", nArray[i]);
  }
  printf("\n\n");
}

*/

#define  ARRAY_SIZE 12

void Swap(int *x, int *y);   //交换函数
void InsertSort(int nArray[], int left, int right);        //插入排序
int RandomizedPartition(int a[], int left, int right);     //分隔函数
int RandomizedSelect(int a[], int left, int right, int k); //线性选择主函数
int MidNum(int nArray[], int left, int right);             //选取5元组的中位数的中位数

const int nMidArraySize = ARRAY_SIZE / 5 + 1;
int nMidArray[nMidArraySize];


//------------------------测试-------------------------
int main(void)
{
	int nArray[ARRAY_SIZE];
	RndINitArray(nArray, ARRAY_SIZE);
	printf("\n\tArray Size is %d\n\n", ARRAY_SIZE);
	printf("original Array:\n");
	OutPrint(nArray, ARRAY_SIZE);
	printf("RandomizedSelected from 1 to %d\n", ARRAY_SIZE);
 	for (int i = 1; i <= ARRAY_SIZE; i++)
 	{
 		printf("%3d", RandomizedSelect(nArray, 0, ARRAY_SIZE, i));  
 	}
//	printf("%d\n", RandomizedSelect(nArray, 0, ARRAY_SIZE, 5));
	printf("\n\n");
	printf("Compare with InsertSort:\n");
	InsertSort(nArray, 0, ARRAY_SIZE);
	OutPrint(nArray, ARRAY_SIZE);
	return 1;
}

//----------------------------插入---------------------------
void InsertSort(int nArray[], int left, int right)
{
	for (int i = left + 1; right > 1; right--)
	{
		int nTmpKey = nArray[i];
		int j = i++ - 1;

		while (j >= left && nArray[j] > nTmpKey)
		{
			nArray[j + 1] = nArray[j];
			j--;
		}
		nArray[j + 1] = nTmpKey;
	}
}
void Swap(int *x, int *y)  
{
    int t = *x;
	*x =  *y;
	*y = t;
}


int RandomizedPartition(int a[], int left, int right)  
{
	//寻找中位数
    int x =  MidNum(a, left, right);

	int index = left;
	while(x != a[index++]);       //寻找中位数在数组中的位置
		index--;				  //也可以从新设计存储结构

	Swap(&a[index], &a[left]);
    int i = left, 
        j = right;
    while (true)
    {
		while (a[++i] < x);
		while (a[--j] > x);
		
		if (i >= j)
		{
			break;
		}
		Swap(&a[i], &a[j]);
    }
	Swap(&a[j], &a[left]);
    return j;        //返回Select位置
}

//--------------------------利用快排思想,一半一半的找--------------

int RandomizedSelect(int a[], int left, int right, int k)
{

    int i = RandomizedPartition(a, left, right),
        j = i - left+1;

	if (j == k)       //终结状态
    {
        return a[i];
    }
    else if (k < j)
    {
        return RandomizedSelect(a, left, i, k);        //在左边
    }
    else
    {
        return RandomizedSelect(a, i+1, right, k-j);    //在右边
    }
}
//-------------------------选取5元组的中位数----------------------------

int MidNum(int nArray[], int left, int right)
{
	if (1 == right)   //直到最后只剩一个元素,即是中位数的中位数
	{
		return nArray[left];
	}

	int remainder = (right - left) % 5;
	int i = left, j = 0;
	for (; i < right - remainder; i += 5)
	{
		InsertSort(nArray, i, 5);          //对每个5元组进行插入排序
		nMidArray[j++] = nArray[i + 2];    //存放中位数
	}
	
	//如果存在剩余元素则处理剩余元素
	if (remainder > 0)
	{
		InsertSort(nArray, i, remainder);
		nMidArray[j++] = nArray[i + remainder / 2]; 
	}

	//从中位数组中寻找中位数
	int nMidElem = (right - left) / 5 + (0 != remainder); 
	return MidNum(nMidArray, 0, nMidElem);

}


下面是测试结果:


你可能感兴趣的:(算法导论笔记)