tournamentsort
来源:互联网 发布:仙剑奇侠传3mac打不开 编辑:程序博客网 时间:2024/06/10 01:58
/* * 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 30void 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;}