Anda di halaman 1dari 10

1Sorted Array to Balanced BST

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

Input: Array {1, 2, 3, 4}


Output: A Balanced BST
3
/ \
2

/
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;

};

struct TNode* right;

struct TNode* newNode(int data);


/* A function that constructs Balanced Binary Search Tree from a sorted
array */
struct TNode* sortedArrayToBST(int arr[], int start, int end)
{
/* Base Case */
if (start > end)
return NULL;
/* Get the middle element and make it root */
int mid = (start + end)/2;
struct TNode *root = newNode(arr[mid]);
/* Recursively construct the left subtree and make it
left child of root */
root->left = sortedArrayToBST(arr, start, mid-1);
/* Recursively construct the right subtree and make it
right child of root */
root->right = sortedArrayToBST(arr, mid+1, end);
return root;
}
/* Helper function that allocates a new node with the
given data and NULL left and right pointers. */
struct TNode* newNode(int data)
{
struct TNode* node = (struct TNode*)
malloc(sizeof(struct TNode));
node->data = data;
node->left = NULL;
node->right = NULL;

return node;

/* A utility function to print preorder traversal of BST */


void preOrder(struct TNode* node)
{
if (node == NULL)
return;
printf("%d ", node->data);
preOrder(node->left);
preOrder(node->right);
}
/* Driver program to test above functions */
int main()
{

int arr[] = {1, 2, 3, 4, 5, 6, 7};


int n = sizeof(arr)/sizeof(arr[0]);
/* Convert List to BST */
struct TNode *root = sortedArrayToBST(arr, 0, n-1);
printf("\n PreOrder Traversal of constructed BST ");
preOrder(root);
return 0;
}
Run on IDE
Time
Complexity:
Following is the recurrance relation for sortedArrayToBST().

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)

Find the row with maximum number of 1s


Given a boolean 2D array, where each row is sorted. Find the row with the maximum number of 1s.
Example
Input matrix
0111
0011
1 1 1 1 // this row has maximum 1s
0000

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,
};

to test above functions */


= { {0, 0, 0, 1},
1},
1},
0}

printf("Index of row with maximum 1s is %d \n", rowWithMax1s(mat));


return 0;
}
Run on IDE
Output:
Index of row with maximum 1s is 2

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;

for (int i = 1; i < R; i++)


{
// Move left until a 0 is found
while (j >= 0 && mat[i][j] == 1)
{
j = j-1; // Update the index of leftmost 1 seen so far
max_row_index = i; // Update max_row_index
}
}
return max_row_index;

Memory Layout of C Programs


A typical memory representation of C program consists of following sections.
1.
Text
2.
Initialized
data
3.
Uninitialized
data

segment
segment
segment

4.
5. Heap

Stack

A typical memory layout of a running process


1.
Text
Segment:
A text segment , also known as a code segment or simply as text, is one of the sections of a program in
an object file or in memory, which contains executable instructions.
As a memory region, a text segment may be placed below the heap or stack in order to prevent heaps
and stack overflows from overwriting it.
Usually, the text segment is sharable so that only a single copy needs to be in memory for frequently
executed programs, such as text editors, the C compiler, the shells, and so on. Also, the text segment is
often read-only, to prevent a program from accidentally modifying its instructions.
2.
Initialized
Data
Segment:
Initialized data segment, usually called simply the Data Segment. A data segment is a portion of virtual
address space of a program, which contains the global variables and static variables that are initialized by
the programmer.
Note that, data segment is not read-only, since the values of the variables can be altered at run time.
This segment can be further classified into initialized read-only area and initialized read-write area.
For instance the global string defined by char s[] = hello world in C and a C statement like int debug=1
outside the main (i.e. global) would be stored in initialized read-write area. And a global C statement like
const char* string = hello world makes the string literal hello world to be stored in initialized read-only
area and the character pointer variable string in initialized read-write area.
Ex: static int i = 10 will be stored in data segment and global int i = 10 will also be stored in data segment

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

[narendra@CentOS]$ gcc memory-layout.c -o memory-layout


[narendra@CentOS]$ size memory-layout
text

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

3. Let us add one static variable which is also stored in bss.


#include <stdio.h>
int global; /* Uninitialized variable stored in bss*/
int main(void)
{
static int i; /* Uninitialized static variable stored in bss */
return 0;
}
Run on IDE
[narendra@CentOS]$ gcc memory-layout.c -o memory-layout
[narendra@CentOS]$ size memory-layout
text

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