寫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一端所看到的同乙個故事的另乙個版本,我相信,這個版本不會有那麼多傳奇,有的...
樹言樹語 盛大的文學之路
無意之中看到一則關於盛大收購某乙個 的資訊,其實不會感覺到奇怪,不過讓人發覺到,盛大開始有點動靜了。從起點收購後,盛大文學的模式是乙個大膽的嘗試,這種嘗試成功了。但是,它的壯大也扼殺了國內其他的文學 的發展,而最近的收購當中,我們不難發現國內一些有名文學的 也被收入了囊中,這一點可以推敲出盛大下一步...