tournamentsort

/*
 * szlTournamentSort.h
 */
#ifndef SZL_TOURNAMENT_H
#define SZL_TOURNAMENT_H

/*
 * 锦标赛排序
 * 时间复杂度:O(nlog(n))
 * 空间复杂度:O(n)
 */
 void tournament(int a[], int n);
 
#endif
/*
 * szlTournamentSort.c
 */
#include "szlTournamentSort.h"
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
/*
 * 锦标赛排序
 * 时间复杂度:O(lgN)
 * 空间复杂度:O(n)
 * 1.首先以n个元素为叶子节点建立一颗完全二叉树;(有技巧:叶子节点数量确定的完全
 * 二叉树是并不一定是唯一确定的,但是如果给定叶子节点数量必须比非叶子节点数量
 * 多1个而且只多1个,那么这棵二叉树就是完全确定的);
 * 1.1将当前叶子节点两两比较,将其父节点设置为较大的孩子节点,以此类推;
 * 1.2得到的这棵树中,树根节点就是所有n个节点中的最大值;
 * 1.3将这个树根节点对应的叶子节点的值设置为最小值,沿着该节点往树根方向逐个比较;
 * 并将节点之较大的拷贝给父节点;
 * 1.4最后得到的树根节点即为当前叶子节点中的最大节点;
 * ...
 * 直到最后只剩下一个其值未被修改的叶子节点;删除节点的顺序就是已排好序的一列元素;
 * 算法分析:
 * 第1次建立二叉树时需要开辟大小为2*n-1的节点空间,并执行n-1次比较;
 * 第2次执行了log(2n-1)次比较;
 * 第3次执行了log(2n-1)次比较;
 * ...
 * 第n次执行了log(2n-1)次比较;
 * 所以时间复杂度为O(2*n-1+(n-1)*log(2*n-1)) = O(n*log(n));
 */
void tournamentSort(int a[], int n){
  /*
   * 使用数组来存储一棵完全二叉树;
   * 如果使用索引为0的元素(以下称为节点0)表示n个节点的完全二叉树的树根,那么;
   * 1.节点i的孩子为节点2*i+1及2*i+2;
   * 2.节点i的父节点为(i-1)/2;
   * 3.左孩子为奇数索引的节点,右孩子和树根为偶数索引的节点;
   * 4.第一个叶子节点是节点n/2;
   * 5.最后一个内部节点为n/2-1;
   * 6.叶子节点的数量(n-n/2)要么和内部节点数量(n/2)相等,要么比内部节点数量多1;我们
   * 这里使用的是叶子数量较内部节点数量多1个完全二叉树;
   */
   
   int f,i,j,k;
   
   /*
    * 定义结构体保存树中叶子的索引,用于稍后修改叶子的值;
	*/
   typedef struct _treeNode{
     int value; // 树节点的值
	 int index; // 保存value的叶子节点在实现这棵树的数组中的索引
   }TreeNode,*ptrTreeNode;
   
   ptrTreeNode tree;
   
   tree = (ptrTreeNode)malloc(sizeof(TreeNode)*(2*n-1));
   assert(NULL!=tree);
   
   /*
    * 将数组元素拷贝到叶子中去
	*/
   for(i=0,j=n-1;i<n;i++,j++){
     tree[j].value = a[i]; // 保存数组的值
	 tree[j].index = j; // 记住当前叶子节点的在数组中的索引
   }
   
   /*
    * 第1轮:由最底层的最后一个叶子开始,自底向上,构造一颗树;
	* 相当于是从后往前扫描元素然后作比较的;
	* 亦即从这棵树的最后一个内部节点开始,内部节点逐个被赋值;
	*/
   for(i=2*n-2;i>=1;i=i-2){
     /*
	  * 父节点设置为较大的元素
	  */
     if(tree[i].value < tree[i-1].value){
	   tree[(i-1)/2].value = tree[i-1].value;
	   tree[(i-1)/2].index = tree[i-1].index;
	 }
	 else{ // tree[i-1]<=tree[i]
	   tree[(i-1)/2].value = tree[i].value;
	   tree[(i-1)/2].index = tree[i].index;
	 }
   } // 树根节点已经是最大值  
  
   /*
    * 然后从该叶子节点开始往根节点方向回溯n-1轮;每回溯一次产生一个最大值
	*/
   for(i=0;i<n-1;i++){
     /*
      * 取出第树根元素,然后放到数组a的末尾
	  */
     a[n-1-i] = tree[0].value;  
	 /*
      * 记住取出的元素在树的叶子节点的索引
	  */
     j = tree[0].index;
     /*
      * 并将取出的节点对应的叶子设置为最小值
	  */
     tree[j].value = INT_MIN;
	 
     do{
	   /*
	    * 该节点的兄弟节点
	    */
       k = (j&1)?(j+1):(j-1);
	   /*
	    * 父节点
	    */
	   f = (k-1)/2;
	   /*
	    * 设置父节点的值和索引
		*/
	   if(tree[j].value < tree[k].value){
	     tree[f].value = tree[k].value;
		 tree[f].index = tree[k].index;
	   }
	   else{ //tree[j].value > tree[k].value
	     tree[f].value = tree[j].value;
		 tree[f].index = tree[j].index;	   
	   }
	   
	   j=f; //往上一层
	 }while(f);
   }	 
	 
   /*
    * 最后一个元素
    */
   a[0] = tree[0].value;
   
   /*
    * 释放空间
	*/
   if(tree){
     free(tree);
   }
}

关于那棵二叉树:



和其他O(n*log(n))的算法运行比较还是不错的。



另一个版本:

/*
 * tournamentsort.c
 * space: O(n)
 * time : O(logn)
 */

#include <stdio.h>
#include <limits.h>
#define N 30

void tournamentsort (int a[], int n){
    int j;
    int me;
    int father;
    int sibiling;
    int max_child;
    int value[2*N-1];
    int index[2*N-1];
    
    for (j = n - 1; j < (2 * n - 1); j++){
        value[j] = a[j-n+1];
        index[j] = j;
    }

    for (j = 2 * n - 2; j >= 2; j -= 2){
        max_child = value[j] > value[j-1] ? j : (j-1);
        father = (j - 1) / 2;
        index[father] = index[max_child];  /* bugs occurs here */
        value[father] = value[max_child]; 
    }

    for (j = n-1; j > 0; j--){
        a[j] = value[0];
        me = index[0];
        value[me] = INT_MIN;
        do{
            sibiling = (1 & me) ? (me + 1) : (me - 1);
            father = (me - 1) / 2;
            index[father] = value[sibiling] > value[me] ? index[sibiling] : index[me]; /* bugs occurs here */
            value[father] = value[index[father]];
            me = father;
        } while (me);
    }
    a[0] = value[0];
}

int main (int argc, char ** argv){
    int n;
    int i;
    int a[N];
#ifdef DEBUG1
    freopen ("in.txt", "r", stdin);
#endif
    scanf ("%d", &n);
    for (i=0; i<n; i++)
        scanf ("%d", &a[i]);

    tournamentsort (a, n);

    for (i=0; i<n; i++)
        printf ("%d ", a[i]);
    
#ifdef DEBUG1
    fclose (stdin);
#endif
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值