//
/*
// [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);
}
下面是测试结果: