This question was deleted right after it was originally posted, because the OP was not upfront about the fact that she asked a homework question. As the due date for the assignment has since passed, I have decided to undelete this answer.
I'm not a Prolog guru, but I gave your question some thought. I think heap1/1 is easier to implement than heap2/1, because its "tree structure" is more apparent. In fact, what I will do is implement heap1/1, after which I'll implement heap2/1 in terms of heap1/1.
The tree implementation: heap1/1
The empty tree is a binary heap. As for node(K, L, R), it is a heap if:
L and R are heaps.
- If
L and/or R are not empty, then their key is less than or equal to K.
- If
L has depth d, then R has depth d or d - 1.
So what needs to be done, is to check whether a given binary tree satisfies those properties.
Given a node, we will need to traverse it to know the depth of the tree it represents. Hence we will define heap/1 in terms of a predicate heap/2, in which the second argument is the tree depth. In the end we don't care about the actual depth, so we define:
heap1(H) :- heap1(H, _).
So what about heap/2? The base case is empty, which is a heap with depth 0 (or 1, but that is irrelevant for our purposes). Thus:
heap1(empty, 0).
For node(K, L, R) we'll have to recurse. We need to test the heap properties outlined above and calculate the tree depth H. Thus:
heap1(empty, 0).
heap1(node(K, L, R), H) :-
heap1(L, LH), heap1(R, RH),
(L = node(LK, _, _) *-> K @>= LK; true),
(R = node(RK, _, _) *-> K @>= RK; true),
(LH is RH; LH is RH + 1), H is LH + 1.
This code uses SWI-Prolog's soft cut (*->)/2. This mechanism is used to test whether the subtrees are non-empty, and if so, to verify that their key is smaller or equal to K (using (@>=)/2). The predicate true/0 corresponds to the situation in which one of the subtrees is empty. (is)/2 is used to compare the depths of the subtrees and to calculate the depth of the whole tree.
The list implementation: heap2/1
I assume that heap2/1 is supposed to check whether its sole argument is a list representing a heap stored as an array (or Ahnentafel list). If so, then, assuming a zero-indexed list, the children of a node at index i are found at indices 2i + 1 and 2i + 2.
As a result, a parent node is not necessarily stored right next to its child nodes. More specifically, at position i in the list we'll need to skip i or i + 1 places to reach the keys of the subtrees. We'll define a predicate skipn/3 to help us with that:
skipn(N, L, E) :- (length(F, N), append(F, E, L)) *-> true; E = [].
skipn(N, L, E) succeeds if E equals L after the first N elements of L have been stripped or if E is the empty list while the length of L is not greater than N. This implementation uses length/2, append/3 and again (*->)/2 and true/0.
Next up: the definition of heap2/1. Again we will call in the help of an auxiliary predicate, this time heap2/3. heap2(H, N, T) succeeds if the list H, of which the head is at position N of a possibly greater list, can be converted to a binary tree T. heap2/1 will call heap2/3 with initial index 0 and will verify that the resulting binary tree is in fact a heap. Thus:
heap2(H) :- heap2(H, 0, T), heap1(T).
Alright, we're almost there. At position N the index LI of the root of the left subtree is 2 * N + 1. Likewise RI = 2 * N + 2 is the index of the root of the right subtree. Since at position N already N list items have been skipped, only LI - N and RI - N elements need to be skipped to reach indices LI and RI, respectively. Thus:
heap2([], _, empty).
heap2([H|T], N, node(H, L, R)) :-
LI is 2 * N + 1, RI is 2 * N + 2,
LS is LI - N, RS is RI - N,
skipn(LS, [H|T], LT), skipn(RS, [H|T], RT),
heap2(LT, LI, L), heap2(RT, RI, R).