30 Days of Code Tutorial
30 Days of Code Tutorial
At its most basic level, a class is a collection of variables (fields) and functions
called methods. A program is a collection of classes. The basic code for declaring a
Java class is as follows:
class MyClass{
// This is a single-line comment.
Note: Class names cannot begin with numbers or contain any spaces.
Variable
Think of this as a name (identifier) that points to (references) a location in memory
where information associated with that name can be stored. In Java (and many other
languages), it is a best practice to always start variable names with a lowercase letter
and use CamelCase for variable names composed from compound phrases.
Variable names cannot contain spaces or special characters (except underscores),
though they can contain (but not begin with) numbers. A variable that is a member
of a class is called a field.
Each variable has a data type associated with it, which essentially restricts what that
variable is allowed to reference. This means your code will not work if you attempt
to perform operations on your variables that aren't allowed for that data type.
To declare a variable named having the data type DataType, we write the following:
DataType myVariable;
In English, the above code is basically saying: "I'm creating a variable named; it refers
to something of type DataType, and is assigned an initial value of .”
The compiler will interpret the characters between the two quotation marks as
a String. Saving a reference to it as variable myString allows us to refer to it again and
again by referencing our variable name, myString.
Note: Some coders use lowercase letters in conjunction with underscores to simulate
spaces when declaring variables (e.g.: "my_variable"). This is a style called "lower
snake case" and is not the naming convention used in Java, though there are many
other languages where you might see this used frequently (e.g.: C, C++, Python, etc.);
however, you may see some Java coders begin certain special variable names (e.g.:
private class variables or constants) with an underscore to distinguish them from
other variables used throughout their program.
Function
A sequence of packaged instructions that perform a task.
Method
In Object-Oriented programming, a method is a type of function that operates on
the fields of a class.
int myMethod(){
// ...does cool stuff.
}
void myMethod(int myInt){
// ...does cool stuff.
}
Check out Oracle's Method documentation to learn more.
Object
An Object is an instance (or variable) of a class.
Stream
Think of this as the flow of data from one place to another. Most of our
challenges require you to read input from System.in (also known as stdin,
the standard input stream), and write output to System.out (also known
as stdout, the standard output stream).
In Java, the Scanner class is widely used to read input, but each language
has its own mechanism for handling IO (input and output).
The syntax for reading from stdin using the Scanner class is as follows:
scan.close();
Let's say we want to assign a value received from stdin to some String that we'll
name , and then print it. We can accomplish this with the following code:
If the input token is Hi!, the above code will print Hi!.
You can also print text in quotes using System.out.println, or combine
quoted text with a variable
(e.g.: System.out.println("Input received: " + s);).
Day 1: Data Types
Terms you'll find helpful in completing today's challenge are outlined below, along
with sample Java code (where appropriate).
Data Types
Data types define and restrict what type values can be stored in a variable, as well as
set the rules for what types of operations can be performed on it.
Scanner
Yesterday, we discussed Scanner's next, nextLine, hasNext and
hasNextLine methods.
Scanner also has readNext and hasNext methods for different data types, which is
very helpful when you know exactly what type of input you'll be reading.
The next methods scan for tokens (you can think of this as a word), and
the nextLine methods reads from the Scanner's current location until the
beginning of the next line.
For example, nextInt() will scan the next token of input as an int,
and nextDouble() will scan the next token of input as a double. You should only
ever use 1 scanner object for your entire program.
Each line of multi-line input contains an invisible separator indicating that
the end of a line of input has been reached. When you use Scanner
functions that read tokens (e.g.: next(), nextInt(), etc.), the Scanner reads
and returns the next token.
When you read an entire line i.e.: readLine(), it reads from the current
position until the beginning of the next line. Because of this, a call
to nextLine() may return an empty string if there are no characters
between the end of the last read and the beginning of the next line.
Note: You will struggle with this challenge if you do not review this section. You
must understand what happens when you switch between reading a token (single
word) of input and reading an entire line of input to successfully complete this
challenge.
Additive Operator
The + operator is used for mathematical addition and String concatenation (i.e.:
combining two Strings into one new String). If you add the contents of two variables
together (e.g.: a + b), you can assign their result to another variable using
the assignment operator (=).
You can also pass the result to a function instead of assigning it to a variable; for
example, if a=1 and b=2, System.out.println(a + b); will print 3 on a new line.
C++
You may find this information helpful when completing this challenge in C++.
To consume the whitespace or newline between the end of a token and the
beginning of the next line:
if (getline(cin >> ws, s2)) { // eat whitespace
getline(cin, s2);
}
where s2 is a string. In addition, you can specify the scale of floating-point
output with the following code:
#include <iostream>
#include <iomanip>
Operators
These allow you to perform certain operations on your data. There are 3 basic types:
1. Unary: operates on 1 operand
2. Binary: operates on 2 operands
3. Ternary: operates on 3 operands
Arithmetic Operators
The binary operators used for arithmetic are as follows:
+: Additive
-: Subtraction
*: Multiplication
/: Division
%: Remainder (modulo)
Additional Operators
+: A binary operator used for String concatenation
++: This unary operator is used to pre-increment (increment by 1
before use) when prepended to a variable name or post-
increment (increment by 1 after use) when appended to a variable.
--: This unary operator is used to pre-decrement (decrement by 1
before use) when prepended to a variable name or post-
decrement (decrement by 1 after use) when appended to a variable.
!: This unary operator means not (negation). It's used before a variable
or logical expression that evaluates to true or false.
==: This binary operator is used to check the equality of 2 primitives.
!=: This binary operator is used to check the inequality of 2 primitives.
<, >, <=, >=: These are the respective binary operators for less
than, greater than, less than or equal to, and greater than or equal to,
and are used to compare two operands.
&&, ||: These are the respective binary operators used to
perform logical AND and logical OR operations on two boolean (i.e.:
true or false) statements.
? : This ternary operator is used for simple conditional statements (i.e.:
if ? then : else).
Boolean
A logical statement that evaluates to true or false. In some languages, true is
interchangeable with the number and false is interchangeable with the number 0.
Conditional Statements
These are a way of programming different workflows depending on some boolean
condition. The if-else statement is probably the most widely used conditional in
programming, and its workflow is demonstrated below:
The basic syntax used by Java (and many other languages) is:
if(condition) {
// do this if 'condition' is true
}
else {
// do this if 'condition' is false
}
where condition is a boolean statement that evaluates to true or false.
You can also use an if without an else, or follow an if(condition) with else
if(secondCondition) if you have a second condition that only need be checked
when condition is false.
If the if (or else if) condition evaluates to true, any other sequential statements
connected to it (i.e.: else or an additional else if) will not execute.
Logical Operators
Customize your condition checks by using logical operators. Here are the three to
know:
|| is the OR operator, also known as logical disjunction.
&& is the AND operator, also known as logical conjunction.
! is the NOT operator, also known as negation.
v = c ? a : b;
In other words, you can read c ? a : b as "if is true, then a; otherwise, b".
Whichever value is chosen by the statement is then assigned to ‘v’.
Switch Statement
This is a great control structure for when your control flow depends on a number
of known values. Let's say we have a variable, condition, whose possible values
are val0, val1, val2, and each value has an action to perform (which we will call some
variant of behaviour). We can switch between actions with the following code:
switch (condition) {
case val0: behavior0;
break;
case val1: behavior1;
break;
case val2: behavior2;
break;
default: behavior;
break;
}
Note: Unless you include break; at the end of each case statement, the statements will
execute sequentially. Also, while it's good practice to include a default: case (even if it's
just to print an error message), it's not strictly necessary.
Day 4: Class vs. Instance
Terms you'll find helpful in completing today's challenge are outlined below, along
with sample Java code (where appropriate).
Class
A blueprint defining the charactaristics and behaviors of an object of that class type.
Class names should be written in CamelCase, starting with a capital letter.
class MyClass{
...
}
Each class has two types of variables: class variables and instance variables; class
variables point to the same (static) variable across all instances of a class, and instance
variables have distinct values that vary from instance to instance.
Class Constructor
Creates an instance of a class (i.e.: calling the Dog constructor creates an instance of
Dog). A class can have one or more constructors that build different versions of the
same type of object. A constructor with no parameters is called a default constructor;
it creates an object with default initial values specified by the programmer.
A constructor that takes one or more parameters (i.e.: values in parentheses) is
called a parameterized constructor.
Many languages allow you to have multiple constructors, provided that each
constructor takes different types of parameters; these are called overloaded
constructors.
Method
A sort of named procedure associated with a class that performs a predefined action.
In the sample code below, returnType will either be a data type or void if no value
need be returned. Like a constructor, a method can have 0 or more parameters.
Most classes will have methods called getters and setters that get (return) or set the
values of its instance variables. Standard getter/setter syntax:
class MyClass{
dataType instanceVariable;
...
void setInstanceVariable(int value){
this.instanceVariable = value;
}
dataType getInstanceVariable(){
return instanceVariable;
}
}
Structuring code this way is a means of managing how the instance variable is
accessed and/or modified.
Parameter
A parenthetical variable in a function or constructor declaration (e.g.: in int
methodOne(int x), the parameter is int x).
Argument
The actual value of a parameter (e.g.: in methodOne(5), the argument passed as
variable x is 5).
Day 5: Loops
For Loop
This is an iterative loop that is widely used. The basic syntax is as follows:
The termination component is the condition which, once met, you would like to exit
(or break) the loop and proceed to the next line in your code. This is the ending
point for your loop, and is typically written as i < endValue, where ‘i’ is the variable
from the initialization section and endValue is some variable holding the stopping
point for your iteration.
The increment component is executed each time the end of the code inside the
loop's brackets is reached, and should generally be some modification on the
initialization variable that brings it closer to the termination variable. This will
typically be i++. The ++ operator is also called the post-increment operator, and it
will increment a variable by ‘1’ after a line executes (for more detail and an example,
see the While section).
While Loop
This type of loop requires a single boolean condition and continues looping as long
as that condition continues to be true. Each time the the end of the loop is reached,
it loops back to the top and checks if the condition is still true. If it's true, the loop
will run again; if it's false, then the program will skip over the loop and continue
executing the rest of the code.
Much like in the For section, the code below prints the numbers 0 through 3. Notice
that we are using the post-increment operator on ‘min’:
int min = 0;
int max = 4;
while(min < max){
System.out.println(min++);
}
Once ‘min >= max’ , the boolean condition ( min < max ) evaluates to false and the
loop is broken. The line System.out.println(min++); is a compact way of writing:
System.out.println(min);
min = min + 1;
Do-While Loop
This is a variation on the While loop where the condition is checked at the end of
the brackets. Because of this, the content between the brackets is guaranteed to
always be executed at least once:
do{
// this will execute once
// it will execute again each time while(condition) is true
} while(condition);
Unlabeled Break
You may recall break; from our previous discussion of Switch Statements. It will
break you out of a loop even if the loop's termination condition still holds true.
Observe the (int) before the variable name in the code above. This is called explicit
casting, which is a method of representing one thing as another. Putting a data type
inside parentheses right before a variable is essentially saying: "The next thing after
this should be represented as this data type".
Casting only works for certain types of relationships, such as between primitives
or objects that inherit from another class.
To break a String down into its component characters, you can use
the String.toCharArray method. For example, this code:
Day 7: Arrays
Data Structures
A way of organizing data that enables efficient storage, retrieval, and use.
Arrays
A type of data structure that stores elements of the same type (generally). It's important to note that
you'll often see arrays referred to as A in documentation, but the variable names you use when
coding should be descriptive and begin with lowercase letters.
You can think of an array, A, of size n as a contiguous block of cells sequentially
indexed from 0 to n-1 which serve as containers for elements of the array's declared
data type. To store an element, value, in some index i of array A, use the
syntax A[i] and treat it as you would any other variable (i.e., A[i] = value;). For
example, the following code:
Most languages also have a method, attribute, or member that allows you to retrieve
the size of an array. In Java, arrays have a length attribute; in other words, you can
get the length of some array, arrayName, by using the arrayName.length syntax.
Note: The final keyword used in the code above is a means of protecting the
variable's value by locking it to its initialized value. Any attempt to reassign
(overwrite) the value of a final variable will generate an error.
Day 8: Dictionaries and Maps
Java Maps
Map is an interface that provides a blueprint for data structures that take (key,
value) pairs and map keys to their associated values (it's important to note that both
the key and the value must be Objects and not primitives). The implementation is
done by implementing classes such as HashMap or LinkedHashMap. Consider the
following code:
// Declare a String to String map
Map<String, String> myMap;
Here are a few Map methods you will find helpful for this challenge:
containsKey(Object key): Returns true if the map contains a mapping
for key; returns false if there is no such mapping.
get(Object key): Returns the value to which the key is mapped;
returns null if there is no such mapping.
put(K key, V value): Adds the (Key, Value) mapping to the Map; if
the key is already in the map, the value is overwritten.
Example (Java)
The code below:
// Create a Map of String Keys to String Values, implemented by the Has
hMap class
Map<String,String> myMap = new HashMap<String,String>();
The diagram below depicts the execution of the code above. Each call
to nTimesK is represented by a bubble, and each new recursive call bubble is
stacked inside and on top of the bubble that was responsible for calling it. The
function recursively calls itself using reduced values until it reaches the base case (n =
1).
Once it reaches the base case, it passes back the base case's return value (k = 4) to
the bubble that called it and continues passing back k + the previously returned value
until the final result (i.e.: the multiplication by addition result of n * k) is returned.
Day 11: 2D Arrays
2D Arrays
Also known as multidimensional arrays, they are very similar to the regular 1D
Array data structure we've already discussed.
Consider the following code:
int rowSize = 2;
int colSize = 4;
int[][] myArray = new int[rowSize][colSize];
This creates a 2 x 4 matrix where each element, (i, j), can be graphically represented
as follows:
(0, 0) (0, 1) (0, 2) (0, 3)
(1, 0) (1, 1) (1, 2) (1, 3)
You may find it helpful to think of these (i, j) elements in terms of real-world
structures such as the cells in a spreadsheet table.
To fill the array's cells with values, you can use a nested loop. The outer loop
represents the matrix's rows and uses i as its variable, and the inner loop represents
the matrix's columns and uses j as its variable. The code below assigns the value
of count to each element in the2D array we declared previously:
int count = 0;
Subclass
A subclass is defined with the extends keyword. For example, the syntax ClassB
extends ClassA establishes ClassB as a subclass of ClassA.
Java only supports single inheritance, meaning a subclass cannot extend more than
one superclass.
Subclass Constructors
Because a constructor initializes an instance of a class, they are never inherited;
however, the subclass must call a superclass constructor as it is an extension of a
superclass object. This can be done in either of the two ways shown below.
Consider the following class:
class MySuperclass{
// superclass instance variable:
String myString;
Note: If a superclass does not have a default constructor, any subclasses extending
it must make an explicit call to one of the superclass' parameterized constructors.
Overriding Methods
When overriding a method, it is best practice to precede the method with
the @Override annotation. This signifies to both the reader and the compiler that
this method is overriding an inherited method, and will also help you check your
work by generating a compiler error if no such method exists in the superclass.
Method overriding is demonstrated in the example below.
Example
Let's say a not-for-profit organization has an Employee class, and each instance of
the Employee class contains the name and salary for an employee. Then they decide
that they need a similar-yet-different way to store information about volunteers, so
they decide to write a Volunteer class that inherits from Employee. This is beneficial
because any fields and methods added to Employee will also be accessible
to Volunteer.
Take some time to review the code below. Observe that the Volunteer class calls the
superclass' getName method, but overrides its print method.
import java.util.Locale;
import java.text.NumberFormat;
class Employee {
// instance variables:
protected String name;
private int salary;
/** @param salary The integer to set as the salary instance variable. **/
void setSalary(int salary){
this.salary = salary;
}
@Override
/** Overrides the superclass' print method and prints information abou
t an instance of Volunteer. **/
void print(){
System.out.println("Volunteer Name: " + this.getName()
+ "\nHours: " + this.getHours());
}
}
Error: No salary set for Erica; please set salary and try again.
One real world example of this concept is a snack machine, where you give the
machine money, make a selection, and the machine dispenses the snack. The only
thing that matters is what the machine does (i.e.: dispenses the selected snack); you
can easily buy a snack from any number of snack machines without knowing how the
machine's internals are designed (i.e.: the implementation details).
Abstract Class
This type of class can have abstract methods as well as defined methods, but it
cannot be instantiated (meaning you cannot create a new instance of it).
The Java code below demonstrates an abstract Canine class and 2 of its canine breed
subclasses, KleeKai and SiberianHusky:
/** Superclass **/
abstract class Canine{
// instance variables
String name;
String color;
String gender;
int age;
The Canine class has 1 abstract method, abstract void getBreed(), and 1 defined
method, void printInfo(). Because an abstract class is not fully defined, attempting to
instantiate it like so:
Canine myPuppy = new Canine("Lilah", "Grey/White", 5, 'F');
results in error: Canine is abstract; cannot be instantiated. This type of class is only
meant to serve as a base or blueprint for connecting the subclasses that inherit
(extend) it.
While we can't instantiate Canine, we can instantiate its
subclasses, KleeKai and SiberianHusky.
This code:
Canine c = new KleeKai("Lilah", "Grey/White", 5, 'F');
Canine d = new SiberianHusky("Alaska", "Grey/Black/White", 16, 'F');
c.printInfo();
d.printInfo();
Note: When dealing with a class variable (field), it's always best to explicitly refer to it
using the this keyword. For example:
class MyClass{
private int myInt;
Even though there is a myInt field in the class, the constructor has a completely
different myInt parameter. The field (this.myInt) is then assigned the value of the
parameter, so any argument passed as that parameter will initialize the myInt field in
the class.
Still confused? Take some time to review the Scope class below and understand why
variables with the same name will be of different types (and produce different
outputs) at various points in the program:
Scope(){
double d = 9.0;
example(d);
classVariable();
}
void classVariable(){
System.out.println( "----- classVariable():\n"
+ "Instance Variable `b`: " + b + "\n"
+ "Instance Variable `x`: " + x);
}
The sample code below demonstrates how to create a LinkedList of Strings, and
some of the operations that can be performed on it.
Java: Integer.parseInt(token)
C++: stoi(token)
Python: int(token)
Ruby: Integer(token)
Each of these functions will raise an error (i.e.: throw an exception) when the
argument passed as the token parameter cannot be converted to an integer. Most
languages have built-in exception handling methods such as try-catch or begin-
rescue that enable you to code specific behaviors to handle execution issues.
Exceptions
If your code attempts to perform an action that cannot be completed, the flow of
control is halted and an exception is thrown. This means that an Exception object is
created as a response to this unusual condition. The control flow is then transferred
(or handed off) to an exception handler. By anticipating and writing handlers for
exceptional conditions in your program's logic, you can resolve the issue that raised
the exception and your program can continue executing. A program "crash" is
generally the result of an unhandled exception.
catch(Exception e){
// write exception handling logic here
}
Part of writing good code is knowing, circumventing, and anticipating exactly what
type of exceptions your instructions might throw, but if your parameter is of type
Exception, it will catch any exception that is a subclass of Exception. If the code in
your try block has the potential to throw more than one type of exception, you can
have multiple catch blocks to catch each type of anticipated exception.
class Solution{
LinkedList<String> list;
int[] intArray = new int[4];
try{
// throws ArrayIndexOutOfBoundsException if index > intArray.length
int myInt = intArray[i];
// creates a Solution object whose 'list' instance variable points to a list containin
g 1 element ("x"):
Solution sNotNullList = new Solution("x");
// attempt to access an invalid index of 'intArray' instance variable, throws Excep
tion
sNotNullList.exceptionDemo(100, "x");
}
}
Produces the following output:
Oh no! You tried to perform an operation on an object whose value is null!
This is printing regardless of whether or not the program finishes executing.
The program was able to continue execution!
class PropagatedException {
The java.util package has a Stack class that implements these methods; check out the
documentation (linked above) on the peek(), push(object), and pop() methods.
Queues
A queue is a data structure that uses a principle called First-In-First-Out (FIFO),
meaning that the first object added to the queue must be the first object removed
from it. You can analogize this to a checkout line at a store where the line only
moves forward when the person at the head of it has been helped, and each person
in the line is directly behind the person whose arrival immediately preceded theirs.
At minimum, any queue, q, should be able to perform the following two operations:
Enqueue: Add an object to the back of the line.
Dequeue: Remove the object at the head of the line and return it; the element
that was previously second in line is now at the head of the line.
The java.util package has a Queue interface that can be implemented by a number
of classes, including LinkedList. Much like abstract classes, interfaces cannot be
instantiated so we must declare a variable of type Queue and initialize it to reference
a new LinkedList object.
Check out the documentation (linked above) on the add(object)(enqueue)
and remove() (dequeue) methods.
Example
Consider a polygon. How do we interact with polygons? What properties are
common among polygons? Take some time to review the simple Polygon interface
below, as well as the classes that implement it.
/**
* This is a collection of methods we expect and require a polygon to have
**/
interface Polygon{
/** @return The number of sides of the Polygon **/
int getNumberOfSides();
/** @return The perimeter of the Polygon **/
double getPerimeter();
}
class Triangle implements Polygon {
private static int numberOfSides = 3;
private double side1;
private double side2;
private double side3;
class Solution{
public static void print(Polygon p){
System.out.println( "A " + p.getClass().getSimpleName() + " has " + p.getNumber
OfSides() + " sides." );
System.out.println( "The perimeter of this shape is: " + p.getPerimeter() + '\n');
}
}
}
Example (Java)
class Sorting {
private static void printArray(String s, int[] x) {
System.out.print(s + " Array: ");
for(int i : x){
System.out.print(i + " ");
}
System.out.println();
}
swapPosition = i;
} // end if
printArray("Current", x);
} // end for
endPosition = swapPosition;
} // end while
printArray("Sorted", x);
} // end bubbleSort
Sounds confusing? You've already used generics in more than one challenge!
Consider the List and Map interfaces, as well as the classes that implement them.
Their respective headings are:
The letters enclosed between angle brackets (< and >) are type parameters and, like
many things in programming, there is a convention behind them (remember,
following conventions help us write clean, readable code!). The letters below are
commonly-used generic type parameters:
E - Element
K - Key
V - Value
N - Number
T - Type (e.g.: data type)
S,U,V, etc. These are second, third, and fourth types for when T is already in
use.
Just like we pass arguments to functions and methods, we need to specify data types
for our type parameters when we instantiate generic objects. For example:
Once a data type is specified and an object is created, the specified type replaces
every occurrence of the generic type parameter in the instantiated class. The
compiler also performs strict type checking to ensure you haven't tried to do
anything not allowable for that data type (e.g.: trying to add an element
to integerList that isn't of type Integer).
Check out Oracle's tutorials on Generic Types, Generic Methods, and Type
Inference to learn more.
Day 22: Binary Search Trees
Binary Tree
Fundamentally, a binary tree is composed of nodes connected by edges (with further
restrictions discussed below). Some binary tree, t, is either empty or consists of a
single root element with two distinct binary tree child elements known as the left
subtree and the right subtree of t. As the name binary suggests, a node in a binary
tree has a maximum of 2 children.
The following diagrams depict two different binary trees:
Here are the basic facts and terms to know about binary trees:
1. The convention for binary tree diagrams is that the root is at the top, and the
subtrees branch down from it.
2. A node's left and right subtrees are referred to as children, and that node can be
referred to as the parent of those subtrees.
3. A non-root node with no children is called a leaf.
4. Some node a is an ancestor of some node b if b is located in a left or right subtree
whose root node is a. This means that the root node of binary tree t is the
ancestor of all other nodes in the tree.
5. If some node a is an ancestor of some node b, then the path from a to b is the
sequence of nodes starting with a, moving down the ancestral chain of children,
and ending with b.
6. The depth (or level) of some node a is its distance (i.e., number of edges) from
the tree's root node.
7. Simply put, the height of a tree is the number of edges between
the root node and its furthest leaf. More technically put, it's 1 +
max(height(leftSubtree), height(rightSubtree)) (i.e., one more than the
maximum of the heights of its left and right subtrees). Any node has a
height of 1, and the height of an empty subtree is -1. Because the height
of each node is 1+ the maximum height of its subtrees and an empty
subtree's height is -1, the height of a single-element tree or leaf node
is 0.
Day 23: BST Level-Order Traversal
Tree Traversal
A traversal of some binary tree, t, is an algorithm that iterates through each node
in exactly time.
InOrder Traversal
An inorder traversal of tree t is a recursive algorithm that follows the left subtree;
once there are no more left subtrees to process, we process the right subtree. The
elements are processed in left-root-right order. The basic algorithm is as follows:
inOrder(t) {
if(t is not empty) {
inOrder( left subtree of t )
process t's root element
inOrder( right subtree of t )
}
}
An inorder traversal of a binary search tree will process the tree's elements
in ascending order.
PostOrder Traversal
A postorder traversal of tree t is a recursive algorithm that follows the left and right
subtrees before processing the root element. The elements are processed in left-
right-root order.
The basic algorithm is as follows:
postOrder(t) {
if(t is not empty) {
postOrder( left subtree of t )
postOrder( right subtree of t )
process t's root element
}
}
PreOrder Traversal (DFS)
A preorder traversal of tree t is a recursive algorithm that processes the root and
then performs preorder traversals of the left and right subtrees. The elements are
processed root-left-right order.
The basic algorithm is as follows:
preOrder(t) {
if(t is not empty) {
process t's root element
preOrder( left subtree of t )
preOrder( right subtree of t )
}
}
Because a preorder traversal goes as deeply to the left as possible, it's also known as
a depth-first-search or DFS.
Regular expressions add a level of abstraction to strings that makes it easier to search
and manipulate text. While they can seem very confusing at first, the syntax for
regular expressions is pretty standard across different programming languages (so
knowledge of RegEx is universally applicable).
Character Classes
This is not a class in the traditional sense, but rather a term that refers to a set of one
or more characters that can be used to match a single character from some input
string. Here are the basic forms:
Enclosed within square brackets. Specify what you'd like your
expression to match within square brackets; for example, [a-f] will
match any lowercase a through f character.
Predefined: \ followed by a letter. For example, \d for matching digits
(0-9) or \D for matching non-digits. There are also additional
predefined character classes that are followed by a set of curly braces,
such as \p{Punct} which matches punctuation (i.e.: !"#$%&'()*+,-
./:;<=>?@[]^_`{|}~).
Some key constructs to know are:
. The period will match any character; it does not have to be a letter.
+ When appended to a character or character class, it means 'one or
more instances of the previous character'.
* When appended to a character or character class, it means 'zero or
more instances of the previous character'.
^ if this is before a character class, it means you're matching the first
character; however, if this is the first character inside a bracketed
character class, it means negation/not. For
example, ^[a].+ or ^a.+ matches any consecutive sequence of 2 or more
characters starting with the letter a, and ^[^a].+ matches any consecutive
sequence of 2 or more characters not starting with a.
$ When appended to a character or character class, it means 'ends with
the previous character'. For example, .+a$ will match a sequence of 2 or
more characters ending in a.
Java's Pattern class documentation is a really handy reference for predefined
character classes, but there are also many other RegEx resources available
throughout the internet.
For example, when printing a string containing the newline character (\n); the
backslash tells the compiler that you're writing a special instruction, and the n
immediately following it indicates that your instruction is that it be a newline
character. If your intent is to have the literal backslash character in your string, then
you must write it as \\. When writing a RegEx string in code, you need your
predefined character classes to be part of the string so you must precede them with
an additional backslash. For example, a string intended to be used as a regex
containing the \d character class must be written as "\\d".
For Java, the important classes to know are Pattern and Matcher. A Pattern object is
a compiled representation of a regular expression. To create a Pattern object, you
must invoke one of its static compile methods (usually Pattern.compile(String
regex)). You must then create a Matcher object that matches your Pattern object
(compiled RegEx) to the String you want to check. To do this, you must
invoke Pattern's matcher method on your Pattern object and assign it to a variable of
type Matcher. Once created, a Matcher object can be used to perform matching
operations.
// This will match a sequence of 1 or more uppercase and lowercase Engli
sh letters as well as spaces
String myRegExString = "[a-zA-Z\\s]+";
Notice that the ellipsis (...) at the end of the myString was not printed as part of the
match; that is because myRegExString only matches lowercase and uppercase
English letters and spaces—not punctuation. Thus, the ellipsis serves as a boundary
for the end of our matched text.
For a string with more parts, you could do something like the following code which
matches strings of one or more sequential alphanumeric characters:
String s = "Hello, Goodbye, Farewell";
Pattern p = Pattern.compile("\\p{Alpha}+");
Matcher m = p.matcher(s);
while( m.find() ){
System.out.println(m.group());
}
This loops through the string, finds each match, and prints it:
Hello
Goodbye
Farewell
String Functions
Regular expressions can be pretty confusing, especially to new coders. Fortunately,
there are still some workarounds using simpler regular expressions that you may find
a little easier to use. If you know something about the structure of the strings you'll
be working with (i.e.: that they all follow the same format), you can use a split
method (e.g.: String.split(String regex) in Java, strtok in C++, str.split() in
Python, split in Ruby, etc.). If you're able to section/cut your input string into
components, then you can choose the pieces you want to use. For example, the code
below splits two strings at a delimiter (a comma followed by a space) and puts
whatever falls between the delimiter into an array:
Note: This only really makes sense to do when you know that the input strings
follow a uniform format. You may also find the replaceAll method helpful when
using this approach.
https://docs.oracle.com/javase/tutorial/essential/regex/