Skip to content

Commit d3daabd

Browse files
committed
Initial commit
0 parents  commit d3daabd

File tree

10 files changed

+513
-0
lines changed

10 files changed

+513
-0
lines changed

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Skew Heaps
2+
3+
A skew heap is a very simple implementation of a priority queue,
4+
which is just a data structure that returns the lowest (or highest)
5+
value stored in the structure. It is often useful for search algorithms
6+
where you want to search starting at best possible location. Assume
7+
for now that it is storing the lowest value.
8+
9+
The skew heap is implemented as a binary tree where the top node
10+
in the tree is the minimum (or maximum value) and every node value
11+
in the tree is greater than that of its parent. The main operation
12+
in a skew heap is the merger of two heaps. Given two trees, `h1` and
13+
`h2`, you compare the values at the top of each heap, and the minimum
14+
value is the value for the top of the merged heap. Now, if `h1` is
15+
the tree with the minimum value, let `l1` and `r1` be the left and
16+
right children of `h1`.
17+
18+
The new heap has the value of `h1` at the root, and then the left
19+
child of the new heap is the merge of `r1` and `h2`, and the right
20+
child is `l1`.
21+
22+
Here is an example of a merge. This image is from the paper on [Self-Adjusting
23+
Heaps](https://www.cs.cmu.edu/~sleator/papers/adjusting-heaps.pdf) by Sleator
24+
and Tarjan.
25+
26+
![Skew Heap Merge](images/merge.png)
27+
28+
To add a new item to the heap, just create a new heap with the new
29+
value and empty left and right children, and use the merge function to
30+
merge it with the heap.
31+
32+
To extract the minimum value from the heap, take the value at the
33+
top of the heap, and then merge the left and right children to
34+
create a new heap.
35+
36+
This repo is intended for us to implement a Skew Heap in various
37+
functional languages. Just add a new directory with your name, a +,
38+
and the implementation language (e.g. `markwutka+haskell`).

images/merge.png

61.1 KB
Loading

markwutka+haskell/README.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Haskell Skew Heap
2+
3+
A Skew Heap node is defined as:
4+
```haskell
5+
data SkewHeap a = Empty | SkewNode a (SkewHeap a) (SkewHeap a) deriving (Show)
6+
```
7+
8+
The merge function is defined as an inline function named `+++`.
9+
Note the requirement that the data type stored in the heap must
10+
implement the Ord interface:
11+
```haskell
12+
(+++) :: Ord a => SkewHeap a -> SkewHeap a -> SkewHeap a
13+
heap1@(SkewNode x1 l1 r1) +++ heap2@(SkewNode x2 l2 r2)
14+
| x1 <= x2 = SkewNode x1 (heap2 +++ r1) l1
15+
| otherwise = SkewNode x2 (heap1 +++ r2) l2
16+
Empty +++ heap = heap
17+
heap +++ Empty = heap
18+
```
19+
20+
To extract the minimum value, you receive both the minimum value
21+
and the updated heap with the item removed.
22+
```haskell
23+
extractMin Empty = Nothing
24+
extractMin (SkewNode x l r ) = Just (x , l +++ r )
25+
```
26+
27+
The `singleton` function creates a Skew Heap containing only
28+
one value.
29+
```haskell
30+
singleton :: Ord a => a -> SkewHeap a
31+
singleton x = SkewNode x Empty Empty
32+
```
33+
34+
In addition to these functions, there are some utility functions to
35+
convert to and from lists:
36+
```haskell
37+
toList :: Ord a => SkewHeap a -> [a]
38+
toList h = toList' (extractMin h) []
39+
40+
toList' :: Ord a => Maybe (a,SkewHeap a) -> [a] -> [a]
41+
toList' Nothing acc = reverse acc
42+
toList' (Just (v, h2)) acc = toList' (extractMin h2) (v:acc)
43+
44+
fromList :: Ord a => [a] -> SkewHeap a
45+
fromList l = foldl' (+++) Empty $ map singleton l
46+
```
47+
48+
Finally, there are two examples of creating a Skew Heap from a list
49+
of numbers:
50+
```haskell
51+
fooHeap = fromList [1..10]
52+
barHeap = fromList [9,2,5,7,1,10,8,3,6,4]
53+
```
54+
55+
Here is an example session that displays one of the default heaps
56+
and then converts it into a list, which will be sorted:
57+
```shell
58+
$ ghci SkewHeap.hs
59+
GHCi, version 8.10.7: https://www.haskell.org/ghc/ :? for help
60+
[1 of 1] Compiling SkewHeap ( SkewHeap.hs, interpreted )
61+
Ok, one module loaded.
62+
*SkewHeap> fooHeap
63+
SkewNode 1 (SkewNode 2 (SkewNode 6 (SkewNode 10 Empty Empty) Empty) (SkewNode 4 (SkewNode 8 Empty Empty) Empty)) (SkewNode 3 (SkewNode 5 (SkewNode 9 Empty Empty) Empty) (SkewNode 7 Empty Empty))
64+
*SkewHeap> toList fooHeap
65+
[1,2,3,4,5,6,7,8,9,10]
66+
*SkewHeap>
67+
```
68+
69+
To demonstrate Haskell type classes, here is another data item that
70+
can be stored in a Skew Heap (this is in `heaptest.hs`):
71+
```haskell
72+
data Thing = Thing String Int
73+
deriving Show
74+
```
75+
76+
In order to store something of type `Thing` in a Skew Heap, you need
77+
to implement the `Ord` type class for it, as well as `Eq`. Note that
78+
the compare function reverses the arguments, meaning that the Skew
79+
Heap will actually return the maximum value in the heap instead of
80+
the minimum:
81+
```haskell
82+
instance Eq Thing where
83+
(==) (Thing _ i) (Thing _ j) = i == j
84+
85+
instance Ord Thing where
86+
compare (Thing _ i) (Thing _ j) = compare j i
87+
```
88+
89+
Here is a list of some example data:
90+
```haskell
91+
things = [ Thing "Curly" 30, Thing "Moe" 100, Thing "Joe" 5,
92+
Thing "Curly Joe" 15, Thing "Shemp" 50, Thing "Larry" 85 ]
93+
94+
```
95+
96+
Here is an example session that converts this list of things into
97+
a skew heap, and then converts that back into a list:
98+
99+
```shell
100+
$ ghci heaptest.hs
101+
GHCi, version 8.10.7: https://www.haskell.org/ghc/ :? for help
102+
[1 of 2] Compiling SkewHeap ( SkewHeap.hs, interpreted )
103+
[2 of 2] Compiling Main ( heaptest.hs, interpreted )
104+
Ok, two modules loaded.
105+
*Main> let sh = fromList things
106+
*Main> sh
107+
SkewNode (Thing "Moe" 100) (SkewNode (Thing "Larry" 85) (SkewNode (Thing "Curly" 30) (SkewNode (Thing "Curly Joe" 15) Empty Empty) Empty) Empty) (SkewNode (Thing "Shemp" 50) (SkewNode (Thing "Joe" 5) Empty Empty) Empty)
108+
*Main> toList sh
109+
[Thing "Moe" 100,Thing "Larry" 85,Thing "Shemp" 50,Thing "Curly" 30,Thing "Curly Joe" 15,Thing "Joe" 5]
110+
*Main>
111+
```

markwutka+haskell/SkewHeap.hs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
module SkewHeap where
2+
import Data.Maybe
3+
import Data.List
4+
5+
-- This implementation of a Skew Heap (Sleator & Tarjan) comes from
6+
-- Louis Wasserman's "Playing with Priority Queues" in
7+
-- The Monad Reader #16
8+
-- There may be faster Haskell priority queue implementations,
9+
-- but this one is just so simple
10+
11+
data SkewHeap a = Empty | SkewNode a (SkewHeap a) (SkewHeap a) deriving (Show)
12+
13+
(+++) :: Ord a => SkewHeap a -> SkewHeap a -> SkewHeap a
14+
heap1@(SkewNode x1 l1 r1) +++ heap2@(SkewNode x2 l2 r2)
15+
| x1 <= x2 = SkewNode x1 (heap2 +++ r1) l1
16+
| otherwise = SkewNode x2 (heap1 +++ r2) l2
17+
Empty +++ heap = heap
18+
heap +++ Empty = heap
19+
20+
extractMin Empty = Nothing
21+
extractMin (SkewNode x l r ) = Just (x , l +++ r )
22+
23+
singleton :: Ord a => a -> SkewHeap a
24+
singleton x = SkewNode x Empty Empty
25+
26+
toList :: Ord a => SkewHeap a -> [a]
27+
toList h = toList' (extractMin h) []
28+
29+
toList' :: Ord a => Maybe (a,SkewHeap a) -> [a] -> [a]
30+
toList' Nothing acc = reverse acc
31+
toList' (Just (v, h2)) acc = toList' (extractMin h2) (v:acc)
32+
33+
fromList :: Ord a => [a] -> SkewHeap a
34+
fromList l = foldl' (+++) Empty $ map singleton l
35+
36+
fooHeap = fromList [1..10]
37+
barHeap = fromList [9,2,5,7,1,10,8,3,6,4]

markwutka+haskell/heaptest.hs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import SkewHeap
2+
3+
data Thing = Thing String Int
4+
deriving Show
5+
6+
instance Eq Thing where
7+
(==) (Thing _ i) (Thing _ j) = i == j
8+
9+
instance Ord Thing where
10+
compare (Thing _ i) (Thing _ j) = compare j i
11+
12+
things = [ Thing "Curly" 30, Thing "Moe" 100, Thing "Joe" 5,
13+
Thing "Curly Joe" 15, Thing "Shemp" 50, Thing "Larry" 85 ]
14+
15+

0 commit comments

Comments
 (0)