01/10/2018, 10:35

Cải thiện thuật toán tính chiều cao của cây (thuật toán cũ bị time limited)

Như tiêu đề bài viết, thuật toán tính chiều cao của cây của em chạy quá lâu, fail ở 1 vài test unit. Mong mọi người giúp đỡ, cải thiện thuật toán này. Em cám ơn.
Mô tả: tính chiều cao của 1 cây bất kì( số con của 1 nút tùy ý)
Đây là code của em:

class node {
private:
	int data;
	node* child;
	node *sibling;
public:
	node() {
		data = 0;
		child = NULL;
		sibling = NULL;
	}
	int getData() {
		return data;
	}
	void setData(int key) {
		data = key;
	}
	node * getChild() {
		return child;
	}
	void setChild(node* _child)
	{
		child = _child;
	}
	node* getSibling() {
		return sibling;
	}
	void setSibling(node *_sibling)
	{
		sibling = _sibling;
	}
};
class tree {
	//    root
	//  /
	// child 1st -> child 2nd ->child 3rd ....
	// ...
private:
	node *root;
public:
	tree()
	{
		root = NULL;
	}
	void constructTheTree(int *p, int n) {
		node *temp = new node[n];
		std::cout << "constructing..." << std::endl;
		for (int i = 0; i < n; i++)
		{
			temp[i].setData(p[i]);
			if (p[i] == -1)
			{
				root = &temp[i];
			}
			else {
				if (temp[p[i]].getChild() == NULL)
				{
					temp[p[i]].setChild(&temp[i]);
				}
				else {
					node* posSibling = temp[p[i]].getChild();
					while (posSibling->getSibling() != NULL)
					{
						posSibling = posSibling->getSibling();
					}
					posSibling->setSibling(&temp[i]);

				}
			}
		}
		std::cout << "constructed!" << std::endl;
	}
	void display(node *n)
	{
		if (n->getChild() == NULL)
		{
			std::cout << n->getData() << " ";
		}
		else {
			node *sibling = n->getChild();
			display(sibling);
			std::cout << n->getData() << " ";
			while (sibling->getSibling() != NULL)
			{
				display(sibling->getSibling());
				sibling = sibling->getSibling();
			}
		}
	}
	node *getRoot() {
		return root;
	}
	int height(node* n)
	{
		if (n->getChild() == NULL)
		{
			return 1;
		}
		
		else
		{
			int heightChild = -1;
			node* pos = n->getChild();
			while (pos!= NULL)
			{
				heightChild = (heightChild < height(pos)) ? height(pos) : heightChild;
				pos = pos->getSibling();
			}
			return 1 + heightChild;
		}
		
	}
};

Bài này cách dựng cây của nó khá đặc biệt. Cho 1 mảng số gồm n -1 số từ -1 đến n-1, phần tử -1 trong dãy sẽ là Root, phần tử thứ i có giá trị là value[i] sẽ là con của phần tử thứ value[i].
Ví dụ dãy 4 -1 4 1 1. đánh số từ 0 đến 4.
Phần tử thứ 1 là gốc, phần tử thứ 0 và thứ 2 là con của phần tử thứ 4, và phần tử thứ 3 và thứ 4 là 2 con của phần tử thứ 1.
Bài cho 24 test unit. Thuật toán của em tính đúng, nhưng có vẻ quá phức tạp nên bị time limited, ở các test số 7, số 10, …
Mô tả bài toán trong file PDF, problem 2, tree height. Test và code của em trong file .rar.

fshare.vn

Not Found - Fshare

Fshare là dịch vụ lưu trữ và chia sẻ dữ liệu trực tuyến giúp khách hàng lưu trữ thông tin, dữ liệu (album ảnh, phim, phần mềm, tài liệu, game, nhạc, v.v...) mọi lúc, mọi nơi, tương thích trên mọi thiết bị.


À mn thử thì nhớ dẫn đường dẫn cho file ở các vị trí em đã comment nhé.

HK boy viết 12:39 ngày 01/10/2018

Format lại code bạn nhé. Thêm 3 dấu ` vào đầu và cuối code.

phần tử thứ i có giá trị là value[i] sẽ là con của phần tử thứ value[i]

Thế thì khác gì là cha của nút i là value[i]? Bạn viết thế này hơi khó hiểu.

Tùng Quách Huy viết 12:40 ngày 01/10/2018

Tớ đã sửa, cám ơn c đã góp ý

HK boy viết 12:48 ngày 01/10/2018

ôi…

Tùng Quách Huy viết 12:47 ngày 01/10/2018

t đổi quyền truy cập rồi c ạ. C xem giúp tớ chút nhé

HK boy viết 12:42 ngày 01/10/2018

Thay vì viết hàm tính height() đệ quy thì bạn chuyển thành void.
Tạo 1 mảng h[] là chiều sâu của nút so với gốc.
h[root] = 0, chiều cao cả cây = max(h[]) + 1

Tùng Quách Huy viết 12:50 ngày 01/10/2018

Nếu tớ k hiểu nhầm ý bạn thì có phải bạn định tính chiều cao của tất cả các node trong cây, rồi đưa nó vào 1 mảng, rồi lấy max của mảng đó +1 không? Tớ thấy như vậy thì tương đương với cách tớ làm, vì mỗi node tớ cũng chỉ đi qua 1 lần thôi. Với lại để tính chiều cao của 1 node mà không sử dụng đệ quy thì làm cách nào? nếu c tính đủ số node trên cây, thì sẽ bị tính đi tính lại chiều cao của 1 vài node nhiều lần :))))

HK boy viết 12:44 ngày 01/10/2018

nếu c tính đủ số node trên cây, thì sẽ bị tính đi tính lại chiều cao của 1 vài node nhiều lần :))))

Linh tinh. DFS đâu có tính các node nhiều lần thớt chưa hiểu về DFS rồi.

để tính chiều cao của 1 node mà không sử dụng đệ quy thì làm cách nào?

BFS.

Gió viết 12:45 ngày 01/10/2018

heightChild = (heightChild < height(pos)) ? height(pos) : heightChild;

Cái này chỉ cần thay height(pos) thành 1 biến phụ và tính một lần sẽ giảm được độ phức tạp.

Giả sử cây suy biến thành một linked-list (chỉ có 1 node bên trái) Độ phức tạp của thuật toán tinh height
T(n) = 2*T(n-1) + O(1) => T ~ O(2^n)
nếu thay hieght bằng biến phụ và chỉ tính 1 lần
T(n) = T(n-1) + O(1) =>T ~ O(n)
Nhanh hơn rất nhiều so với không dùng biến phụ

Tùng Quách Huy viết 12:50 ngày 01/10/2018

Đã sửa được và chạy ổn, cám ơn 2 bạn. Tớ đã sửa được thêm phần dựng cây để nó chỉ còn là O(n) chứ không còn là O(n^2) như code trên( lại bị quá thời gian ở test 17 đến 20).
Đến test 21, chương trình lại báo lỗi stack overflow. Tại dữ liệu đầu vào lớn quá, 100 000 số dựng thành cây có chiều cao 100 000!

class node {
private:
	int data;
	node* child;
	node *sibling;
	node *posLastChild;
public:
	node() {
		data = 0;
		child = NULL;
		sibling = NULL;
		posLastChild = NULL;
	}
	int getData() {
		return data;
	}
	void setData(int key) {
		data = key;
	}
	node * getChild() {
		return child;
	}
	void setChild(node* _child)
	{
		child = _child;
	}
	node* getSibling() {
		return sibling;
	}
	void setSibling(node *_sibling)
	{
		sibling = _sibling;
	}
	node *getLastChild()
	{
		return posLastChild;
	}
	void setLastChild(node* temp)
	{
		posLastChild = temp;
	}
};
class tree {
	//    root
	//  /
	// child 1st -> child 2nd ->child 3rd ....
	// ...
private:
	node *root;
public:
	tree()
	{
		root = NULL;
	}
	void constructTheTree(int *p, int n) {
		node *temp = new node[n];
		std::cout << "constructing..." << std::endl;
		for (int i = 0; i < n; i++)
		{
			temp[i].setData(p[i]);
			if (p[i] == -1)
			{
				root = &temp[i];
			}
			else {
				if (temp[p[i]].getChild() == NULL)
				{
					temp[p[i]].setChild(&temp[i]);
					temp[p[i]].setLastChild(&temp[i]);
				}
				else {
					node *posLastChild = temp[p[i]].getLastChild();
					posLastChild->setSibling(&temp[i]);
					temp[p[i]].setLastChild(&temp[i]);

					/*node* posSibling = temp[p[i]].getChild();
					while (posSibling->getSibling() != NULL)
					{
						posSibling = posSibling->getSibling();
					}
					posSibling->setSibling(&temp[i]);*/
				}
			}
		}
		std::cout << "constructed!" << std::endl;
	}
	void display(node *n)
	{
		if (n->getChild() == NULL)
		{
			std::cout << n->getData() << " ";
		}
		else {
			node *sibling = n->getChild();
			display(sibling);
			std::cout << n->getData() << " ";
			while (sibling->getSibling() != NULL)
			{
				display(sibling->getSibling());
				sibling = sibling->getSibling();
			}
		}
	}
	node *getRoot() {
		return root;
	}
	int height(node* n)
	{
		if (n->getChild() == NULL)
		{
			return 1;
		}
		
		else
		{
			int heightChild = -1;
			node* pos = n->getChild();
			while (pos!= NULL)
			{
				int heightPos = height(pos);
				heightChild = (heightChild < heightPos) ? heightPos : heightChild;
				pos = pos->getSibling();
			}
			return 1 + heightChild;
		}
		
	}
};

bộ test 21: https://www.fshare.vn/file/4BYN22SJ5PW4 ( open with note pad)
đáp án cho test 21 này là 100 000!

HK boy viết 12:39 ngày 01/10/2018

100000 vẫn chưa phải là lớn.

node *temp = new node[n];

Hình như với mỗi node bạn lại xây một mảng node[n]?

Tùng Quách Huy viết 12:37 ngày 01/10/2018

Hình như với mỗi node bạn lại xây một mảng node[n]?

Không phải, tớ chỉ xây dựng 1 mảng có n node, xong rồi gán các con trỏ của các node( con trỏ child, sibling, lastChild), gán con trỏ root của tree ,để hình thành cây thôi.
Hàm constructTheTree chỉ cần gọi 1 lần với mỗi bộ số liệu vào. C mở file test sẽ thấy dạng của nó, dòng đầu chứa số phần tử, dòng thứ 2 trở đi chứa các số. Tớ lưu các số đó dưới dạng mảng có số phần tử như dòng 1 đã cho. Đó là lý do mà hàm này có 2 tham số là int*p, và int N.
Test 21, xác định được lỗi ở phần tính chiều cao. Các phần khác đã thử, không bị lỗi

Bài liên quan
0