一.基础(Fundamentals)
1.最小值和最大值(Min and Max)
从元素的成对比较开始,如果要查找最大的元素和最小的元素,可以使数组的元素成对比较后,大的和最大值比较,小的和最小值比较,从而使比较次数达到最小3n/2-2次。
2.最小值和次小值(Min and Sec_Min)
沿用上述求解最大值和最小值的成对比较思想,在比较过程中同样进行两两比较,如果最小的比Min小,再比较Min和比较过程中的较大值,更新Sec_Min,再更新Min。(注意先后次序,以免在更新Min的过程中丢失原值);如果最小的比Min大,则比较该值和Sec_Min的大小关系,如果小则更新,大则舍弃。
3.带权的中位数(Weight Middle Number)
一般的中位数指的是一组有序数据,排在中间的数,在偶数情况中,该中间数不唯一,分为上中位数和下中位数两种。在基本中位数中,我们是将中位数做了平均权化处理,即认为该数组中的值都是均等的,没有谁权大权小的问题。
然而在很多实际问题中,对一个事物的影响程度不同,你的结果的价值就不同。好比你在清华北大拿了个年级第一,和在重庆交通大学拿了个年级第一,尽管都是很高的分数,但含金量就远远不可比拟了。
利用快速排序排序数据,然后从头到尾累加权值,直到和在0.5附近。这个附近,可以稍微大,可以稍微小,这和你要求的是上中位数还是下中位数同样的道理,你可以自己选择。
上述1、2种算法都较容易实现,难点在于起点的处理,即Min和Max,或者Min和Sec_Min初始值如何设计,因而要分数组元素是奇数和偶数的情况,保证数组在移动的过程中能保持成对比较。3中的算法主要在排序中,下面给出借助快速排序完成。
C语言代码如下:
1、2中的Min_Max 和 Min_Sec_Min 代码如下
#include <stdio.h>
#include <stdlib.h>
//顺序统计量
//寻找最小和最大值
void Find_Min_Max(const int *array,int size,int *max,int *min);
//寻找最小和次小值
int Find_Second_Min(const int *array,int size);
int main(int argc, char *argv[]) {
int array[20];
for(int i=0;i<20;i++){
array[i] = rand()%171;
}
int max,min;
Find_Min_Max(array,20,&max,&min);
int res = Find_Second_Min(array,20);
for(int i=0;i<20;i++){
printf("%d ",array[i]);
}
printf("\nMax: %d Min:%d\n",max,min);
printf("Second Min:%d\n",res);
return 0;
}
void Find_Min_Max(const int *array,int size,int *max,int *min){
//成对比较减少比较次数
if(size==0){
return;
}
int i=0;
//偶数情况
if(size%2==0){
if(array[0]>array[1]){
*max = array[0];
*min = array[1];
}
else{
*min = array[0];
*max = array[1];
}
i=2;
}
//奇数情况
else{
*max = *min = array[0];
i=1;
}
for(int k=i;k<size;k+=2){
if(array[k]>array[k+1]){
if(*max < array[k]){
*max = array[k];
}
if(*min > array[k+1]){
*min = array[k+1];
}
}
else{
if(*max < array[k+1]){
*max = array[k+1];
}
if(*min > array[k]){
*min = array[k];
}
}
}
}
int Find_Second_Min(const int *array,int size){
int min,sec_min;
//沿用上述的思想查找第二小的元素
//成对比较减少比较次数
if(size==0){
return -1;//没有元素
}
//偶数情况
int i=0;
if(size%2==0){
if(array[0]>array[1]){
sec_min = array[0];
min = array[1];
}
else{
min = array[0];
sec_min = array[1];
}
i=2;
}
else{
min = sec_min = array[0];
i=1;
}
for(int k=i;k<size;k+=2){
//两者中的小元素
int min_temp = array[k];
int sec_temp = array[k+1];
if(array[k] > array[k+1]){
min_temp = array[k+1];
sec_temp = array[k];
}
if(min > min_temp){
if(sec_temp < min){
sec_min = sec_temp;
}
else{
sec_min = min;
}
min = min_temp;
}
//两者不能相等
else if(min < min_temp){
if(min_temp < sec_min){
sec_min = min_temp;
}
}
}
//如果不存在该元素
if(sec_min == min){
return -1;
}
return sec_min;
}
代码运行结果:
问题三的算法如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//快速排序
//对外的接口
void quicksort(int *array,int size);
void quick(int *array,int left,int right);
void Showarray(int *array,int size);//打印数组
int RandomParting(int *array,int left,int right);
int WeightMiddleNumber(int *array,int size);//查找带权的中位数
//交换两个数据
void swap(int *num1,int *num2);
int main(void){
int num[]={10,35,5,10,15,5,20};
int size = sizeof num / sizeof num[0];
int res = WeightMiddleNumber(num,size-1);
printf("%d\n",res);
return 0;
}
int WeightMiddleNumber(int *array,int size){
//先排序 然后对权求和 直到权值为0.5则停止 该数就是带权的中位数
quicksort(array,size);
int sum = 0;
int i;
for(i=0;i<size;i++){
if(sum >= 50){
break;
}
sum += array[i];
}
return array[i];
}
void Showarray(int *array,int size){
for(int i=0;i<size;i++){
printf("%2d ",array[i]);
if((i+1)%10==0){
putchar('\n');
}
}
putc('\n',stdout);
}
//对外的接口
void quicksort(int *array,int size){
quick(array, 0,size-1);
}
void quick(int *array,int left,int right){
//添加阈值
while(left<right){
int p=RandomParting(array,left, right);
//p为主元的位置 主元的位置无需改变
quick(array,left,p-1);
left=p+1;//向右递归
}
}
//利用随机函数划分主元
int RandomParting(int *array,int left,int right){
//生成left到right之间左开右闭区间的随机数
//如果和边界相等又该如何
//不存在right == left 的情况
int site = rand()%(right-left)+left+1;//如果遇到right和left相等的情况 这样取值就会error
if(site>right||site<left){
fprintf(stderr,"This random number is wrong\n");
}
//保证最右侧数据为pivot
if(array[left]>array[right]){
swap(&array[left], &array[right]);
}
if(array[left]>array[site]){
swap(&array[left],&array[site]);
}
if(array[site]>array[right]){
swap(&array[site],&array[right]);
}
swap(&array[site], &array[right]);//交换right和site的位置
int pivot = array[right];
//划分pivot左右两边的数据
int i=left-1,j=left;//令i为left-1开始 可以避免极端情况
for(;j<right;j++){
//如果小于左元则和i交换 令i+1处永远都是大于主元的数据
if(array[j]<pivot){
i++;
swap(&array[j],&array[i]);
}
}
swap(&array[i+1],&array[right]);//不能直接拿pivot交换 而要改变right的数据
return i+1;//i+1则是pivot所在的位置
}
void swap(int *num1,int *num2){
int temp = *num1;
*num1 = *num2;
*num2 = temp;
}
算法运行结果如下,以数据自身为权值,笔者将其权值都放大了100倍,截止条件同样又0.5变到了50,便于计算。
欢迎到大家指正!