贏者樹WinnerTree C語言

2021-08-21 10:03:17 字數 4931 閱讀 9141

寫winnertree實在是一件燒腦的事情。。。

贏者樹:對於 n名選手,贏者樹是一棵包含n個外部節點,n-1個內部節點的完全二叉樹。其中每個內部節點記錄了相應賽局的贏家。

最大贏者樹即每次都是值大的勝出;最小贏者樹每次都是值小的勝出。

贏者樹採用陣列公式化描述,最核心的是如何根據外部節點的序號計算內部節點的序號,公式如下:

內部節點inner_index = (外部節點outer_index + offset) / 2,當外部節點outer_index <= lowext(最底層的外部節點數).

內部節點inner_index = (外部節點outer_index - lowext + n - 1) / 2,當外部節點outer_index > lowext(最底層的外部節點數).

其中 ,n為外部節點的數量,offset = 2的(s+1)次方 - 1;s = log2(n-1).

內部節點是一棵完全二叉樹,再回顧一下父子節點之間的序號計算關係:

設完全二叉樹中一元素的序號為i,1 <= i <= n,則以下關係成立:

1. 當i=1時,該元素為二叉樹的根。若i>1,則該元素父節點的編號為i/2(int取整);

2. 當2i>n時,該元素無左孩子,否則,其左孩子的編號為2i;

3. 當2i+1>n時,該元素無右孩子 ,否則,其右孩子的編號為2i+1。

程式輸出如下:

$ ./maxwinnertree 

outer nodes: 34 32 56 76 19 50 22 89

inner nodes: 89 76 89 34 76 50 89

the winner: 89

replay with 99:

outer nodes: 34 32 99 76 19 50 22 89

inner nodes: 99 99 89 34 99 50 89

the winner: 99

outer nodes: 34 32 56 76 19

inner nodes: 76 56 76 34

the winner: 76

replay with 88:

outer nodes: 34 32 88 76 19

inner nodes: 88 88 76 34

the winner: 88

outer nodes: 34 32 56 76 19 67

inner nodes: 76 76 67 34 76

the winner: 76

replay with 100:

outer nodes: 34 32 100 76 19 67

inner nodes: 100 100 67 34 100

the winner: 100

maxwinnertree.c:

#include #include #include #define ret_true  0

#define ret_false 1

struct winner_tree ;

static int winner_tree_init(struct winner_tree *tree, int arr, int size);

static void winner_tree_destroy(struct winner_tree *tree);

static int winner_tree_play(struct winner_tree *tree, int from, int to);

static void winner_tree_replay(struct winner_tree *tree, int replay_outer_i, int replay_outer_value);

static int get_inner_index_by_outer(struct winner_tree *tree, int outer_index);

static int get_winner(int left, int right);

/* * 贏者的判斷規則:最大贏者樹. 也可替換此函式,生成最小贏者樹.

*/static int get_winner(int left, int right)

/* * @param int arr: 儲存外部節點的陣列

* @param size: 儲存外部節點陣列的大小

* * 根據外部節點,生成winner tree,給儲存內部節點的陣列賦值.

*/static int winner_tree_init(struct winner_tree *tree, int arr, int size)

memset(tree->inner_nodes, 0, (size-1)*sizeof(int));

// 遍歷最底層的外部節點,給其對應的內部父節點賦值

for(outer_i = 1; outer_i <= tree->low_ext; outer_i += 2)

// 遍歷最底層已經賦值的內部節點,給其對應的內部父節點賦值.

// 2<<(s-1)是最底層最左邊的內部節點的序號,inner_i由上面for迴圈而來.

winner_tree_play(tree, 2<<(s-1), inner_i);

// 遍歷倒數第二層的外部節點,給其對應的內部父節點賦值

if(outer_i > tree->outer_nodes_num) else

// 繼續遍歷剩餘的外部節點

for(; outer_i <= tree->outer_nodes_num; outer_i += 2)

end_i = inner_i; // 倒數第三層待遍歷的最右邊內部節點序號

inner_i = (2<<(s-1))/2; // 倒數第三層待遍歷的最左邊內部節點序號

} // 依次遍歷每一次內部節點

while(inner_i != 1)

return ret_true;

}static void winner_tree_destroy(struct winner_tree *tree)

if(tree->outer_nodes) }}

/* * @param int from: 待遍歷的內部節點的起始序號

* @param int to: 待遍歷的內部節點的結束序號

* * 遍歷指定的內部節點,生成其對應的父節點

*/ static int winner_tree_play(struct winner_tree *tree, int from, int to)

return ret_true;}/*

* @param replay_outer_i: 待重賽的外部節點序號

* @param replay_outer_value: 待重賽的外部節點的值

* * 沿著待重賽的外部節點,往上遍歷其父節點,判斷是否要重新生成比賽結果.

*/static void winner_tree_replay(struct winner_tree *tree, int replay_outer_i, int replay_outer_value)

else

break; // 中止比賽 }}

/* * 根據外部節點的序號outer_i,生成對應的內部節點的序號

*/ static int get_inner_index_by_outer(struct winner_tree *tree, int outer_i)

/* * test code

*/static void print_winner_tree(struct winner_tree *tree)

int main()

; int arr2 = ;

int arr3 = ;

struct winner_tree *tree;

tree = malloc(sizeof(struct winner_tree));

memset(tree, 0, sizeof(struct winner_tree));

// 初始化: 8個外部節點,生成乙個7個內部節點的滿二叉樹

winner_tree_init(tree, arr1, sizeof(arr1)/sizeof(arr1[0]));

print_winner_tree(tree);

// 重賽

printf("replay with 99: \n");

winner_tree_replay(tree, 3, 99);

print_winner_tree(tree);

winner_tree_destroy(tree);

// 初始化: 5個外部節點,生成乙個4個內部節點的完全二叉樹. 最底層只有乙個內部節點

winner_tree_init(tree, arr2, sizeof(arr2)/sizeof(arr2[0]));

print_winner_tree(tree);

// 重賽

printf("replay with 88: \n");

winner_tree_replay(tree, 3, 88);

print_winner_tree(tree);

winner_tree_destroy(tree);

// 初始化: 6個外部節點,生成乙個5個內部節點的完全二叉樹. 最底層有兩個內部節點

winner_tree_init(tree, arr3, sizeof(arr3)/sizeof(arr3[0]));

print_winner_tree(tree);

// 重賽

printf("replay with 100: \n");

winner_tree_replay(tree, 3, 100);

print_winner_tree(tree);

winner_tree_destroy(tree);

return 0;

}

堆,贏者樹,敗者樹的區別與聯絡

今天做leetcode的23.merge k sorted lists這道題的時候,遇到的這個問題。這道題本質上就是乙個多路歸併的問題,而這道題主要就是考察多路歸併時候的選擇問題。按照之前本科上課學的,最好的辦法就是用競賽樹 敗者樹 可是我嫌麻煩就用堆來做了,也順利能過。所以就想到,堆,贏者樹,敗者...

東拉西扯 倒立者贏?

沈威風的書 倒立者贏 是站在 這一端講述的 戰勝ebay的傳奇故事,我沒有讀過這本書,所以不便評價。但讀了吳曉波為該書寫的序之後,我覺得這個故事確實被傳奇化了,其後果就是馬雲成了神話人物。其實我更希望讀到的,是站在輸家ebay一端所看到的同乙個故事的另乙個版本,我相信,這個版本不會有那麼多傳奇,有的...

樹言樹語 盛大的文學之路

無意之中看到一則關於盛大收購某乙個 的資訊,其實不會感覺到奇怪,不過讓人發覺到,盛大開始有點動靜了。從起點收購後,盛大文學的模式是乙個大膽的嘗試,這種嘗試成功了。但是,它的壯大也扼殺了國內其他的文學 的發展,而最近的收購當中,我們不難發現國內一些有名文學的 也被收入了囊中,這一點可以推敲出盛大下一步...