Unit 0:
2024-07-12
Decision making/Planning stages
-> Primitive types, arrays, records, string and string processing, data representation in memory, pointers and references, linked structures, knowledge of hashing function, use of stacks and queues, use of graphs and trees, and strategies for choosing the right data structure
-> This course provides a comprehensive introduction to fundamental data structures and their implementations. Students will gain a solid understanding of how data is organized and manipulated within computer systems, and how to select appropriate data structures for different problem-solving scenarios.
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:
\[\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 stored, retrieved, and processed.
You have 8 sec determine the following:
Are they all the same or not?
If they are not the same, identify the follow:
Which one does not belong?
What part of the trucks is different?
\[\rm Choose\ 2:\ \left\{\begin{matrix}Fast\\ Small\\ Cheap\\ \end{matrix}\right.\]
\[\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 \\ }\]
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
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
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
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
# 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
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
# TEST FOR ALL POSSIBLE FACTORS
def is_factor?(num,factor)
(num % factor).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
| 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 |
\[\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}\]
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}\]
\[\tt A\ sample \ 64\ bit\ number: (8\ bytes)\] \[\tt \ 0xFE\ DC\ BA\ 98\ 76\ 54\ 32\ 10\ \quad \small Hexadecimal \] \[\small\tt\matrix{ |\ 1111\ 1110\ |\ 1101\ 1100\ |\ 1011\ 1010\ |\ 1001\ 1000\ |\\ |\ 0111\ 0110\ |\ 0101\ 0100\ |\ 0011\ 0010\ |\ 0001\ 0000\ |\\}\quad Binary\]
\[\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}\\ &\uparrow most\ significant\ byte\ first\\ Little-Endian:& \fbox{10}\fbox{32}\fbox{54}\fbox{76}\fbox{98}\fbox{BA}\fbox{DC}\fbox{FE}\\ & \qquad most\ significant\ byte\uparrow\\ }\]
\[\abs(-11) \rightarrow 00001011\]
\[00001011 \rightarrow 11110100\]
\[11110100 + 00000001 \rightarrow 11110101\]
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
puts\(\tiny {Mantissa: }\tiny\fbox{....|........|........|........|........|........|........}\ \ \ {Exponent: } \fbox{....|........|........}\)
Bignum: linked numerical structures no limits
2.size => 4 bytes
88888888888888888888888888888888888888888888888.size => 20 bytes
#!/usr/bin/env ruby
# Demonstration of Ruby's standard numeric data types
puts "Ruby Data Types".upcase
def unitheader(header)
puts
puts header
puts "-" * 65
end
def example(lbl, op)
x = eval(op)
puts " #{lbl.ljust(12)}, #{op.ljust(27)} #{x} (#{x.class})"
end
unitheader("Integer examples:")
example("@int_a","@int_a = 42")
example("@int_b","@int_b = -100")
example("Sum:","@int_a + @int_b")
unitheader("Float examples:")
example("@float_a","@float_a=3.14159")
example("@float_b","@float_b = -0.5")
example("Product:","@float_a * @float_b")
unitheader("Rational examples:")
example("@rational_a","@rational_a = Rational(2,3)")
example("@rational_b","@rational_b = 5r")
example("Sum:","@rational_a + @rational_b")
unitheader("Complex examples:")
example("@complex_a","@complex_a = Complex(2,3)")
example("@complex_b","@complex_b = 4 + 5i")
example("Sum:","@complex_a + @complex_b")
unitheader("Type conversions:")
example("Int2Float:","@int_a.to_f")
example("Float2Int","@float_a.to_i")
example("Int2Rational","@int_a.to_r")
example("Int2Complex","@int_a.to_c")
unitheader("Automatic type promotion:")
example("Int2Float", "@int_a + @float_a")
example("Int2Rational","@int_a + @rational_a")
example("Rational2Flt","@rational_a+@float_a")
example("Complex2Int","@complex_a + @int_a")
unitheader("Edge case: very large integers")
example("@bigInt", "@big_int = 10**20")
example("inc(@bigInt)","@big_int += 1")
puts "-" * 65RUBY DATA TYPES
Integer examples:
-----------------------------------------------------------------
@int_a , @int_a = 42 42 (Integer)
@int_b , @int_b = -100 -100 (Integer)
Sum: , @int_a + @int_b -58 (Integer)
Float examples:
-----------------------------------------------------------------
@float_a , @float_a=3.14159 3.14159 (Float)
@float_b , @float_b = -0.5 -0.5 (Float)
Product: , @float_a * @float_b -1.570795 (Float)
Rational examples:
-----------------------------------------------------------------
@rational_a , @rational_a = Rational(2,3) 2/3 (Rational)
@rational_b , @rational_b = 5r 5/1 (Rational)
Sum: , @rational_a + @rational_b 17/3 (Rational)
Complex examples:
-----------------------------------------------------------------
@complex_a , @complex_a = Complex(2,3) 2+3i (Complex)
@complex_b , @complex_b = 4 + 5i 4+5i (Complex)
Sum: , @complex_a + @complex_b 6+8i (Complex)
Type conversions:
-----------------------------------------------------------------
Int2Float: , @int_a.to_f 42.0 (Float)
Float2Int , @float_a.to_i 3 (Integer)
Int2Rational, @int_a.to_r 42/1 (Rational)
Int2Complex , @int_a.to_c 42+0i (Complex)
Automatic type promotion:
-----------------------------------------------------------------
Int2Float , @int_a + @float_a 45.14159 (Float)
Int2Rational, @int_a + @rational_a 128/3 (Rational)
Rational2Flt, @rational_a+@float_a 3.8082566666666664 (Float)
Complex2Int , @complex_a + @int_a 44+3i (Complex)
Edge case: very large integers
-----------------------------------------------------------------
@bigInt , @big_int = 10**20 100000000000000000000 (Integer)
inc(@bigInt), @big_int += 1 100000000000000000001 (Integer)
-----------------------------------------------------------------
https://www.ruby-lang.org/en/downloads/
$ irb: to write ruby in the terminal' in ruby, use " instead{} with do end and vice versa –– not true for hashes or #{} escapings[1,2].map(&:to_i)integer: number without decimalfloat: number with decimal$: global variable@: instance variable@@: class variable1_000_000: 1000000 –– just easier to readmy_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.
MY_CONSTANT = # something
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)
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
: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" }
"bla,bla".split(“,”) # takes string and returns an array (here ["bla","bla"])
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
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
class DerivedClass < Base
def some_method
super(optional args) #
# Some stuff
end
end
end
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 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}. " }
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
lambda { |param| block }
multiply = lambda { |x| x * 3 }
y = [1, 2].collect(&multiply) # 3 , 6
"A " << "B" (yields) "A B"# single line comment
=begin
This is indeed
a multiline comment
=end
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 false
puts “I’m here”
else
puts “not here”
end
puts "not printed" unless true
case my_var
when "some value"
###
when "some other value"
###
else
###
end
case my_var
when "some value" then ###
when "some other value" then ###
else ###
end
&&: and||: or!: not(x && (y || w)) && z: use parenthesis to combine argumentsputs "first line" # puts text in a separate line
print "blank"
puts "test" # Both both on a single line
"Hello".length # 5
"Hello#{123}" # Hello123
"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"
gets # reads a line
gets.chomp # removes new line char
i = 1
while i < 11
puts i
i = i + 1
end
i = 0
until i == 6
puts i
i += 1
end
for i in 1...10
puts i
end
i = 0
loop do
i += 1
print "I'm currently number #{i}”
break if i > 5
end
for i in 1..5
next if i % 2 == 0
print i
end
things.each do |item|
print “#{item}"
end
on hashes like so:
hashes.each do |x,y|
print "#{x}: #{y}"
end
10.times do
print “this text will appear 10 times”
end
1.upto(5) { |x| print x, " " } # 1 2 3 4 5
"a".upto("c") { |x| print x, " " } # a b c
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
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
IT212 Data Structure