Tree Traversals (Inorder, Preorder and Postorder)
Tree Traversals (Inorder, Preorder and Postorder)
Tree Traversals (Inorder, Preorder and Postorder)
Unlike linear data structures (Array, Linked List, Queues, Stacks, etc) which have only one
Object 1
logical way to traverse them, trees can be traversed in different ways. Following are the
Example Tree
Inorder Traversal:
Algorithm Inorder(tree)
1. Traverse the left subtree, i.e., call Inorder(left-subtree)
2. Visit the root.
3. Traverse the right subtree, i.e., call Inorder(right-subtree)
Uses of Inorder
In case of binary search trees (BST), Inorder traversal gives nodes in non-decreasing order. To
get nodes of BST in non-increasing order, a variation of Inorder traversal where Inorder
Algorithm Preorder(tree)
1. Visit the root.
2. Traverse the left subtree, i.e., call Preorder(left-subtree)
3. Traverse the right subtree, i.e., call Preorder(right-subtree)
Uses of Preorder
Preorder traversal is used to create a copy of the tree. Preorder traversal is also used to get
Postorder Traversal:
Algorithm Postorder(tree)
1. Traverse the left subtree, i.e., call Postorder(left-subtree)
2. Traverse the right subtree, i.e., call Postorder(right-subtree)
3. Visit the root.
Uses of Postorder
Postorder traversal is used to delete the tree. Please see the question for deletion of tree for
details. Postorder traversal is also useful to get the postfix expression of an expression tree.
expression.
https://practice.geeksforgeeks.org/problems/postorder-traversal/1Practice Postorder
Traversal
return 0;
}
Output:
Complexity function T(n) — for all problem where tree traversal is involved — can be defined
as:
Where k is the number of nodes on one side of root and n-k-1 on the other side.
k is 0 in this case.
…………………………………………
………………………………………….
Value of T(0) will be some constant say d. (traversing a empty tree will take some constants
time)
T(n) = n(c+d)
Case 2: Both left and right subtrees have equal number of nodes.
T(n) = 2T(|_n/2_|) + c
This recursive function is in the standard form (T(n) = aT(n/b) + (-)(n) ) for master
(-)(n)
Auxiliary Space : If we don’t consider size of stack for function calls then O(1) otherwise O(n).
Part 5: Hashing with SHA-
256
An overview of SHA-256, a standard secure hash
function, as described in official document FIPS 180–4.
This continues a series on bitwise operations and their applications, written by a non-expert, for non-
experts. Follow Biffures for future updates.
Hash functions transform arbitrary large bit strings called messages, into small, fixed-length bit strings
called message digests, such that digestsidentify the messages that produced them with a very high
probability. Digests are in that sense fingerprints: a function of the message, simple, yet complex
enough that they allow identification of their message, with a very low probability that different
messages will share the same digests.
In SHA-256, messages up to 2⁶⁴ bit (2.3 exabytes, or 2.3 billion gigabytes) are transformed into digests
of size 256 bits (32 bytes). For perspective, this means that an object 7 times the size of Facebook’s
data warehouse in 2014passed to SHA-256 would produce a chunk of data the size of a 32-letter string
of ASCII characters, and that string would the object’s very special fingerprint.
A prominent use case of hashing is data integrity verification of large files, which relies on the
comparison of actual and expected message digests, orchecksums. Another is hashing as part of the
encryption/decryption journey. Before a message can be encrypted with an algorithm like RSA, it needs
to be hashed. In the rest of this article, we explore what hashing does to a message, with a view to later
develop a better understanding of RSA.
Step by step hashing with SHA-256
This article now draws heavily from FIPS 180–4 all the while trying to offer some simplifications, but
for the full details you may want to refer to the source material instead. With this in mind:
Pre-processing
1. Padding. If we note M the message to be hashed, and l its length in bits where l < 2⁶⁴, then as a first
step we create the padded message M’, which is message M plus a right padding, such that M’ is of
length l’, a multiple of 512. Specifically, we use a padding P such that M’ is:
l’, a multiple of 512. The inclusion of L in padding P helps avoid trivial collisions (i.e. messages “00”
and “000” would produce identical padded messages in the absence of L). The original message can be
extracted by reading the last 64 for bits for length, and then fetching the message from left to right, of
length l.
2. Blocks. M’ is parsed into N blocks of size 512 bits, M¹ to Mᴺ, and each block is expressed as
16 input blocks of size 32 bits, M₀ to M₁₅.
3. Hash initialization. The initial hash value H⁰ of length 256 bits (8 input blocks of
32 bits) is set by taking the first 32 bits of the fractional parts of the square roots of the
first eight prime numbers:
Those values can be reproduced with the below snippet in most browsers and Node
≥8.2.1 (ECMAScript 2017):
Algorithm
The hash is produced by processing each message block Mⁱ of M’ in order. For each of
message block Mⁱ:
1. Message schedule. We create a message schedule Wⁱ, consisting of four 512-bit
message blocks (each made of 16 input blocks). The first block of Wⁱ is message
block Mⁱ, and the next three blocks are variations of Mⁱ, obtained through the formulas
in the illustration below:
a previous article on Bitwise Patterns.
A detail: note that if we align all message schedules Wⁱ vertically, the first column
reads from top to bottom as the complete message M’ = M¹‖..‖Mᴺ.
2. The big shuffle. The input blocks of message schedule W are fed, one after the
other, to a function represented below as a graph. The graph takes as inputs a
hash ωⁱ(t) and a message schedule input block Wⁱ(t), and outputs a hash ωⁱ(t+1). The
initial hash ωⁱ(0) fed to the graph is the intermediate hash Hⁱ⁻¹: in the case of W¹,
it’s H⁰ defined in the pre-processing step. ωⁱ(0) and Wⁱ(0) produce ωⁱ(1); in
turn ωⁱ(1) and Wⁱ(1)produce ωⁱ(2), etc., until ωⁱ(63) is produced.
⁰ using input blocks from the message schedule W. The operation must be repeated 64
times until ω(63) is produced.
a, .., h are initialized as Hi⁻¹(0), .., Hi⁻¹(7). Sigma0, Sigma1, Ch, and Maj are
functions using AND, XOR and negations; K is a bit word with 64 input blocks.
3. New hash. After all input blocks from Wⁱ have been used and we ω(63) has been
created, we can create the new hash Hⁱ such that each input block of Hⁱ is the sum of
the corresponding input block of Hⁱ⁻¹ plus the corresponding input block of ωⁱ(63):
Hⁱ(j) = Hⁱ⁻¹(j) + ωⁱ(63)(j) where + is the addition modulo 2ⁿ
If other message blocks Mⁱ remain, repeat the process (message schedule, big shuffle,
creation of the new hash Hⁱ)
If Wⁱ was the last message schedule, then Hⁱ = H is message M’s final hash or digest—
its so very special fingerprint.
This concludes the overview of SHA-256 as described in FIPS 180–4. A few closing thoughts:
• SHA-256 projects into B²⁵⁶, a space of ~1e77 possible values, which is lots of potential digests: a
good thing that provides the intuition that collisions are unlikely
• That being said, we do not prove here that collisions are unlikely, we even know they
exist given the surjective nature of the hashing function. We know however that (i) given a limited
amount of things to hash, we’re unlikely to find collisions (ii) no collisions have been found to
date for SHA-256.
• Finally, coding your own SHA-256 for production use is probably not a good idea…
…but just for fun and education purposes, here is a version I wrote in JavaScript. Thanks for reading,
hope you enjoyed, and see you next time for a review of RSA.
'use strict';
(function (shs) {
const H0 = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
0x5be0cd19,
];
const K = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc,
0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351,
0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e,
0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585,
0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
0xc67178f2,
];
/**
* Rotate to right
* @param {number} x - An integer to be shifted right
* @param {number} n - The number of bits by which to shift
* @param {string} [w] - The bit notation width. Defaults to x.toString(2).length
* @return {number} The shifted number
*/
function rotr(x, n, w) {
if (!w) { var w = x.toString(2).length; }
n = n % w;
return ((x >>> n) | (x << w - n)) >>> 0 % Math.pow(2, w);
};
/**
* Rotate to left
* @param {number} x - An integer to be shifted right
* @param {number} n - The number of bits by which to shift
* @return {number} The shifted number
*/
function rotl(x, n) {
let w = x.toString(2).length;
n = n % w;
return ((x << n) | (x >>> w - n)) >>> 0 % Math.pow(2, w);
};
/**
* SHA-256 logical functions
*/
function Ch(x, y, z) { return (x & y) ^ (~x & z); };
function Maj(x, y, z) { return (x & y) ^ (x & z) ^ (y & z); };
function Σ0(x) { return rotr(x, 2, 32) ^ rotr(x, 13, 32) ^ rotr(x, 22, 32); };
function Σ1(x) { return rotr(x, 6, 32) ^ rotr(x, 11, 32) ^ rotr(x, 25, 32); };
function σ0(x) { return rotr(x, 7, 32) ^ rotr(x, 18, 32) ^ (x >>> 3); };
function σ1(x) { return rotr(x, 17, 32) ^ rotr(x, 19, 32) ^ (x >>> 10); };
/**
* Padding
* @param {string} m - The message, a binary string (e.g., 011010)
* @return {string} A padded message
*/
function pad(m) {
if (m.length > Math.pow(2, 64)) {
return console.error('Message out of range.');
}
let padding1 = '0'.repeat(512 - ((m.length + 1 + 64) % 512));
let padding2 = '0'.repeat(64 - m.length.toString(2).length);
return `${m}1${padding1}${padding2}${m.length.toString(2)}`;
}
/**
* Parse
* @param {string} paddedMessage - A padded message of length N x 512
* @return {array} An array containing 512 segments, further split in 32-bit segments
* expressed as numbers
*/
function parse(m) {
let M = m.match(/[0-1]{512}/g);
return M.map((x) => {
let arr = x.match(/[0-1]{32}/g);
return arr.map((x) => {
return parseInt(x, 2);
});
});
};
/**
* Hash
* @param {string} m - A binary string to be hashed
* @return {string} A SHA-256 digest
*/
shs.hash = function (m) {
// Pre-processing
let M = parse(pad(m));
// Initialize the hash
let H = [H0];
// Hash computation
for (let i = 0, N = M.length; i < N; i++) {
// Prepare the message schedule
let W = [];
for (let t = 0; t < 64; t++) {
if (t <= 15) {
W[t] = M[i][t];
} else {
W[t] = (σ1(W[t - 2]) + W[t - 7] + σ0(W[t - 15]) + W[t - 16]) % Math.pow(2, 32);
}
}
// Initialize the working variables
let a = H[i][0];
let b = H[i][1];
let c = H[i][2];
let d = H[i][3];
let e = H[i][4];
let f = H[i][5];
let g = H[i][6];
let h = H[i][7];
let T1;
let T2;
// Do stuff
for (let t = 0; t < 64; t++) {
T1 = (h + Σ1(e) + Ch(e, f, g) + K[t] + W[t]) % Math.pow(2, 32);
T2 = (Σ0(a) + Maj(a, b, c)) % Math.pow(2, 32);
h = g;
g = f;
f = e;
e = (d + T1) % Math.pow(2, 32);
d = c;
c = b;
b = a;
a = (T1 + T2) % Math.pow(2, 32);
}
// Compute the i-th intermediate hash
H[i + 1] = [];
H[i + 1][0] = (a + H[i][0]) % Math.pow(2, 32);
H[i + 1][1] = (b + H[i][1]) % Math.pow(2, 32);
H[i + 1][2] = (c + H[i][2]) % Math.pow(2, 32);
H[i + 1][3] = (d + H[i][3]) % Math.pow(2, 32);
H[i + 1][4] = (e + H[i][4]) % Math.pow(2, 32);
H[i + 1][5] = (f + H[i][5]) % Math.pow(2, 32);
H[i + 1][6] = (g + H[i][6]) % Math.pow(2, 32);
H[i + 1][7] = (h + H[i][7]) % Math.pow(2, 32);
}
let digestWords = H[M.length];
let digestTemp = digestWords.map((x) => {
let hex = (x >>> 0).toString(16);
return `${'0'.repeat(8 - hex.length)}${hex}`;
});
let digest = digestTemp.join('');
return digest;
};
/**
* Hash a UTF-8 string
* @param {string} string - A string, encoded using UTF-8
* @return {string} A SHA-256 digest
*/
shs.hashString = function (str) {
return shs.hash(helpers.toUTF8OctetString(str).replace(/ /g, '')); // remove whitespqces
}
}(window.shs = window.shs || {}));
view raw_sha256.js hosted with ❤ by GitHub
'use strict';
(function (helpers) {
/**
* Converts binary code point into a UTF-8 binary string
* @param {number} codepoint - A Unicode codepoint as a number
* @return {string} The UTF-8 binary string for that codepoint
*/
function codePointToUtf8BinaryNotation(codePoint) {
const str = codePoint.toString(2);
const len = str.length;
// 0xxxxxxx
if (len <= 7) {
let padding = '0'.repeat(7 - len);
return `0${padding}${str}`;
}
// 110xxxxx 10xxxxxx
else if (len <= 11) {
let padding = '0'.repeat(11 - len);
return `110${padding}${str.slice(0, -6)} 10${str.slice(-6)}`;
}
// 1110xxxx 10xxxxxx 10xxxxxx
else if (len <= 16) {
let padding = '0'.repeat(16 - len);
return `1110${padding}${str.slice(0, -12)} 10${str.slice(-12, -6)} 10${str.slice(-6)}`;
}
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
else if (len <= 21) {
// Special case where bits can still fit in the last 3 octets
if (len == 17) {
return `11110000 100${str.slice(0, -12)} 10${str.slice(-12, -6)} 10${str.slice(-6)}`;
}
let padding = '0'.repeat(21 - len);
return `11110${padding}${str.slice(0, -18)} 10${str.slice(-18, -12)} 10${str.slice(-12, -6)} 10${str.slice(-
6)}`;
}
// Error
else {
console.error('Did not receive a valid Unicode binaryNotation');
}
};
/**
* Converts a human-readble string into a UTF-8 binary string
* @param {string} str - A human-readable string
* @return {string} An octet string
*/
helpers.toUTF8OctetString = function (str) {
let octetStringArray = [];
// Use for..of to avoid counting of surrogate pairs
// see: https://mathiasbynens.be/notes/javascript-unicode
for (let symbol of str) {
let codePoint = symbol.codePointAt(0);
let utf8BinaryNotation = codePointToUtf8BinaryNotation(codePoint);
octetStringArray.push(utf8BinaryNotation);
};
return octetStringArray.join(' ');
};
/**
* Converts a UTF-8 octet string in human-readable formate
* @param {string} str - A UTF-8 octet string in binary format
* @return {string} A human-readable string
*/
helpers.fromUTF8OctetString = function (str) {
let octetStringArray = str.split(' ');
let i = 0;
let output = [];
while (i < octetStringArray.length) {
// 0xxxxxxx
if (octetStringArray[i].slice(0, 1) == '0') {
let codeUnit = octetStringArray.slice(i, i + 1).join(' ');
let codePointBin = codeUnit.slice(1);
let codePoint = parseInt(codePointBin, 2);
output.push(String.fromCodePoint(codePoint));
i += 1;
}
// 110xxxxx 10xxxxxx
else if (octetStringArray[i].slice(0, 3) == '110') {
let codeUnit = octetStringArray.slice(i, i + 2).join(' ');
let codePointBin = `${codeUnit.slice(3, 8)}${codeUnit.slice(11)}`;
let codePoint = parseInt(codePointBin, 2);
output.push(String.fromCodePoint(codePoint));
i += 2;
}
// 1110xxxx 10xxxxxx 10xxxxxx
else if (octetStringArray[i].slice(0, 4) == '1110') {
let codeUnit = octetStringArray.slice(i, i + 3).join(' ');
let codePointBin = `${codeUnit.slice(4, 8)}${codeUnit.slice(11, 17)}${codeUnit.slice(20)}`;
let codePoint = parseInt(codePointBin, 2);
output.push(String.fromCodePoint(codePoint));
i += 3;
}
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
else if (octetStringArray[i].slice(0, 5) == '11110') {
let codeUnit = octetStringArray.slice(i, i + 4).join(' ');
let codePointBin = `${codeUnit.slice(4, 8)}${codeUnit.slice(11, 17)}${codeUnit.slice(20, 26)}$
{codeUnit.slice(29)}`;
let codePoint = parseInt(codePointBin, 2);
output.push(String.fromCodePoint(codePoint));
i += 4;
}
// Else, there is an error
else {
return console.error('Unexpected UTF-8 octet');
}
}
return output.join('');
};
In SHA-512 the size of the blocks is 1024 bit. The last block must contain:
• the rest of data in message (mod 1024).
• some filling (padding)
• the last 128 bits as length