1. 程式人生 > >二叉樹最近公共父節點

二叉樹最近公共父節點

在二叉樹中找最近公共父節點。分為兩種情況,一種是有父指標,一種沒有父指標。

1、有父指標

這種情況比較簡單,計算兩個結點的深度,再把深度大的向上移,移到同一深度。在同時向上移動,直到兩個結點相同,這樣便找到了父節點。這個演算法時間複雜度為O(N)。 程式碼實現:
#include<iostream>
struct Node
{
	int data;
	Node* left;
	Node* right;
	Node* parent;
	Node() :left(NULL), right(NULL), parent(NULL)
	{}
};
int getDpeth(Node *n)//結點n到根節點深度
{
	int count = 0;
	while (n)
	{
		++count;
		n = n->parent;
	}
	return count;
}
Node* findNearestCommonAncestor(Node* n1, Node* n2)
{
	int depth1 = getDpeth(n1);
	int depth2 = getDpeth(n2);

	//移動同一深度
	while (depth1 > depth2)
	{
		n1 = n1->parent;
		--depth1;
	}
	while (depth1 < depth2)
	{
		n2 = n2->parent;
		--depth2;
	}
	//向上找
	while (n1 != n2)
	{
		n1 = n1->parent;
		n2 = n2->parent;
	}
	return n1;
}

int main()
{
	//測試
	Node* A[11];
	for (int i = 0; i < 11; ++i)
	{
		A[i] = new Node();
		A[i]->data = i;
	}

	for (int i = 0; i < 5; ++i)
	{
		A[i]->left = A[i * 2 + 1];
		A[i * 2 + 1]->parent = A[i];

		A[i]->right = A[i * 2 + 2];
		A[i * 2 + 2]->parent = A[i];
	}

	Node* Ancestor = findNearestCommonAncestor(A[7], A[6]);


}

2、沒有父指標

這種情況有點難。首先從根節點開始向下找,如果根節點等於其中一個子節點,那麼根節點便是最近公共父結點。否則計算左子樹和右子樹中包含n1或n2的個數。如果左子樹包含n1、n2那麼最近公共父結點在左子樹,如果右子樹包含n1和n2,那麼在右子樹。如果左右子樹各包含一個,那麼最近公共父結點就是當前結點。如果二叉樹是平衡的,那麼演算法複雜度為O(logN)。最壞情況就是樹成了連結串列,演算法時間負責度為O(N^2)。 思路清晰了,可以編寫程式碼:
#include<iostream>
struct Node
{
	int data;
	Node* left;
	Node* right;
	Node() :left(NULL), right(NULL)
	{}
};
//計算當前結點包含n1、n2個數
int countMatch(Node *current, Node* n1, Node* n2)
{
	if (current == NULL)
		return 0;
	int count = countMatch(current->left, n1, n2) + countMatch(current->right, n1, n2);
	if (current == n1 || current == n2)
		return 1 + count;
	return count;	
}
Node* findLCA(Node* root, Node* n1, Node* n2)
{
	if (root == NULL)
		return NULL;
	if (root == n1 || root == n2)
		return root;
	int count = countMatch(root->left, n1, n2);//左子樹包含n1和n2的個數
	if (count == 1)
		return root;//左子樹一個,右子樹肯定也有一個
	else if (count == 2)//都在左子樹
		return findLCA(root->left, n1, n2);
	else//都在右子樹
		return findLCA(root->right, n1, n2);
}
int main()
{
	//測試
	Node* A[11];
	for (int i = 0; i < 11; ++i)
	{
		A[i] = new Node();
		A[i]->data = i;
	}

	for (int i = 0; i < 5; ++i)
	{
		A[i]->left = A[i * 2 + 1];
		
		A[i]->right = A[i * 2 + 2];
	
	}

	Node* Ancestor = findLCA(A[0],A[7], A[10]);


}


還有一種方法,從下向上找。如果找到n1或n2,就把它傳給它的父結點,如果向下到頭都沒有找到,那麼返回NULL。如果當前結點左右子樹都返回非NULL,那麼當前結點就是最近公共父結點。這樣只需要遍歷一遍,演算法時間複雜度為O(N)。
#include<iostream>
struct Node
{
	int data;
	Node* left;
	Node* right;
	Node() :left(NULL), right(NULL)
	{}
};
Node* findLCA(Node *root, Node* n1, Node* n2)
{
	if (root == NULL)//沒找到
		return NULL;
	if (root == n1 || root == n2)//找到
		return root;
	Node* L = findLCA(root->left, n1, n2);//左子樹
	Node* R = findLCA(root->right, n1, n2);//右子樹
	//當前結點左右子樹都找到了n1和n2,那麼這個結點就是LCA結點
	if (L != NULL&R != NULL)
		return root;
	//否則是不為NULL的結點,或者兩個都為NULL
	else
		return L !=NULL ? L : R;
}

int main()
{
	//測試
	Node* A[11];
	for (int i = 0; i < 11; ++i)
	{
		A[i] = new Node();
		A[i]->data = i;
	}

	for (int i = 0; i < 5; ++i)
	{
		A[i]->left = A[i * 2 + 1];

		A[i]->right = A[i * 2 + 2];

	}

	Node* Ancestor = findLCA(A[0], A[7], A[10]);


}

原始碼在github備份:https://github.com/KangRoger/Example/tree/master/LeastCommonAncestor