Given a sorted array. Write a function that creates a Balanced Binary Search Tree using array elements.
Examples:
Input: Array {1, 2, 3}
Output: A Balanced BST
2
/ \
1
/
1
Algorithm
In the previous post, we discussed construction of BST from sorted Linked List. Constructing from sorted
array in O(n) time is simpler as we can get the middle element in O(1) time. Following is a simple
algorithm where we first find the middle node of list and make it root of the tree to be constructed.
1) Get the Middle of the array and make it root.
2) Recursively do same for left half and right half.
a) Get the middle of left half and make it left child of the root
created in step 1.
b) Get the middle of right half and make it right child of the
root created in step 1.
Following is the implementation of the above algorithm. The main code which creates Balanced BST is
highlighted.
#include<stdio.h>
#include<stdlib.h>
/* A Binary Tree node */
struct TNode
{
int data;
struct TNode* left;
};
return node;
O(n)
T(n) = 2T(n/2) + C
T(n) --> Time taken for an array of size n
C --> Constant (Finding middle of array and linking root to left
and right subtrees take constant time)
Output: 2
A simple method is to do a row wise traversal of the matrix, count the number of 1s in each row and
compare the count with max. Finally, return the index of row with maximum 1s. The time complexity of this
method is O(m*n) where m is number of rows and n is number of columns in matrix.
We can do better. Since each row is sorted, we can use Binary Search to count of 1s in each row. We
find the index of first instance of 1 in each row. The count of 1s will be equal to total number of columns
minus the index of first 1.
See the following code for implementation of the above approach.
#include <stdio.h>
#define R 4
#define C 4
/* A function to find the index of first index of 1 in a boolean array arr[] */
int first(bool arr[], int low, int high)
{
if(high >= low)
{
// get the middle index
int mid = low + (high - low)/2;
// check if the element at middle index is first 1
if ( ( mid == 0 || arr[mid-1] == 0) && arr[mid] == 1)
return mid;
// if the element is 0, recur for right side
else if (arr[mid] == 0)
return first(arr, (mid + 1), high);
else // If element is not first 1, recur for left side
return first(arr, low, (mid -1));
}
return -1;
// The main function that returns index of row with maximum number of 1s.
int rowWithMax1s(bool mat[R][C])
{
int max_row_index = 0, max = -1; // Initialize max values
// Traverse for each row and count number of 1s by finding the index
// of first 1
int i, index;
for (i = 0; i < R; i++)
{
index = first (mat[i], 0, C-1);
if (index != -1 && C-index > max)
{
max = C - index;
max_row_index = i;
}
}
return max_row_index;
/* Driver program
int main()
{
bool mat[R][C]
{0, 1, 1,
{1, 1, 1,
{0, 0, 0,
};
Time Complexity: O(mLogn) where m is number of rows and n is number of columns in matrix.
The above solution can be optimized further. Instead of doing binary search in every row, we first check
whether the row has more 1s than max so far. If the row has more 1s, then only count 1s in the row. Also,
to count 1s in a row, we dont do binary search in complete row, we do search in before the index of last
max.
Following is an optimized version of the above solution.
// The main function that returns index of row with maximum number of 1s.
int rowWithMax1s(bool mat[R][C])
{
int i, index;
// Initialize max using values from first row.
int max_row_index = 0;
int max = first(mat[0], 0, C-1);
// Traverse for each row and count number of 1s by finding the index
// of first 1
for (i = 1; i < R; i++)
{
// Count 1s in this row only if this row has more 1s than
// max so far
// Count 1s in this row only if this row has more 1s than
// max so far
if (max != -1 && mat[i][C-max-1] == 1)
{
// Note the optimization here also
index = first (mat[i], 0, C-max);
if (index != -1 && C-index > max)
{
max = C - index;
max_row_index = i;
}
}
else {
max = first(mat[i], 0, C - 1);
}
}
return max_row_index;
}
Run on IDE
The worst case time complexity of the above optimized version is also O(mLogn), the will solution work
better on average. Thanks to Naveen Kumar Singh for suggesting the above solution.
Sources: this and this
The
worst
case
of
the
above
solution
occurs
for
a
matrix
like
following.
0
0
0
0
1
0
0
0
..0
1
1
0
0
1
1
1
.0 1 1 1 1
Following method works in O(m+n) time complexity in worst case.
Step1: Get the index of first (or leftmost) 1 in the first row.
Step2:
Do
following
for
every
row
after
the
first
row
IF
the
element
on
left
of
previous
leftmost
1
is
0,
ignore
this
row.
ELSE Move left until a 0 is found. Update the leftmost index to this index and max_row_index to be the
current row.
The time complexity is O(m+n) because we can possibly go as far left as we came ahead in the first step.
Following is C++ implementation of this method.
// The main function that returns index of row with maximum number of 1s.
int rowWithMax1s(bool mat[R][C])
{
// Initialize first row as row with max 1s
int max_row_index = 0;
// The function first() returns index of first 1 in row 0.
// Use this index to initialize the index of leftmost 1 seen so far
int j = first(mat[0], 0, C-1);
if (j == -1) // if 1 is not present in first row
j = C - 1;
segment
segment
segment
4.
5. Heap
Stack
3.
Uninitialized
Data
Segment:
Uninitialized data segment, often called the bss segment, named after an ancient assembler operator
that stood for block started by symbol. Data in this segment is initialized by the kernel to arithmetic 0
before the program starts executing
uninitialized data starts at the end of the data segment and contains all global variables and static
variables that are initialized to zero or do not have explicit initialization in source code.
For instance a variable declared static int i; would be contained in the BSS segment.
For instance a global variable declared int j; would be contained in the BSS segment.
4.
Stack:
The stack area traditionally adjoined the heap area and grew the opposite direction; when the stack
pointer met the heap pointer, free memory was exhausted. (With modern large address spaces and virtual
memory techniques they may be placed almost anywhere, but they still typically grow opposite directions.)
The stack area contains the program stack, a LIFO structure, typically located in the higher parts of
memory. On the standard PC x86 computer architecture it grows toward address zero; on some other
architectures it grows the opposite direction. A stack pointer register tracks the top of the stack; it is
adjusted each time a value is pushed onto the stack. The set of values pushed for one function call is
termed a stack frame; A stack frame consists at minimum of a return address.
Stack, where automatic variables are stored, along with information that is saved each time a function is
called. Each time a function is called, the address of where to return to and certain information about the
callers environment, such as some of the machine registers, are saved on the stack. The newly called
function then allocates room on the stack for its automatic and temporary variables. This is how recursive
functions in C can work. Each time a recursive function calls itself, a new stack frame is used, so one set
of variables doesnt interfere with the variables from another instance of the function.
5.
Heap:
Heap is the segment where dynamic memory allocation usually takes place.
The heap area begins at the end of the BSS segment and grows to larger addresses from there.The Heap
area is managed by malloc, realloc, and free, which may use the brk and sbrk system calls to adjust its
size (note that the use of brk/sbrk and a single heap area is not required to fulfill the contract of
malloc/realloc/free; they may also be implemented using mmap to reserve potentially non-contiguous
regions of virtual memory into the process virtual address space). The Heap area is shared by all shared
libraries and dynamically loaded modules in a process.
Examples.
The size(1) command reports the sizes (in bytes) of the text, data, and bss segments. ( for more details
please refer man page of size(1) )
1. Check the following simple C program
#include <stdio.h>
int main(void)
{
return 0;
}
Run on IDE
data
bss
960
248
dec
hex
filename
1216
4c0
memory-layout
2. Let us add one global variable in program, now check the size of bss (highlighted in red color).
#include <stdio.h>
int global; /* Uninitialized variable stored in bss*/
int main(void)
{
return 0;
}
Run on IDE
[narendra@CentOS]$ gcc memory-layout.c -o memory-layout
[narendra@CentOS]$ size memory-layout
text
data
bss
960
248
12
dec
1220
hex
filename
4c4
memory-layout
data
bss
960
248
16
dec
1224
hex
4c8
filename
memory-layout
4. Let us initialize the static variable which will then be stored in Data Segment (DS)
#include <stdio.h>
int global; /* Uninitialized variable stored in bss*/
int main(void)
{
static int i = 100; /* Initialized static variable stored in DS*/
return 0;
}
Run on IDE
[narendra@CentOS]$ gcc memory-layout.c -o memory-layout
[narendra@CentOS]$ size memory-layout
text
960
data
252
bss
12
dec
1224
hex
filename
4c8
memory-layout
5. Let us initialize the global variable which will then be stored in Data Segment (DS)
#include <stdio.h>
int global = 10; /* initialized global variable stored in DS*/
int main(void)
{
static int i = 100; /* Initialized static variable stored in DS*/
return 0;
}
Run on IDE
[narendra@CentOS]$ gcc memory-layout.c -o memory-layout
[narendra@CentOS]$ size memory-layout
text
960
data
256
bss
8
dec
1224
hex
4c8
filename
memory-layout