第k小元素——分治法

一、代码

//编译环境vs 2019
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <iostream>
using namespace std;
#include <algorithm>

int select(int arr[], int low, int high, int k)
{
    
    
	int p = high - low + 1;			//需要select的元素个数 

	if (p < 6)
	{
    
    
		sort(arr + 1, arr + p+1);	//待比较的元素个数为p
		return arr[k];				//如果数组长度比较小,那就返回
	}

	int q = p / 5;					//将A分成q组,每组5个元素。如果5不整除p,则排除剩余的元素

	int M[80] = {
    
     0 };

	//将q组中的每一项单独排序,找出中项。所有中项集合为M
	for (int i = 1; i <= q; i++)
	{
    
    
		sort(arr + 1 + 5 * (i - 1), arr +1+ 5 * i);	//数组的话,应该会改变值的吧 
		M[i] = arr[3 + 5 * (i - 1)];
	}
	
	int mm = select(M, 1, q, q / 2+1);		//无论数组多大,递归后的最后总是一个确定的值 

	int A1[800] = {
    
     0 }, A2[800] = {
    
     0 }, A3[800] = {
    
     0 };	//开始划分
	int r = 0, s = 0, t = 0;
	for (int i = 1; arr[i] != 0; i++)
	{
    
    
		if (arr[i] < mm)		//小于中项的放在A1 
		{
    
    
			r++;
			A1[r] = arr[i];
		}

		if (arr[i] == mm)		//等于中项的放在A2 
		{
    
    
			s++;
			A2[s] = arr[i];
		}

		if (arr[i] > mm)		//大于中项的放在A3 
		{
    
    
			t++;
			A3[t] = arr[i];
		}
	}

	if (r >= k)  return select(A1, 1, r, k);	//为什么是return select,直接select不行吗?不行!!!
	if (r + s >= k)	return mm;
	if (r + s < k) return select(A3, 1, t, k - r - s);
}

int main()
{
    
    
	int a[800] = {
    
    0 };
	int n,k;
	
	scanf("%d",&n);					//数列元素个数
	for (int i = 1; i <= n; i++)	//输入数列元素
		scanf("%d",&a[i]);
	scanf("%d",&k);	
	
	printf("%d", select(a,1,25,k));
	return 0;
}

二、时间复杂度

见沙特版【算法设计技巧与分析】P109-111
在这里插入图片描述

三、总结

1、本程序采用了分治的思想。

先求出中项——将数组分为5个一组,排好序,然后可以得出每个小组的中项,构成中项集,再将中项集M递归求中项,就得到了原数组的中项mm;

再根据中项将原数组划分为三个数组,A1(<mm),A2(=mm),A3(>mm);

然后判断第k小元素将会出现在三个数组中的哪一个,然后递归。

ps: 中项并不是中位数! 就算p不能被5整除,也没关系,应为找中项就是为了尽量将原数组划分成三部分——中项左边全部小于中项,中项右边全部大于中项。

2、algorithm库的sort函数用得还不是很熟练。

sort(i,j),i为待排的起始元素,j为待排的最后一个元素。

例如:
sort(arr,arr+4)是将数组arr的前4个元素排序;
sort(arr+1,arr+4)是将数组arr的第1-4个元素排序。

3、因书上伪代码都是类似A[1···n],所以为避免混淆,将数组元素A[0]弃用,下标就对应A[1]…A[n]了。

猜你喜欢

转载自blog.csdn.net/m0_51336041/article/details/121523223