bTree.js

  1. import { BTreeNode } from './node.js';
  2. import { EXIST_VALUE, INVALID_VALUE, NON_EXIST_VALUE } from './error.js';
  3. /**
  4. * B - Tree 클래스
  5. */
  6. class BTree {
  7. /**
  8. * BTree 클래스의 생성자로 현재 n이 홀수인 Btree만 구현되어 있습니다.
  9. * @param {*} limit
  10. * @throws {INVALID_VALUE} n이 number타입이 아니거나 짝수인 경우 예외 발생.
  11. */
  12. constructor(limit) {
  13. if (typeof limit !== 'number' || limit % 2 === 0 || limit <= 1) throw new Error(INVALID_VALUE);
  14. this.root = null;
  15. this.limit = limit;
  16. this.size = 0;
  17. }
  18. /**
  19. * remove 메소드에서 사용되는 swap 헬퍼 함수.
  20. * @param {BTreeNode} delNode 삭제될 노드.
  21. * @param {number} index 삭제를 위해 검색을 시작할 자식노드의 index.
  22. * @returns {*} 삭제할 값을 리턴.
  23. */
  24. _swap(delNode, index) {
  25. let subsParent = delNode;
  26. let subsNode = delNode.rightChildOf(index);
  27. while (subsNode.leftChildOf(0) && subsNode.leftChildOf(0) instanceof BTreeNode) {
  28. subsParent = subsNode;
  29. subsNode = subsParent.leftChildOf(0);
  30. }
  31. delNode.values[index] = subsNode.value(0);
  32. return subsNode.value(0);
  33. }
  34. /**
  35. * 하나의 노드를 분할한다. 분할된 노드의 부모 노드를 리턴한다.
  36. * @param {BTreeNode} parent
  37. * @param {number} key 기준이 되는 value값
  38. * @returns {BTreeNode} 분할된 노드의 부모 노드.
  39. */
  40. _split(parent, key) {
  41. let current;
  42. if (!parent) {
  43. // 분할 대상 노드가 루트 노드인 경우 (부모가 없는 경우)
  44. current = this.root;
  45. const leftChildNode = new BTreeNode(this.limit);
  46. let i;
  47. for (i = 0; i < Math.floor(this.limit / 2); i += 1) {
  48. leftChildNode.values[i] = current.value(i);
  49. leftChildNode.children[i] = current.leftChildOf(i);
  50. }
  51. leftChildNode.children[i] = current.leftChildOf(i);
  52. leftChildNode.valueLength = Math.floor(this.limit / 2);
  53. const midValue = current.value(i);
  54. const rightChildNode = new BTreeNode(this.limit);
  55. let j;
  56. for (j = 0, i += 1; i < this.limit; i += 1, j += 1) {
  57. rightChildNode.values[j] = current.value(i);
  58. rightChildNode.children[j] = current.leftChildOf(i);
  59. }
  60. rightChildNode.children[j] = current.leftChildOf(i);
  61. rightChildNode.valueLength = j;
  62. current.clear();
  63. current.addValue(midValue, leftChildNode, rightChildNode);
  64. } else {
  65. // 분할 대상 노드가 루트 노드가 아닌 경우 (부모가 있는 경우)
  66. let i;
  67. for (i = 0; parent.value(i) < key; i += 1);
  68. current = parent.leftChildOf(i);
  69. let k = Math.floor(this.limit / 2);
  70. const midValue = current.value(k);
  71. const rightChildNode = new BTreeNode(this.limit);
  72. let j;
  73. for (j = 0, k += 1; k < this.limit; k += 1, j += 1) {
  74. rightChildNode.values[j] = current.value(k);
  75. rightChildNode.children[j] = current.leftChildOf(k);
  76. }
  77. rightChildNode.children[j] = current.leftChildOf(k);
  78. rightChildNode.valueLength = j;
  79. current.values.splice(Math.floor(this.limit / 2));
  80. current.children.splice(Math.floor(this.limit / 2) + 1);
  81. current.valueLength = Math.floor(this.limit / 2);
  82. parent.addValue(midValue, current, rightChildNode);
  83. current = parent;
  84. }
  85. return current;
  86. }
  87. /**
  88. * 형제 노드에게서 value를 빌려온다.
  89. * @param {BTreeNode} parent
  90. * @param {number} index
  91. * @returns {boolean} 빌려 오는 것이 가능한지 여부를 리턴한다.
  92. */
  93. _borrowKey(parent, index) {
  94. let fromIndex = index + 1;
  95. if (index === parent.size()) fromIndex = index - 1;
  96. const from = parent.leftChildOf(fromIndex);
  97. const to = parent.leftChildOf(index);
  98. if (from.size() <= Math.floor(this.limit / 2)) return false;
  99. if (fromIndex > index) {
  100. // 오른쪽 형제에게 빌려오는 경우
  101. to.addValue(parent.value(index), null, from.leftChildOf(0));
  102. parent.values[index] = from.value(0);
  103. from.removeValue(0);
  104. } else {
  105. // 왼쪽 형제에게 빌려오는 경우
  106. to.addValue(parent.value(index - 1), from.leftChildOf(from.size()), null);
  107. parent.values[index - 1] = from.value(from.size() - 1);
  108. from.removeValue(from.size() - 1);
  109. }
  110. return true;
  111. }
  112. /**
  113. * 형제 노드와 합친다.
  114. * @param {BTreeNode} parent
  115. * @param {number} index
  116. * @returns {BTreeNode} 합쳐진 노드를 리턴한다.
  117. */
  118. _bindNode(parent, index) {
  119. if (index === parent.size()) index -= 1;
  120. const left = parent.leftChildOf(index);
  121. const right = parent.rightChildOf(index);
  122. left.addValue(parent.value(index), null, null);
  123. let i;
  124. let j;
  125. for (i = 0, j = left.size(); i < right.size(); i += 1, j += 1) {
  126. left.values[j] = right.value(i);
  127. left.children[j] = right.children[i];
  128. }
  129. left.children[j] = right.children[i];
  130. left.valueLength = left.size() + right.size();
  131. parent.removeValue(index);
  132. parent.children[index] = left;
  133. // 부모 노드가 뿌리노드인 경우
  134. if (!parent.size()) this.root = left;
  135. return left;
  136. }
  137. /**
  138. * 현재 BTree에 있는 노드 개수를 리턴한다.
  139. * @returns {number}
  140. */
  141. size() {
  142. return this.size;
  143. }
  144. /**
  145. * b-tree 에서 해당 값을 찾는다.
  146. * @param {*} value
  147. * @returns {BTreeNode} 찾은 노드를 반환한다. 값이 존재하지 않으면 false를 반환한다.
  148. */
  149. find(value) {
  150. let targetNode = this.root;
  151. while (targetNode && targetNode.findValue(value) < 0) {
  152. let i;
  153. for (i = 0; i < targetNode.size() && targetNode.value(i) < value; i += 1);
  154. targetNode = targetNode.leftChildOf(i);
  155. }
  156. if (!targetNode) return false;
  157. return targetNode;
  158. }
  159. /**
  160. * b-tree에 주어진 값을 삽입한다.
  161. * @param {*} value
  162. * @throws {EXIST_VALUE} 이미 값이 존재하면 예외 발생.
  163. * @returns {BTree} 값이 삽입된 자기 자신 리턴.
  164. */
  165. insert(value) {
  166. if (!this.root) this.root = new BTreeNode(this.limit);
  167. let current = this.root;
  168. let parent = null;
  169. while (current) {
  170. if (current.findValue(value) >= 0) throw Error(EXIST_VALUE);
  171. if (current.size() >= this.limit) {
  172. current = this._split(parent, value);
  173. }
  174. parent = current;
  175. let i;
  176. for (i = 0; i < current.size() && current.value(i) < value; i += 1);
  177. current = current.leftChildOf(i);
  178. }
  179. parent.addValue(value, undefined, undefined);
  180. this.size += 1;
  181. return this;
  182. }
  183. /**
  184. * b-tree 에서 주어진 값을 삭제한다.
  185. * @param {*} value
  186. * @throws {NON_EXIST_VALUE} 주어진 값이 존재하지 않으면 예외 발생.
  187. * @returns {boolean} 삭제한 뒤 true를 반환한다.
  188. */
  189. remove(value) {
  190. let parent = null;
  191. let current = this.root;
  192. let i = 0;
  193. while (current) {
  194. if (parent && current.size() <= Math.floor(this.limit / 2)) {
  195. if (!this._borrowKey(parent, i)) current = this._bindNode(parent, i);
  196. }
  197. if (current.findValue(value) >= 0) {
  198. if (!current.leftChildOf(0)) break; // current 가 leaf 노드 인 경우
  199. value = this._swap(current, current.findValue(value)); // leaf 노드가 아니면 swap 한 다음, 다시 새로운 value를 지우는 알고리즘 실행
  200. }
  201. parent = current;
  202. for (i = 0; i < current.size() && current.value(i) <= value; i += 1);
  203. current = current.leftChildOf(i);
  204. }
  205. if (!current) throw new Error(NON_EXIST_VALUE); // break 하지 못한 경우, value를 트리에서 찾지 못한 것이므로 false 리턴.
  206. if (current.size() <= Math.floor(this.limit / 2)) {
  207. if (!this._borrowKey(parent, i)) current = this._bindNode(parent, i);
  208. }
  209. current.removeValue(current.findValue(value));
  210. this.size -= 1;
  211. return true;
  212. }
  213. // generators * * * * * * * * * * * * *
  214. /**
  215. * dequeue 메소드의 진행 상태를 generate 하는 제너레이터 함수.
  216. * @generator
  217. * @param {BTreeNode} parent
  218. * @param {number} index
  219. * @yields {BTree} 진행 상태가 시각적으로 표시된 자기 자신.
  220. * @returns {boolean} 빌려 오는 것이 가능한지 여부를 리턴한다.
  221. */
  222. *_borrowKeyGen(parent, index) {
  223. let fromIndex = index + 1;
  224. if (index === parent.size()) fromIndex = index - 1;
  225. const from = parent.leftChildOf(fromIndex);
  226. const to = parent.leftChildOf(index);
  227. if (from.size() <= Math.floor(this.limit / 2)) {
  228. return false;
  229. }
  230. yield true;
  231. to.colored = 'blue';
  232. from.colored = 'green';
  233. yield this;
  234. delete to.colored;
  235. delete from.colored;
  236. if (fromIndex > index) {
  237. to.valuesColor[to.size() - 1] = 'blue';
  238. parent.valuesColor[index] = 'orange';
  239. from.valuesColor[0] = 'green';
  240. yield this;
  241. to.valuesColor = [];
  242. parent.valuesColor = [];
  243. from.valuesColor = [];
  244. // 오른쪽 형제에게 빌려오는 경우
  245. to.addValue(parent.value(index), null, from.leftChildOf(0));
  246. parent.values[index] = from.value(0);
  247. from.removeValue(0);
  248. to.valuesColor[to.size() - 2] = 'blue';
  249. to.valuesColor[to.size() - 1] = 'orange';
  250. parent.valuesColor[index] = 'green';
  251. } else {
  252. to.valuesColor[0] = 'blue';
  253. parent.valuesColor[index - 1] = 'orange';
  254. from.valuesColor[from.size() - 1] = 'green';
  255. yield this;
  256. to.valuesColor = [];
  257. parent.valuesColor = [];
  258. from.valuesColor = [];
  259. // 왼쪽 형제에게 빌려오는 경우
  260. to.addValue(parent.value(index - 1), from.leftChildOf(from.size()), null);
  261. parent.values[index - 1] = from.value(from.size() - 1);
  262. from.removeValue(from.size() - 1);
  263. to.valuesColor[1] = 'blue';
  264. to.valuesColor[0] = 'orange';
  265. parent.valuesColor[index - 1] = 'green';
  266. }
  267. const temp = this;
  268. to.valuesColor = [];
  269. parent.valuesColor = [];
  270. return temp;
  271. }
  272. /**
  273. * _bindNode 메소드의 진행 상태를 generate 하는 제너레이터 함수.
  274. * @generator
  275. * @param {BTreeNode} parent
  276. * @param {number} index
  277. * @yields {BTree} 진행 상태가 시각적으로 표시된 자기 자신.
  278. * @returns {BTreeNode} 합쳐진 노드를 리턴한다.
  279. */
  280. *_bindNodeGen(parent, index) {
  281. let isLeft = true;
  282. if (index === parent.size()) {
  283. index -= 1;
  284. isLeft = false;
  285. }
  286. const left = parent.leftChildOf(index);
  287. const right = parent.rightChildOf(index);
  288. left.colored = isLeft ? 'blue' : 'green';
  289. right.colored = isLeft ? 'green' : 'blue';
  290. parent.valuesColor[index] = 'orange';
  291. yield this;
  292. delete left.colored;
  293. delete right.colored;
  294. parent.valuesColor = [];
  295. left.addValue(parent.value(index), null, null);
  296. let i;
  297. let j;
  298. for (i = 0, j = left.size(); i < right.size(); i += 1, j += 1) {
  299. left.values[j] = right.value(i);
  300. left.children[j] = right.children[i];
  301. }
  302. left.children[j] = right.children[i];
  303. left.valueLength = left.size() + right.size();
  304. parent.removeValue(index);
  305. parent.children[index] = left;
  306. // 부모 노드가 뿌리노드인 경우
  307. if (!parent.size()) this.root = left;
  308. const mid = Math.floor(this.limit / 2);
  309. for (let j = 0; j < this.limit; j += 1) {
  310. if (j < mid) {
  311. left.valuesColor[j] = isLeft ? 'blue' : 'green';
  312. } else if (j === mid) {
  313. left.valuesColor[j] = 'orange';
  314. } else {
  315. left.valuesColor[j] = isLeft ? 'green' : 'blue';
  316. }
  317. }
  318. yield this;
  319. left.valuesColor = [];
  320. return left;
  321. }
  322. /**
  323. * _swap 메소드의 진행 상태를 generate 하는 제너레이터 함수.
  324. * @generator
  325. * @param {BTreeNode} delNode 삭제될 노드.
  326. * @param {number} index 삭제를 위해 검색을 시작할 자식노드의 index.
  327. * @yields {BTree} 진행 상태가 시각적으로 표시된 자기 자신.
  328. * @returns {*} 삭제할 값을 리턴.
  329. */
  330. *_swapGen(delNode, index) {
  331. let subsParent = delNode;
  332. let subsNode = delNode.rightChildOf(index);
  333. delNode.valuesColor[index] = 'blue';
  334. subsNode.colored = 'green';
  335. yield this;
  336. delete subsNode.colored;
  337. while (subsNode.leftChildOf(0) && subsNode.leftChildOf(0) instanceof BTreeNode) {
  338. subsParent = subsNode;
  339. subsNode = subsParent.leftChildOf(0);
  340. subsNode.colored = 'green';
  341. yield this;
  342. delete subsNode.colored;
  343. }
  344. subsNode.valuesColor[0] = 'green';
  345. yield this;
  346. delNode.valuesColor[index] = 'white';
  347. yield this;
  348. delNode.values[index] = subsNode.value(0);
  349. delNode.valuesColor[index] = 'green';
  350. yield this;
  351. delNode.valuesColor = [];
  352. subsNode.valuesColor = [];
  353. yield this;
  354. return subsNode.value(0);
  355. }
  356. /**
  357. * _swap 메소드의 진행 상태를 generate 하는 제너레이터 함수.
  358. * @generator
  359. * @param {BTreeNode} parent
  360. * @param {number} key 기준이 되는 value 값
  361. * @yields {BTree} 진행 상태가 시각적으로 표시된 자기 자신.
  362. * @returns {BTreeNode} 분할된 노드의 부모 노드.
  363. */
  364. *_splitGen(parent, key) {
  365. let current;
  366. if (!parent) {
  367. // 분할 대상 노드가 루트 노드인 경우 (부모가 없는 경우)
  368. current = this.root;
  369. const mid = Math.floor(this.limit / 2);
  370. current.values.forEach((v, index) => {
  371. if (index < mid) {
  372. current.valuesColor[index] = 'green';
  373. } else if (index === mid) {
  374. current.valuesColor[index] = 'blue';
  375. } else {
  376. current.valuesColor[index] = 'orange';
  377. }
  378. });
  379. yield this;
  380. current.valuesColor = [];
  381. const leftChildNode = new BTreeNode(this.limit);
  382. let i;
  383. for (i = 0; i < Math.floor(this.limit / 2); i += 1) {
  384. leftChildNode.values[i] = current.value(i);
  385. leftChildNode.children[i] = current.leftChildOf(i);
  386. }
  387. leftChildNode.children[i] = current.leftChildOf(i);
  388. leftChildNode.valueLength = Math.floor(this.limit / 2);
  389. const midValue = current.value(i);
  390. const rightChildNode = new BTreeNode(this.limit);
  391. let j;
  392. for (j = 0, i += 1; i < this.limit; i += 1, j += 1) {
  393. rightChildNode.values[j] = current.value(i);
  394. rightChildNode.children[j] = current.leftChildOf(i);
  395. }
  396. rightChildNode.children[j] = current.leftChildOf(i);
  397. rightChildNode.valueLength = j;
  398. current.clear();
  399. current.addValue(midValue, leftChildNode, rightChildNode);
  400. current.valuesColor[0] = 'blue';
  401. current.leftChildOf(0).values.forEach((v, index) => {
  402. current.leftChildOf(0).valuesColor[index] = 'green';
  403. });
  404. current.rightChildOf(0).values.forEach((v, index) => {
  405. current.rightChildOf(0).valuesColor[index] = 'orange';
  406. });
  407. yield this;
  408. current.valuesColor = [];
  409. current.leftChildOf(0).valuesColor = [];
  410. current.rightChildOf(0).valuesColor = [];
  411. } else {
  412. // 분할 대상 노드가 루트 노드가 아닌 경우 (부모가 있는 경우)
  413. let i;
  414. for (i = 0; parent.value(i) < key; i += 1);
  415. current = parent.leftChildOf(i);
  416. const mid = Math.floor(this.limit / 2);
  417. current.values.forEach((v, index) => {
  418. if (index < mid) {
  419. current.valuesColor[index] = 'green';
  420. } else if (index === mid) {
  421. current.valuesColor[index] = 'blue';
  422. } else {
  423. current.valuesColor[index] = 'orange';
  424. }
  425. });
  426. yield this;
  427. current.valuesColor = [];
  428. let k = Math.floor(this.limit / 2);
  429. const midValue = current.value(k);
  430. const rightChildNode = new BTreeNode(this.limit);
  431. let j;
  432. for (j = 0, k += 1; k < this.limit; k += 1, j += 1) {
  433. rightChildNode.values[j] = current.value(k);
  434. rightChildNode.children[j] = current.leftChildOf(k);
  435. }
  436. rightChildNode.children[j] = current.leftChildOf(k);
  437. rightChildNode.valueLength = j;
  438. current.values.splice(Math.floor(this.limit / 2));
  439. current.children.splice(Math.floor(this.limit / 2) + 1);
  440. current.valueLength = Math.floor(this.limit / 2);
  441. parent.addValue(midValue, current, rightChildNode);
  442. current = parent;
  443. const pi = current.findValue(midValue);
  444. current.valuesColor[pi] = 'blue';
  445. current.leftChildOf(pi).values.forEach((v, index) => {
  446. current.leftChildOf(pi).valuesColor[index] = 'green';
  447. });
  448. current.rightChildOf(pi).values.forEach((v, index) => {
  449. current.rightChildOf(pi).valuesColor[index] = 'orange';
  450. });
  451. yield this;
  452. current.valuesColor = [];
  453. current.leftChildOf(pi).valuesColor = [];
  454. current.rightChildOf(pi).valuesColor = [];
  455. }
  456. return current;
  457. }
  458. /**
  459. * insert 메소드의 진행 상태를 generate 하는 제너레이터 함수.
  460. * @generator
  461. * @param {*} value
  462. * @throws {EXIST_VALUE} 이미 값이 존재하면 예외 발생.
  463. * @yields {BTree} 진행 상태가 시각적으로 표시된 자기 자신.
  464. * @returns {BTree} 값이 삽입된 자기 자신 리턴.
  465. */
  466. *insertGen(value) {
  467. if (!this.root) this.root = new BTreeNode(this.limit);
  468. let current = this.root;
  469. let parent = null;
  470. while (current) {
  471. current.colored = 'blue';
  472. yield this;
  473. delete current.colored;
  474. if (current.findValue(value) >= 0) throw Error(EXIST_VALUE);
  475. if (current.size() >= this.limit) {
  476. current = yield* this._splitGen(parent, value);
  477. }
  478. parent = current;
  479. let i;
  480. for (i = 0; i < current.size() && current.value(i) < value; i += 1) {
  481. current.valuesColor[i] = 'blue';
  482. yield this;
  483. current.valuesColor = [];
  484. }
  485. current = current.leftChildOf(i);
  486. }
  487. parent.addValue(value, undefined, undefined);
  488. this.size += 1;
  489. return this;
  490. }
  491. /**
  492. * remove 메소드의 진행 상태를 generate 하는 제너레이터 함수.
  493. * @generator
  494. * @param {*} value
  495. * @throws {NON_EXIST_VALUE} 주어진 값이 존재하지 않으면 예외 발생.
  496. * @yields {BTree} 진행 상태가 시각적으로 표시된 자기 자신.
  497. * @returns {boolean} 삭제한 뒤 true를 반환한다.
  498. */
  499. *removeGen(value) {
  500. let parent = null;
  501. let current = this.root;
  502. let i = 0;
  503. while (current) {
  504. current.colored = 'blue';
  505. yield this;
  506. delete current.colored;
  507. if (parent && current.size() <= Math.floor(this.limit / 2)) {
  508. const bkIter = this._borrowKeyGen(parent, i);
  509. if (bkIter.next().value) {
  510. yield* bkIter;
  511. } else {
  512. current = yield* this._bindNodeGen(parent, i);
  513. }
  514. }
  515. if (current.findValue(value) >= 0) {
  516. if (!current.leftChildOf(0)) break; // current 가 leaf 노드 인 경우
  517. value = yield* this._swapGen(current, current.findValue(value));
  518. }
  519. parent = current;
  520. for (i = 0; i < current.size() && current.value(i) <= value; i += 1) {
  521. current.valuesColor[i] = 'blue';
  522. yield this;
  523. current.valuesColor = [];
  524. }
  525. current = current.leftChildOf(i);
  526. }
  527. if (!current) throw new Error(NON_EXIST_VALUE); // break 하지 못한 경우, value를 트리에서 찾지 못한 것이므로 false 리턴.
  528. if (current.size() <= Math.floor(this.limit / 2)) {
  529. const bkIter = this._borrowKeyGen(parent, i);
  530. if (bkIter.next().value) {
  531. yield* bkIter;
  532. } else {
  533. current = yield* this._bindNodeGen(parent, i);
  534. }
  535. }
  536. current.colored = 'blue';
  537. yield this;
  538. delete current.colored;
  539. current.removeValue(current.findValue(value));
  540. this.size -= 1;
  541. return this;
  542. }
  543. }
  544. export { BTree };