给定一个整数数组,在小于2n的比较中找到最小(或最大)元素,并且该元素大于(或小于)该元素。给定的数组不一定要排序。允许额外的空间。
例子:
Input: {3, 6, 100, 9, 10, 12, 7, -1, 10}
Output: Minimum: -1, Second minimum: 3
我们已经在下面的文章中讨论了一种方法。
查找数组中的最小和第二个最小元素
如果数组元素的大小较大(例如,大字符串),则比较数组元素可能会非常昂贵。我们可以最小化上述方法中使用的比较次数。
这个想法是基于锦标赛树。将给定数组中的每个元素视为叶节点。
首先,我们找到最低限度的元素,如建造锦标赛三通所说明的那样。为了构建树,我们将所有相邻的元素对(叶节点)彼此进行比较。现在我们有n / 2个元素,它们比它们的对应元素要小(从每对元素中,较小的元素形成的级别高于叶子的级别)。同样,在n / 4对中的每一个中找到较小的元素。继续此过程,直到创建树的根为止。根是最小值。
接下来,我们遍历树,并在此过程中丢弃根大于最小元素的子树。在丢弃之前,我们更新第二个最小的元素,它将保留我们的结果。这里要理解的是树是高度平衡的,我们只需要花费logn时间来遍历所有与步骤1中的最小值进行比较的元素。因此,总的时间复杂度为n + logn 。辅助空间为O(2n),因为叶节点的数量将近似等于内部节点的数量。
// C++ program to find minimum and second minimum
// using minimum number of comparisons
#include
using namespace std;
// Tournament Tree node
struct Node
{
int idx;
Node *left, *right;
};
// Utility function to create a tournament tree node
Node *createNode(int idx)
{
Node *t = new Node;
t->left = t->right = NULL;
t->idx = idx;
return t;
}
// This function traverses tree across height to
// find second smallest element in tournament tree.
// Note that root is smallest element of tournament
// tree.
void traverseHeight(Node *root, int arr[], int &res)
{
// Base case
if (root == NULL || (root->left == NULL &&
root->right == NULL))
return;
// If left child is smaller than current result,
// update result and recur for left subarray.
if (res > arr[root->left->idx] &&
root->left->idx != root->idx)
{
res = arr[root->left->idx];
traverseHeight(root->right, arr, res);
}
// If right child is smaller than current result,
// update result and recur for left subarray.
else if (res > arr[root->right->idx] &&
root->right->idx != root->idx)
{
res = arr[root->right->idx];
traverseHeight(root->left, arr, res);
}
}
// Prints minimum and second minimum in arr[0..n-1]
void findSecondMin(int arr[], int n)
{
// Create a list to store nodes of current
// level
list li;
Node *root = NULL;
for (int i = 0; i < n; i += 2)
{
Node *t1 = createNode(i);
Node *t2 = NULL;
if (i + 1 < n)
{
// Make a node for next element
t2 = createNode(i + 1);
// Make smaller of two as root
root = (arr[i] < arr[i + 1])? createNode(i) :
createNode(i + 1);
// Make two nodes as children of smaller
root->left = t1;
root->right = t2;
// Add root
li.push_back(root);
}
else
li.push_back(t1);
}
int lsize = li.size();
// Construct the complete tournament tree from above
// prepared list of winners in first round.
while (lsize != 1)
{
// Find index of last pair
int last = (lsize & 1)? (lsize - 2) : (lsize - 1);
// Process current list items in pair
for (int i = 0; i < last; i += 2)
{
// Extract two nodes from list, make a new
// node for winner of two
Node *f1 = li.front();
li.pop_front();
Node *f2 = li.front();
li.pop_front();
root = (arr[f1->idx] < arr[f2->idx])?
createNode(f1->idx) : createNode(f2->idx);
// Make winner as parent of two
root->left = f1;
root->right = f2;
// Add winner to list of next level
li.push_back(root);
}
if (lsize & 1)
{
li.push_back(li.front());
li.pop_front();
}
lsize = li.size();
}
// Traverse tree from root to find second minimum
// Note that minimum is already known and root of
// tournament tree.
int res = INT_MAX;
traverseHeight(root, arr, res);
cout << "Minimum: " << arr[root->idx]
<< ", Second minimum: " << res << endl;
}
// Driver code
int main()
{
int arr[] = {61, 6, 100, 9, 10, 12, 17};
int n = sizeof(arr)/sizeof(arr[0]);
findSecondMin(arr, n);
return 0;
}
输出:
Minimum: 6, Second minimum: 9
我们可以通过避免创建叶节点来优化上述代码,因为该信息存储在数组本身中(并且我们知道元素是成对比较的)。