有败者树就有胜者树。
胜者树和败者树的区别:
胜者树每个节点保存的是子节点中胜出节点的编号让其继续向上比较。
败者树每个节点保存的是子节点中失败节点的编号让胜者继续向上比较。
败者树相对于胜者树的优点:
败者树的每个节点向上更新时,只需要和父节点比较,让父节点保存败者,当确定n-1个败者时,胜者就确定取出就行,由于每个父节点保存的是败者,那么此节点不可能被取走,所以直接用子节点比较就行,而胜者树的父节点可能被取走了,所以需要和自己的兄弟节点比较(就稍微麻烦一点,其实差不多),败者树的优势就在于更新简单一点。
在多个有序的数组归并时,败者树并不比归并排序优势大,甚至有可能慢,但是在对多个文件归并时就要快,因为归并排序每次归并时,需要读取一个文件中的所有数据,合并后再写入文件中,这样效率就低在读写文件的次数上,败者树只需要每次取一个文件中的一个数据将胜者存入最终文件,这样保证每个数据只会读写一次,效率就要高一些。
以下为败者树代码,实现为数组归并。
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <utility>
#include <map>
#include <set>
#include <queue>
#include <vector>
#include <iostream>
#include <stack>
using namespace std;
#define INF 0x3f3f3f3f
#define eps 1e-6
#define CLR( a, v ) memset ( a, v, sizeof ( a ) )
#define LL long long
#define DBUG printf ( "here!!!\n" )
#define rep( i, a, b ) for ( int i = ( a ); i < ( b ); i ++ )
#define PB push_back
#define ULL unsigned long long
#define PI acos ( -1.0 )
#define lson l, m, rt << 1
#define rson m+1, r, rt << 1 | 1
#define lowbit( x ) ( ( x )&( -x ) )
#define CASE int Test; scanf ( "%d", &Test ); for ( int cas = 1; cas <= Test; cas ++ )
#define ALL( x ) x.begin ( ), x.end ( )
#define INS( x ) x, x.begin ( )
typedef pair < int, int > Pii;
typedef pair < double, double > Pdd;
typedef set < int > Set;
const int maxn = 105;
int read_int ( )
{
int res = 0;
int ch;
while ( ( ch = getchar ( ) ) && ! ( ch >= '0' && ch <= '9' ) )
{
if ( ch == -1 )
return -1;
}
while ( ch >= '0' && ch <= '9' )
{
res = res*10+( ch-'0' );
ch = getchar ( );
}
return res;
}
int a[maxn][maxn], pos[maxn], cnt[maxn];
int ans[maxn*maxn], cc, ls[maxn], b[maxn];
int Get_Val ( int x ) //如果是文件将其改成文件读写即可
{
if ( pos[x] >= cnt[x] )
return INF;
return a[x][ pos[x] ++ ];
}
void Adjust ( int s, int K )
{
int x = ( s+K ) >> 1;
while ( x > 0 )
{
if ( b[s] > b[ ls[x] ] )
swap ( s, ls[x] ); //ls[x]保存失败者编号
x >>= 1;
}
ls[0] = s;
}
void K_merge ( int K )
{
for ( int i = 0; i < K; i ++ )
b[i] = Get_Val ( i );
b[K] = -INF;
for ( int i = 0; i < K; i ++ )
ls[i] = K;
for ( int i = K-1; i >= 0; i -- )
Adjust ( i, K ); //调整K个归并段
while ( b[ ls[0] ] != INF )
{
int j = ls[0]; //最终胜者
ans[cc ++] = b[j];
b[j] = Get_Val ( j );
Adjust ( j, K ); //新值往上调整
}
}
void solve ( )
{
int n;
scanf ( "%d", &n );
for ( int i = 0; i < n; i ++ )
{
pos[i] = 0;
scanf ( "%d", &cnt[i] );
for ( int j = 0; j < cnt[i]; j ++ )
scanf ( "%d", &a[i][j] );
}
K_merge ( n );
for ( int i = 0; i < cc; i ++ )
printf ( "%d ", ans[i] );
printf ( "\n" );
}
int main ( )
{
solve ( );
return 0;
}
败者树是对K个归并段进行归并,而置换-选择排序则是对选取归并段的优化,置换-选择排序其实也是用败者树实现,每个归并段是有序的,首先输入K个值至败者树中,第一关键字为0(0也为归并段),每个取出胜者,然后继续取值(没有值就返回INF),如果加入的数字比取出的胜者小就放入下一个归并段中,即关键字加1,一直重复直到值为INF,置换-选择排序结束。
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <utility>
#include <map>
#include <set>
#include <queue>
#include <vector>
#include <iostream>
#include <stack>
using namespace std;
#define INF 0x3f3f3f3f
#define eps 1e-6
#define CLR( a, v ) memset ( a, v, sizeof ( a ) )
#define LL long long
#define DBUG printf ( "here!!!\n" )
#define rep( i, a, b ) for ( int i = ( a ); i < ( b ); i ++ )
#define PB push_back
#define ULL unsigned long long
#define PI acos ( -1.0 )
#define lson l, m, rt << 1
#define rson m+1, r, rt << 1 | 1
#define lowbit( x ) ( ( x )&( -x ) )
#define CASE int Test; scanf ( "%d", &Test ); for ( int cas = 1; cas <= Test; cas ++ )
#define ALL( x ) x.begin ( ), x.end ( )
#define INS( x ) x, x.begin ( )
typedef pair < int, int > Pii;
typedef pair < double, double > Pdd;
typedef set < int > Set;
const int maxn = 100005;
int read_int ( )
{
int res = 0;
int ch;
while ( ( ch = getchar ( ) ) && ! ( ch >= '0' && ch <= '9' ) )
{
if ( ch == -1 )
return -1;
}
while ( ch >= '0' && ch <= '9' )
{
res = res*10+( ch-'0' );
ch = getchar ( );
}
return res;
}
int a[maxn], ls[maxn], pos, m;
struct Point
{
int x, y;
friend bool operator < ( Point a, Point b )
{
return a.x < b.x || a.x == b.x && a.y < b.y;
}
}b[maxn];
vector < int > workArea[maxn];
int Get_Val ( int n )
{
if ( pos > n )
return INF;
return a[pos ++];
}
void Adjust ( int s, int K )
{
int x = ( s+K ) >> 1;
while ( x > 0 )
{
if ( b[ ls[x] ] < b[s] )
swap ( s, ls[x] );
x >>= 1;
}
ls[0] = s;
}
void K_merge ( int K, int n )
{
for ( int i = 0; i < K; i ++ )
{
b[i].y = Get_Val ( n );
b[i].x = 0;
}
b[K].x = b[K].y = -INF;
for ( int i = 0; i < K; i ++ )
ls[i] = K;
for ( int i = K-1; i >= 0; i -- )
Adjust ( i, K );
while ( b[ ls[0] ].y != INF )
{
int j = ls[0];
int p = b[j].x, tmp = b[j].y;
m = p; //保证归并段个数
workArea[p].PB ( tmp );
b[j].y = Get_Val ( n );
if ( b[j].y < tmp ) //小于上一个值 就将其分配下一个归并段中
b[j].x = b[j].x+1;
else
b[j].x = b[j].x;
Adjust ( j, K ); //调整
}
}
void Replace_Select ( int n )
{
int K = 6; //K任意取 一般sqrt(n)左右时间烧快
pos = 1; //a数组的下标
K_merge ( K, n ); //败者树
for ( int i = 0; i <= m; i ++ )
{
for ( int j = 0; j < workArea[i].size ( ); j ++ )
printf ( "%d ", workArea[i][j] );
printf ( "\n" );
}
}
void solve ( )
{
int n;
scanf ( "%d", &n );
for ( int i = 1; i <= n; i ++ )
{
scanf ( "%d", &a[i] );
workArea[i].clear ( ); //保存每个归并段的值
}
Replace_Select ( n ); //置换-选择排序
}
int main ( )
{
solve ( );
return 0;
}
/*
24
51 49 39 46 38 29 14 61 15 30 1 48 52 3 63 27 4 13 89 24 46 58 33 76
*/