kswang2400/data-struct

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# data-struct

A simple gem that provides several useful data structures in Ruby.

[![Gem Version](https://badge.fury.io/rb/data-struct.svg)](http://badge.fury.io/rb/data-struct)
[![Build Status](https://travis-ci.org/kswang2400/data-struct.svg?branch=master)](https://travis-ci.org/kswang2400/data-struct)
[![Dependency Status](https://gemnasium.com/kswang2400/data-struct.svg)](https://gemnasium.com/kswang2400/data-struct)
<a href="https://codeclimate.com/github/kswang2400/data-struct"><img src="https://codeclimate.com/github/kswang2400/data-struct/badges/gpa.svg" /></a>
<a href="https://codeclimate.com/github/kswang2400/data-struct/coverage"><img src="https://codeclimate.com/github/kswang2400/data-struct/badges/coverage.svg" /></a>

##Usage

Install the gem:

```ruby
  gem install data-struct
```

Or require in Gemfile:

```ruby
  gem 'data-struct'
```

then run

```
  bundle install
```

To use the gem, initialize a new object through the DataStruct module (optional).

```ruby

  require "data-struct"

  linked_list = DataStruct::SinglyLinkedList.new
  # default LinkedList class is doubly linked; use SinglyLinkedList for singly linked list
  dynamic_array = DynamicArray.new(10)
  # same thing, DataStruct module not necessary
```

Class names correspond with list below:

Abstract Data Type
- [ ] Map
- [ ] Set
- [x] MaxStack
- [x] MinMaxStack
- [ ] Priority Queue

Data Structures
- [x] DynamicArray
- [ ] HashMap
- [x] SinglyLinkedList
- [ ] LinkedList
- [ ] LRUCache
- [x] BinarySearchTree
- [x] BinHeap

## Contents

* [1. Abstract Data Type](#1-abstract-data-type)
  * [1.1 Max Stack](#11-max-stack)
  * [1.2 Min Max Stack](#12-min-max-stack)
* [2. Data Structures](#2-data-structures)
  * [2.1 Dynamic Array](#21-dynamic-array)
  * [2.2 Singly Linked List](#22-singly-linked-list)
  * [2.3 Binary Search Tree](#23-binary-search-tree-self-balancing)
  * [2.4 Binary Heap](#24-binary-heap)
* [Contact](#contact)
* [Contributing](#contributing)
* [License](#license)

## 1. Abstract Data Types

### 1.1 Max Stack

#### #initialize

Initialize your max stack.

```ruby
  max_stack = DataStruct::MaxStack.new
  => #<MaxStack:0x007f86c8a04c80 @store=[]>
```

#### #push(val)

Pushes the value onto the end of the stack

```ruby
  max_stack.store
  => []
  max_stack.push(5)
  => [[5, 5]]
  max_stack.store
  => [[5, 5]]
```

#### #pop

Pops the last element in the stack

```ruby
  max_stack.store
  => [[5, 5]]
  max_stack.pop
  => 5
  max_stack.store
  => []
```

#### #max

Returns in the max value in O(1) time.

```ruby
  max_stack.store
  => [[5, 5], [2, 5], [6, 6], [7, 7]]
  max_stack.max
  => 7
```

### 1.2 Min Max Stack

#### #initialize

Initialize your min max stack.

```ruby
mms = DataStruct::MinMaxStack.new
=> #<DataStruct::MinMaxStack:0x007fd2a2f6d3b8 @store=[]>
```

#### #push(val)

Pushes values into the stack.

```ruby
mms.push(2)
=> [[2, 2, 2]]
mms.push(5)
=> [[2, 2, 2], [5, 2, 5]]
mms.push(-1)
=> [[2, 2, 2], [5, 2, 5], [-1, -1, 5]]
```

#### #length

Returns the current length of the stack.

```ruby
mms.length
=> 3
```

#### #max

Returns the max value of the stack in O(1) time.

```ruby
mms.max
=> 5
```

#### #min

Similarly to max, returns min value of stack in O(1) time.

```ruby
mms.min
=> -1
```

#### #pop

Pops from the stack and returns the value.

```ruby
mms.pop
=> -1
mms.pop
=> 5
mms.pop
=> 2
mms.length
=> 0
```

## 2. Data Structures

### 2.1 Dynamic Array

#### #initialize

Initialize with the size of your dynamic array

```ruby
  d_array = DataStruct::DynamicArray.new(4)
  => #<DynamicArray:0x007ffe5c089460 @num_items=0, @size=4, @start=0, @store=[nil, nil, nil, nil]>
```

#### #pop

Pops the last element in the array

```ruby
  d_array.store
  => [1, 2, 3, 4, nil, nil, nil, nil, nil, nil]
  d_array.pop
  => 4
  d_array.store
  [1, 2, 3, nil, nil, nil, nil, nil, nil, nil]
```

#### #push(val)

Pushes the value onto the end of the array

```ruby
  d_array.store
  => [1, 2, 3, 4, nil, nil, nil, nil, nil, nil]
  d_array.push(10)
  => [1, 2, 3, 4, 10, nil, nil, nil, nil, nil]
```

Resizes when pushed onto full array

```ruby
  d_array.store
  => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  d_array.push(11)
  => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, nil, nil, nil, nil, nil, nil, nil, nil, nil]
```

#### #shift

Shifts and returns the value at the front of the array; doesn't copy the array

```ruby
  d_array.store
  => [1, 2, 3, 4, 10, nil, nil, nil, nil, nil]
  d_array.shift
  => 1
  d_array.store
  => [nil, 2, 3, 4, 10, nil, nil, nil, nil, nil]
```

#### #unshift(val)

Unshifts the value into the front of the array

```ruby
  d_array.store
  => [nil, 2, 3, 4, 5, 6, 7, 8, nil, nil]
  d_array.unshift(100)
  => [100, 2, 3, 4, 5, 6, 7, 8, nil, nil]
```

Uses ring buffer to wrap around

```ruby
  d_array.store
  => [100, 2, 3, 4, 5, 6, 7, 8, nil, nil]
  d_array.unshift(101)
  => [100, 2, 3, 4, 5, 6, 7, 8, nil, 101]
```

#### #insert(val, idx)

Inserts the value at idx and pushes everything over

```ruby
  d_array.store
  => [1, 2, 3, 4, nil, nil, nil, nil, nil, nil]
  d_array.insert(10, 0)
  => [10, 1, 2, 3, 4, nil, nil, nil, nil, nil]
  d_array.insert(100, 2)
  => [10, 1 100, 2, 3, 4, nil, nil, nil, nil]
```

Ring buffer still in effect

```ruby
  d_array.store
  => [100, 2, 3, 4, 5, 6, 7, 8, nil, 101]
  d_array.insert(200, 0)
  => [101, 100, 2, 3, 4, 5, 6, 7, 8, 200]
```

### 2.2 Singly Linked List

#### #initialize

Initialize an empty Linked List object with a sentinel Link

```ruby
  linked_list = DataStruct::SinglyLinkedList.new
  => #<SinglyLinkedList:0x007fabbb1af5b8 @sentinel=#<SinglyLink:0x007fabbb1af590 @next=nil, @val=nil>>
```

#### #pop

Pops from the end and returns the value

```ruby
  linked_list.push(1)
  linked_list.push(2)
  linked_list.push(3)
  linked_list.pop
  => 3
  linked_list.pop
  => 2
```

#### #push

Pushes onto the end of the list (inner most nested pointer)

```ruby
  linked_list.push(1)
  linked_list.push(2)
  linked_list.push(3)
  => #<SinglyLinkedList:0x007fd349bc2220
      @sentinel=
      #<SinglyLink:0x007fd349bc21f8 @next=
        #<SinglyLink:0x007fd349087b30 @next=
          #<SinglyLink:0x007fd3490b2ce0 @next=
            #<SinglyLink:0x007fd3490cbdf8 @next=nil,
            @val=3>,
          @val=2>,
        @val=1>,
      @val=nil>>
```

#### #shift

Shifts from the front and returns the value

```ruby
  linked_list.push(1)
  linked_list.push(2)
  linked_list.push(3)
  linked_list.shift
  => 1
  linked_list.shift
  => 2
```

#### #unshift

unshifts to the front of the list and is attached to the sentinel (outer most pointer)

```ruby
  linked_list.unshift(10)
  linked_list.unshift(20)
  linked_list.unshift(30)
  => #<SinglyLinkedList:0x007fd34a551b38
      @sentinel=
      #<SinglyLink:0x007fd34a551b10 @next=
        #<SinglyLink:0x007fd349bdab18 @next=
          #<SinglyLink:0x007fd34910da28 @next=
            #<SinglyLink:0x007fd348a0e880 @next=nil,
            @val=10>,
          @val=20>,
        @val=30>,
      @val=nil>>
```

### 2.3 Binary Search Tree (self balancing)

#### #initialize

initialize with your root node

```ruby
  tree = BinarySearchTree.new(10)
  =>  { 10 : {} | {} }
```

#### #children

// deprecated //

#### ::from_array

class method to initialize tree from an array

```ruby
  tree = BinarySearchTree.from_array([1, 5, 2, 6, 8, 10, 3])
  =>  { 6 :  
        { 2 :  
          { 1 : {} | {} }  |  { 5 :  
                        { 3 : {} | {} }  | {}
                                }  
        }  |  
        { 8 : {} |  
          { 10 : {} | {} }  
        }  
      }
```

#### #insert(val)

inserts the value in the correct position, then rebalances

```ruby
  tree = BinarySearchTree.new(10)
  tree.insert(15)
  =>  { 10 : {} |  { 15 : {} | {} }  }
  tree.insert(13)
  =>  { 13 :  { 10 : {} | {} }  |  { 15 : {} | {} }  }
```

#### #include?(val)

checks tree for presence of value

```ruby
  tree = BinarySearchTree.new(10)
  tree.insert(15)
  tree.include?(15)
  => true
  tree.include?(14)
  => false
```

#### #to_a

returns the tree in sorted array form

```ruby
  tree = BinarySearchTree.from_array([10, 5, 7, 15, 12, 0])
  =>  { 7 :  { 5 :  { 0 : {} | {} }  | {} }  |  { 12 :  { 10 : {} | {} }  |  { 15 : {} | {} }  }  }
  tree.to_a
  => [0, 5, 7, 10, 12, 15]
```

#### benchmark testing

```ruby
  test_array = []
  5000.times { test_array << (rand 5000) }

  tree = BinarySearchTree.new(test_array.first)
  test_array.each { |v| tree.insert(v) }
  test_hash = Hash[test_array.map { |x| [x, true] }]

  Benchmark.bm do |benchmark|
    benchmark.report("test_array include" ) { (1..5000).each { |n| test_array.include? n } }
    benchmark.report("binary tree serach")  { (1..5000).each { |n| tree.include? n } }
    benchmark.report("test_hash lookup ")   { (1..5000).each { |n| test_hash.has_key? n }}
  end

                        user     system      total        real
  test_array include  0.880000   0.010000   0.890000 (  0.895702)
  binary tree search  0.010000   0.000000   0.010000 (  0.012694)
  test_hash lookup    0.000000   0.000000   0.000000 (  0.001391)

  80x faster than Ruby array, 10x slower than Ruby hash
```

### 2.4 Binary Heap

#### #initialize(&prc)

Initialize the binary heap:

```ruby
  heap = DataStruct::BinHeap.new
  => #<BinHeap:0x007f8e38bc3928 @prc=#<Proc:0x007f8e38bc38b0@lib/bin_heap.rb:6>, @store=[]>
```

Default comparison Proc (MinHeap):

```ruby
  @prc = Proc.new { |el1, el2| el1 <=> el2 }
```

#### #insert(element)

Insert a element into the heap, which will be placed in the correct position via ::heapify_up

```ruby
  bin_heap.insert(4)
  bin_heap.insert(3)
  bin_heap.insert(6)
  bin_heap.insert(0)
  bin_heap.insert(5)
  bin_heap.insert(2)

  bin_heap.store
  => [0, 3, 2, 4, 5, 6]
```

#### #extract

Removes the element with the highest priority and re-organizes the binary heap accordingly via ::heapify_down

```ruby
  bin_heap.extract
  => 0

  bin_heap.store
  => [2, 3, 6, 4, 5]
```

#### ::heapify_up(store, child_idx, &prc)

1. Starts with the last element, store[child_idx], and compares it to its parent to see if priority holds.
2. If it does, the binary heap is ordered properly.
3. If it does not, swap the child and parent elements, and call ::heapify_up recursively until the binary heap is ordered.

#### ::heapify_down(store, parent_idx, &prc)

1. Starts with the first element and compares it to its children to see if priority holds
2. If it does, the binary heap is ordered properly
3. If it does not, swap with the child of higher priority if applicable and call ::heapify_down recursively until the heap is ordered.

#### ::find_children(last_idx, parent_idx)

Due to the nature of a binary heap being a full tree, we can find children indices using a parent_idx by using some arithmetic:

```ruby
  left_child_idx = (parent_idx * 2) + 1
  right_child_idx = (parent_idx * 2) + 2
```

#### ::find_parent(child_idx)

Similary to ::find_children, we can find a child's parent by solving for parent_idx.

```ruby
  parent_idx = (child_idx - 1) / 2
```

##Contact

* [Aaron Hong](https://github.com/gnoha)
* [Karen Ling](https://github.com/karenling)
* [Daniel Ng](https://github.com/danielng09)
* [Conan Tzou](https://github.com/conanza)
* [Kevin Wang](https://github.com/kswang2400)

##Contributing

* Fork it ( https://github.com/kswang2400/data-structures.git )
* Create your feature branch (git checkout -b my-new-feature)
* Commit your changes (git commit -am 'Add some feature')
* Push to the branch (git push origin my-new-feature)
* Create new Pull Request

##License

This code is free to use under the terms of the MIT license. © 2015