1. 程式人生 > >整數對A滿足二叉查詢樹,B滿足最大堆

整數對A滿足二叉查詢樹,B滿足最大堆

1 題目

給出一組整數對 { (a[0], b[0]), (a[1], b[1]) ... (a[n-1], b[n-1]) },所有 a 值和 b 值分別不重複(任意 i != j 滿足 a[i] != a[j] 且 b[i] != b[j])。構造一棵 n 結點的二叉樹,將這 n 個整數對分配到各個結點上。根和所有子樹滿足以下條件:1) 所有結點的 a 值滿足二叉查詢樹的順序,即 left->a <root->a && root->a < right->a;2) 所有結點的 b 值滿足最大堆的順序,即 root->b >left->b && root->b > right->b。

問題一:實現 build 函式,輸入 n 個整數對,返回一棵構造好的二叉樹。struct pair_t {    int a,b;};struct node_t {    int a, b;    node_t *left, *right;};node_t*build(pair_t* pair, int n);

問題二:已知滿足上述條件的二叉樹,設計演算法實現插入一個整對 (a, b),使新的二叉樹仍滿足上述條件。該演算法比較複雜,候選人只需描述思路。


若有錯誤歡迎大家指正,若有更好的方法,歡迎大家指教!

2 分析

該問題的關鍵就是找樹根。

方法一:當前所有整數對的樹根為b中最大值對應的整數對I,因為只有這樣才能滿足最大堆的性質。然後根據I中的a值將整數對分為比a大於比a小兩組,小的作為左子樹,大的作為右子樹。當整數對個數為0時,返回NULL。該方法時間複雜度O(n2)。

方法二(華南理工大神ohm提供):將整數對按照b進行逆向排序,然後按照a進行二分叉樹的插入操作即可。該方法的時間複雜度為O(nlogn)。

3 實現

方法一實現:

struct pair_t
{
	int a, b;
};

struct node_t
{
	int a, b;
	node_t *left, *right;
};

node_t *build(pair_t *pair, int n);
int findMaxB(pair_t *pair, int n);

int findMaxB(pair_t *pair, int n)
{
	int pos = 0;

	for (int i = 1; i < n; ++i)
	{
		if (pair[pos].b < pair[i].b)
		{
			pos = i;
		}
	}

	return pos;
}

node_t *build(pair_t *pair, int n)
{
	if (0 == n)
	{
		return NULL;
	}
	node_t *root = new node_t[1];
	pair_t *pair1 = new pair_t[n];
	pair_t *pair2 = new pair_t[n];
	int num1 = 0;
	int num2 = 0;
	int maxB = findMaxB(pair, n);
	root->a = pair[maxB].a;
	root->b = pair[maxB].b;
	int maxBA = pair[maxB].a;
	for (int i = 0; i < maxB; ++i)
	{
		if (pair[i].a < maxBA)
		{
			pair1[num1].a = pair[i].a;
			pair1[num1++].b = pair[i].b;
		}
		else
		{
			pair2[num2].a = pair[i].a;
			pair2[num2++].b = pair[i].b;
		}
	}
	for (int i = maxB + 1; i < n; ++i)
	{
		if (pair[i].a < maxBA)
		{
			pair1[num1].a = pair[i].a;
			pair1[num1++].b = pair[i].b;
		}
		else
		{
			pair2[num2].a = pair[i].a;
			pair2[num2++].b = pair[i].b;
		}
	}
 	root->left = build(pair1, num1);
	delete []pair1;

 	root->right = build(pair2, num2);
	delete []pair2;
	return root;
}


方法二實現:

void insert(node_t *&root, pair_t p)
{
	if (root == NULL)
	{
		root = new node_t;
		root->a = p.a;
		root->b = p.b;
		root->left = NULL;
		root->right = NULL;
		return;
	}
	if (root->a < p.a)
	{
		insert(root->right, p);
	}
	else
	{
		insert(root->left, p);
	}
}

node_t *build(pair_t *pair, int n)
{
	if (0 == n)
	{
		return NULL;
	}
	node_t *root = NULL;
	sort(pair, pair + n);
	for (int i = 0; i < n; ++i)
	{
		insert(root, pair[i]);
	}
	return root;
}


4 問題二

設插入整數對為(nA, nB),當前訪問樹中結點為curNode,插入過程如下:

(1) 若curNode.b>nB,則比較curNode.a與nA,若curNode.a>nA,則curNode=curNode->left;反之,curNode=curNode->right。直到curNode.b<nB或curNode=NULL,停止查詢。

(2) 若curNode=NULL,則直接將該整數對插入到此位置即可;反之,將(nA,nB)作為curNode父結點的孩子結點,curNode作為插入結點的孩子結點(根據a的值確定是左孩子還是右孩子)。

(3) curNode作為新插入結點的右(左)孩子,則需要遍歷curNode的左(右)子樹,找到a值小於nA的子樹的根作為新插入結點的左(右)孩子。當然若不存在時先插入結點不存在左(右)孩子。