お問い合わせ区間Kの米国多数

質問:カオス系列は、所定の間隔でKの多数を見つけます

方法1:第Kの多数に直接次いで選別し

        この方法は、ほとんどの従来の最も簡単ない制約と考えられている;しかし、効率が比較的低い、時間複雑度は、O(N * Nログ)(高い効率的なソートアルゴリズム)であります

        こうしたクイックソート、ヒープソートアルゴリズム、時間Oであるの複雑さ(N * Nログ)など、いくつかのより効率的なソートアルゴリズム、もし

方法2:リニアKをスキャン

        この方法の前提条件は、Kが大きすぎないことであり、時間複雑性はO(K * n)が

        具体的に:たとえば、プロセスごとに現在の最大数を見つけるためにスキャンし、それがシーケンスから削除されるので、スキャンK回、あなたが最初のKの多数を見つけることができます......:最初のスキャン、検索次いで、最大数、および配列のそれを移動させる、第2の走査に続いて、最大数を見つけるためには、これは、この配列のうち、第二の多数あり、第3の走査......

(もちろん、あなたは一度だけスキャンし、その後、監督K多数を記録することができます。また、前提の下で、小さなKがそうすることができます......)

方法3:時間のためのスペース

        この方法の前提条件は、配列内のすべての値に対して非負であり、最大値があまりにもすることができません

        具体的には:アレイ、可能な最大の配列長を開くために、初期値0が割り当てられ、0は発生数が図で示す;配列は順次各番号(例えば、配列の出現数カウント:23 、1,4,50,4; ARR [1] = 1、ARR [4] = 2、ARR [23] = 1、ARR [50] = 1、残りは0です)

        ときに探し始めるために0から非配列の値の最大数から、Kの多数を探して、後方に引き、そしてときのカウント> = K、Kの多数を見つけることが最初のカウントアップ

方法4:迅速なシーケンシング原理(強調の把握をし、アルゴリズムの考えを理解して)を使用して

        原理のクイックドレーン:順序付けられたシーケンスに迅速ランダムシーケンスを使用してソート処理、いくつかの分割後;(これは降順に、想定している)、各部門の回動軸の配列に見出される(ピボット)、結果はそれが大きい場合よりも左側にピボットの数で分割され、それが右の数よりも小さい場合、同じ動作、サブシーケンス、再帰的にこれまで順序付けられたすべてのサブシーケンスまで。

        復興は:私たちの目標は、最初の分割後、Kの合計(自体プラス)の左に旋回軸の数は、その後、Kは、我々は、ピボット位置に大きな数字を探している最初の番号であれば、Kの多数を見つけることです。元の配列のすべての分割は、左右二つの配列に分割され、アルゴリズムは2つの高速行が必要です。:第一、第二、我々は、プレK-1は非常に再帰的分割を進行しない、注文の多数を必要はありませんサブシーケンスは、再帰的に分割継続され、我々はサブシーケンスを選択(特定のアルゴリズムの手順を参照)のみ必要がある場合には分割継続することができ、Kこれは以下のクイックソートアルゴリズムよりも複雑さの多数を必要とせOの複雑さ(N * logK)

        アルゴリズムは、ステップ:1つの分割(パーティション)の後に、ピボットピボット元の配列二つの部分に:SおよびTは、[すなわち、プロ配列(ST)となり、降順で使用されるサブシーケンスSでピボットが、注目含みます]次の3つの条件が存在します。

    そこシーケンス番号S Kは、この時点で多数のK、アルゴリズム端部のピボットの位置であり、
    Sは、Kよりも小さい桁の数字列、番号がLであると仮定され、T外シーケンス再帰的分割を続けることが必要です前(KL)の数
    が再帰的に分割される前に、シーケンスSの桁はK、サブシーケンスのSの必要個数Kよりも大きいです


コードを見てみましょう:
 

#include <iostream>

/* run this program using the console pauser or add your own getch, system("pause") or input loop */

using namespace std;

//快速排序的一次划分 
int sort(int a[],int low,int high)
{
	int i=low,j=high,x=a[low];
	while(i<j)
	{
		while(i<j && a[j]<=x)
		{
			j--;
		}
		a[i]=a[j];
		while(i<j && a[i]>=x)
		{
			i++;
		}
		a[j]=a[i];	
	}

	a[i]=x;
	return i;
}

void FindMake(int a[], int low, int high, int k)
{
	if(low<high)
	{
		int pivot=sort(a,low,high);
		int len=pivot-low+1;
		if(len<k)
		{
			FindMake(a,pivot+1,high,k-len);
		}
		else if(len>k)
		{
			FindMake(a,low,pivot-1,k);
		}
	}
}

int main(int argc, char *argv[]) {
	
	int arr[]={3,2,6,8,9};
	int leng=sizeof(arr)/sizeof(int);
	int l,r,k;//区间从1开始
	cin>>l>>r>>k;
	
	FindMake(arr,l-1,r-1,k); 
	cout<<arr[l-1+k-1]<<endl;
	
	return 0;
}

ヒント:ここでは、アレイへの直接の要件の多数の前に[L-1]をK、Kの多数を見つける〜 [L-1 + K-1] Kの合計数を出力することができます。もちろん、 、必ずしも完全に順序付けされていない要素のシーケンス。

結果:

方法5:ヒープソートの原則使用して(把握をし、自分の考えを理解します)

        私たちの目標は、Kの多数を見つけることです、それに応じて、この方法の原理は、トップ小さなヒープ(最小ヒープ)を維持することであるKの要素を持っている、ヒープのトップは、我々が探している多数の最初のK要素です。実用的なアプリケーションないクイックソートアルゴリズムの考え方を用いて、上述した高効率、Oの時間複雑さと同じ(N * Kログ)

        具体的なアルゴリズムのステップ:

    トップスタックの小さな数に所定の間隔K用のプレプロ配列、初期構築時のスタック
    とスタックの上部を交換残りのセクションと要素がスタックの最上部よりも大きい場合には、順次、比較スタックの最上位の数、および再構造のメンテナンスヒープ

特定のコードを見てみましょう:
 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
 
/*
 小顶堆:筛选过程的子过程
  对当前的i结点进行调整,保证i结点的值<=其左右孩子的值
  然后对其左右孩子进行同样的操作……(递归进行)
  这也是需要逆向筛选(由最后一个非终端结点到根结点)的原因
*/
void adjust_loop(int *heap, int i, int len) {
    if (i < len) {// 因为用的是数组heap来保存堆,这里的作用是判断是否出界
        int l = 2 * i + 1, r = 2 * i + 2;// i结点的左右孩子
        if (r < len && heap[r] < heap[i]) {// 如果存在右孩子且i结点的值小于~
            int tmp = heap[i];
            heap[i] = heap[r];
            heap[r] = tmp;
            adjust_loop(heap, r, len);// 继续筛选调整右孩子
        }
        if (l < len && heap[l] < heap[i]) {// 如果存在左孩子且i结点的值小于~
            int tmp = heap[i];
            heap[i] = heap[l];
            heap[l] = tmp;
            adjust_loop(heap, l, len);// 继续筛选调整左孩子
        }
    }
}
 
/*
 小顶堆:筛选过程
  从最后一个非终端结点开始进行筛选,直到根结点
*/
void heap_adjust(int *heap, int len) {
    for (int i = (len-1)/2; i >= 0; i--) {
        adjust_loop(heap, i, len);
    }
}
 
/*
 完成建立一个小顶堆的过程
  即此函数的目标是 把最大的K个数放入一个小顶堆中,堆顶即为第K大的数
*/
void find_kmax(int arr[], int l, int r, int k,  int *heap) {
    // 区间[l, r]中的前k个元素先建堆
    for (int i = l; i < k+l; i++) {
        heap[i-l] = arr[i];
    }
    heap_adjust(heap, k);
 
    // 剩余的(r-l+1)-k个元素进行更新
    for (int j = k+l; j <= r; j++) {
        // 比堆中最小的值还要大,可以插入堆中
        // (因为我们要找到前k大的数进入堆中)
        if (arr[j] > heap[0]) {
            heap[0] = arr[j];
            heap_adjust(heap, k);
        }
    }
}
 
int main() {
    int arr[] = {23, 50, 500, 4, 100, 300, 200, 99, 400};
    int len = sizeof(arr) / sizeof(int);// 求数组的长度
 
    int l, r, k;// 区间为[l, r],从1开始
    scanf("%d %d %d", &l, &r, &k);
 
    int *heap = new int[k];
    find_kmax(arr, l-1, r-1, k, heap);// 注意区间下标与数组下标的转换
    printf("%d\n", heap[0]);// 建立的是小顶堆,堆顶即为第k大的元素
    delete heap;
 
    return 0;
}

ヒント:また、直接スタック出力の先頭に要求の多数の最初のKを小さくすることができる。もちろん、必ずしも十分と秩序ない要素の配列。

 

 

 

 

 

 

 

公開された74元の記事 ウォンの賞賛146 ・は 10000 +を見て

おすすめ

転載: blog.csdn.net/weixin_44350205/article/details/104165282