Cây phân đoạn( Segment Tree) trong cấu trúc dữ liệu và giải thuật

Đối tượng hướng đến: Các bạn đang học cấu trúc dữ liệu và giải thuật nâng cao
Nội dung: 

   Trong các cuộc thi lập trình thuật toán, hay trong chương trình học ở trường, đôi khi chúng ta sẽ gặp những bài toán NHIỀU TRUY VẤN TRÊN CÙNG MỘT MẢNG, tiêu biểu nhất là bài toán cho một mảng gồm N số nguyên và M truy vấn. Trong M truy vấn đó mỗi truy vấn có dạng M x y, nghĩa là tìm số lớn nhất trong đoạn từ phần tử có chỉ số x đến phần tử có chỉ số y. Nếu chỉ dùng mảng một chiều đơn thuần để giải bài toán này thì cần rất nhiều thời gian cho việc truy xuất, độ phức tạp của thuật toán sẽ là O(N) cho mỗi truy xuất. Từ đó, người ta đã nghĩ ra một cấu trúc dữ liệu để tổ chức dữ liệu nhằm giải quyết nhanh gọn mấy bài toán chia khoảng kiểu này với độ phức tạp cho mỗi truy xuất là O(logN), và đó là cây phân đoạn( Segment Tree). Trên mạng đã có rất nhiều tài liệu hoặc bài viết nói về vấn đề này. Ở đây mình chỉ xin post code lên cho các bạn muốn dùng nó, chẳng hạn như dùng trong các project nhỏ trên trường, hoặc bài tập thầy cô giao. Ở đây mình đang tạo cây phân đoạn để tìm max và min trong từng đoạn cho trước.

Full source code và cách sử dụng có trong hàm main.
  1. #include<iostream>
  2. #include<vector>
  3. #include<math.h>
  4. #include<string>
  5. #include<algorithm>
  6. using namespace std;
  7. class Node
  8. {
  9. public:
  10. int max;
  11. int min;
  12. int leftBounder;
  13. int rightBounder;
  14. Node*leftNode;
  15. Node*rightNode;
  16. Node(int leftBounder = -1, int rightBounder = -1)
  17. {
  18. max = -2140000000;
  19. min = -max;
  20. this->leftBounder = leftBounder;
  21. this->rightBounder = rightBounder;
  22. leftNode = NULL;
  23. rightNode = NULL;
  24. }
  25. };
  26. class SegmentTree
  27. {
  28. private:
  29. Node*rootNode;
  30. void _build(Node*&root,vector<int>& vectorData)
  31. {
  32. if (root == NULL)
  33. {
  34. root = new Node(0,vectorData.size() - 1);
  35. _build(root, vectorData);
  36. }
  37. else
  38. {
  39. if (root->leftBounder == root->rightBounder)
  40. {
  41. root->max = root->min = vectorData[root->leftBounder];
  42. }
  43. if (root->leftBounder < root->rightBounder)
  44. {
  45. int mid = (root->leftBounder + root->rightBounder) / 2;
  46. root->leftNode = new Node(root->leftBounder, mid);
  47. root->rightNode = new Node(mid + 1, root->rightBounder);
  48. _build(root->leftNode, vectorData);
  49. _build(root->rightNode, vectorData);
  50. }
  51. }
  52. }
  53. void _clear(Node*&root)
  54. {
  55. if (root == NULL) return;
  56. _clear(root->rightNode);
  57. _clear(root->rightNode);
  58. delete root;
  59. root = NULL;
  60. }
  61. void _setMaxMin(Node*&root)
  62. {
  63. if (root == NULL) return;
  64. if (root->rightNode||root->leftNode) // both of two childs unequal NULL then root is not a leaf now
  65. {
  66. _setMaxMin(root->leftNode);
  67. _setMaxMin(root->rightNode);
  68. root->max = max(root->leftNode->max, root->rightNode->max);
  69. root->min = min(root->leftNode->min, root->rightNode->min);
  70. }
  71. }
  72. void _update(Node*&root, int index, int newValue)
  73. {
  74. if (root == NULL) return;
  75. if (!root->rightNode&&!root->leftNode) //then root is a leaf node
  76. {
  77. root->max = newValue;
  78. root->min = newValue;
  79. return;
  80. }
  81. if (index <= (root->leftBounder + root->rightBounder) / 2)
  82. {
  83. _update(root->leftNode, index, newValue);
  84. }
  85. else
  86. {
  87. _update(root->rightNode, index, newValue);
  88. }
  89. }
  90. int _getMax(Node*& root, int left, int right) //find max from left to right
  91. {
  92. if (left > right||root==NULL) return -2140000000;
  93. if (left == root->leftBounder&&right == root->rightBounder)
  94. {
  95. return root->max;
  96. }
  97. int mid = (root->leftBounder + root->rightBounder) / 2;
  98. if (left > mid)
  99. return _getMax(root->rightNode,left,right);
  100. if (right < mid)
  101. return _getMax(root->leftNode, left, right);
  102. int maxAtLeft = _getMax(root->leftNode, left, mid);
  103. int maxAtRight = _getMax(root->rightNode, mid+1, right);
  104. return max(maxAtLeft, maxAtRight);
  105. }
  106. int _getMin(Node*& root, int left, int right) //find min from left to right
  107. {
  108. if (left > right || root == NULL) return 2140000000;
  109. if (left == root->leftBounder&&right == root->rightBounder)
  110. {
  111. return root->min;
  112. }
  113. int mid = (root->leftBounder + root->rightBounder) / 2;
  114. if (left > mid)
  115. return _getMax(root->rightNode, left, right);
  116. if (right < mid)
  117. return _getMax(root->leftNode, left, right);
  118. int minAtLeft = _getMin(root->leftNode, left, mid);
  119. int minAtRight = _getMin(root->rightNode, mid + 1, right);
  120. return min(minAtLeft, minAtRight);
  121. }
  122. public:
  123. SegmentTree()//default constructor
  124. {
  125. //do nothing
  126. }
  127. SegmentTree(vector<int> vectorData)
  128. {
  129. _build(this->rootNode, vectorData);
  130. _setMaxMin(this->rootNode);
  131. }
  132. ~SegmentTree()
  133. {
  134. _clear(this->rootNode);
  135. }
  136. void build(vector<int> vectorData)
  137. {
  138. //clear all data befor build tree with new data
  139. _clear(this->rootNode);
  140. _build(this->rootNode, vectorData);
  141. _setMaxMin(this->rootNode);
  142. }
  143. void update(int index, int newValue)
  144. {
  145. _update(this->rootNode, index, newValue);
  146. _setMaxMin(this->rootNode);
  147. }
  148. int getMax(int left, int right) //find max from left to right
  149. {
  150. try
  151. {
  152. if (rootNode == NULL) throw "NULL POINTER EXCEPTION";
  153. if (left<0 || right>rootNode->rightBounder) throw "Query out of array";
  154. if (left > right) throw "Bad query, left index must be less than or equals right index";
  155. return _getMax(this->rootNode, left, right);
  156. }
  157. catch (char* exceptionMessage)
  158. {
  159. cout << exceptionMessage << endl;
  160. return -99999999;
  161. }
  162. }
  163. int getMin(int left, int right) //get min from left to right
  164. {
  165. try
  166. {
  167. if (rootNode == NULL) throw "NULL POINTER EXCEPTION";
  168. if (left<0 || right>rootNode->rightBounder) throw "Query out of array";
  169. if (left > right) throw "Bad query, left index must be less than or equals right index";
  170. return _getMin(this->rootNode, left, right);
  171. }
  172. catch (char* exceptionMessage)
  173. {
  174. cout << exceptionMessage << endl;
  175. return -99999999;
  176. }
  177. }
  178. };
  179. int main()
  180. {
  181. //my data
  182. vector<int> vectorData;
  183. vectorData.push_back(23);
  184. vectorData.push_back(2324);
  185. vectorData.push_back(56534);
  186. vectorData.push_back(4433);
  187. vectorData.push_back(2343);
  188. //build an Segment Tree base on above data
  189. SegmentTree segmentTree(vectorData);
  190. //then make a query on segment tree
  191. cout << segmentTree.getMin(0, 4) << endl;
  192. cout << segmentTree.getMax(0, 4) << endl;
  193. //try to update the tree
  194. segmentTree.update(2,11); //replace value at index = 2 by new value 11
  195. //then make a query on segment tree after updating
  196. cout << segmentTree.getMin(0, 4) << endl;
  197. cout << segmentTree.getMax(0, 4) << endl;
  198. return 0;
  199. }
Cây phân đoạn (Segment Tree) trong cấu trúc dữ liệu và giải thuật

Comments

Popular posts from this blog

Duyệt cây nhị phân theo chiều rộng _Lập trình C++

Duyệt cây không dùng đệ quy và stack _Lập trình C++ căn bản

Bài toán Upper Bound và Lower Bound trên cây nhị phân tìm kiếm