IT212 Data Structure

Unit 1:

R Batzinger

2024-07-12

MODULE 1: INTRODUCTION TO DATA STRUCTURES AND ALGORITHMS

Module 1: Overview

  • Module name: Introduction to Data Structures and Algorithms

  • Educational Goal: To introduce the fundamental concepts of data structures and algorithms, their importance in computer science, and the relationship between them.

  • Topics:

    • Definition of data structures and algorithms
    • Importance of data structures in computer science
    • Algorithm design and analysis (time and space complexity)
    • Abstract data types (ADTs)
    • Installation of Ruby software

Programming Insight 1

\[\large\rm Program = Algorithm + Data\ Structure\]

  • Algorithm: A series of steps needed for a process or a function

  • Data Structure: A system of storing data in a way that can be retrieved.

Programming Insight 2

\[\rm Choose\ 2:\ \left\{\begin{matrix}Fast\\ Small\\ Cheap\\ \end{matrix}\right.\]

  • The nature, quality and performance of a program depend on choices made
  • In the end, the role of a programmer is to choose the right variation of algorithms and data structures that balances the tradeoff with the time and resources available

Finding Prime Numbers

\[\tiny\rm\eqalign{Number & &Factors\\ 2 &\rightarrow& 2 {}^{(Prime)}\\ 3 &\rightarrow& 3 {}^{(Prime)}\\ 4 &\rightarrow& 2 \times 2 \\ 5 &\rightarrow& 5 {}^{(Prime)}\\ 6 &\rightarrow& 2 \times 3 \\ 7 &\rightarrow& 7 {}^{(Prime)}\\ 8 &\rightarrow& 2 \times 2 \times 2\\ 9 &\rightarrow& 3 \times 3 \\ 10 &\rightarrow& 2 \times 5 \\ }\]

  • Factors can be rendered as a series of prime numbers
  • Factors never exceed the square root of the number
  • All non-prime numbers are multiples of prime numbers

TEST FOR ALL POSSIBLE FACTORS


def find_primes1
  # SETUP A CONTAINER FOR THE RESULTS
  # FOR ALL NUMBERS IN TEST RANGE
    # PRESUME NUM AS PRIME 
    # CHECK FOR FACTORS LESS THAN NUM
      # IF FACTOR FOUND
         # MARK AS NON-PRIME
    # APPEND PRIME TO LIST OF PRIMES

TEST FOR ALL POSSIBLE FACTORS


def is_factor?(num1,num2)
  return (num1 % num2).eql?(0)
end
  
def find_primes1
  # SETUP A CONTAINER FOR THE RESULTS
  @primes = []
   
  # FOR ALL NUMBERS IN TEST RANGE
  (2..MAX_NUM).each do |num|
  
    # PRESUME NUM AS PRIME 
      is_prime = true
   
    # CHECK FOR FACTORS LESS THAN NUM
    (2...num).each do |num2|
    
      # IF FACTOR FOUND
      if is_factor?(num,num2)
      
        # MARK NUMBER AS NON-PRIME
        is_prime = false
          break
        end
      end
    end
     
    # APPEND PRIME TO LIST OF PRIMES
    if is_prime
      @primes << num
    end
end

CROSS MULTIPLES OF PRIMES OFF A TALLY

def find_primes2
  # CREATE A TALLY OF NUMBERS IN RANGE
  # CREATE AN ARRAY FOR THE RESULTS
  # TEST ALL NUMBERS IN THE RANGE
    # ELIMINATE NUMBERS LESS THAN 2
    # IF NEW PRIME FOUND
        # APPEND PRIME TO MASTER LIST
        # ELIMINATE ALL MULTIPLES OF PRIME

CROSS MULTIPLES OF PRIMES OFF A TALLY


def find_primes2
  # CREATE A TALLY OF ALL NUMBERS
  tally = (0..MAX).to_a

  # CREATE A LIST FOR PRIMES FOUND
  @primes = []

  # TEST ALL NUMBERS IN THE RANGE
  (0..MAX).each do |num|

    # ELIMINATE NUMBERS LESS THAN 2
    if num < 2
      tally[num] = nil
        
    # IF NEW PRIME FOUND
    elsif !numbers[num].nil?
    
      #APPEND PRIME TO MASTER LIST
      @primes << num
      
      # ELIMINATE MULTIPLES OF PRIME
      xnum = num
        while xnum <= MAX do
          numbers[xnum] = nil
        xnum += num
      end 
    end
  end
end

USE RUBY LIBRARY FUNCTION


# LOAD PRIME LIBRARY CLASS

def find_primes3
  # SETUP A CONTAINER FOR THE RESULTS
  # FOR ALL NUMBERS IN TEST RANGE
    # IF A NUMBER IS PRIME
      # SAVE IT
      

USE RUBY LIBRARY FUNCTION


require 'prime'

def find_primes3
  # SETUP A CONTAINER FOR THE RESULTS
  @primes = []
   
  # FOR ALL NUMBERS IN TEST RANGE
  (2..MAX_NUM).each do |num|
   
    # IF A NUMBER IS PRIME
    if num.prime?    
      # SAVE IT
      @primes << num
    end
  end
end

Code for finding prime numbers


# TEST FOR POSSIBLE FACTORS

def is_factor?(num1,num2)
  (num1 % num2).eql?(0)
end
  
def find_primes1
  @primes = []
  (2..MAX).each do |num|
    is_prime = true
    (2...num).each do |num2|
      if is_factor?(num,num2)
        is_prime = false
        break
      end
    end
    if is_prime
      @primes << num
    end
  end
end

# REMOVE MULTIPLES FROM TALLY 

def find_primes2
  tally = (0..MAX).to_a
  @primes = []
  (0..MAX).each do |num|
    if num < 2
      tally[num] = nil
    elsif !tally[num].nil?
      @primes << num
      xnum = num
      while xnum <= MAX do
        tally[xnum] = nil
        xnum += num
      end 
    end
  end
end

# RUBY LIBRARY CALL

require 'prime'

def find_primes3
  @primes = []
  (2..MAX).each do
      |num|
    if num.prime?    
      @primes << num
    end
  end
end

Comparison of Algorithms

Parameter Algorithm 1 Algorithm 2 Algorithm 3
Method used Test for all possible factors Cross multiples off a tally Compare to list of prime numbers
Origin User-defined User-defined Ruby Class
Time Required (msec) 6.944 0.053 0.077
Memory Requirements (bytes) 5 400 100
Program complexity 40 18 15
Programming effort (min) 8 5 3
Lines of psuedocode 6 6 4
Lines of code 14 15 8

Digital Memory

\[\begin{matrix} Bit & 7 & 6 & 5 & 4 & 3 & 2 & 1 & 0\\ \hline HexDec& x80 & x40 & x20 & x10 & x08 & x04 & x02 & x01\\ 2^n &128 & 64 & 32 & 16 & 8 & 4 & 2 & 1\\ \rightarrow & 0 & 1& 1 & 0 & 0& 1 & 1 & 0 & = & 102\\ \hline BCD & 8 & 4 & 2 & 1& 8 & 4 & 2 & 1\\ \rightarrow & 0 & 1& 1 & 0 & 0& 1 & 1 & 0 & = & 66\\ \hline \end{matrix}\]

Binary numbers

Decimal vs Hexidecimal

\[\begin{matrix} \hline xx \rightarrow & 00 & 01 & 10 & 11 & | & 00 & 01 & 10 & 11\\ \hline xx00 & 0 & 4 & 8 & 12 & | & 0 & 4 & 8 & C\\ xx01 & 1 & 5 & 9 & 13 & | &1 & 5 & 9 & D\\ xx10 & 2 & 6 &10 & 14 & | &2 & 6 &A & E \\ xx11 & 3 & 7 &11 & 15 & | &3 & 7 &B & F\\ \hline \end{matrix}\]

Big-endian vs Little-endian orientation

\[Given\ a\ 64\ bit\ number:\ 0xFEDCBA9876543210\]

\[\rm\eqalign{Byte:&\ \ 0\quad 1\quad 2\quad \ 3\quad 4\quad 5\quad 6\quad 7\quad 8\quad\\ Big-Endian: &\fbox{FE}\fbox{DC}\fbox{BA}\fbox{98}\fbox{76}\fbox{54}\fbox{32}\fbox{10}\\ &most\ significant\ byte\ first\\ Little-Endian:& \fbox{01}\fbox{23}\fbox{45}\fbox{67}\fbox{89}\fbox{AB}\fbox{CD}\fbox{EF}\\ & most\ significant\ byte\ last\\ }\]

Negative Integer in 3 steps

  1. Represent the absolute value in binary

\[\abs(-11) \rightarrow 00001011\]

  1. Invert the bits

\[00001011 \rightarrow 11110100\]

  1. Add one to the result

\[11110100 + 00000001 \rightarrow 11110101\]

Numbers

Short Integer:  32 bit, -2,147,483,648 to 2,147,483,647 \(\tiny \fbox{........|........|........|........}\)

Long Integer: 64 bit, -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

\(\tiny \fbox{........|........|........|........|........|........|........|........}\)

Rational Number: 128 bit (2x64 bit) largest number: 9,223,372,036,854,775,807 smallest number: 0.0000000000000000000108

\(\tiny {Numerator:\ } \fbox{........|........|........|........} {,\ \ Denominator:\ } \fbox{........|........|........|........}\)

Floating Number: 96 bit * Max value: 1.7976931348623157e+308 * Smallest value: 2.2250738585072014e-308

  • Display digits (Precision): 15
  • Mantissa digits: 53
  • Expotential digits: 12
  • Maximum number: 1.7976931348623157e+308
  • Minimal number: 2.2250738585072014e-308

puts\(\tiny {Mantissa: }\tiny\fbox{....|........|........|........|........|........|........}\ \ \ {Exponent: } \fbox{....|........|........}\)

Bignum: linked numerical structures no limits

2.size => 4 bytes

88888888888888888888888888888888888888888888888.size => 20 bytes

Interpretation

\[\large 14103_{10}\]

Mode Values
Binary: 1 1 0 1 1 1 0 0 0 1 0 1 1 1
Base 4: 11 01 11 00 01 01 11
Base 8: 11 011 100 010 111
Base 16: 11 0111 0001 0111
Base 32: 1101 11000 10111

BCD

BCD Arithmetric

Addition with adjustment

\[\eqalign{ Number\ A: & 1&9&5&3\\ Number\ B: & 0&5&4&3\\ === Sum & 1&e&9&6\\ Adjusted: & 2&4&9&6\\ }\]

Subtraction

\[\eqalign{ Number\ A: & 2&0&0&0\\ Adjusted A: & 1&9&9&a\\ Number\ B: & 0&5&4&3\\ Diff: &1&4&3&7\\ }\]

Big Numbers

x = 2
200.times do |y|
  GC.start
  t = Time.now
  10000.times do
    x *= 2
  end
  puts "#{Time.now - t}, #{#{x.size}}"
end

Ruby installation

https://www.ruby-lang.org/en/downloads/

  • Install Ruby
  • Install robocop
  • Notepad++

Ruby Cheatsheet

Basics .{scrollable}

  • $ irb: to write ruby in the terminal
  • don’t use ' in ruby, use " instead
  • you can replace most {} with do end and vice versa –– not true for hashes or #{} escapings
  • Best Practice: end names that produce booleans with question mark
  • CRUD: create, read, update, delete
  • [1,2].map(&:to_i)
  • integer: number without decimal
  • float: number with decimal
  • tag your variables:
    • $: global variable
    • @: instance variable
    • @@: class variable
  • 1_000_000: 1000000 –– just easier to read

Vars, Constants, Arrays, Hashes & Symbols

my_variable = “Hello”
my_variable.capitalize! # ! changes the value of the var same as my_name = my_name.capitalize
my_variable ||= "Hi" # ||= is a conditional assignment only set the variable if it was not set before.

Constants

MY_CONSTANT = # something

Arrays

my_array = [a,b,c,d,e]
my_array[1] # b
my_array[2..-1] # c , d , e
multi_d = [[0,1],[0,1]]
[1, 2, 3] << 4 # [1, 2, 3, 4] same as [1, 2, 3].push(4)

Hashes

hash = { "key1" => "value1", "key2" => "value2" } # same as objects in JavaScript
hash = { key1: "value1", key2: "value2" } # the same hash using symbols instead of strings
my_hash = Hash.new # same as my_hash = {} – set a new key like so: pets["Stevie"] = "cat"
pets["key1"] # value1
pets["Stevie"] # cat
my_hash = Hash.new("default value")
hash.select{ |key, value| value > 3 } # selects all keys in hash that have a value greater than 3
hash.each_key { |k| print k, " " } # ==> key1 key2
hash.each_value { |v| print v } # ==> value1value2

my_hash.each_value { |v| print v, " " }
# ==> 1 2 3

Symbols

:symbol # symbol is like an ID in html. :symbol != "symbol"
# Symbols are often used as Hash keys or referencing method names.
# They can not be changed once created. They save memory (only one copy at a given time). Faster.
:test.to_s # converts to "test"
"test".to_sym # converts to :test
"test".intern # :test
# Symbols can be used like this as well:
my_hash = { key: "value", key2: "value" } # is equal to { :key => "value", :key2 => "value" }

Functions to create Arrays

"bla,bla".split(“,”) # takes string and returns an array (here  ["bla","bla"])

Methods

Methods

def greeting(hello, *names) # *names is a split argument, takes several parameters passed in an array 
  return "#{hello}, #{names}"
end

start = greeting("Hi", "Justin", "Maria", "Herbert") # call a method by name

def name(variable=default)
  ### The last line in here gets returned by default
end

Classes

custom objects

class Person # class names are rather written in PascalCase (It is similar to camelCase, but the first letter is capitalized)
  @@count = 0
  attr_reader :name # make it readable
  attr_writer :name # make it writable
  attr_accessor :name # makes it readable and writable

  def Methodname(parameter)
    @classVariable = parameter
    @@count += 1
  end

  def self.show_classVariable
    @classVariable
  end

  def Person.get_counts # is a class method
    return @@count
  end

  private

  def private_method; end # Private methods go here
end

matz = Person.new("Yukihiro")
matz.show_name # Yukihiro

Inheritance

class DerivedClass < Base
  def some_method
    super(optional args) # 
      # Some stuff
    end
  end
end

Modules

module ModuleName 
end

Math::PI # using PI constant from Math module. 

require 'date' # to use external modules.
puts Date.today # 2016-03-18

module Action
  def jump
    @distance = rand(4) + 2
    puts "I jumped forward #{@distance} feet!"
  end
end

class Rabbit
  include Action 
  extend Action 
  attr_reader :name
  def initialize(name)
    @name = name
  end
end

peter = Rabbit.new("Peter")
peter.jump # include
Rabbit.jump # extend

Blocks & Procs

Code Blocks

Blocks are not objects. A block is just a bit of code between do..end or {}. It’s not an object on its own, but it can be passed to methods like .each or .select.

def yield_name(name)
  yield("Kim") # print "My name is Kim. "
  yield(name) # print "My name is Eric. "
end

yield_name("Eric") { |n| print "My name is #{n}. " } 
yield_name("Peter") { |n| print "My name is #{n}. " }

Proc

Saves blocks and are objects. A proc is a saved block we can use over and over.

cube = Proc.new { |x| x ** 3 }
[1, 2, 3].collect!(&cube) # [1, 8, 27]
cube.call 

Lambdas

lambda { |param| block }
multiply = lambda { |x| x * 3 }
y = [1, 2].collect(&multiply) # 3 , 6

Calculation

  • Addition (+)
  • Subtraction (-)
  • Multiplication (*)
  • Division (/)
  • Exponentiation (**)
  • Modulo (%)
  • The concatenation operator (<<)
  • you can do 1 += 1
  • "A " << "B" => "A B"
  • "A " + "B"
  • "A #{4} B" => "A 4 B"

Commenting

=begin
Bla
Multiline comment
=end
# single line comment

Conditions

If

if 1 < 2
  puts “one smaller than two”
elsif 1 > 2 
  puts “elsif”
else
  puts “false”
end

puts "be printed" if true
puts 3 > 4 ? "if true" : "else" 

Unless

unless false
  puts “I’m here”
else
  puts “not here”
end

puts "not printed" unless true

Case

case my_var
  when "some value"
    ###
  when "some other value"
    ###
  else
    ###
end

or

case my_var
  when "some value" then ###
  when "some other value" then ###
  else ###
end

Logical functions

  • &&: and
  • ||: or
  • !: not
  • (x && (y || w)) && z: use parenthesis to combine arguments
  • problem = false
  • print “Good to go!” unless problem

Printing & Putting

print "bla"
puts "test" # puts the text in a separate line

String Methods

"Hello".length # 5
"Hello".reverse # “olleH”
"Hello".upcase # “HELLO”
"Hello".downcase # “hello”
"hello".capitalize # “Hello”
"Hello".include? "i" # equals to false because there is no i in Hello
"Hello".gsub!(/e/, "o") # Hollo
"1".to_i # transform string to integer –– 1
"test".to_sym # converts to :test
"test".intern # :test
:test.to_s # converts to "test"

User Input

gets # reads a line
gets.chomp # removes new line char

Loops

While loop

i = 1
while i < 11
  puts i
  i = i + 1
end

Until loop

i = 0
until i == 6
  puts i
  i += 1
end

For loop

for i in 1...10 
  puts i
end

Loop iterator

i = 0
loop do
  i += 1
  print "I'm currently number #{i}” 
  break if i > 5
end

Next

for i in 1..5
  next if i % 2 == 0
  print i
end

.each

things.each do |item|
  print “#{item}"
end

on hashes like so:

hashes.each do |x,y|
  print "#{x}: #{y}"
end

.times

10.times do |i|
  print "#{i} this text will appear 10 times"
end

.upto / .downto

1.upto(5) { |x| print x, " " } # 1 2 3 4 5
"a".upto("c") { |x| print x, " " } # a b c

Sorting & Comparing

array = [5,4,1,3,2]
array.sort! # = [1,2,3,4,5] 
"a" <=> "b" # 0 if a = b, 1 if a > b, -1 if a < b
array.sort! { |a, b| b <=> a } # to sort from Z to A 

Useful Methods

Table of Contents

1.is_a? Integer # returns true
:1.is_a? Symbol # returns true
"1".is_a? String # returns true
[1,2,3].collect!() # does something to every element (overwrites original with ! mark)
.map() # is the same as .collect
1.2.floor # 1 # rounds down to the nearest integer.
cube.call # call calls procs directly
Time.now # displays the actual time

Characters

UTF -8 Bit Streaming

Scalar Value 1st Byte 2nd Byte 3rd Byte 4th Byte
00000000
0xxxxxxx
0xxxxxxx
00000yyy
yyxxxxxx
110yyyyy 10xxxxxx
zzzzyyyy
yyxxxxxx
1110zzzz 10yyyyyy 10xxxxxx
000uuuuu
zzzzyyyy
yyxxxxxx
11110uuu 10uuzzzz 10yyyyy 10xxxxxx

Examples

Char Coding

A
U+0041
\[\small\eqalign{UTF-8: & \verb|0x41| \hfil &...&\hfil 01000001 \hfil\cr UTF-16:& \verb|0x0041|\hfil & ...&\hfil00000000 01000001\hfil\cr UTF-32: &\verb|0x00000041|\hfil & ... & \hfil0000000001000001\hfil\cr}\]

ŋ
U+14B
\[\small\eqalign{UTF-8:& \verb|0xC5 0x8B|&... &\hfil11000101 10001011\hfil\cr UTF-16:& \verb|0x014B|\hfil& ... &\hfil0000000101001011\hfil\cr UTF-32:& \verb|0x0000014B| \hfil &...& \hfil0000000101001011\hfil\cr}\]


U+E01
\[\small\eqalign{UTF-8:& \verb|0xE0 0xB8 0x81|\hfil &...&\hfil11100000\ 10111000\ 10000001\hfil\cr UTF-16:& \verb|0x0E01| \hfil &...&\hfil00001110\ 00000001\hfil\cr UTF-32:& \verb|0x00000E01|\hfil &...&\hfil0000111000000001\hfil\cr}\]

က
U+1000
\[\small\eqalign{UTF-8:&\verb|0xE1 0x80 0x80|\hfil&...&\hfil11100001\ 1000000\ 100000000\hfil\cr UTF-16:& \verb|0x1000| \hfil&...&\hfil00010000\ 00000000\hfil\cr UTF-32:& \verb|0x00001000|\hfil&...&\hfil0001000000000000\hfil\cr}\]


U+2602
\[\small\eqalign{UTF-8:& \verb|0xE2 0x98 0x82|\hfil&... &\hfil11100010\ 10011000\ 10000010\hfil\cr UTF-16:& \verb|0x2602|\hfil&...&\hfil 00100110\ 00000010\hfil\cr UTF-32:& \verb|0x00002602|\hfil&...&\hfil0010011000000010\hfil\cr}\]


U+4EBA
\[\tiny\eqalign{UTF-8: & \verb|0xE4 0xBA 0xBA|\hfil\ &...&\hfil11100100\ 10111010 10111010\hfil\cr UTF-16:& \verb|0x4EBA|\ \hfil&...&\hfil 01001110\ 10111010\hfil\cr UTF-32:& \verb|0x00004EBA|\hfil\ &...& \hfil 0100111010111010\hfil\cr}\]

你
U+2F804
\[\small\eqalign{UTF-8: & \verb|0xF0 0xAF 0xA0 0x84|\hfil\ &...&\hfil11110000\ 10101111\ 10100000\ 10000100\hfil\cr UTF-16:& \verb|0xD87E 0xDC04|\hfil&...&\hfil1101100001111110\ 1101110000000100\hfil\cr UTF-32: & \verb|0x0002F804|\hfil&...&\hfil0000000000000010\ 1111100000000100\hfil\cr}\]

String

Literal

     a = 'apple'
     b = 'banana'

Dynamic

     c = 'I love ' + a.pluralize + ' and ' +
        b.pluralize + '.'
        
     d = <<EOM # Hereis document
 I love #{a.pluralize} and #{b.pluralize}.
 EOM
 
     e = "I love #{a.pluralize} and #{b.pluralize}."


     f =  "I love %s and %s." %
             [a.pluralize, b.pluralize]