锦标赛排序(胜者树,记录胜者)

锦标赛排序:快速找到第k小元素
锦标赛排序是一种选择排序的优化版,利用类似体育淘汰赛的方式,通过构建二叉树并逐步筛选,减少重复比较,从而提高寻找第k小元素的效率。在给定数组中,通过构建胜者树,可以以O(n*log2n)的时间复杂度找出最小值,并且通过调整树结构,可以方便地找到第2小、第3小等元素。这是一种空间换时间的算法,适用于需要多次查找次小元素的场景。

    百度一道面试题说起,题目是这样的: 给出一个长度是N的数组,现在要找出最小的两个元素,最少要多少次比较。(题目应该是n个数组中,求第2大的元素。。。。,本文只是简单的用一维数组来模拟一下,说明锦标赛排序的原理)

     分析: 如果找出1个最小的,比较次数无疑是 n - 1, ;如果用选择排序,再取选择第二个最小的又得比较n-2次。这种寻找的办法其实是可以优化的,在第一次寻找最小元素过程中,其实我们已经比较了很多元素了,那么为什么不利用前面比较的结果来寻找第二个最小的呢。

     这用到锦标赛排序的方法,这样就可以再使用 logn就可以找到了第二小的元素。 


锦标赛排序原理


     锦标赛排序又叫树型排序,属于选择排序的一种。直接选择排序之所以不够高效就是因为没有把前一趟比较的结果保留下来,每次都有很多重复的比较。锦标赛排序就是要克服这一缺点。它的基本思想与体育淘汰赛类似,首先取得n个元素的关键字,进行两两比较,得到 n/2 个比较的优胜者,将其作为第一次比较的结果保留下来,然后对这些元素再进行关键值的两两比较,…,如此重复,直到选出一个关键字最小的对象为止。

    下面举个例子,假设arr[] = {3,4,1,6,2,8,7,9},我们首先需要建立一棵完全二叉树,注意如果不够ar

### POJ1442 名次问题的C++实现时间复杂度分析 POJ1442 名次(Rank Tree)问题涉及构建和查询一种特殊的数据结构,通常称为平衡二叉搜索或基于数组的堆式结构。在C++中实现时,需要对插入、删除、查找等操作的时间复杂度进行详细分析。 #### 1. 插入操作的时间复杂度 在名次中,插入一个新元素的操作通常涉及维护的平衡性。如果使用的是自平衡二叉搜索(如AVL或红黑),插入操作的时间复杂度为 \(O(\log n)\) [^1]。这是因为每次插入后最多需要调整 \(O(\log n)\) 层节点以恢复的平衡性。 #### 2. 删除操作的时间复杂度 类似地,删除操作也需要维护的平衡性。在自平衡二叉搜索中,删除操作的时间复杂度同样为 \(O(\log n)\) [^1]。删除操作可能涉及重新分配子,但这仍然可以通过 \(O(\log n)\) 的调整完成。 #### 3. 查找操作的时间复杂度 查找操作在平衡二叉搜索中的时间复杂度为 \(O(\log n)\) [^1],因为从根节点到目标节点的路径长度最多为的高度,而平衡二叉的高度为 \(O(\log n)\)。 #### 4. 构建整个的时间复杂度 如果初始时需要将 \(n\) 个元素插入到空中,则总的时间复杂度为 \(O(n \log n)\) [^1]。这是因为在插入每个元素时,都需要执行 \(O(\log n)\) 的调整操作。 #### 5. 查询排名的时间复杂度 在名次中,查询某个值的排名通常需要从根节点开始向下遍历,并根据左子的大小计算排名。假设是完全平衡的,则查询排名的时间复杂度为 \(O(\log n)\) [^1]。 #### 6. 综合时间复杂度 综合来看,名次的基本操作(插入、删除、查找、排名查询)的时间复杂度均为 \(O(\log n)\),而在构建整个时的时间复杂度为 \(O(n \log n)\)。 ```cpp #include <bits/stdc++.h> using namespace std; struct Node { int val, size; Node *left, *right; Node(int v) : val(v), size(1), left(nullptr), right(nullptr) {} }; int getSize(Node* node) { return node ? node->size : 0; } void updateSize(Node* node) { if (node) node->size = 1 + getSize(node->left) + getSize(node->right); } Node* rotateRight(Node* y) { Node* x = y->left; Node* T2 = x->right; x->right = y; y->left = T2; updateSize(y); updateSize(x); return x; } Node* rotateLeft(Node* x) { Node* y = x->right; Node* T2 = y->left; y->left = x; x->right = T2; updateSize(x); updateSize(y); return y; } Node* splay(Node* root, int key) { if (!root || root->val == key) return root; if (key < root->val) { if (!root->left) return root; if (key < root->left->val) { root->left->left = splay(root->left->left, key); root = rotateRight(root); } else if (key > root->left->val) { root->left->right = splay(root->left->right, key); if (root->left->right) root->left = rotateLeft(root->left); } return root->left ? rotateRight(root) : root; } else { if (!root->right) return root; if (key < root->right->val) { root->right->left = splay(root->right->left, key); if (root->right->left) root->right = rotateRight(root->right); } else if (key > root->right->val) { root->right->right = splay(root->right->right, key); root = rotateLeft(root); } return root->right ? rotateLeft(root) : root; } } Node* insert(Node* root, int key) { if (!root) return new Node(key); root = splay(root, key); if (root->val == key) return root; Node* newNode = new Node(key); if (key < root->val) { newNode->right = root; newNode->left = root->left; root->left = nullptr; } else { newNode->left = root; newNode->right = root->right; root->right = nullptr; } updateSize(newNode); return newNode; } ```
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值