@@ -1423,43 +1423,172 @@ List.length t;;
1423
1423
1424
1424
Consider a height-balanced binary tree of height ` h ` . What is the
1425
1425
maximum number of nodes it can contain? Clearly,
1426
- _ maxN = 2<sup >` h ` </sup > - 1_ .
1427
- However, what is the minimum number * minN* ? This question is more
1426
+ max_nodes = 2<sup >` h ` </sup > - 1.
1427
+
1428
+ ``` ocamltop
1429
+ let max_nodes h = 1 lsl h - 1
1430
+ ```
1431
+
1432
+ However, what is the minimum number min_nodes? This question is more
1428
1433
difficult. Try to find a recursive statement and turn it into a function
1429
1434
` min_nodes ` defined as follows: ` min_nodes h ` returns the minimum number
1430
1435
of nodes in a height-balanced binary tree of height ` h ` .
1431
1436
1432
1437
SOLUTION
1433
1438
1439
+ > The following solution comes directly from translating the question.
1434
1440
> ``` ocamltop
1435
1441
> let rec min_nodes h =
1436
1442
> if h <= 0 then 0
1437
1443
> else if h = 1 then 1
1438
1444
> else min_nodes (h - 1) + min_nodes (h - 2) + 1
1439
1445
> ```
1446
+ > It is not the more efficient one however. One should use the last
1447
+ > two values as the state to avoid the double recursion.
1448
+ > ```ocamltop
1449
+ > let rec min_nodes_loop m0 m1 h =
1450
+ > if h <= 1 then m1
1451
+ > else min_nodes_loop m1 (m1 + m0 + 1) (h - 1)
1452
+ >
1453
+ > let min_nodes h =
1454
+ > if h <= 0 then 0 else min_nodes_loop 0 1 h
1455
+ > ```
1456
+ >
1457
+ > It is not difficult to show that `min_nodes h` = F<sub>h+2</sub> - 1,
1458
+ > where (F<sub>n</sub>) is the
1459
+ > [Fibonacci sequence](https://en.wikipedia.org/wiki/Fibonacci_number).
1440
1460
1441
- On the other hand, we might ask: what is the maximum height H a
1442
- height-balanced binary tree with N nodes can have? `max_height n` returns
1443
- the maximum height of a height-balanced binary tree with `n` nodes.
1461
+ On the other hand, we might ask: what are the minimum (resp. maximum)
1462
+ height H a
1463
+ height-balanced binary tree with N nodes can have?
1464
+ `min_height` (resp. `max_height n`) returns
1465
+ the minimum (resp. maximum) height of a height-balanced binary tree
1466
+ with `n` nodes.
1444
1467
1445
1468
SOLUTION
1446
1469
1470
+ > Inverting the formula max_nodes = 2<sup>`h`</sup> - 1, one directly
1471
+ > find that Hₘᵢₙ(n) = ⌈log₂(n+1)⌉ which is readily
1472
+ > implemented:
1473
+ >
1474
+ > ```ocamltop
1475
+ > let min_height n = int_of_float(ceil(log(float(n + 1)) /. log 2.))
1476
+ > ```
1477
+ >
1478
+ > Let us give a proof that the formula for Hₘᵢₙ is valid. First, if h
1479
+ > = `min_height` n, there exists a height-balanced tree of height h
1480
+ > with n nodes. Thus 2ʰ - 1 = `max_nodes h` ≥ n i.e., h ≥ log₂(n+1).
1481
+ > To establish equality for Hₘᵢₙ(n), one has to show that, for any n,
1482
+ > there exists a height-balanced tree with height Hₘᵢₙ(n). This is
1483
+ > due to the relation Hₘᵢₙ(n) = 1 + Hₘᵢₙ(n/2) where n/2 is the integer
1484
+ > division. For n odd, this is readily proved — so one can build a
1485
+ > tree with a top node and two sub-trees with n/2 nodes of height
1486
+ > Hₘᵢₙ(n) - 1. For n even, the same proof works if one first remarks
1487
+ > that, in that case, ⌈log₂(n+2)⌉ = ⌈log₂(n+1)⌉ — use log₂(n+1) ≤ h ∈
1488
+ > ℕ ⇔ 2ʰ ≥ n + 1 and the fact that 2ʰ is even for that. This allows
1489
+ > to have a sub-tree with n/2 nodes. For the other sub-tree with
1490
+ > n/2-1 nodes, one has to establish that Hₘᵢₙ(n/2-1) ≥ Hₘᵢₙ(n) - 2
1491
+ > which is easy because, if h = Hₘᵢₙ(n/2-1), then h+2 ≥ log₂(2n) ≥
1492
+ > log₂(n+1).
1493
+ >
1494
+ > The above function is not the best one however. Indeed, not every
1495
+ > 64 bits integer can be represented exactly as a floating point
1496
+ > number. Here is one that only uses integer operations:
1497
+ >
1498
+ > ```ocamltop
1499
+ > let rec ceil_log2_loop log plus1 n =
1500
+ > if n = 1 then if plus1 then log + 1 else log
1501
+ > else ceil_log2_loop (log + 1) (plus1 || n land 1 <> 0) (n / 2)
1502
+ >
1503
+ > let ceil_log2 n = ceil_log2_loop 0 false n
1504
+ > ```
1505
+ >
1506
+ > This algorithm is still not the fastest however. See for example
1507
+ > the [Hacker's Delight](http://www.hackersdelight.org/), section 5-3
1508
+ > (and 11-4).
1509
+ >
1510
+ > Following the same idea as above, if h = `max_height` n, then one
1511
+ > easily deduces that `min_nodes` h ≤ n < `min_nodes`(h+1). This
1512
+ > yields the following code:
1513
+ >
1514
+ > ```ocamltop
1515
+ > let rec max_height_search h n =
1516
+ > if min_nodes h <= n then max_height_search (h+1) n else h-1
1517
+ > let max_height n = max_height_search 0 n
1518
+ > ```
1519
+ >
1520
+ > Of course, since `min_nodes` is computed recursively, there is no
1521
+ > need to recompute everything to go from `min_nodes h` to
1522
+ > `min_nodes(h+1)`:
1523
+ >
1447
1524
> ```ocamltop
1448
- > let rec max_height = function
1449
- > | 0 -> 0
1450
- > | n ->
1451
- > let h = max_height (n - 1) in
1452
- > if max_height (n - min_nodes (h - 1) - 1) = h then h + 1 else h
1525
+ > let rec max_height_search h m_h m_h1 n =
1526
+ > if m_h <= n then max_height_search (h+1) m_h1 (m_h1 + m_h + 1) n else h-1
1527
+ >
1528
+ > let max_height n = max_height_search 0 0 1 n
1453
1529
> ```
1454
1530
1455
1531
Now, we can attack the main problem: construct all the height-balanced
1456
1532
binary trees with a given number of nodes. `hbal_tree_nodes n` returns a
1457
1533
list of all height-balanced binary tree with `n` nodes.
1458
1534
1535
+ SOLUTION
1536
+
1537
+ > First, we define some convenience functions `fold_range` that folds
1538
+ > a function `f` on the range `n0`...`n1` i.e., it computes
1539
+ > `f (... f (f (f init n0) (n0+1)) (n0+2) ...) n1`. You can think it
1540
+ > as performing the assignment `init ← f init n` for `n = n0,..., n1`
1541
+ > except that there is no mutable variable in the code.
1542
+ >
1543
+ > ```ocamltop
1544
+ > let rec fold_range ~f ~init n0 n1 =
1545
+ > if n0 > n1 then init else fold_range ~f ~init:(f init n0) (n0 + 1) n1
1546
+ > ```
1547
+ >
1548
+ > When constructing trees, there is an obvious symmetry: if one swaps
1549
+ > the left and right sub-trees of a balanced tree, we still have a
1550
+ > balanced tree. The following function returns all trees in `trees`
1551
+ > together with their permutation.
1552
+ >
1553
+ > ```ocamltop
1554
+ > let rec add_swap_left_right trees =
1555
+ > List.fold_left (fun a n -> match n with
1556
+ > | Node(v, t1, t2) -> Node(v, t2, t1) :: a
1557
+ > | Empty -> a) trees trees
1558
+ > ```
1559
+ >
1560
+ > Finally we generate all trees recursively, using a priori the bounds
1561
+ > computed above. It could be further optimized but our aim is to
1562
+ > straightforwardly express the idea.
1563
+ >
1564
+ > ```ocamltop
1565
+ > let rec hbal_tree_nodes_height h n =
1566
+ > assert(min_nodes h <= n && n <= max_nodes h);
1567
+ > if h = 0 then [Empty]
1568
+ > else
1569
+ > let acc = add_hbal_tree_node [] (h-1) (h-2) n in
1570
+ > let acc = add_swap_left_right acc in
1571
+ > add_hbal_tree_node acc (h-1) (h-1) n
1572
+ > and add_hbal_tree_node l h1 h2 n =
1573
+ > let min_n1 = max (min_nodes h1) (n - 1 - max_nodes h2) in
1574
+ > let max_n1 = min (max_nodes h1) (n - 1 - min_nodes h2) in
1575
+ > fold_range min_n1 max_n1 ~init:l ~f:(fun l n1 ->
1576
+ > let t1 = hbal_tree_nodes_height h1 n1 in
1577
+ > let t2 = hbal_tree_nodes_height h2 (n - 1 - n1) in
1578
+ > List.fold_left (fun l t1 ->
1579
+ > List.fold_left (fun l t2 -> Node('x', t1, t2) :: l) l t2) l t1
1580
+ > )
1581
+ >
1582
+ > let hbal_tree_nodes n =
1583
+ > fold_range (min_height n) (max_height n) ~init:[] ~f:(fun l h ->
1584
+ > List.rev_append (hbal_tree_nodes_height h n) l)
1585
+ > ```
1586
+
1459
1587
Find out how many height-balanced trees exist for `n = 15`.
1460
1588
1461
1589
```ocamltop
1462
- List.length (hbal_tree_nodes 15)
1590
+ List.length (hbal_tree_nodes 15);;
1591
+ List.map hbal_tree_nodes [0; 1; 2; 3];;
1463
1592
```
1464
1593
1465
1594
0 commit comments