0 Голоса «за»0 Голоса «против»

Просмотров: 280144 стр.Selected interview questions compiled

Oct 03, 2013

© Attribution Non-Commercial (BY-NC)

PDF, TXT или читайте онлайн в Scribd

Selected interview questions compiled

Attribution Non-Commercial (BY-NC)

Просмотров: 280

Selected interview questions compiled

Attribution Non-Commercial (BY-NC)

- Steve Jobs
- Wheel of Time
- NIV, Holy Bible, eBook
- NIV, Holy Bible, eBook, Red Letter Edition
- Cryptonomicon
- The Woman Who Smashed Codes: A True Story of Love, Spies, and the Unlikely Heroine who Outwitted America's Enemies
- Contagious: Why Things Catch On
- Crossing the Chasm: Marketing and Selling Technology Project
- Everybody Lies: Big Data, New Data, and What the Internet Can Tell Us About Who We Really Are
- Zero to One: Notes on Start-ups, or How to Build the Future
- Console Wars: Sega, Nintendo, and the Battle that Defined a Generation
- Dust: Scarpetta (Book 21)
- Hit Refresh: The Quest to Rediscover Microsoft's Soul and Imagine a Better Future for Everyone
- The Innovators: How a Group of Hackers, Geniuses, and Geeks Created the Digital Revolution
- Crushing It!: How Great Entrepreneurs Build Their Business and Influence—and How You Can, Too
- Make Time: How to Focus on What Matters Every Day
- Algorithms to Live By: The Computer Science of Human Decisions
- Wild Cards

Вы находитесь на странице: 1из 144

Data Structure

TIPS: 1. In case of a linked list test for 0 elements, a single element and two elements. Often two elements is a special case . Remember to test for negative numbers 2. Check for overflow errors for integers. Suppose the integer is one byte then the maximum positive number it can store is 2 raised to the power 7, since one bit has to be reserved for indicating positive or negative number. What if the integer variable is storing the largest number already and then is incremented.

1.Median of two sorted arrays Question: There are 2 sorted arrays A and B of size n each. Write an algorithm to find the median of the array obtained after merging the above 2 arrays(i.e. array of length 2n). The complexity should be O(log(n))

-->Median: In probability theory and statistics, a median is described as the number separating the higher half of a sample, a population, or a probability distribution, from the lower half. The median of a finite list of numbers can be found by arranging all the numbers from lowest value to highest value and picking the middle one. For getting the median of input array { 12, 11, 15, 10, 20 }, first sort the array. We get { 10, 11, 12, 15, 20 } after sorting. Median is the middle element of the sorted array which is 12. There are different conventions to take median of an array with even number of elements, one can take the mean of the two middle values, or first middle value, or second middle value. Let us see different methods to get the median of two sorted arrays of size n each. Since size of the set for which we are looking for median is even (2n), we are taking average of two middle two numbers in all below solutions. Method 1 (Simply count while Merging) Use merge procedure of merge sort. Keep track of count while comparing elements of two arrays. If count becomes n(For 2n elements), we have reached the median. Take the average of the elements at indexes n-1 and n in the merged array. See the below implementation. Implementation:

#include <stdio.h> /* This function returns median of ar1[] and ar2[]. Assumptions in this function: Both ar1[] and ar2[] are sorted arrays Both have n elements */ int getMedian(int ar1[], int ar2[], int n) { int i = 0; /* Current index of i/p array ar1[] */ int j = 0; /* Current index of i/p array ar2[] */ int count; int m1 = -1, m2 = -1;

Notes

Page 1

Abhishek Kumar

Data Structure

/* Since there are 2n elements, median will be average of elements at index n-1 and n in the array obtained after merging ar1 and ar2 */ for(count = 0; count <= n; count++) { if(ar1[i] < ar2[j]) { m1 = m2; /* Store the prev median */ m2 = ar1[i]; i++; } else { m1 = m2; /* Store the prev median */ m2 = ar2[j]; j++; } } return (m1 + m2)/2; } /* Driver program to test above function */ int main() { int ar1[] = {1, 12, 15, 26, 38}; int ar2[] = {2, 13, 17, 30, 45}; printf("%d", getMedian(ar1, ar2, 5)) ; getchar(); return 0; } Time Complexity: O(n) Space Complexity: O(1)

Method 2 (By comparing the medians of two arrays) This method works by first getting medians of the two sorted arrays and then comparing them. Let ar1 and ar2 be the input arrays. Algorithm: 1) Calculate the medians m1 and m2 of the input arrays ar1[] and ar2[] respectively. 2) If m1 and m2 both are equal then we are done. return m1 (or m2) 3) If m1 is greater than m2, then median is present in one of the below two subarrays.

Notes

Page 2

Abhishek Kumar

a) From first element of ar1 to m1 (ar1[0...|_n/2_|]) b) From m2 to last element of ar2 (ar2[|_n/2_|...n-1]) 4) If m2 is greater than m1, then median is present in one of the below two subarrays. a) From m1 to last element of ar1 (ar1[|_n/2_|...n-1]) b) From first element of ar2 to m2 (ar2[0...|_n/2_|]) 4) Repeat the above process until size of both the subarrays becomes 2. 5) If size of the two arrays is 2 then use below formula to get the median. Median = (max(ar1[0], ar2[0]) + min(ar1[1], ar2[1]))/2 Example: ar1[] = {1, 12, 15, 26, 38} ar2[] = {2, 13, 17, 30, 45} For above two arrays m1 = ar1[n/2] = 15 and m2 = ar2[n/2] = 17

Data Structure

For the above ar1[] and ar2[], m1 is smaller than m2. So median is present in one of the following two subarrays. [15, 26, 38] and [2, 13, 17] Let us repeat the process for above two subarrays: m1 = 26 m2 = 13. m1 is greater than m2. So the subarrays become [15, 26] and [13, 17] Now size is 2, so median = (max(ar1[0], ar2[0]) + min(ar1[1], ar2[1]))/2 = (max(15, 13) + min(26, 17))/2 = (15 + 17)/2 = 16 Implementation: #include<stdio.h> int max(int, int); int min(int, int); /* This function returns median of ar1[] and ar2[]. Assumptions in this function: Both ar1[] and ar2[] are sorted arrays Both have n elements */ int getMedian(int ar1[], int ar2[], int n) { int m1; /* For median of ar1 */ int m2; /* For median of ar2 */ if (n == 2)

Notes

Page 3

Abhishek Kumar

return (max(ar1[0], ar2[0]) + min(ar1[1], ar2[1])) / 2; m1 = ar1[n/2]; /* get median of ar1 */ m2 = ar2[n/2]; /* get median of ar2 */ /* If both medians are equal then no need to dig further */ if(m1 == m2) return m1; /* if m1 < m2 then median must exist in ar1[m1....] and ar2[....m2] */ if (m1 < m2) return getMedian(ar1 + n/2, ar2, n - n/2); /* if m1 > m2 then median must exist in ar1[....m1] and ar2[m2...] */ return getMedian(ar2 + n/2, ar1, n - n/2); } /* Driver program to test above function */ int main() { int ar1[] = {1, 12, 15, 26, 38}; int ar2[] = {2, 13, 17, 30, 45}; printf("%d", getMedian(ar1, ar2, 5)) ; getchar(); return 0; } /* Utility functions */ int max(int x, int y) { return x > y? x : y; } int min(int x, int y) { return x > y? y : x; } Time Complexity: O(logn) Space Complexity: O(1) Algorithmic Paradigm: Divide and Conquer

Data Structure

Method 3 (By doing binary search for the median): The basic idea is that if you are given two arrays ar1[] and ar2[] and know the length of each, you can check whether an element ar1[i] is the median in constant time. Suppose that the median is ar1[i]. Since the array is sorted, it is greater than exactly i-1 values in array ar1[]. Then if it is the median, it is also greater than exactly j = n i 1 elements in ar2[]. It requires constant time to check if ar2[j] <= ar1[i] <= ar2[j + 1]. If ar1[i] is not the median, then depending on whether ar1[i] is greater or less than ar2[j] and ar2[j + 1], you know that ar1[i] is either greater than or less than the median. Thus you can binary search for median in O(lg n) worst-case time.

Notes

Page 4

Abhishek Kumar

Data Structure

For two arrays ar1 and ar2, first do binary search in ar1[]. If you reach at the end (left or right) of the first array and don't find median, start searching in the second array ar2[]. 1) Get the middle element of ar1[] using array indexes left and right. Let index of the middle element be i. 2) Calculate the corresponding index j of ar2[] j=ni1 3) If ar1[i] >= ar2[j] and ar1[i] <= ar2[j+1] then ar1[i] and ar2[j] are the middle elements. return average of ar2[j] and ar1[i] 4) If ar1[i] is greater than both ar2[j] and ar2[j+1] then do binary search in left half (i.e., arr[left ... i-1]) 5) If ar1[i] is smaller than both ar2[j] and ar2[j+1] then do binary search in right half (i.e., arr[i+1....right]) 6) If you reach at any corner of ar1[] then do binary search in ar2[] Example: ar1[] = {1, 5, 7, 10, 13} ar2[] = {11, 15, 23, 30, 45} Middle element of ar1[] is 7. Let us compare 7 with 23 and 30, since 7 smaller than both 23 and 30, move to right in ar1[]. Do binary search in {10, 13}, this step will pick 10. Now compare 10 with 15 and 23. Since 10 is smaller than both 15 and 23, again move to right. Only 13 is there in right side now. Since 13 is greater than 11 and smaller than 15, terminate here. We have got the median as 12 (average of 11 and 13) Implementation:

#include<stdio.h> int getMedianRec(int ar1[], int ar2[], int left, int right, int n); /* This function returns median of ar1[] and ar2[]. Assumptions in this function: Both ar1[] and ar2[] are sorted arrays Both have n elements */ int getMedian(int ar1[], int ar2[], int n) { return getMedianRec(ar1, ar2, 0, n-1, n); } /* A recursive function to get the median of ar1[] and ar2[] using binary search */ int getMedianRec(int ar1[], int ar2[], int left, int right, int n) { int i, j; /* We have reached at the end (left or right) of ar1[] */ if(left > right) return getMedianRec(ar2, ar1, 0, n-1, n);

Notes

Page 5

Abhishek Kumar

Data Structure

i = (left + right)/2; j = n - i - 1; /* Index of ar2[] */ /* Recursion terminates here.*/ if(ar1[i] > ar2[j] && (j == n-1 || ar1[i] <= ar2[j+1])) { /*ar1[i] is decided as median 2, now select the median 1 (element just before ar1[i] in merged array) to get the average of both*/ if(ar2[j] > ar1[i-1] || i == 0) return (ar1[i] + ar2[j])/2; else return (ar1[i] + ar1[i-1])/2; } /*Search in left half of ar1[]*/ else if (ar1[i] > ar2[j] && j != n-1 && ar1[i] > ar2[j+1]) return getMedianRec(ar1, ar2, left, i-1, n); /*Search in right half of ar1[]*/ else /* ar1[i] is smaller than both ar2[j] and ar2[j+1]*/ return getMedianRec(ar1, ar2, i+1, right, n); } /* Driver program to test above function */ int main() { int ar1[] = {1, 12, 15, 26, 38}; int ar2[] = {2, 13, 17, 30, 45}; printf("%d", getMedian(ar1, ar2, 5)) ; getchar(); return 0; } Time Complexity: O(logn) Space Complexity: O(1) Algorithmic Paradigm: Divide and Conquer References: http://en.wikipedia.org/wiki/Median http://ocw.alfaisal.edu/NR/rdonlyres/Electrical-Engineering-and-Computer-Science/6-046JFall2005/30C68118-E436-4FE3-8C79-6BAFBB07D935/0/ps9sol.pdf ds3etph5wn

2. Rotate bits of a number Bit Rotation: A rotation (or circular shift) is an operation similar to shift except that the bits that fall off at one end are put back to the other end. For Example:

Notes

Page 6

Abhishek Kumar

Data Structure

Left rotation of x = 11100101 by 3 will make x = 00101111 (Left shifted by 3 and first 3 bits are put back in last ) And right rotation of x = 11100101 by 3 will make x = 10111100 (Right shifted by 3 and last 3 bits are put back in first ) The below implementation assumes that integers are stored using 32 bits (NO_OF_BITS). #include<stdio.h> #define INT_BITS 32 /*Function to left rotate n by d bits*/ int leftRotate(int n, unsigned int d) { return (n << d)|(n >> (INT_BITS - d)); } /*Function to right rotate n by d bits*/ int rightRotate(int n, unsigned int d) { return (n >> d)|(n << (INT_BITS - d)); } /* Driver program to test above functions */ int main() { int n = 16; int d = 2; printf("Left Rotation of %d by %d is ", n, d); printf("%d", leftRotate(n, d)); printf("\nRight Rotation of %d by %d is ", n, d); printf("%d", rightRotate(n, d)); getchar(); } 3. Search an element in a sorted and pivoted array

Question: An element in a sorted array can be found in O(log n) time via binary search. But suppose I rotate the sorted array at some pivot unknown to you beforehand. So for instance, 1 2 3 4 5 might become 3 4 5 1 2. Now devise a way to find an element in the rotated array in O(log n) time. sortedPivotedArray : 3 4 5 1 2

Solution: Thanks to Ajay Mishra for initial solution. Algorithm: Find the pivot point, divide the array in two sub-arrays and call binary search. The main idea for finding pivot is for a sorted (in increasing order) and pivoted array, pivot element is the only only element for which next element to it is smaller than it. Using above criteria and binary search methodology we can get pivot element in O(logn) time

Notes

Page 7

Abhishek Kumar

Input arr[] = {3, 4, 5, 1, 2} Element to Search = 1 1) Find out pivot point and divide the array in two sub-arrays. (pivot = 2) /*Index of 5*/ 2) Now call binary search for one of the two sub-arrays. (a) If element is greater than 0th element then search in left array (b) Else Search in right array (1 will go in else as 1 < 0th element(3)) 3) If element is found in selected sub-array then return index Else return -1. Implementation:

Data Structure

/*Program to search an element in a sorted and pivoted array*/ #include <stdio.h> int findPivot(int[], int, int); int binarySearch(int[], int, int, int); /* Searches an element no in a pivoted sorted array arrp[] of size arr_size */ int pivotedBinarySearch(int arr[], int arr_size, int no) { int pivot = findPivot(arr, 0, arr_size-1); if(arr[pivot] == no) return pivot; if(arr[0] <= no) return binarySearch(arr, 0, pivot-1, no); else return binarySearch(arr, pivot+1, arr_size-1, no); } /* Function to get pivot. For array 3, 4, 5, 6, 1, 2 it will return 3 */ int findPivot(int arr[], int low, int high) { int mid = (low + high)/2; /*low + (high - low)/2;*/ if(arr[mid] > arr[mid + 1]) return mid; if(arr[low] > arr[mid]) return findPivot(arr, low, mid-1); else return findPivot(arr, mid + 1, high); } /* Standard Binary Search function*/ int binarySearch(int arr[], int low, int high, int no) { if(high >= low) { int mid = (low + high)/2; /*low + (high - low)/2;*/

Notes

Page 8

Abhishek Kumar

Data Structure

if(no == arr[mid]) return mid; if(no > arr[mid]) return binarySearch(arr, (mid + 1), high, no); else return binarySearch(arr, low, (mid -1), no); } /*Return -1 if element is not found*/ return -1; } /* Driver program to check above functions */ int main() { int arr[10] = {3, 4, 5, 6, 7, 1, 2}; int n = 5; printf("Index of the element is %d", pivotedBinarySearch(arr, 7, 5)); getchar(); return 0; } Time Complexity O(logn) Space Complexity O(1)

Im sure that this post will be as interesting and informative to C virgins (i.e. beginners) as it will be to those who are well versed in C. So let me start with saying that extern keyword applies to C variables (data objects) and C functions. Basically extern keyword extends the visibility of the C variables and C functions. Probably thats is the reason why it was named as extern. Though (almost) everyone knows the meaning of declaration and definition of a variable/function yet for the sake of completeness of this post, I would like to clarify them. Declaration of a variable/function simply declares that the variable/function exists somewhere in the program but the memory is not allocated for them. But the declaration of a variable/function serves an important role. And that is the type of the variable/function. Therefore, when a variable is declared, the program knows the data type of that variable. In case of function declaration, the program knows what are the arguments to that functions, their data types, the order of arguments and the return type of the function. So thats all about declaration. Coming to the definition, when we define a variable/function, apart from the role of declaration, it also allocates memory for that variable/function. Therefore, we can think of definition as a super set of declaration. (or declaration as a subset of definition). From this explanation, it should be obvious that a variable/function can be declared any number of times but it can be defined only once. (Remember the basic principle that you cant have two locations of the same variable/function). So thats all about declaration and definition. Now coming back to our main objective: Understanding extern keyword in C. Ive explained the role of declaration/definition because its mandatory to understand them to understand the extern keyword. Let us first take the easy case. Use of extern with C functions. By default, the declaration and

Notes

Page 9

Abhishek Kumar

Data Structure

definition of a C function have extern prepended with them. It means even though we dont use extern with the declaration/definition of C functions, it is present there. For example, when we write. int foo(int arg1, char arg2); Theres an extern present in the beginning which is hidden and the compiler treats it as below. extern int foo(int arg1, char arg2); Same is the case with the definition of a C function (Definition of a C function means writing the body of the function). Therefore whenever we define a C function, an extern is present there in the beginning of the function definition. Since the declaration can be done any number of times and definition can be done only once, we can notice that declaration of a function can be added in several C/H files or in a single C/H file several times. But we notice the actual definition of the function only once (i.e. in one file only). And as the extern extends the visibility to the whole program, the functions can be used (called) anywhere in any of the files of the whole program provided the declaration of the function is known. (By knowing the declaration of the function, C compiler knows that the definition of the function exists and it goes ahead to compile the program). So thats all about extern with C functions. Now let us the take the second and final case i.e. use of extern with C variables. I feel that it more interesting and information than the previous case where extern is present by default with C functions. So let me ask the question, how would you declare a C variable without defining it? Many of you would see it trivial but its important question to understand extern with C variables. The answer goes as follows. extern int var; Here, an integer type variable called var has been declared (remember no definition i.e. no memory allocation for var so far). And we can do this declaration as many times as needed. (remember that declaration can be done any number of times) So far so good. :) Now how would you define a variable. Now I agree that it is the most trivial question in programming and the answer is as follows. int var; Here, an integer type variable called var has been declared as well as defined. (remember that definition is the super set of declaration). Here the memory for var is also allocated. Now here comes the surprise, when we declared/defined a C function, we saw that an extern was present by default. While defining a function, we can prepend it with extern without any issues. But it is not the case with C variables. If we put the presence of extern in variable as default then the memory for them will not be allocated ever, they will be declared only. Therefore, we put extern explicitly for C variables when we want to declare them without defining them. Also, as the extern extends the visibility to the whole program, by externing a variable we can use the variables anywhere in the program provided we know the declaration of them and the variable is defined somewhere. Now let us try to understand extern with examples. Example 1: int var; int main(void)

Notes

Page 10

Abhishek Kumar

{ var = 10; return 0; }

Data Structure

Analysis: This program is compiled successfully. Here var is defined (and declared implicitly) globally. Example 2: extern int var; int main(void) { return 0; } Analysis: This program is compiled successfully. Here var is declared only. Notice var is never used so no problems. Example 3: extern int var; int main(void) { var = 10; return 0; } Analysis: This program throws error in compilation. Because var is declared but not defined anywhere. Essentially, the var isnt allocated any memory. And the program is trying to change the value to 10 of a variable that doesnt exist at all. Example 4: #include "somefile.h" extern int var; int main(void) { var = 10; return 0; } Analysis: Supposing that somefile.h has the definition of var. This program will be compiled successfully. Example 5: extern int var = 0; int main(void) { var = 10; return 0; }

Notes

Page 11

Abhishek Kumar

Data Structure

Analysis: Guess this program will work? Well, here comes another surprise from C standards. They say that..if a variable is only declared and an initializer is also provided with that declaration, then the memory for that variable will be allocated i.e. that variable will be considered as defined. Therefore, as per the C standard, this program will compile successfully and work. So that was a preliminary look at extern keyword in C. Im sure that you want to have some take away from the reading of this post. And I would not disappoint you. :) In short, we can say 1. Declaration can be done any number of times but definition only once. 2. extern keyword is used to extend the visibility of variables/functions(). 3. Since functions are visible through out the program by default. The use of extern is not needed in function declaration/definition. Its use is redundant. 4. When extern is used with a variable, its only declared not defined. 5. As an exception, when an extern variable is declared with initialization, it is taken as definition of the variable as well.

5. Program for array rotation Write a function rotate(ar[], d, n) that rotates arr[] of size n by d elements.

METHOD 1 (Use temp array) Input arr[] = [1, 2, 3, 4, 5, 6, 7], d = 2, n =7 1) Store d elements in a temp array temp[] = [1, 2] 2) Shift rest of the arr[] arr[] = [3, 4, 5, 6, 7, 6, 7] 3) Store back the d elements arr[] = [3, 4, 5, 6, 7, 1, 2] Time complexity O(n) Space complexity O(n) METHOD 2 (Rotate one by one) leftRotate(arr[], d, n) start For i = 0 to i < d Left rotate all elements of arr[] by one end To rotate by one, store arr[0] in a temporary variable temp, move arr[1] to arr[0], arr[2] to arr[1] and finally temp to arr[n-1] Let us take the same example arr[] = [1, 2, 3, 4, 5, 6, 7], d = 2 Rotate arr[] by one 2 times We get [2, 3, 4, 5, 6, 7, 1] after first rotation and [ 3, 4, 5, 6, 7, 1, 2] after second rotation.

Notes

Page 12

Abhishek Kumar

Data Structure

/*Function to left Rotate arr[] of size n by 1*/ void leftRotatebyOne(int arr[], int n);

/*Function to left rotate arr[] of size n by d*/ void leftRotate(int arr[], int d, int n) { int i; for (i = 0; i < d; i++) leftRotatebyOne(arr, n); }

void leftRotatebyOne(int arr[], int n) { int i, temp; temp = arr[0]; for (i = 0; i < n-1; i++) arr[i] = arr[i+1]; arr[i] = temp; }

/* utility function to print an array */ void printArray(int arr[], int size) { int i; for(i = 0; i < size; i++) printf("%d ", arr[i]); }

/* Driver program to test above functions */ int main() { int arr[] = {1, 2, 3, 4, 5, 6, 7}; leftRotate(arr, 2, 7); printArray(arr, 7); getchar(); return 0; }

Notes

Page 13

Abhishek Kumar

Time complexity: O(n*d) Space complexity: O(1)

Data Structure

METHOD 3 (A Juggling Algorithm) This is an extension of method 2. Instead of moving one by one, divide the array in different sets where number of sets is equal to GCD of n and d and move the elements within sets. If GCD is 1 as is for the above example array (n = 7 and d =2), then elements will be moved within one set only, we just start with temp = arr[0] and keep moving arr[I+d] to arr[I] and finally store temp at the right place. Here is an example for n =12 and d = 3. GCD is 3 and Let arr[] be {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} a) Elements are first moved in first set (See below diagram for this movement)

arr[] after this step --> {4 2 3 7 5 6 10 8 9 1 11 12} b) c) Then in second set. arr[] after this step --> {4 5 3 7 8 6 10 11 9 1 2 12} Finally in third set. arr[] after this step --> {4 5 6 7 8 9 10 11 12 1 2 3}

/*Function to left rotate arr[] of siz n by d*/ void leftRotate(int arr[], int d, int n) { int i, j, k, temp; for (i = 0; i < gcd(d, n); i++) { /* move i-th values of blocks */ temp = arr[i]; j = i; while(1)

Notes

Page 14

Abhishek Kumar

{ k = j + d; if (k >= n) k = k - n; if (k == i) break; arr[j] = arr[k]; j = k; } arr[j] = temp; } }

Data Structure

/*UTILITY FUNCTIONS*/ /* function to print an array */ void printArray(int arr[], int size) { int i; for(i = 0; i < size; i++) printf("%d ", arr[i]); }

/*Fuction to get gcd of a and b*/ int gcd(int a,int b) { if(b==0) return a; else return gcd(b, a%b); }

/* Driver program to test above functions */ int main() { int arr[] = {1, 2, 3, 4, 5, 6, 7}; leftRotate(arr, 2, 7); printArray(arr, 7); getchar();

Notes

Page 15

Abhishek Kumar

return 0; } Time complexity: O(n) Space complexity: O(1) OR

Data Structure

1.Rotate the whole array 2.Find the index at which your array is rotating .(That is the point at which the last and the first element meets). 3.Now rotate these two arrays individually . Ex. -> 1 2 3 4 Rotate 3 elements from beginning. 5 6 7

3 ^ ||

3.

Algorithm: rotate(arr[], d, n) reverse(arr[], 1, d) ; reverse(arr[], d + 1, n); reverse(arr[], l, n); Let AB are the two parts of the input array where A = arr[0..d-1] and B = arr[d..n-1]. The idea of the algorithm is: Reverse A to get ArB. /* Ar is reverse of A */ Reverse B to get ArBr. /* Br is reverse of B */ Reverse all to get (ArBr) r = BA. For arr[] = [1, 2, 3, 4, 5, 6, 7], d =2 and n = 7 A = [1, 2] and B = [3, 4, 5, 6, 7] Reverse A, we get ArB = [2, 1, 3, 4, 5, 6, 7] Reverse B, we get ArBr = [2, 1, 7, 6, 5, 4, 3] Reverse all, we get (ArBr)r = [3, 4, 5, 6, 7, 1, 2] Implementation: /*Utility function to print an array */ void printArray(int arr[], int size);

/* Utility function to reverse arr[] from start to end */ void rvereseArray(int arr[], int start, int end);

/* Function to left rotate arr[] of size n by d */ void leftRotate(int arr[], int d, int n)

Notes

Page 16

Abhishek Kumar

{ rvereseArray(arr, 0, d-1); rvereseArray(arr, d, n-1); rvereseArray(arr, 0, n-1); }

Data Structure

/*UTILITY FUNCTIONS*/ /* function to print an array */ void printArray(int arr[], int size) { int i; for(i = 0; i < size; i++) printf("%d ", arr[i]); printf("%\n "); }

/*Function to reverse arr[] from index start to end*/ void rvereseArray(int arr[], int start, int end) { int i; int temp; while(start < end) { temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; start++; end--; } }

/* Driver program to test above functions */ int main() { int arr[] = {1, 2, 3, 4, 5, 6, 7}; leftRotate(arr, 2, 7); printArray(arr, 7); getchar();

Notes

Page 17

Abhishek Kumar

return 0; } Time Complexity: O(n) Space Complexity: O(1) References: http://www.cs.bell-labs.com/cm/cs/pearls/s02b.pdf

Data Structure

6. Given a binary tree, print out all of its root-to-leaf paths one per line. Here is the solution. Algorithm: initialize: pathlen = 0, path[1000] /*1000 is some max limit for paths, it can change*/ /*printPathsRecur traverses nodes of tree in preorder */ printPathsRecur(tree, path[], pathlen) 1) If node is not NULL then a) push data to path array: path[pathlen] = node->data. b) increment pathlen pathlen++ 2) If node is a leaf node then print the path array. 3) Else a) Call printPathsRecur for left subtree printPathsRecur(node->left, path, pathLen) b) Call printPathsRecur for right subtree. printPathsRecur(node->right, path, pathLen) Example:

Example Tree Output for the above example will be 124 125 13 Implementation: /*program to print all of its root-to-leaf paths for a tree*/ #include <stdio.h> #include <stdlib.h>

/* A binary tree node has data, pointer to left child and a pointer to right child */

Notes

Page 18

Abhishek Kumar

struct node { int data; struct node* left; struct node* right; };

Data Structure

void printArray(int [], int); void printPathsRecur(struct node*, int [], int); struct node* newNode(int ); void printPaths(struct node*);

/* Given a binary tree, print out all of its root-to-leaf paths, one per line. Uses a recursive helper to do the work.*/ void printPaths(struct node* node) { int path[1000]; printPathsRecur(node, path, 0); }

/* Recursive helper function -- given a node, and an array containing the path from the root node up to but not including this node, print out all the root-leaf paths. */ void printPathsRecur(struct node* node, int path[], int pathLen) { if (node==NULL) return;

/* it's a leaf, so print the path that led to here */ if (node->left==NULL && node->right==NULL) { printArray(path, pathLen); } else {

Notes

Page 19

Abhishek Kumar

/* otherwise try both subtrees */ printPathsRecur(node->left, path, pathLen); printPathsRecur(node->right, path, pathLen); } }

Data Structure

/* Helper function that allocates a new node with the given data and NULL left and right pointers. */ struct node* newNode(int data) { struct node* node = (struct node*) malloc(sizeof(struct node)); node->data = data; node->left = NULL; node->right = NULL;

return(node); }

/* Utility that prints out an array on a line */ void printArray(int ints[], int len) { int i; for (i=0; i<len; i++) { printf("%d ", ints[i]); } printf("\n"); }

/* Driver program to test mirror() */ int main() { struct node *root = newNode(1); root->left = newNode(2); root->right = newNode(3); root->left->left = newNode(4); root->left->right = newNode(5);

Notes

Page 20

Abhishek Kumar

/* Print all root-to-leaf paths of the input tree */ printPaths(root);

Data Structure

getchar(); return 0; }

7. Little and Big Endian Mystery What are these? Little and big endian are two ways of storing multibyte data-types ( int, float, etc). In little endian machines, last byte of binary representation of the multibyte data-type is stored first. On the other hand, in big endian machines, first byte of binary representation of the multibyte data-type is stored last. Suppose integer is stored as 4 bytes (For those who are using DOS based compilers such as C++ 3.0 , integer is 2 bytes) then a variable x with value 001234567 will be stored as following.

Memory representation of integer ox01234567 inside Big and little endian machines How to see memory representation of multibyte data types on your machine? Here is a sample C code that shows the byte representation of int, float and pointer.

#include <stdio.h>

/* function to show bytes in memory, from location start to start+n*/ void show_mem_rep(char *start, int n) { int i; for (i = 0; i < n; i++) printf(" %.2x", start[i]); printf("\n"); }

Notes

Page 21

Abhishek Kumar

int main() { int i = 0x01234567; show_mem_rep((char *)&i, sizeof(i)); getchar(); return 0; }

Data Structure

When above program is run on little endian machine, gives 67 45 23 01 as output , while if it is run on endian machine, gives 01 23 45 67 as output. Is there a quick way to determine endianness of your machine? There are n no. of ways for determining endianness of your machine. Here is one quick way of doing the same.

#include <stdio.h> int main() { unsigned int i = 1; char *c = (char*)&i; if (*c) printf("Little endian"); else printf("Big endian"); getchar(); return 0; } In the above program, a character pointer c is pointing to an integer i. Since size of character is 1 byte when the character pointer is de-referenced it will contain only first byte of integer. If machine is little endian then *c will be 1 (because last byte is stored first) and if machine is big endian then *c will be 0. Does endianness matter for programmers? Most of the times compiler takes care of endianness, however, endianness becomes an issue in following cases. It matters in network programming: Suppose you write integers to file on a little endian machine and you transfer this file to a big endian machine. Unless there is little andian to big endian transformation, big endian machine will read the file in reverse order. You can find such a practical example here. Standard byte order for networks is big endian, also known as network byte order. Before transferring data on network, data is first converted to network byte order (big endian). Sometimes it matters when you are using type casting, below program is an example. #include <stdio.h> int main() { unsigned char arr[2] = {0x01, 0x00}; unsigned short int x = *(unsigned short int *) arr;

Notes

Page 22

Abhishek Kumar

printf("%d", x); getchar(); return 0; }

Data Structure

In the above program, a char array is typecasted to an unsigned short integer type. When I run above program on little endian machine, I get 1 as output, while if I run it on a big endian machine I get 512. To make programs endianness independent, above programming style should be avoided. What are bi-endians? Bi-endian processors can run in both modes little and big endian. What are the examples of little, big endian and bi-endian machines ? Intel based processors are little endians. ARM processors were little endians. Current generation ARM processors are bi-endian. Motorola 68K processors are big endians. PowerPC (by Motorola) and SPARK (by Sun) processors were big endian. Current version of these processors are bi-endians. Does endianness effects file formats? File formats which have 1 byte as a basic unit are independent of endianness e..g., ASCII files . Other file formats use some fixed endianness forrmat e.g, JPEG files are stored in big endian format. Which one is better little endian or big endian The term little and big endian came from Gullivers Travels by Jonathan Swift. Two groups could not agree by which end a egg should be opened -a-the little or the big. Just like the egg issue, there is no technological reason to choose one byte ordering convention over the other, hence the arguments degenerate into bickering about sociopolitical issues. As long as one of the conventions is selected and adhered to consistently, the choice is arbitrary.

8. Given a string, find its first non-repeating character Algorithm: 1) Scan the string from left to right and construct the count array. 2) Again, scan the string from left to right and check for counts, if you find an element who's count is 1, return it. Example: Input string: str = geeksforgeeks 1: Construct character count array from the input string. .... count['e'] = 4 count['f'] = 1 count['g'] = 2 count['k'] = 2 2: Get the first character who's count is 1 ('f'). Implementation: #define NO_OF_CHARS 256

/* Returns an array of size 256 containg count of characters in the passed char array */ int *getCharCountArray(char *str) {

Notes

Page 23

Abhishek Kumar

int *count = (int *)calloc(sizeof(int), NO_OF_CHARS); int i; for (i = 0; *(str+i); i++) count[*(str+i)]++; return count; }

Data Structure

/* The function returns index of first non-repeating character in a string. If all characters are repeating then reurns -1 */ int firstNonRepeating(char *str) { int *count = getCharCountArray(str); int index = -1, i;

/* Driver program to test above function */ int main() { char str[] = "geeksforgeeks"; int index = firstNonRepeating(str); if(index == -1) printf("Either all characters are repeating or string is empty"); else printf("First non-repeating character is %c", str[index]); getchar(); return 0; }

Notes

Page 24

Abhishek Kumar

Time Complexity: O(n) Space Complexity: O(1)

Data Structure

9. Maximum sum such that no two elements are adjacent Question: Given an array all of whose elements are positive numbers, find the maximum sum of a subsequence with the constraint that no 2 numbers in the sequence should be adjacent in the array. So 3 2 7 10 should return 13 (sum of 3 and 10) or 3 2 5 10 7 should return 15 (sum of 3, 5 and 7).Answer the question in most efficient way. Algorithm: Loop for all elements in arr[] and maintain two sums incl and excl where incl = Max sum including the previous element and excl = Max sum excluding the previous element. Max sum excluding the current element will be max(incl, excl) and max sum including the current element will be excl + current element (Note that only excl is considered because elements cannot be adjacent). At the end of the loop return max of incl and excl. Example: arr[] = {5, 5, 10, 40, 50, 35} inc = 5 exc = 0 For i = 1 (current element is 5) inc = 5 (excl + arr[i]) exc = 5 max(5, 0) For i = 2 (current element is 10) inc = 15 (excl + arr[i]) exc = 5 max(5, 5) For i = 3 (current element is 40) inc = 45 (excl + arr[i]) exc = 15 max(5, 15) For i = 4 (current element is 50) inc = 65 (excl + arr[i]) exc = 45 max(45, 15) For i = 5 (current element is 35) inc = 80 (excl + arr[i]) exc = 65 max(45, 65) And 35 is the last element. So, answer is max(incl, excl) = 80 Implementation:

#include<stdio.h>

/*Function to return max sum such that no two elements are adjacent */ int FindMaxSum(int arr[], int n) { int incl = arr[0]; int excl = 0;

Notes

Page 25

Abhishek Kumar

int excl_new; int i;

Data Structure

for (i = 1; i < n; i++) { /* current max excluding i */ excl_new = (incl > excl)? incl: excl;

/* return max of incl and excl */ return ((incl > excl)? incl : excl); }

/* Driver program to test above function */ int main() { int arr[] = {5, 5, 10, 100, 10, 5}; printf("%d \n", FindMaxSum(arr, 6)); getchar(); return 0; } Time Complexity: O(n) Space Complexity: O(1) Now try the same problem for array with negative numbers also. 10. Efficient way to multiply with 7 May 30, 2009 We can multiply a number by 7 using bitwise operator. First left shift the number by 3 bits (you will get 8n) then subtract the original numberfrom the shifted number and return the difference (8n n). Program: # include<stdio.h>

int multiplyBySeven(unsigned int n) { /* Note the inner bracket here. This is needed

Notes

Page 26

Abhishek Kumar

because precedence of '-' operator is higher than '<<' */ return ((n<<3) - n); }

Data Structure

/* Driver program to test above function */ int main() { unsigned int n = 4; printf("%u", multiplyBySeven(n));

11. A program to check if a binary tree is BST or not A binary search tree (BST) is a node based binary tree data structure which has the following properties. The left subtree of a node contains only nodes with keys less than the nodes key. The right subtree of a node contains only nodes with keys greater than the nodes key. Both the left and right subtrees must also be binary search trees. From the above properties it naturally follows that: Each node (item in the tree) has a distinct key.

METHOD 1 (Simple but Wrong) Following is a simple program. For each node, check if left node of it is smaller than the node and right node of it is greater than the node.

Notes

Page 27

Abhishek Kumar

{ if (node == NULL) return 1;

Data Structure

/* false if left is > than node */ if (node->left != NULL && node->left->data > node->data) return 0;

/* false if right is < than node */ if (node->right != NULL && node->right->data <= node->data) return 0;

/* false if, recursively, the left or right is not a BST */ if (!isBST(node->left) || !isBST(node->right)) return 0;

/* passing all that, it's a BST */ return 1; } This approach is wrong as this will return true for below binary tree (and below tree is not a BST because 4 is in left subtree of 3)

METHOD 2 (Correct but not efficient) For each node, check if max value in left subtree is smaller than the node and min value in right subtree greater than the node. /* Returns true if a binary tree is a binary search tree */ int isBST(struct node* node) { if (node == NULL) return(false);

Notes

Page 28

Abhishek Kumar

/* false if the max of the left is > than us */ if (node->left!=NULL && maxValue(node->left) > node->data) return(false);

Data Structure

/* false if the min of the right is <= than us */ if (node->right!=NULL && minValue(node->right) <= node->data) return(false);

/* false if, recursively, the left or right is not a BST */ if (!isBST(node->left) || !isBST(node->right)) return(false);

/* passing all that, it's a BST */ return(true); } It is assumed that you have helper functions minValue() and maxValue() that return the min or max int value from a non-empty tree METHOD 3 (Correct and Efficient) Method 2 above runs slowly since it traverses over some parts of the tree many times. A better solution looks at each node only once. The trick is to write a utility helper function isBSTUtil(struct node* node, int min, int max) that traverses down the tree keeping track of the narrowing min and max allowed values as it goes, looking at each node only once. The initial values for min and max should be INT_MIN and INT_MAX they narrow from there. /* Returns true if the given tree is a binary search tree (efficient version). */ int isBST(struct node* node) { return(isBSTUtil(node, INT_MIN, INT_MAX)); } /* Returns true if the given tree is a BST and its values are >= min and <= max. */ int isBSTUtil(struct node* node, int min, int max) Implementation: #include <stdio.h> #include <stdlib.h> #include <limits.h>

/* A binary tree node has data, pointer to left child and a pointer to right child */ struct node { int data; struct node* left;

Notes

Page 29

Abhishek Kumar

struct node* right; };

Data Structure

/* Returns true if the given tree is a binary search tree (efficient version). */ int isBST(struct node* node) { return(isBSTUtil(node, INT_MIN, INT_MAX)); }

/* Returns true if the given tree is a BST and its values are >= min and <= max. */ int isBSTUtil(struct node* node, int min, int max) {

/* false if this node violates the min/max constraint */ if (node->data < min || node->data > max) return 0;

/* otherwise check the subtrees recursively, tightening the min or max constraint */ return isBSTUtil(node->left, min, node->data) && isBSTUtil(node->right, node->data+1, max); }

/* Helper function that allocates a new node with the given data and NULL left and right pointers. */ struct node* newNode(int data) { struct node* node = (struct node*) malloc(sizeof(struct node));

Notes

Page 30

Abhishek Kumar

node->data = data; node->left = NULL; node->right = NULL;

Data Structure

return(node); }

/* Driver program to test above functions*/ int main() { struct node *root = newNode(4); root->left = newNode(2); root->right = newNode(5); root->left->left = newNode(1); root->left->right = newNode(3);

getchar(); return 0; } Time Complexity: O(n) Space Complexity: O(1) 12. Position of rightmost set bit June 19, 2009 Write a one line C function to return position of first 1 from right to left, in binary representation of an Integer. I/P 18, Binary Representation 010010 O/P 2 I/P 19, Binary Representation 010011 O/P 1 Let I/P be 12 (1100) Algorithm: (Example 18(010010)) 1. Take two's complement of the given no as all bits are reverted except the first '1' from right to left (10111) 2 Do an bit-wise & with original no, this will return no with the required one only (00010)

Notes

Page 31

Abhishek Kumar

3 Take the log2 of the no, you will get position -1 (1) 4 Add 1 (2) Program:

Data Structure

#include<stdio.h> #include<math.h>

1 comment so far

1. game says:

November 27, 2009 at 03:15 Using log is never a good idea, iterating over the bits will be much more efficient than taking a log. log and % are highly costly operations because they generally include memory accesses. Below is a ~40-50 instruction O(1) solution

Notes

Page 32

Abhishek Kumar

{ int b =0; n--; if((n&0xffff) == 0xffff) { b+=16; n>>=16; } if((n&0xff) == 0xff) { b+=8; n>>=8; } if((n&0xf) == 0xf) { b+=4; n>>=4; } if((n&0x3) == 0x3) { b+=2; n>>=2; } if ((n&1) == 1)

Data Structure

Notes

Page 33

Abhishek Kumar

{ b++; n>>=1; } return b; }

Data Structure

/*Driver program for testing above function*/ int main() { int n = 12; /*Change n for testing other numbers*/ printf("%u\n", getFirstSetBitPos(n)); getchar(); return 0; }

13. Write an Efficient Method to Check if a Number is Multiple of 3 May 30, 2009 The very first solution that comes to our mind is the one that we learned in school. If sum of digits in a number is multiple of 3 then number is multiple of 3 e.g., for 612 sum of digits is 9 so its a multiple of 3. But this solution is not efficient. You have to get all decimal digits one by one, add them and then check if sum is multiple of 3. There is a pattern in binary representation of the number that can be used to find if number is a multiple of 3. If difference between count of odd set bits (Bits set at odd positions) and even set bits is multiple of 3 then is the number. Example: 23 (00..10111) 1) Get count of all set bits at odd positions (For 23 its 3). 2) Get count of all set bits at even positions (For 23 its 1). 3) If difference of above two counts is a multiple of 3 then number is also a multiple of 3. (For 23 its 2 so 23 is not a multiple of 3) Take some more examples like 21, 15, etc Algorithm: isMutlipleOf3(n) 1) Make n positive if n is negative. 2) If number is 0 then return 1 3) If number is 1 then return 0 4) Initialize: odd_count = 0, even_count = 0

Notes

Page 34

Abhishek Kumar

Data Structure

5) Loop while n != 0 a) If rightmost bit is set then increment odd count. b) Right-shift n by 1 bit c) If rightmost bit is set then increment even count. d) Right-shift n by 1 bit 6) return isMutlipleOf3(odd_count - even_count) Proof: Above can be proved by taking the example of 11 in decimal numbers. (In this context 11 in decimal numbers is same as 3 in binary numbers) If difference between sum of odd digits and even digits is multiple of 11 then decimal number is multiple of 11. Lets see how. Lets take the example of 2 digit numbers in decimal AB = 11A -A + B = 11A + (B A) So if (B A) is a multiple of 11 then is AB. Let us take 3 digit numbers. ABC = 99A + A + 11B B + C = (99A + 11B) + (A + C B) So if (A + C B) is a multiple of 11 then is (A+C-B) Let us take 4 digit numbers now. ABCD = 1001A + D + 11C C + 999B + B A = (1001A 999B + 11C) + (D + B A -C ) So, if (B + D A C) is a multiple of 11 then is ABCD. This can be continued for all decimal numbers. Above concept can be proved for 3 in binary numbers in the same way. Time Complexity: O(logn) Space Complexity: O(1) Program: #include<stdio.h>

/* Fnction to check if n is a multiple of 3*/ int isMultipleOf3(int n) { int odd_count = 0; int even_count = 0;

/* Make no positive if +n is multiple of 3 then is -n. We are doing this to avoid stack overflow in recursion*/ if(n < 0) n = -n; if(n == 0) return 1; if(n == 1) return 0;

while(n) { /* If odd bit is set then increment odd counter */ if(n & 1) odd_count++;

Notes

Page 35

Abhishek Kumar

n = n>>1;

Data Structure

/* If even bit is set then increment even counter */ if(n & 1) even_count++; n = n>>1; }

/* Program to test function isMultipleOf3 */ int main() { int num = 23; if (isMultipleOf3(num)) printf("num is multiple of 3"); else printf("num is not a multiple of 3"); getchar(); return 0; }

14. Count set bits in an integer August 19, 2009 Write an efficient program to count number of 1s in binary representation of an integer. 1. Simple Method Loop through all bits in an integer, check if a bit is set and if it is then increment the set bit count. See below program.

/* Function to get no of set bits in binary representation of passed binary no. */ int countSetBits(int n) { unsigned int count = 0; while(n) { count += n & 1; n >>= 1;

Notes

Page 36

Abhishek Kumar

} return count; }

Data Structure

/* Program to test function countSetBits */ int main() { int i = 9; printf("%d", countSetBits(i)); getchar(); return 0; } Time Complexity: (-)(logn) (Theta of logn) Space Complexity: O(1) 2. Brian Kernighans Algorithm: Subtraction of 1 from a number toggles all the bits (from right to left) till the rightmost set bit(including the righmost set bit). So if we subtract a number by 1 and do bitwise & with itself (n & (n-1)), we unset the righmost set bit. If we do n & (n-1) in a loop and count the no of times loop executes we get the set bit count. Beauty of the this solution is number of times it loops is equal to the number of set bits in a given integer. 1 Initialize count: = 0 2 If integer n is not zero (a) Do bitwise & with (n-1) and assign the value back to n n: = n&(n-1) (b) Increment count by 1 (c) go to step 2 3 Else return count Implementation of Brian Kernighans Algorithm: #include<stdio.h>

/* Function to get no of set bits in binary representation of passed binary no. */ int countSetBits(int n) { unsigned int count = 0; while (n) { n &= (n-1) ; count++; } return count; }

Notes

Page 37

Abhishek Kumar

Data Structure

/* Program to test function countSetBits */ int main() { int i = 9; printf("%d", countSetBits(i)); getchar(); return 0; } Example for Brian Kernighans Algorithm: n = 9 (1001) count = 0 Since 9 > 0, subtract by 1 and do bitwise & with (9-1) n = 9&8 (1001 & 1000) n=8 count = 1 Since 8 > 0, subtract by 1 and do bitwise & with (8-1) n = 8&7 (1000 & 0111) n=0 count = 2 Since n = 0, return count which is 2 now. Time Complexity: O(logn) Space Complexity: O(1) 3. Using Lookup table: We can count bits in O(1) time using lookup table. Please see http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable for details. 15. A Program to check if strings are rotations of each other or not May 26, 2009 Given a string s1 and a string s2, write a snippet to say whether s2 is a rotation of s1 using only one call to strstr routine? (eg given s1 = ABCD and s2 = CDAB, return true, given s1 = ABCD, and s2 = ACBD , return false) Algorithm: areRotations(str1, str2) 1. Create a temp string and store concatenation of str1 to str1 in temp. temp = str1.str2 2. If str2 is a substring of temp then str1 and str2 are rotations of each other. Example: str1 = "ABACD" str2 = "CDABA" temp = str1.str1 = "ABACDABACD" Since str2 is a substring of temp, str1 and str2 are rotations of each other. Implementation: # include <stdio.h> # include <string.h> # include <stdlib.h>

Notes

Page 38

Abhishek Kumar

Data Structure

/* Function checks if passed strings (str1 and str2) are rotations of each other */ int areRotations(char *str1, char *str2) { int size1 = strlen(str1); int size2 = strlen(str2); char *temp; void *ptr;

/* Create a temp string with value str1.str1 */ temp = (char *)malloc(sizeof(char)*size1*2 + 1); temp[0] = '\0'; strcat(temp, str1); strcat(temp, str1);

/* strstr returns NULL if second string is a substring of first string */ if(ptr != NULL) return 1; else return 0; }

/* Driver program to test areRotations */ int main() { char *str1 = "ABCD"; char *str2 = "ABCDA";

if(areRotations(str1, str2))

Notes

Page 39

Abhishek Kumar

printf("Strings are rotations of each other"); else printf("Strings are not rotations of each other");

Data Structure

getchar(); return 0; } Library Functions Used: strstr: strstr finds a sub-string within a string. Prototype: char * strstr(const char *s1, const char *s2); See http://www.lix.polytechnique.fr/Labo/Leo.Liberti/public/computing/prog/c/C/MAN/strstr.htm for more details strcat: strncat concatenate two strings Prototype: char *strcat(char *dest, const char *src); See http://www.lix.polytechnique.fr/Labo/Leo.Liberti/public/computing/prog/c/C/MAN/strcat.htm for more details Time Complexity: Time complexity of this problem depends on the implementation of strstr function. If implementation of strstr is done using KMP matcher then compleity of the above program is (-)(n1 + n2) where n1 and n2 are lengths of strings. KMP matcher takes (-)(n) time to find a substrng in a string of length n where length of substring is assumed to be smaller than the string Space Complexity: (-) (n) if n1 == n2. (where n = n1 = n2) else (-)(1) 16. Write a C program to print all permutations of a given string August 2, 2009 A permutation, also called an arrangement number or order, is a rearrangement of the elements of an ordered list S into a one-to-one correspondence with S itself. A string of length n has n! permutation. Source: Mathword(http://mathworld.wolfram.com/Permutation.html) Below are the permutations of string ABC. ABC, ACB, BAC, BCA, CAB, CBA

Notes

Page 40

Abhishek Kumar

Here is a solution using backtracking.

Data Structure

/* Function to swap values at two pointers */ void swap (char *x, char *y) { char temp; temp = *x; *x = *y; *y = temp; }

/* Function to print permutations of string This function takes three parameters: 1. String 2. Starting index of the string 3. Ending index of the string. */ void permute(char *a, int i, int n) { int j; if (i == n)

Notes

Page 41

Abhishek Kumar

printf("%s\n", a); else { for (j = i; j <= n; j++) { swap((a+i), (a+j)); permute(a, i+1, n); swap((a+i), (a+j)); //backtrack } } }

Data Structure

/* Driver program to test above functions */ int main() { char a[] = "ABC"; permute(a, 0, 2); getchar(); return 0; } Algorithm Paradigm: Backtracking Time Complexity: O(n!) Space Complexity: O(n) 17. Compute the minimum or maximum of two integers without branching November 1, 2009 On some rare machines where branching is expensive, the below obvious approach to find minimum can be slow as it uses branching. /* The obvious approach to find minimum (involves branching) */ int min(int x, int y) { return (x < y) ? x : y } Below are the methods to get minimum(or maximum) without using branching. Typically, the obvious approach is best, though. Method 1(Use XOR and comparison operator) Minimum of x and y will be y ^ ((x ^ y) & -(x < y)) It works because if x < y, then -(x < y) will be all ones, so r = y ^ (x ^ y) & ~0 = y ^ x ^ y = x. Otherwise, if x >= y, then -(x < y) will be all zeros, so r = y ^ ((x ^ y) & 0) = y. On some machines, evaluating (x < y) as 0 or 1 requires a branch instruction, so there may be no advantage.

Notes

Page 42

Abhishek Kumar

To find the maximum, use x ^ ((x ^ y) & -(x < y)); #include<stdio.h>

Data Structure

/*Function to find minimum of x and y*/ int min(int x, int y) { return y ^ ((x ^ y) & -(x < y)); }

/*Function to find maximum of x and y*/ int max(int x, int y) { return x ^ ((x ^ y) & -(x < y)); }

/* Driver program to test above functions */ int main() { int x = 15; int y = 6; printf("Minimum of %d and %d is ", x, y); printf("%d", min(x, y)); printf("\nMaximum of %d and %d is ", x, y); printf("%d", max(x, y)); getchar(); } Method 2(Use subtraction and shift) If we know that INT_MIN <= (x - y) <= INT_MAX , then we can use the following, which are faster because (x - y) only needs to be evaluated once. Minimum of x and y will be y + ((x - y) & ((x - y) >>(sizeof(int) * CHAR_BIT - 1))) This method shifts the subtraction of x and y by 31 (if size of integer is 32). If (x-y) is smaller than 0, then (x -y)>>31 will be 1. If (x-y) is greater than or equal to 0, then (x -y)>>31 will be 0. So if x >= y, we get minimum as y + (x-y)&0 which is y. If x < y, we get minimum as y + (x-y)&1 which is x. Similarly, to find the maximum use x - ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1))) #include<stdio.h> #define CHAR_BIT 8

Notes

Page 43

Abhishek Kumar

Data Structure

/*Function to find minimum of x and y*/ int min(int x, int y) { return y + ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1))); }

/*Function to find maximum of x and y*/ int max(int x, int y) { return x - ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1))); }

/* Driver program to test above functions */ int main() { int x = 15; int y = 6; printf("Minimum of %d and %d is ", x, y); printf("%d", min(x, y)); printf("\nMaximum of %d and %d is ", x, y); printf("%d", max(x, y)); getchar(); } Note that the 1989 ANSI C specification doesn't specify the result of signed right-shift, so above method is not portable. If exceptions are thrown on overflows, then the values of x and y should be unsigned or cast to unsigned for the subtractions to avoid unnecessarily throwing an exception, however the right-shift needs a signed operand to produce all one bits when negative, so cast to signed there. Source: http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax

18. Copy a linked list with next and arbit pointer Question: You are given a Double Link List with one pointer of each node pointing to the next node just like in a single link list. The second pointer however CAN point to any node in the list and not just the previous node. Now write a program in O(n) time to duplicate this list. That is, write a program which will create a copy of this list. Let us call the second pointer as arbit pointer as it can point to any arbitrary node in the linked list.

Notes

Page 44

Abhishek Kumar

Data Structure

1. Solution for Restricted Version of the Question: Let us first solve the restricted version of the original question. The restriction here is that a node will be pointed by only one arbit pointer in a linked list. In below diagram, we have obeyed the restriction.

Figure 1 Algorithm: 1) Create all nodes in copy linked list using next pointers. 2) Change next of original linked list to the corresponding node in copy linked list. 3) Change the arbit pointer of copy linked list to point corresponding node in original linked list. See below diagram after above three steps.

Figure 2 4) Now construct the arbit pointer in copy linked list as below and restore the next pointer of the original linked list. copy_list_node->arbit = copy_list_node->arbit->arbit->next /* This can not be done if we remove the restriction*/ orig_list_node->next = orig_list_node->next->next->arbit Time Complexity: O(n) Space Complexity: O(1) 2. Solution for the Original Question: If we remove the restriction given in above solution then we CANNOT restore the next pointer of the original linked in step 4 of above solution. We have to store the node and its next pointer mapping in original linked list. Below diagram doesnt obey the restriction as node 3 is pointed by arbit of 1 and 4.

Notes

Page 45

Abhishek Kumar

Data Structure

Figure 3 Algorithm: 1) Create all nodes in copy linked list using next pointers. 3) Store the node and its next pointer mappings of original linked list. 3) Change next of original linked list to the corresponding node in copy linked list.

Figure 4 4) Change the arbit pointer of copy linked list to point corresponding node in original linked list. 5) Now construct the arbit pointer in copy linked list as below and restore the next pointer of the original linked list. copy_list_node->arbit = copy_list_node->arbit->arbit->next 6) Restore the node and its next pointer of original linked list from the stored mappings(in step 2). Time Complexity: O(n) Space Complexity: O(n) 3. A Constant Space and O(n) Time Solution for the Original Question: Thanks to Saravanan Mani for providing this solution. This is the best solution among all three as it works in constant space and without any restriction on original linked list. 1) Create the copy of 1 and insert it between 1 & 2, create the copy of 2 and insert it between 2 & 3.. Continue in this fashion, add the copy of N to Nth node 2) Now copy the arbitrary link in this fashion original->next->arbitrary = original->arbitrary->next; /*TRAVERSE TWO NODES*/ This works because original->next is nothing but copy of original and Original->arbitrary->next is nothing but copy of arbitrary. 3) Now restore the original and copy linked lists in this fashion in a single loop. original->next = original->next->next; copy->next = copy->next->next; 4) Make sure that last element of original->next is NULL.

Notes

Page 46

Abhishek Kumar

Time Complexity: O(n) Space Complexity: O(1) 19. Function to check if a singly linked list is palindrome August 13, 2009 Asked by Varun Bhatia.

Data Structure

Here is the solution. Algorithm: 1. Get the middle of the linked list. 2. Reverse the second half of the linked list. 3. Compare the first half and second half. 4. Construct the original linked list by reversing the second half again and attaching it back to the first half Implementation:

/* Link list node */ struct node { char data; struct node* next; };

/* Function to check if given linked list is palindrome or not */ int isPalindrome(struct node *head) { struct node *slow_ptr = head; struct node *fast_ptr = head; struct node *second_half; struct node *prev_of_slow_ptr = head; char res;

if(head!=NULL) {

Notes

Page 47

Abhishek Kumar

/* Get the middle of the list. Move slow_ptr by 1 and fast_ptrr by 2, slow_ptr will have the |_n/2_|th node */ while((fast_ptr->next)!=NULL && (fast_ptr->next->next)!=NULL) { fast_ptr = fast_ptr->next->next;

Data Structure

/*We need previous of the slow_ptr for linked lists with odd elements */ prev_of_slow_ptr = slow_ptr; slow_ptr = slow_ptr->next; }

/* Case where we have even no of elements */ if(fast_ptr->next != NULL) { second_half = slow_ptr->next; reverse(&second_half); slow_ptr->next = NULL; res = compareLists(head, second_half);

/* Case where we have odd no. of elements. Neither first nor second list should have the middle element */ else { second_half = slow_ptr->next; prev_of_slow_ptr->next = NULL; reverse(&second_half); res = compareLists(head, second_half);

Notes

Page 48

Abhishek Kumar

prev_of_slow_ptr->next = slow_ptr; slow_ptr->next = second_half; }

Data Structure

return res; } }

/* Function to reverse the linked list Note that this function may change the head */ void reverse(struct node** head_ref) { struct node* prev = NULL; struct node* current = *head_ref; struct node* next; while (current != NULL) { next = current->next; current->next = prev; prev = current; current = next; } *head_ref = prev; }

/* Function to check if two input lists have same data*/ int compareLists(struct node* head1, struct node *head2) { struct node* temp1 = head1; struct node* temp2 = head2;

Notes

Page 49

Abhishek Kumar

else return -1; }

Data Structure

/* Both are empty reurn 1*/ if(temp1 == NULL && temp2 == NULL) return 1;

/* Will reach here when one is NULL and other is not */ return -1; }

/* Push a node to linked list. Note that this function changes the head */ void push(struct node** head_ref, char new_data) { /* allocate node */ struct node* new_node = (struct node*) malloc(sizeof(struct node));

/* link the old list off the new node */ new_node->next = (*head_ref);

/* Drier program to test above function*/ int main() { /* Start with the empty list */ struct node* head = NULL;

Notes

Page 50

Abhishek Kumar

push(&head, 'e'); push(&head, 'p');

Data Structure

/* p->e->e->p */ if(isPalindrome(head) == 1) printf("Linked list is Palindrome"); else printf("Linked list is not Palindrome");

getchar(); return 0; } Time Complexity O(n) Space Complexity O(1) Function for checking palindrome for a circular linked list can be implemented in a much easier way. We will have a separate post for that. 20. Write a C Program to Find the Maximum Depth or Height of a Tree

Example Tree Recursively calculate height of left and right subtrees of a node and assign height to the node as max of the heights of two children plus 1. See below pseudo code and program for details. Algorithm: maxDepth() 1. If tree is empty then return 0 2. Else (a) Get the max depth of left subtree recursively i.e., call maxDepth( tree->left-subtree) (a) Get the max depth of right subtree recursively i.e., call maxDepth( tree->right-subtree) (c) Get the max of max depths of left and right subtrees and add 1 to it for the current node. max_depth = max(max dept of left subtree, max depth of right subtree) +1 (d) Return max_depth

Notes

Page 51

Abhishek Kumar

Data Structure

#include<stdio.h> #include<stdlib.h>

/* A binary tree node has data, pointer to left child and a pointer to right child */ struct node { int data; struct node* left; struct node* right; };

/* Compute the "maxDepth" of a tree -- the number of nodes along the longest path from the root node down to the farthest leaf node.*/ int maxDepth(struct node* node) { if (node==NULL) return 0; else { /* compute the depth of each subtree */ int lDepth = maxDepth(node->left); int rDepth = maxDepth(node->right);

/* use the larger one */ if (lDepth > rDepth) return(lDepth+1); else return(rDepth+1); } }

/* Helper function that allocates a new node with the given data and NULL left and right pointers. */ struct node* newNode(int data) {

Notes

Page 52

Abhishek Kumar

struct node* node = (struct node*) malloc(sizeof(struct node)); node->data = data; node->left = NULL; node->right = NULL;

Data Structure

return(node); }

getchar(); return 0; } Time Complexity: O(n) (Please see our post Tree Traversal for details) References: http://cslibrary.stanford.edu/110/BinaryTrees.html

21. Write a C program that, given a set A of n numbers and another number x, determines whether or not there exist two elements in S whose sum is exactly x May 30, 2009 Here is the solution Algorithm: hasArrayTwoCandidates (A[], ar_size, sum) 1) Sort the array in non-decreasing order. 2) Initialize two index variables to find the candidate elements in the sorted array. (a) Initialize first to the leftmost index: l=0 (b) Initialize second the rightmost index: r = ar_size-1 3) Loop while l < r.

Notes

Page 53

Abhishek Kumar

(a) If we have found the candidates the return 1 If (A[l] + A[r] == sum) then return 1 (b) Else, If sum of two elements is smaller then we need move the left index variable Else if( A[l] + A[r] < sum ) then l++ (c) Else (sum of two elements is greater, we need to move right index variable. Else r++

Data Structure

4) No candidates in whole array - return 0 Time Complexity: Depends on what sorting algorithm we are using. If we are using Merge Sort or Heap Sort then (-)(nlogn). If we are using Quick Sort then O(n^2) Space Complexity: Again, depends on sorting algorithm. For Merge Sort or Heap Sort then O(n). If we are using quick sort then O(1). Example: Let Array be {1, 4, 45, 6, 10, -8} and sum to find be 16 Sort the array A = {-8, 1, 4, 6, 10, 45} Initialize l = 0, r = 5 A[l] + A[r] ( -8 + 45) > 16 => decrement r. Now r = 10 A[l] + A[r] ( -8 + 10) < 2 => increment l. Now l = 1 A[l] + A[r] ( 1 + 10) < 16 => increment l. Now l = 2 A[l] + A[r] ( 4 + 10) < 14 => increment l. Now l = 3 A[l] + A[r] ( 6 + 10) == 16 => Found candidates (return 1) Note: If there are more than one pair having the given sum then this algoritm reports only one. Can be easily extended for this though. Program:

/* Now look for the two candidates in the sorted array*/ l = 0; r = arr_size-1; while(l < r) { if(A[l] + A[r] == sum) return 1;

Notes

Page 54

Abhishek Kumar

else if(A[l] + A[r] < sum) l++; else // A[i] + A[j] > sum r--; } return 0; }

Data Structure

/* Driver program to test above function */ int main() { int A[] = {1, 4, 45, 6, 10, -8}; int n = 16; int arr_size = 6;

if( hasArrayTwoCandidates(A, arr_size, n)) printf("Array has two elements who's sum is 16"); else printf("Array doesn't have two elements who's sum " " is 16 ");

getchar(); return 0; }

/* FOLLOWING FUNCTIONS ARE ONLY FOR SORTING PURPOSE */ void exchange(int *a, int *b) { int temp; temp = *a; *a = *b; *b = temp; }

Notes

Page 55

Abhishek Kumar

int i = (si - 1); int j;

Data Structure

for (j = si; j <= ei - 1; j++) { if(A[j] <= x) { i++; exchange(&A[i], &A[j]); } } exchange (&A[i + 1], &A[ei]); return (i + 1); }

/* Implementation of Quick Sort A[] --> Array to be sorted si --> Starting index ei --> Ending index */ void quickSort(int A[], int si, int ei) { int pi; /* Partitioning index */ if(si < ei) { pi = partition(A, si, ei); quickSort(A, si, pi - 1); quickSort(A, pi + 1, ei); } }

22. How can I return multiple values from a function? We all know that a function in C can return only one value. So how do we achieve the purpose of returning multiple values. Well, first take a look at the declaration of a function. int foo(int arg1, int arg2);

Notes

Page 56

Abhishek Kumar

Data Structure

So we can notice here that our interface to the function is through arguments and return value only. (Unless we talk about modifying the globals inside the function) Let us take a deeper lookEven though a function can return only one value but that value can be of pointer type. Thats correct, now youre speculating right! We can declare the function such that, it returns a pointer to a structure type user defined variable. And by the property of a structure, we know that a structure in C can hold multiple values of asymmetrical types (i.e. one int variable, four char variables, two float variables and so on) If we want the function to return multiple values of same data types, we could return the pointer to array of that data types. We can also make the function return multiple values by using the arguments of the function. How? By providing the pointers as arguments. Usually, when a function needs to return several values, we use one pointer in return instead of several pointers as arguments.

23. Merge an array of size n into another array of size m+n Question: There are two sorted arrays. First one is of size m+n containing only m elements. Another one is of size n and contains n elements. Merge these two arrays into the first array of size m+n such that the output is sorted. Input: array with m+n elements (mPlusN[]).

NA => Value is not filled/available in array mPlusN[]. There should be n such array blocks. Input: array with n elements (N[]).

Algorithm: Let first array be mPlusN[] and other array be N[] 1) Move m elements of mPlusN[] to end. 2) Start from nth element of mPlusN[] and 0th element of N[] and merge them into mPlusN[]. Implementation:

/* Assuming -1 is filled for the places where element is not available */ #define NA -1

Notes

Page 57

Abhishek Kumar

void moveToEnd(int mPlusN[], int size) { int i = 0, j = size - 1; for (i = size-1; i >= 0; i--) if(mPlusN[i] != NA) { mPlusN[j] = mPlusN[i]; j--; } }

Data Structure

/* Merges array N[] of size n into array mPlusN[] of size m+n*/ int merge(int mPlusN[], int N[], int m, int n) { int i = n; /* Current index of i/p part of mPlusN[]*/ int j = 0; /* Current index of N[]*/ int k = 0; /* Current index of of output mPlusN[]*/ while(k <= (m+n)) { /* Take the element from mPlusN[] if a) its value is smaller and we have not reached end of it b) We have reached end of N[] */ if((i < (m+n) && mPlusN[i] <= N[j]) || ( j == n)) { mPlusN[k] = mPlusN[i]; k++; i++; } else { mPlusN[k] = N[j]; k++; j++; } } }

Notes

Page 58

Abhishek Kumar

Data Structure

/* Utility that prints out an array on a line */ void printArray(int arr[], int size) { int i; for (i=0; i < size; i++) printf("%d ", arr[i]);

printf("\n"); }

/* Driver function to test above functions */ int main() { /* Initialize arrays */ int mPlusN[9] = {2, 8, NA, NA, NA, 13, NA, 15, 20}; int N[] = {5, 7, 9, 25}; int m = 5, n = 4;

/* Print the resultant mPlusN */ printArray(mPlusN, 9); getchar(); } Time Complexity: O(m+n) Space Complexity: O(1)

24. Largest Sum Contiguous Subarray June 22, 2009 Write an efficient C program to find the sum of contiguous subarray within a one-dimensional array of numbers which has the largest sum. Kadanes Algorithm: Initialize:

Notes

Page 59

Abhishek Kumar

max_so_far = 0 max_ending_here = 0

Data Structure

Loop for each element of the array (a) max_ending_here = max_ending_here + a[i] (b) if(max_ending_here < 0) max_ending_here = 0 (c) if(max_so_far < max_ending_here) max_so_far = max_ending_here return max_so_far Explanation: Simple idea of the Kadane's algorithm is to look for all positive contiguous segments of the array (max_ending_here is used for this). And keep track of maximum sum contiguous segment among all positive segments (max_so_far is used for this). Each time we get a positive sum compare it with max_so_far and update max_so_far if it is greater than max_so_far Lets take the example: {-2, -3, 4, -1, -2, 1, 5, -3} max_so_far = max_ending_here = 0 for i=0, a[0] = -2 max_ending_here = max_ending_here + (-2) Set max_ending_here = 0 because max_ending_here < 0 for i=1, a[1] = -3 max_ending_here = max_ending_here + (-3) Set max_ending_here = 0 because max_ending_here < 0 for i=2, a[2] = 4 max_ending_here = max_ending_here + (4) max_ending_here = 4 max_so_far is updated to 4 because max_ending_here greater than max_so_far which was 0 till now for i=3, a[3] = -1 max_ending_here = max_ending_here + (-1) max_ending_here = 3 for i=4, a[4] = -2 max_ending_here = max_ending_here + (-2) max_ending_here = 1 for i=5, a[5] = 1 max_ending_here = max_ending_here + (1) max_ending_here = 2 for i=6, a[6] = 1 max_ending_here = max_ending_here + (5) max_ending_here = 7 max_so_far is updated to 7 because max_ending_here is greater than max_so_far for i=7, a[7] = -3 max_ending_here = max_ending_here + (-3) max_ending_here = 4 Program:

#include<stdio.h> int maxSubArraySum(int a[], int size) { int max_so_far = 0, max_ending_here = 0; int i; for(i = 0; i < size; i++) { max_ending_here = max_ending_here + a[i];

Notes

Page 60

Abhishek Kumar

if(max_ending_here < 0) max_ending_here = 0; if(max_so_far < max_ending_here) max_so_far = max_ending_here; } return max_so_far; }

Data Structure

/*Driver program to test maxSubArraySum*/ int main() { int a[] = {-2, -3, 4, -1, -2, 1, 5, -3}; int max_sum = maxSubArraySum(a,8); printf("%d\n", max_sum); getchar(); return 0; } Notes: Algorithm doesn't work for all negative numbers. It simply returns 0 if all numbers are negative. For handling this we can add an extra phase before actual implementation. The phase will look if all numbers are negative, if they are it will return maximum of them (or smallest in terms of absolute value). There may be other ways to handle it though. Above program can be optimized further, if we compare max_so_far with max_ending_here only if max_ending_here is greater than 0.

int maxSubArraySum(int a[], int size) { int max_so_far = 0, max_ending_here = 0; int i; for(i = 0; i < size; i++) { max_ending_here = max_ending_here + a[i]; if(max_ending_here < 0) max_ending_here = 0;

/* Do not compare for all elements. Compare only when max_ending_here > 0 */ else if (max_so_far < max_ending_here) max_so_far = max_ending_here;

Notes

Page 61

Abhishek Kumar

} return max_so_far; }

Data Structure

Time Complexity: O(n) Space Complexity: O(1) Algorithmic Paradigm: Dynamic Programming Now try below question Given an array of integers (possibly some of the elements negative), write a C program to find out the *maximum product* possible by adding 'n' consecutive integers in the array, n <= ARRAY_SIZE. Also give where in the array this sequence of n integers starts. References: http://en.wikipedia.org/wiki/Kadane%27s_Algorithm

25. Find the Number Occurring Odd Number of Times Given an array of positive integers. All numbers occur even number of times except one number which occurs odd number of times. Find the number in O(n) time & constant space. Example: I/P = [1, 2, 3, 2, 3, 1, 3] O/P = 3 Algorithm: Do bitwise XOR of all the elements. Finally we get the number which has odd occurrences. Program: #include <stdio.h>

int getOddOccurrence(int ar[], int ar_size) { int i; int res = 0; for(i=0; i < ar_size; i++) res = res ^ ar[i];

return res; }

/* Diver function to test above function */ int main() { int ar[] = {2, 3, 5, 4, 5, 2, 4, 3, 5, 2, 4, 4, 2}; printf("%d", getOddOccurrence(ar, 13)); getchar();

Notes

Page 62

Abhishek Kumar

} Time Complexity: O(n) Space Complexity: O(1)

Data Structure

26. Majority Element Write a function which takes an array and emits the majority element (if it exists), otherwise prints NONE as follows: I/P : 3 3 4 2 4 4 2 4 4 O/P : 4 I/P : 3 3 4 2 4 4 2 4 O/P : NONE Majority Element: A majority element in an array A[] of size n is an element that appears more than n/2 times (and hence there is at most one such element). Very basic solution is to loop through the array and calculate count of each element. The element having maximum count is the majority element. Complexity of this solution is O(n*n). Time complexity of soution can be improved if we divide the solution in two phases. 1. Get an element ocurring most of the time in the array. This pahse will make sure that if there is a majority elemnt then it will return that only. 2. Check if the element obtained from above step is majority element. Algorithm: printMajority (a[], size) 1. Find the candidate for majority 2. If candidate is majority. i.e., appears more than n/2 times. Print the candidate 3. Else Print "NONE" Finding a Candidate: The algorithm for first phase that works in O(n) is known as Moores Voting Algorithm. Basic idea of the algorithm is if we cancel out each occurrence of an element e with all the other elements that are different from e then e will exist till end if it is a majority element. Algorithm: findCandidate(a[], size) 1. Initialize index and count of majority element maj_index = 0, count = 1 2. Loop for i = 1 to size 1 (a)If a[maj_index] == a[i] count++ (b)Else Count--; (c)If count == 0 maj_index = i; count = 1 3. Return a[maj_index] Above algorithm loops through each element and maintains a count of a[maj_index], If next element is same then increments the count, if next element is not same then decrements the coount, and if the count reaches 0 then changes the maj_index to the current element and sets count to 1. First Phase algorithm gives us a candidate element. In second phase we need to check if the candidate is really a mojority element. Second phase is simple and can be easily done in O(n). We just need to check if count of the candidate element is greater than n/2. Example: A[] = 2, 2, 3, 5, 2, 2, 6

Notes

Page 63

Abhishek Kumar

Data Structure

Initialize: maj_index = 0, count = 1 > candidate 2 2, 2, 3, 5, 2, 2, 6 Same as a[maj_index] => count = 2 2, 2, 3, 5, 2, 2, 6 Different from a[maj_index] => count = 1 2, 2, 3, 5, 2, 2, 6 Different from a[maj_index] => count = 0 Since count = 0, change candidate for majority element to 5 => maj_index = 3, count = 1 2, 2, 3, 5, 2, 2, 6 Different from a[maj_index] => count = 0 Since count = 0, change candidate for majority element to 2 => maj_index = 4 2, 2, 3, 5, 2, 2, 6 Same as a[maj_index] => count = 2 2, 2, 3, 5, 2, 2, 6 Different from a[maj_index] => count = 1 Finally candidate for majority element is 2. Program: /* Program for finding out majority element in an array */ # include<stdio.h> # define bool int

/* Function to print Majority Element */ void printMajority(int a[], int size) { /* Find the candidate for Majority*/ int cand = findCandidate(a, size);

/* Print the candidate if it is Majority*/ if(isMajority(a, size, cand)) printf(" %d ", cand); else printf("NO Majority Element"); }

/* Function to find the candidate for Majority */ int findCandidate(int a[], int size) { int maj_index = 0, count = 1; int i; for(i = 1; i < size; i++)

Notes

Page 64

Abhishek Kumar

{ if(a[maj_index] == a[i]) count++; else count--; if(count == 0) { maj_index = i; count = 1; } } return a[maj_index]; }

Data Structure

/* Function to check if the candidate occurs more than n/2 times */ bool isMajority(int a[], int size, int cand) { int i, count = 0; for (i = 0; i < size; i++) if(a[i] == cand) count++; if (count > size/2) return 1; else return 0; }

/* Driver function to test above functions */ int main() { int a[] = {1, 3, 3, 1, 2}; printMajority(a, 5); getchar(); return 0; } Time Complexity: O(n) Space Complexity: O(1)

Notes

Page 65

Abhishek Kumar

Data Structure

Now give a try to below question Given an array of 2n elements of which n elements are same and the remaining n elements are all different. Write a C program to find out the value which is present n times in the array. There is no restriction on the elements in the array. They are random (In particular they not sequential).

27. Power Set June 22, 2009 Power Set Power set P(S) of a set S is the set of all subsets of S. For example S = {a, b, c} then P(s) = {{}, {a}, {b}, {c}, {a,b}, {a, c}, {b, c}, {a, b, c}}. If S has n elements in it then P(s) will have 2^n elements Algorithm: Input: Set[], set_size 1. Get the size of power set powet_set_size = pow(2, set_size) 2 Loop for counter from 0 to pow_set_size (a) Loop for i = 0 to set_size (i) If ith bit in counter is set Print ith element from set for this subset (b) Print seperator for subsets i.e., newline Example: Set = [a,b,c] power_set_size = pow(2, 3) = 8 Run for binary counter = 000 to 111 Value of Counter 000 -> 001 -> 011 -> 100 -> 101 -> 110 -> 111 -> Program: #include <stdio.h> #include <math.h> Subset Empty set a ab c ac ab abc

void printPowerSet(char *set, int set_size) { /*set_size of power set of a set with set_size n is (2**n -1)*/ unsigned int pow_set_size = pow(2, set_size); int counter, j;

/*Run from counter 000..0 to 111..1*/ for(counter = 0; counter < pow_set_size; counter++) { for(j = 0; j < set_size; j++) { /* Check if jth bit in the counter is set

Notes

Page 66

Abhishek Kumar

If set then pront jth element from set */ if(counter & (1<<j)) printf("%c", set[j]); } printf("\n"); } }

Data Structure

/*Driver program to test printPowerSet*/ int main() { char set[] = {'a','b','c'}; printPowerSet(set, 3);

getchar(); return 0; } Time Complexity: O(n2^n) Space Complexity: O(1) There are more efficient ways of doing this. Will update here soon with more efficient method. 28. Write a C macro PRINT(x) which prints x At the first look, it seems that writing a C macro which prints its argument is childs play. Following program should work i.e. it should print x #define PRINT(x) (x) int main() { printf("%s",PRINT(x)); return 0; } But it would issue compile error because the data type of x, which is taken as variable by the compiler, is unknown. Now it doesnt look so obvious. Isnt it? Guess what, the followings also wont work #define PRINT(x) ('x') #define PRINT(x) ("x")

But if we know one of lesser known traits of C language, writing such a macro is really a childs play. In C, theres a # directive, also called Stringizing Operator, which does this magic. Basically # directive converts its argument in a string. Voila! it is so simple to do the rest. So the above program can be modified as below. #define PRINT(x) (#x) int main() {

Notes

Page 67

Abhishek Kumar

printf("%s",PRINT(x)); return 0; }

Data Structure

Now if the input is PRINT(x), it would print x. In fact, if the input is PRINT(geeks), it would print geeks.

29. How to print % using printf()? Here is the standard prototype of printf function in C. int printf(const char *format, ...); The format string is composed of zero or more directives: ordinary characters (not %), which are copied unchanged to the output stream; and conversion specifications, each of argument (and it is an error if insufficiently many arguments are given). The character % is followed by one of the following characters. The flag character The field width The precision The length modifier The conversion specifier: See http://swoolley.org/man.cgi/3/printf for details of all the above characters. The main thing to note in the standard is the below line about conversion specifier. A `%' is written. No argument is converted. The complete conversion specification is`%%'. So we can print % using %% /* Program to print %*/ #include<stdio.h> /* Program to print %*/ int main() { printf("%%"); getchar(); return 0; } We can also print % using below. printf("%c", '%'); printf("%s", "%");

31. Lowest Common Ancestor in a Binary Search Tree. Given the values of two nodes in a *binary search tree*, write a c program to find the lowest common ancestor. You may assume that both values already exist in the tree. The function prototype is as follows: int FindLowestCommonAncestor(node* root, int value1, int value)

Notes

Page 68

Abhishek Kumar

Data Structure

I/P : 4 and 14 O/P : 8 (Here the common ancestors of 4 and 14, are {8,20}. Of {8,20}, the lowest one is 8). Here is the solution Algorithm: The main idea of the solution is While traversing Binary Search Tree from top to bottom, the first node n we encounter with value between n1 and n2, i.e., n1 < n < n2 is the Lowest or Least Common Ancestor(LCA) of n1 and n2 (where n1 < n2). So just traverse the BST in pre-order, if you find a node with value in between n1 and n2 then n is the LCA, if it's value is greater than both n1 and n2 then our LCA lies on left side of the node, if it's value is smaller than both n1 and n2 then LCA lies on right side. Implementation: #include <stdio.h> #include <stdlib.h>

/* A binary tree node has data, pointer to left child and a pointer to right child */ struct node { int data; struct node* left; struct node* right; };

/* Function to find least comman ancestor of n1 and n2 */ int leastCommanAncestor(struct node* root, int n1, int n2) { /* If we have reached a leaf node then LCA doesn't exist If root->data is equal to any of the inputs then input is not valid. For example 20, 22 in the given figure */

Notes

Page 69

Abhishek Kumar

if(root == NULL || root->data == n1 || root->data == n2) return -1;

Data Structure

/* If any of the input nodes is child of the current node we have reached the LCA. For example, in the above figure if we want to calculate LCA of 12 and 14, recursion should terminate when we reach 8*/ if((root->right != NULL) && (root->right->data == n1 || root->right->data == n2)) return root->data; if((root->left != NULL) && (root->left->data == n1 || root->left->data == n2)) return root->data;

if(root->data > n1 && root->data < n2) return root->data; if(root->data > n1 && root->data > n2) return leastCommanAncestor(root->left, n1, n2); if(root->data < n1 && root->data < n2) return leastCommanAncestor(root->right, n1, n2); }

/* Helper function that allocates a new node with the given data and NULL left and right pointers. */ struct node* newNode(int data) { struct node* node = (struct node*) malloc(sizeof(struct node)); node->data = data; node->left = NULL; node->right = NULL;

return(node); }

Notes

Page 70

Abhishek Kumar

struct node *root = newNode(2); root->left = newNode(1); root->right = newNode(4); root->right->left = newNode(3); root->right->right = newNode(5);

Data Structure

/* Constructed binary search tree is 2 /\ 1 4 /\ 3 5 */ printf("\n The Least Common Ancestor is \n"); printf("%d", leastCommanAncestor(root, 3, 5));

getchar(); return 0; } Note that above function assumes that n1 is smaller than n2. Time complexity: O(n) Space complexity: O(1)

32. Consider a machine with 64 MB physical memory and a 32-bit virtual address space. If the page size is 4KB, what is the approximate size of the page table? (GATE 2001) (a) 16 MB (b) 8 MB (c) 2 MB (d) 24 MB Answer: (c) Explanation: A page entry is used to get address of physical memory. Here we assume that single level of Paging is happening. So the resulting page table will contain entries for all the pages of the Virtual address space. Number of entries in page table = (virtual address space size)/(page size) Using above formula we can say that there will be 2^(32-12) = 2^20 entries in page table. No. of bits required to address the 64MB Physical memory = 26. So there will be 2^(26-12) = 2^14 page frames in the physical memory. And page table needs to store the address of all these 2^14 page frames. Therefore, each page table entry will contain 14 bits address of the page frame and 1 bit for valid-invalid bit. Since memory is byte addressable. So we take that each page table entry is 16 bits i.e. 2 bytes long.

Notes

Page 71

Abhishek Kumar

Data Structure

Size of page table = (total number of page table entries) *(size of a page table entry) = (2^20 *2) = 2MB For the clarity of the concept, please see the following figure. As per our question, here p = 20, d = 12 and f = 14.

33. Check for Integer Overflow Write a C function, int addOvf(int* result, int a, int b) If there is no overflow, the function places the resultant = sum a+b in result and returns 0. Otherwise it returns -1. The solution of casting to long and adding to find detecting the overflow is not allowed. Algorithm: addOvf(int* result, int a, int b) 1. If both numbers are positive and sum is negative then return -1 2. Else If both numbers are negative and sum is positive then return -1 3. Else return 0 Explanation: There can be overflow only if signs of two numbers are same, and sign oof sum is opposite to the signs of numbers. Examples: Positive overflow 01100 + 01000 = 10100 Negative overflow 11100 + 11000 = 00100 Program: #include<stdio.h> #include<stdlib.h>

/* Takes pointer to result and two numbers as arguments. If there is no overflow, the function

Notes

Page 72

Abhishek Kumar

places the resultant = sum a+b in result and returns 0, otherwise it returns -1 */ int addOvf(int* result, int a, int b) { *result = a + b; if(a > 0 && b > 0 && *result < 0) return -1; if(a < 0 && b < 0 && *result > 0) return -1; return 0; }

Data Structure

int main() { int *res = (int *)malloc(sizeof(int)); int x = 2147483640; int y = 10;

printf("\n %d", *res); getchar(); return 0; } Time Complexity : O(1) Space Complexity: O(1)

34. Level Order Tree Traversal Level order traversal of a tree is breadth first traversal for the tree.

Notes

Page 73

Abhishek Kumar

Data Structure

Example Tree Level order traversal of the above tree is 1 2 3 4 5 METHOD 1 (Use function to print a given level) Algortihm: There are basically two functions in this method. One is to print all nodes at a given level (printGivenLevel), and other is to print level order traversal of the tree (printLevelorder). printLevelorder makes use of printGivenLevel to print nodes at all levels one by one starting from root. /*Function to print level order traversal of tree*/ printLevelorder(tree) for d = 1 to height(tree) printGivenLevel(tree, d); /*Function to print all nodes at a given level*/ printGivenLevel(tree, level) if tree is null, return; if level is 1, then print(tree->data); else if level greater than 1, then printGivenLevel(tree->left, level-1); printGivenLevel(tree->right, level-1); Implementation: #include <stdio.h> #include <stdlib.h>

/* A binary tree node has data, pointer to left child and a pointer to right child */ struct node { int data; struct node* left; struct node* right; };

/*Function protoypes*/ void printGivenLevel(struct node* root, int level); int height(struct node* node); struct node* newNode(int data);

Notes

Page 74

Abhishek Kumar

Data Structure

/* Function to print level order traversal a tree*/ void printLevelOrder(struct node* root) { int h = height(root); int i; for(i=1; i<=h; i++) printGivenLevel(root, i); }

/* Print nodes at a given level */ void printGivenLevel(struct node* root, int level) { if(root == NULL) return; if(level == 1) printf("%d ", root->data); else if (level > 1) { printGivenLevel(root->left, level-1); printGivenLevel(root->right, level-1); } }

/* Compute the "height" of a tree -- the number of nodes along the longest path from the root node down to the farthest leaf node.*/ int height(struct node* node) { if (node==NULL) return 0; else { /* compute the height of each subtree */ int lheight = height(node->left); int rheight = height(node->right);

Notes

Page 75

Abhishek Kumar

if (lheight > rheight) return(lheight+1); else return(rheight+1); } }

Data Structure

/* Helper function that allocates a new node with the given data and NULL left and right pointers. */ struct node* newNode(int data) { struct node* node = (struct node*) malloc(sizeof(struct node)); node->data = data; node->left = NULL; node->right = NULL;

return(node); }

/* Driver program to test above functions*/ int main() { struct node *root = newNode(1); root->left = newNode(2); root->right = newNode(3); root->left->left = newNode(4); root->left->right = newNode(5);

getchar(); return 0; }

METHOD 2 (Use Queue) Algorithm: For each node, first the node is visited and then its child nodes are put in a FIFO queue.

Notes

Page 76

Abhishek Kumar

Data Structure

printLevelorder(tree) 1) Create an empty queue q 2) temp_node = root /*start from root*/ 3) Loop while temp_node is not NULL a) print temp_node->data. b) Enqueue temp_nodes children (first left then right children) to q c) Dequeue a node from q and assign its value to temp_node Implementation: Here is a simple implementation of the above algorithm. Queue is implemented as array with maximum size of 500. We can implement queue as linked list also. #include <stdio.h> #include <stdlib.h> #define MAX_Q_SIZE 500

/* frunction prototypes */ struct node** createQueue(int *, int *); void enQueue(struct node **, int *, struct node *); struct node *deQueue(struct node **, int *);

/* Given a binary tree, print its nodes in level order using array for implementing queue */ void printLevelOrder(struct node* root) { int rear, front; struct node **queue = createQueue(&front, &rear); struct node *temp_node = root;

Notes

Page 77

Abhishek Kumar

if(root->left) enQueue(queue, &rear, temp_node->left);

Data Structure

/*UTILITY FUNCTIONS*/ struct node** createQueue(int *front, int *rear) { struct node **queue = (struct node **)malloc(sizeof(struct node*)*MAX_Q_SIZE);

void enQueue(struct node **queue, int *rear, struct node *new_node) { queue[*rear] = new_node; (*rear)++; }

struct node *deQueue(struct node **queue, int *front) { (*front)++; return queue[*front - 1]; }

/* Helper function that allocates a new node with the given data and NULL left and right pointers. */ struct node* newNode(int data) {

Notes

Page 78

Abhishek Kumar

struct node* node = (struct node*) malloc(sizeof(struct node)); node->data = data; node->left = NULL; node->right = NULL;

Data Structure

return(node); }

/* Driver program to test above functions*/ int main() { struct node *root = newNode(1); root->left root->right = newNode(2); = newNode(3);

getchar(); return 0; } Time Complexity: O(n) where n is number of nodes in the binary tree Space Complexity: O(n) where n is number of nodes in the binary tree. References: http://www.itl.nist.gov/div897/sqg/dads/HTML/levelOrderTraversal.html http://en.wikipedia.org/wiki/Breadth-first_traversal

35. Write C Code to Determine if Two Trees are Identical Two trees are identical when they have same data and arrangement of data is also same. To identify if two trees are identical, we need to traverse both trees simultaneously, and while traversing we need to compare data and children of the trees. Algorithm: sameTree(tree1, tree2) 1. If both trees are empty then return 1. 2. Else If both trees are non -empty (a) Check data of the root nodes (tree1->data == tree2->data) (b) Check left subtrees recursively i.e., call sameTree( tree1->left_subtree, tree2->left_subtree) (c) Check right subtrees recursively i.e., call sameTree(

Notes

Page 79

Abhishek Kumar

tree1->right_subtree, tree2->right_subtree) (d) If a,b and c are true then return 1. 3 Else return 0 (one is empty and other is not) #include <stdio.h> #include <stdlib.h>

Data Structure

return(node); }

/* Given two trees, return true if they are structurally identical */ int identicalTrees(struct node* a, struct node* b) { /*1. both empty */ if (a==NULL && b==NULL) return 1;

Notes

Page 80

Abhishek Kumar

{ return ( a->data == b->data && identicalTrees(a->left, b->left) && identicalTrees(a->right, b->right) ); } /* 3. one empty, one not -> false */ else return 0; }

Data Structure

/* Driver program to test identicalTrees function*/ int main() { struct node *root1 = newNode(1); struct node *root2 = newNode(1); root1->left = newNode(2); root1->right = newNode(3); root1->left->left = newNode(4); root1->left->right = newNode(5);

if(identicalTrees(root1, root2)) printf("Both tree are identical."); else printf("Trees are not identical.");

getchar(); return 0; } Time Complexity: Complexity of the identicalTree() will be according to the tree with lesser number of nodes. Let number of nodes in two trees be m and n then complexity of sameTree() is O(m) where m < n.

Notes

Page 81

Abhishek Kumar

Data Structure

36. Implement Your Own sizeof Here is an implementation. #define my_sizeof(type) (char *)(&type+1)-(char*)(&type) int main() { double x; printf("%d", my_sizeof(x)); getchar(); return 0; } You can also implement using function instead of macro, but function implementation cannot be done in C as C doesnt support function overloading and sizeof() is supposed to receive parameters of all data types. Note that above implementation assumes that size of character is one byte. Time Complexity: O(1) Space Complexity: O(1)

37. Write a program to add two numbers in base 14 Below are the different ways to add base 14 numbers. Method 1 Thanks to Raj for suggesting this method. 1. Convert both i/p base 14 numbers to base 10. 2. Add numbers. 3. Convert the result back to base 14. Method 2 Just add the numbers in base 14 in same way we add in base 10. Add numerals of both numbers one by one from right to left. If there is a carry while adding two numerals, consider the carry for adding next numerals. Let us consider the presentation of base 14 numbers same as hexadecimal numbers A --> 10 B --> 11 C --> 12 D --> 13 Example: num1 = 1 2 A num2 = C D 3 1. Add A and 3, we get 13(D). Since 13 is smaller than 14, carry becomes 0 and resultant numeral becomes D 2. Add 2, D and carry(0). we get 15. Since 15 is greater than 13, carry becomes 1 and resultant numeral is 15 - 14 = 1 3. Add 1, C and carry(1). we get 14. Since 14 is greater than 13, carry becomes 1 and resultant numeral is 14 - 14 = 0 Finally, there is a carry, so 1 is added as leftmost numeral and the result becomes 101D

Notes

Page 82

Abhishek Kumar

Implementation of Method 2 # include <stdio.h> # include <assert.h> # include <stdlib.h> # define bool int

Data Structure

/* Function to add two numbers in base 14 */ char *sumBase14(char *num1, char *num2) { int l1 = strlen(num1); int l2 = strlen(num2); char *res; int i; int nml1, nml2, res_nml; bool carry = 0;

if(l1 != l2) { printf("Function doesn't support numbers of different" " lengths. If you want to add such numbers then" " prefix smaller number with required no. of zeroes"); getchar(); assert(0); }

/* Note the size of the allocated memory is one more than i/p lenghts for the cases where we have carry at the last like adding D1 and A1 */ res = (char *)malloc(sizeof(char)*(l1 + 1));

/* Add all numerals from right to left */ for(i = l1-1; i >= 0; i--) { /* Get decimal values of the numerals of i/p numbers*/ nml1 = getNumeralValue(num1[i]);

Notes

Page 83

Abhishek Kumar

nml2 = getNumeralValue(num2[i]);

Data Structure

/* Add decimal values of numerals and carry */ res_nml = carry + nml1 + nml2;

/* Check if we have carry for next addition of numerals */ if(res_nml >= 14) { carry = 1; res_nml -= 14; } else { carry = 0; } res[i+1] = getNumeral(res_nml); }

/* if there is no carry after last iteration then result should not include 0th character of the resultant string */ if(carry == 0) return (res + 1);

/* if we have carry after last iteration then result should include 0th character */ res[0] = '1'; return res; }

/* Function to get value of a numeral For example it returns 10 for input 'A' 1 for '1', etc */ int getNumeralValue(char num) { if( num >= '0' && num <= '9') return (num - '0');

Notes

Page 84

Abhishek Kumar

if( num >= 'A' && num <= 'D') return (num - 'A' + 10);

Data Structure

/* If we reach this line caller is giving invalid character so we assert and fail*/ assert(0); }

/* Function to get numeral for a value. For example it returns 'A' for input 10 '1' for 1, etc */ char getNumeral(int val) { if( val >= 0 && val <= 9) return (val + '0'); if( val >= 10 && val <= 14) return (val + 'A' - 10);

/* If we reach this line caller is giving invalid no. so we assert and fail*/ assert(0); }

/*Driver program to test above functions*/ int main() { char *num1 = "DC2"; char *num2 = "0A3";

printf("Result is %s", sumBase14(num1, num2)); getchar(); return 0; } Notes: Above approach can be used to add numbers in any base. We dont have to do string operations if base is smaller than 10. You can try extending the above program for numbers of different lengths.

Notes

Page 85

Abhishek Kumar

Data Structure

38. Lucky Numbers Lucky numbers are subset of integers. Rather than going into much theory, let us see the process of arriving at lucky numbers, Take the set of integers 1,2,3,4,5,6,7,8,9,10,11,12,14,15,16,17,18,19, First, delete every second number, we get following reduced set. 1,3,5,7,9,11,13,15,17,19, Now, delete every third number, we get 1,3,7,9,13,15,19,.. Continue this process indefinitely Any number that does NOT get deleted due to above process is called lucky. Therefore, set of lucky numbers is 1,3,7,13,25, Now, given an integer n, write a function to say whether this number is lucky or not. bool isLucky(int n) Algorithm: Before every iteration, if we calculate position of the given no, then in a given iteration, we can determine if the no will be deleted. Suppose calculated position for the given no. is P before some iteration, and each Ith no. is going to be removed in this iteration, if P < I then input no is lucky, if P is such that P%I == 0 (I is a divisor of P), then input no is not lucky. Recursive Way: #include <stdio.h> #define bool int

/* Returns 1 if n is a lucky no. ohterwise returns 0*/ bool isLucky(int n) { static int counter = 2;

/*variable next_position is just for readability of the program we can remove it and use n only */ int next_position = n; if(counter > n) return 1; if(n%counter == 0) return 0;

Notes

Page 86

Abhishek Kumar

int main() { int x = 5; if( isLucky(x) ) printf("%d is a lucky no.", x); else printf("%d is not a lucky no.", x); getchar(); }

Data Structure

Example: Lets us take an example of 19 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,17,18,19,20,21, 1,3,5,7,9,11,13,15,17,19,.. 1,3,7,9,13,15,19,. 1,3,7,13,15,19, 1,3,7,13,19, In next step every 6th no .in sequence will be deleted. 19 will not be deleted after this step because position of 19 is 5th after this step. Therefore, 19 is lucky. Lets see how above C code finds out: Current function call Position after this call Counter for next call Next Call isLucky(19 ) isLucky(10) isLucky(7) isLucky(6) 10 7 6 5 3 4 5 6 isLucky(10) isLucky(7) isLucky(6) isLucky(5)

39. Ugly Numbers Ugly numbers are numbers whose only prime factors are 2, 3 or 5. The sequence 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, shows the first 11 ugly numbers. By convention, 1 is included. Write a program to find and print the 150th ugly number.

METHOD 1 (Simple) Algorithm: Loop for all positive integers until ugly number count is smaller than n, if an integer is ugly than increment ugly number count. To check if a number is ugly, divide the number by greatest divisible powers of 2, 3 and 5, if the number becomes 1 then it is an ugly number otherwise not. For example, let us see how to check for 300 is ugly or not. Greatest divisible power of 2 is 4, after dividing 300 by 4 we get 75. Greatest divisible power of 3 is 3, after dividing 300 by 3 we get 25. Greatest divisible power of 5 is 25, after dividing 300 by 25 we get 1. Since we get 1 finally, 300 is ugly number. Implementation: # include<stdio.h> # include<stdlib.h>

Notes

Page 87

Abhishek Kumar

/*This function divides a by greatest divisible power of b*/ int maxDivide(int a, int b) { while (a%b == 0) a = a/b; return a; }

Data Structure

/* Function to check if a number is ugly or not */ int isUgly(int no) { no = maxDivide(no, 2); no = maxDivide(no, 3); no = maxDivide(no, 5);

/* Function to get the nth ugly number*/ int getNthUglyNo(int n) { int i = 1; int count = 1; /* ugly number count */

/*Check for all integers untill ugly count becomes n*/ while (n > count) { i++; if (isUgly(i)) count++; } return i; }

Notes

Page 88

Abhishek Kumar

{ unsigned no = getNthUglyNo(150); printf("150th ugly no. is %d ", no); getchar(); return 0; }

Data Structure

This method is not time efficient as it checks for all integers until ugly number count becomes n, but space complexity of this method is O(1) METHOD 2 (Use Dynamic Programming) Here is a time efficient solution with O(n) extra space Algorithm: 1 Declare an array for ugly numbers: ugly[150] 2 Initialize first ugly no: ugly[0] = 1 3 Initialize three array index variables i2, i3, i5 to point to 1st element of the ugly array: i2 = i3 = i5 =0; 4 Initialize 3 choices for the next ugly no: next_mulitple_of_2 = ugly[i2]*2; next_mulitple_of_3 = ugly[i3]*3 next_mulitple_of_5 = ugly[i5]*5; 5 Now go in a loop to fill all ugly numbers till 150: For (i = 1; i < 150; i++ ) { /* These small steps are not optimized for good readability. Will optimize them in C program */ next_ugly_no = Min(next_mulitple_of_2, next_mulitple_of_3, next_mulitple_of_5); if (next_ugly_no == next_mulitple_of_2) { i2 = i2 + 1; next_mulitple_of_2 = ugly[i2]*2; } if (next_ugly_no == next_mulitple_of_3) { i3 = i3 + 1; next_mulitple_of_3 = ugly[i3]*3; } if (next_ugly_no == next_mulitple_of_5) { i5 = i5 + 1; next_mulitple_of_5 = ugly[i5]*5; } ugly[i] = next_ugly_no }/* end of for loop */ 6.return next_ugly_no Example: Let us see how it works initialize ugly[] = | 1 | i2 = i3 = i5 = 0; First iteration ugly[1] = Min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5) = Min(2, 3, 5)

Notes

Page 89

Abhishek Kumar

=2 ugly[] = | 1 | 2 | i2 = 1, i3 = i5 = 0 (i2 got incremented ) Second iteration ugly[2] = Min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5) = Min(4, 3, 5) =3 ugly[] = | 1 | 2 | 3 | i2 = 1, i3 = 1, i5 = 0 (i3 got incremented ) Third iteration ugly[3] = Min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5) = Min(4, 6, 5) =4 ugly[] = | 1 | 2 | 3 | 4 | i2 = 2, i3 = 1, i5 = 0 (i2 got incremented ) Fourth iteration ugly[4] = Min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5) = Min(8, 6, 5) =5 ugly[] = | 1 | 2 | 3 | 4 | 5 | i2 = 2, i3 = 1, i5 = 1 (i5 got incremented ) Will continue same way till I < 150 Program: # include<stdio.h> # include<stdlib.h> # define bool int

Data Structure

/* Function to get the nth ugly number*/ unsigned getNthUglyNo(unsigned n) { unsigned *ugly = (unsigned *)(malloc (sizeof(unsigned)*n)); unsigned i2 = 0, i3 = 0, i5 = 0; unsigned i; unsigned next_multiple_of_2 = 2; unsigned next_multiple_of_3 = 3; unsigned next_multiple_of_5 = 5; unsigned next_ugly_no = 1; *(ugly+0) = 1;

Notes

Page 90

Abhishek Kumar

{ next_ugly_no = min(next_multiple_of_2, next_multiple_of_3, next_multiple_of_5); *(ugly+i) = next_ugly_no; if(next_ugly_no == next_multiple_of_2) { i2 = i2+1; next_multiple_of_2 = *(ugly+i2)*2; } if(next_ugly_no == next_multiple_of_3) { i3 = i3+1; next_multiple_of_3 = *(ugly+i3)*3; } if(next_ugly_no == next_multiple_of_5) { i5 = i5+1; next_multiple_of_5 = *(ugly+i5)*5; } } /*End of for loop (i=1; i<n; i++) */ return next_ugly_no; }

Data Structure

/* Function to find minimum of 3 numbers */ unsigned min(unsigned a, unsigned b, unsigned c) { if(a <= b) { if(a <= c) return a; else return c; } if(b <= c) return b; else return c;

Notes

Page 91

Abhishek Kumar

}

Data Structure

/* Driver program to test above functions */ int main() { unsigned no = getNthUglyNo(150); printf("%dth ugly no. is %d ", 150, no); getchar(); return 0; } Algorithmic Paradigm: Dynamic Programming Time Complexity: O(n) Storage Complexity: O(n)

40. You are given an array of integers. I give you a target number and ask you to find integers in the array which add up to the target? First find two integers that can add upto the target and then generalize it to any number of integers adding up to the target. Solution This is a very interesting question. Let's start with a sample array. Say we are given the following array: 1 3 5 6 7 8 9

So the problem is asking that if we are given a target number = 17 can we find one possible set of integers in the given array which would make up this target number. In the given array we would can make up 17 using 1, 3, 6 and 7. Another possible set is 8 and 9. The brute force solution would be to find all the possible combinations starting from length 1 upto the total length of the array, and finding the sum of each combination to find if it equals the target. The other solution we can understand better if we restrain ourselves to find just two integers which add up to our target. So the first step is that we sort our given array. Note that the sample array I gave is already sorted. Once we sorted the array we pick the first number which is 1 in our case and subtract it from our target which is 17. The result is 16. Now since our array is sorted we just perform a binary search on the array looking for 16. The search would take O(lgn) time and since in the worst case we can scan through the entire array and at every step we would be performing the binary search the total time is O(nlogn). This is also the solution incase you are asked that two integers should add upto the target.

So now let's see how we can generalize to any number of integers. By generalization we mean that we are not limiting ourselves to just two integers, we are just trying to find as many integers as it takes to add up to the target. For instance if target = 20 then we in our given array no two integers add upto 20 however 1, 3, 7 and 10 would add upto 20 and we would write an algorithm that does just that. The key here is to think in recursive terms. Say we start off the usual way that we subtract 1 from 20 and get 19. We immediately conduct a binary search on the rest of the array to figure out if a 19 exists and it doesn't. So now we question is there a way to make up 19 from the rest of the array numbers? Note that you are actually asking a recursive question. If we could make up 19 then we would add our starting number 1 to it and make up 20 and thus answer yes to our intial question that we could indeed make up 20 from the original array.

Notes

Page 92

Abhishek Kumar

Data Structure

Let's see what happens at the second level of recursive algorithm. We know we have used 1 and we are looking to make a 19 from the rest of the array so again apply the same algorithm by making our target 19 but this time our array would be one elment short that is 1 would not be included in our search since we have already used it. Now we subtract 3 from 19 getting a 16. We search the passed in array to look for a 16 but again it doesn't. Note if it did we would add it to our list of integers and return success. However since it failed we again make a recursive call to our function, this time with target as 16 and we pass in an array two elements short that is without 1 and 3 since we have used them. At our third level of recursion, we subtract 5 from 16 to get an 11. We would again make recursive calls with target 11 and passing in the array with the unused integers that is without 1,3 and 5. This track would eventually fail. So we would come back to our third level of recursion, and try the next element in the array which is 6. So we subtract 6 from the target at the third level of recursion which is 10 and we again make a recursive call here but note that when we are passing the array to the fourth level of recursion we would include 5 in it since the integers now being used are 1, 3 and 6. This track would fail too. Finally we try element 7 at the third level of recursion. We subtract 7 from 16 to get a 9. We go to the fourth level of recursion with target = 9 and the array we input would be 5, 6, 8 and 9. We conduct our binary search, find 9, include it our integer list and return success. This travels up all the way back and we are back to the first time we made a recursive call with target as 20. We print out success. Here's a pseudocode for the above algorithm. target = 20; ListOfIntegers List; Array[] = {1, 3, 5, 6, 7, 8, 9}; Sort(Array); bool findIntegers(int[] Array,int target){ int index = BinarySearch(Array, target); if(index){ List.Add(Array[index]); return success; } foreach(element in Array){ bool found = false; List.Add(element); Array = Array without element; found = findIntergers(Array,target - element); if(found) return success; } else{ List.Remove(element); } } } The function BinarySearch would return the index of the target if it is found in the array and false if it isn't found. At the end List would contain all those integers which make up the target if the findIntegers function returns successfully

41. Given two arrays of numbers, find if each of the two arrays have the same set of integers ? Suggest an algo which can run faster than nlogn without extra space? Solution This is indeed a trick question since the first solution that comes to mind is to sort the two arrays and then scan the two arrays noting for any elements which aren't same. Here' obviously the assumption is

Notes

Page 93

Abhishek Kumar

Data Structure

that the arrays of same length. Sorting at its best can take O(nlogn) time and then the scan would take O(n) time for a total of O( n + nlogn) time which is essentially just O(nlogn) time. We can achieve O(n) time but by using O(n) space that is we declare an extra array and mark off the slot numbers we see in the array, say we see element 5 then we mary extraArray[5] = true to indicate we have seen the element 5 in the first array. Next we scan the second array and see if the elements in it are marked off in the corresponding array and signal if the slots are not already set to true. These are just the initial thought process solutions that you'll have but what the interviewer really wants to see is if you can come up with a solution better than O(nlogn) which uses less space. Since we are dealing with numbers, let's try some mathematical operations on them. If the two arrays are the same how about we add the numbers that the arrays contain. If their sum equals one another then the array are same ? Not so fast, the array -1,0,1 and the array -6,0,6 will have the same sum. To take care of negative numbers how about we square each element of the array and then add i.e. -1,0,1 would have a sum 1+0+1 = 2 while the array -6,0,6 would have the sum 36+0+36 = 72. So you might think you are done but the array -1,-2,-3 would have the same sum of squares as 1,2,3. So how about we calculate both the sum and the sum of squares for the two arrays and if they match we declare the array to be the same. Note that this would work if and only if both the arrays have integers and exactly the same number of elements. We can detect very easily whether both the arrays have same number of elements or not while finding the sum and sum of squares for both the arrays. Our total time for the algorithm is O(n+n) = O(2n) = O(n).

42. Question

Implent a function which given a string converts it into an int. Solution Say we are given the string "123" and we want to represent it as an int. The idea is very simple, we maintain a variable called number and initialized to zero. All we do next is that as we scan the array from left to rigth we read in a digit, multiply the number variable by the 10 and then add the just read in digit to the number variable. The reason we multiply the number variable with 10 would become clear if you note that if we read in the first digit 1, our number variable would be also equal 1 at the end of the iteration. The digit 1 is at one's place and before we process digit 2 we would want to move 1 to ten's place. One way of doing that is by multiplying the number variable by 10 before we add the digit 2 to it. Similarly before we add 3 to the number variable, we would again multiply the number variable by 10, pushing digit 1 to hundred's place and digit 2 to ten's place. Here's the code:

int string2int(char* array){ int i, number = 0; for(i = 0; i < length_of_array; i++){ number = number * 10; number = array[i] - '0'; } return number; } The point of asking this question in an interview is really to see if you take care of the following issues, which I delibrately leave out in my code:

The code won't work for negative numbers! Add a check for that If the string is say a 1000 characters long, when we try converting it into a number, we won't be able to fit the number in our integer variable. Overflow error. Error checking for string that contain characters other than digits.

Notes

Page 94

Abhishek Kumar

Data Structure

43. Question

Implent a function which given an integer converts it into it's string representation. Solution This is a very basic question and is a good candidate to be asked since most programming languages offer pre-built functions to do int to string and vice versa conversions for you. Say we are given the number 123 and we want to represent it as a string. We would want to break up the 1, 2 and 3 individually. One way to do is to start dividing 123 by 100. Note we will ignore the decimal porition of the quotient. so 123 divided by 100 gives quotient as 1 and remainder as 23 Next we divide the remainder 23 by 10 and get quotient as 2 Finally we divide the remainder from previous step i.e.3 by 1 and get quotient as 3

char* int2string(int number){ int remainder,quotient, divisor = 1, i = 0; string array[5]; //assume number less than 5 digits while( (divisor*10) < number) divisor*=10; while(number){ quotient = number/divisor; //storing the next element number = number % divisor; //assigning remainder to number array[i] = '0' + quotient; //converting to character divisor /= 10; i++; } return array; } Most people can come up with this code but you will have an edge if you mentiond the following two points to your interviewer and then put appropriate code for them:

The code won't work for negative numbers. Add a check for that The given code doesn't handle the number 0.

44. BFS struct Que q; //assume we have a struct Que already defined q.add(root); //assume add function ques the root void BreadthFirstSearch(){ while(q.isEmpty()){ //Go on till the que is empty node = q.deque(); //deque node at head of que if(node->leftChild) q.add(node->leftChild);

Notes

Page 95

Abhishek Kumar

Data Structure

Assume that we have to do a depth first search on the tree above and print the value of each node visited. then the order of visits would be visit node 4 visit node 2 visit node 1 BACKTRACK to node 2 visit node 3 BACKTRACK to node 2 BACKTRACK to node 4 visit node 6 visit node 5 BACKTACK to node 6 visit node 7 BACKTRACK to node 6 BACKTRACK to node 4 DONE void DepthFirstSearch(*node){ if(!node->leftChild && !node->rightChild){ //base case cout<<node->value<<endl; //Print value of the node return; } if(node->leftChild) DepthFirstSearch(node->leftChild); if(node->rightChild) DepthFirstSearch(node->rightChild); cout<<node->value<<endl; }

Notes

Page 96

Abhishek Kumar

Data Structure

*normal = head; *fast = head->next; *temp = NULL *newHead = NULL //this will point to our merged list while(!normal){ if(normal == fast){ declare the linked list to be circular; break; //exit the loop } normal = normal->next; //increment by one fast = fast->next->next; //increment by two } declare the list is non-cyclical Note that my while condition checks if the normal pointer has reached the end, you could make the loop run faster for non-cyclical lists if you checked for the fast pointers in the while loop condition, since the fast pointer would reach the end of the list in half number of increments. However note the asymptotic time complexity would still remain the same as O(n/2) and O(n) are still both O(n). 47. Write two sorted linked lists, write code to merge the two linked lists.. *trav1 = head1; //pointing to the head 1st list *tave2 = head2; //pointing to the head 2nd list *temp = NULL *newHead = NULL //this will point to our merged list while(trav1 && trav2){ if(trav1 && trav2){//if both lists haven't been traversed if(trav1->val < trav2->val){ temp = trav1->next;

Notes

Page 97

Abhishek Kumar

trav1->next = newHead newHead = trav1; trav1 = temp; } if(trav1->val > trav2->val){ temp = trav2->next; trav2->next = newHead newHead = trav2; trav2 = temp; } } if(!trav1 && trav2){//if list1 has all been traversed while(trav2){ temp = trav2->next; trav2->next = newHead; newHead = trav2; trav2 = temp; } } if(trav1 && !trav2){//if list2 has all been traversed while(trav1){ temp = trav1->next; trav1->next = newHead; newHead = trav1; trav1 = temp; } } } 48. Question

Data Structure

Given a string of numbers, write a function that generates all the possible permutations of the string of numbers?

Solution Very important question for serious software development positions! I got asked this question in my Amazon on-campus interview. I was also asked this question in a few other places. You could also get harder variations of the question such as what if you have a letter or a number repeated in a string since that decreases the number of total possible unique permutations.

The easiest way to think of this problem is in a recursive paradigm. Say I am given a string consisting of four character 1, 2, 3 and 4 i.e. 1234 and asked to show all the permutations for this string. Let me show you the code first and then we can dry run and explain what is going on. void permute(int position, int used[], int instance[]){ if(position == length_of_string+1){ cout<<instance; return; } for(i = 1; i < length_of_string; i++){

Notes

Page 98

Abhishek Kumar

if(used[string[i] == 0){ instance[position] = string[i]; used[string[i]] = 1; permute(position+1,used, instance); used[string[i]] = 0; }//if ends }//for ends

Data Structure

}//function ends So let' start with the description of each of the variables: Note: All arrays are numbered starting from 1. instance: this would store the one single permutation that we would output used: this is an array which is used to keep track of the characters that we used in creating one single permutation. Say in our case we would have each permutation four characters in length _ _ _ _. Say while executing our algorithm we filled the instance like 3 2 _ _ then the used array would reflect this by setting 0 1 1 0 i.e. the second element of used is set to 1 because 2 is already put in the instance. position: This variable is just used to track the position that is suppose to be filled next in the instance array. So if the instance array looks like 1 4 _ _ then position would be set to 3. Now let's start from scracth we start off with the statment permutate(1,instance, used) Where, position = 1 instance = _ _ _ _ used = 0 0 0 0 string = 1 2 3 4 length_of_string = 4 Now since position isn't equal to the length of the string we skip the base case and enter the for loop. Here since the used[1] = 0 we set instance[1] = 1 and make the recursive call to the function permute. permute(2,instance, used); position = 2 instance = 1 _ _ _ used = 1 0 0 0 Now we skip the base case again and enter the for loop. Now it is important to understand that the if(used[string[i]) condition just checks that the current character we are iterating on is already in the instance array or not. If its is then we need to find an unused character to put in our instance array and we keep iterating. If the character is unused then we enter the if condition, mark the character as used put it in the instance array at slot pointed by position and make a recursive call again to the permute function. In this second recursive call, 1 is already used so the for loop iterates over it, 2 is not used and it is put in slot 2 of the instance array and used array's second slot is marked 1. Note that the third permute call would be permute(2,instance,used); position = 3 instance = 1 2 _ _ used = 1 1 0 0 When in the third call we would again enter the for loop. Put 3 in third slot of instance array, mark the third slot of the used array 1 and make the fourth recursive call as permute(4, used, instance); position = 4 instance = 1 2 3 _ used = 1 1 1 0 Now in the fourth recursive call, the for loop iterates over 1 2 and 3 in the string and finally picks 4 puts it in the fourth slot of the instance array, marks the fourth slot of used array as 1 and make the fifth recursive call as follows: permute(5, used, instance)

Notes

Page 99

Abhishek Kumar

Data Structure

position = 5 instance = 1 2 3 4 used = 1 1 1 1 Now since the base case is met in the fifth recursive call because pos = length_of_string+1 therefore we output the instance 1 2 3 4 and return. Note that we return to the fouth recursive call, unset the used slot for 4 back to 0, but at the same time the for loop exists and we simultaneously exit the fourth recursive call. When exiting the fourth recursive this is how used array looks like 1 1 1 0. We track back to the third recursive call on the stack. We unset the third slot of used array back to 0 making the used array look like 1 1 0 0. This is important because now as the for loop continues it hits 4 in the string, checks the used array to find that 4 has yet not been used in the third position so it places it in the instance array and marks the used array as 1 1 0 1. Note the instance array would look like instance 1 2 4 _. Now we make the fourth recursive call and since 3 is unused it would be placed in the fourth slot of the instance array and on the fifth recursive call we would get the permutation 1 2 4 3. I guess this pretty much exlpains the basic design of the algorithm. It's much easier to explain this algorithm using a dry run like we did. Note that the variable position correctly places a character in the instance array and the used array makes note that a particular character has been placed in the instance array so that it is not used again. 49. Question How can you find if a machine is big endian or small endian ? Solution Most of you would remember this question from your Networks class. Simply put Endianess is the order in which a machine stores data. Say I want to store the number 786001 in my computer memory as an integer. The number consists of six digits 7, 8, 6, 0, 0 and 1. The most significant digit being 7 and the least significant digit being 1. You can think of the most significant digit as the digit most important or greatest in value. Since 7 represents the hundred thousands digit thus it is the most significant digit. Now number 786001 whose binary representation is 00010111111111001010001 will be stored inside a computer as bytes. Within a computer memory the binary representation of this number will be stored as following assuming we store the number in an integer variable of four bytes. Assume the addresses for the bytes start at 1. 00000000 00001011 11111110 01010001 Byte at address 1 Byte at address 2 Byte at address 3 Byte at address 4 However the attentive reader would question why couldn't the computer could have stored the same number just as well in the following manner? 01010001 11111110 00001011 00000000 Byte at address 1 Byte at address 2 Byte at address 3 Byte at address 4 The answer is that yes the computer can very well store the data this way and this is exactly the difference between the two kinds of endianess. The first way of storing data is where the most significant byte is stored at the lowest address i called Big Endian and the second way where the most significant byte is stored at the highest address is called little endian.

Given the above explanation all that one is required to do now is somehow figure out how a particular machine is storing data. The easiest way is to write a program. Say our machine stores integers as four bytes. If we are able to declare an integer in such a way that the first and the last bytes out of the four for the integer get distinct values then we have a way to test for endianess. Say we set the integer to 1 then 1 would be represented in the computer memory as follows: 00000000 00000000 00000000 00000001 Now the trick is simple if the machine is Big endian it will store 00000000 at the lowest memory address for the integer variable and if the machine is Little endian it will store 0000001 at the lowest memory address. All we have to do now is to check the value of the byte at the lowest memory address. If it is 1 we declare the machine to be big endian. If it is 0 we declare the machine small endian The remaining issue is how to check the value at the lowest memory address. One way is to use a character pointer since character type is usually one byte and have it point to the integer variable.

Notes

Page 100

Abhishek Kumar

Data Structure

char *ptr; int dumb = 1; ptr = &dumb; if((*ptr) == 0) { cout<<"Machine is big endian"<<endl; } else{ cout<<"Machine is small endian"<<endl; } The effect of setting the character pointer equal to the interger variable is that now the character pointer is only pointing to the first byte of the integer variable. So all you do is to check if your character pointer's value is 0 or 1. If it is 0 you know that the machine is storing the most significant byte at the lowest address and it is Big endian otherwise the machine is storing the most significant byte at the highest address of the memory and is consequently Little Endian. 50. Question

Suggest a way to make the loop run faster. Solution This question was actually asked in one of the on-site interviews at Microsoft. The question is sort of unfair if the candidate isn't asked to think in terms of assembly language.

Say you have the following loop. for(i = 0;i < 10; i++){ some code }

You should note the 10 in the for loop. 10 is a constant. This means that the loop will always be repeated for the same number of times. Why is this special? Because you should consider how is a loop implemented in the assembly language. Code in C Code in Assembly 1: check if the variable i is less than 10 else jump to 4 for(i=0; i< 10; i++){ 2: some code some code 3: jump to statement 1 } 4: Note that when the for loop executes, the processor executes statements 1, 2 and 3 ten times. That is statements 1 and 3 are the overhead. How can we speed up the loop? Simpy remove statemens 1 and 3 and write out statement 2 a total of ten times.

51. Say a request for memory allocation comes in requesting 5KB. Now though there is 5KB of memory free but it isn't contiguous. How would you allocate the memory ?

The answer I gave for the first part was to allocate the first free chunk of memory available and at the end of this chunk put a pointer to the next free chunk of memory. In this way the assigned memory would resemble a linked list. We also discussed other variations of how the hash table would grow and if it's cost could be amortized. 52. Implement the following two C functions . void * aligned_malloc(size_t bytes, size_t alignment); void aligned_free(void * p);

Notes

Page 101

Abhishek Kumar

Data Structure

aligned_malloc and aligned_free functions may only use the C runtime functions malloc and free in their implementation and cannot use any static memory. aligned_malloc takes the size of the buffer you would like to allocate and also alignment which is a power of two that will force the starting address of the buffer you return to the user to start on an alignment boundary. For example, I may request 1000 bytes starting on a 128 byte boundary by calling aligned_malloc(1000, 128). aligned_free frees the buffer returned from aligned_malloc. Write a program that reads N files from disk and removes any fragments that occur in all files, where a fragment is 3 or more consecutive words. Example: INPUT ----------File1.txt It is snowing and I want to drive home. File2.txt It is snowing and I want to go skiing. File3.txt It is hot and I want to go swimming. OUTPUT -------------Out.File1.txt It is snowing drive home. Out.File2.txt It is snowing go skiing. Out.File3.txt It is hot go swimming.

Example command line: ./program File1.txt File2.txt File3.txt The output files should be the input filename with "Out." prepended as seen in the example above. Just as a note, your code will be tested on more and larger files than the ones used in the example, so efficiency will be taken into consideration. We suggest that you concentrate on design and coding of fragment removal first and then file I/O. Do not spend too much time on file I/O Please include comments, a short design document, and instructions on how to compile and run your code under Linux. The program should take input file names from the command line. You are free to use any standard libraries (such as glibc and/or STL). Compress all your submissions into a single zip file. Assumptions:

1. You can ignore capitalization when matching fragments, but preserve it in the output. 2. You can ignore punctuation, i.e. you can strip it out.

You can make any further assumptions you'd like, but please specify them. This exercise is not as simple as it seems Consider the following string: IIIIIIamamamamamam

Say we are to remove the string "Iam" then when we reach the middle of the string we hit the first "Iam", we remove it but wait on, notice that the removel of "Iam" in the middle will result in another "Iam" sub-string being created. However since we have skipped over all the previous 'I' this problem becomes very interesting. I doubt this problem would have an elegant solution especially since in the original question the file is distributed over a number of files. 53.Fork fork() sytem call create child process to handle multi processing activities, It inherites following fields from parent. environment variables, Filedescriptor, data part

Notes

Page 102

Abhishek Kumar

Data Structure

54What is slack byte? Ans: To store any type of data in structure there is minimum fixed byte which must be reserved by memory. This minimum byte is known as word boundary. Word boundary depends upon machine. TURBO C is based on 8086 microprocessor which has two byte word boundary. So any data type reserves at least two byte space. Suppose a structure word1: struct word1 { char a; int b; char c; };

55 isBST1() Solution (C/C++) /* Returns true if a binary tree is a binary search tree. */ int isBST(struct node* node) { if (node==NULL) return(true); // false if the min of the left is > than us if (node->left!=NULL && minValue(node->left) > node->data) return(false); // false if the max of the right is <= than us if (node->right!=NULL && maxValue(node->right) <= node->data) return(false); // false if, recursively, the left or right is not a BST if (!isBST(node->left) || !isBST(node->right)) return(false); // passing all that, it's a BST return(true); } 56 You are provided with a max heap of 'n' elements with a number 'x' and 'k'...You have to find whether 'k' elements in heap are greater than 'x' or not?? Time Complexity should be O(k) Ans: bool checkforxK(struct node *Node, int x, int k) { static int nodeCount=0; if(Node->data > x) { nodeCount++; if(nodeCount==k) { return true; } else { if(!checkforxk(Node->left, x, k));

Notes

Page 103

Abhishek Kumar

{ return checkforxk(Node->right, x, k); } else { return true; } } } else { return false; } } but in general heap is implemented as an array...so Node->left = value at 2* Node_index Node->right = value at 2 * Node_index + 1 Are you implemented each node as an structure of 3 elements(Left, right value) ..it will increases space-complexity as DFS/BFS already takes O(k) space complexity...

Data Structure

57 Puzzle: There is a black table such that u can't see what is inside it..It contains 4 coins placed in at 4-different directions(north, east, south, west).You have to make all the coins facing towards head. You can do following operations: 1. Flip any one coin. 2. Flip adjacent coins. 3. Flip diagonal coins 4. Flip all coins. After your every answer table makes a random number of rounds(so u r not sure which coin was flipped previously) Also after every flip u can ask a question whether all points are facing head...(if all coins are facing head u will get 'yes' as an answer else 'no') The table can be in 16 states. As each coin can be head or tail so 2^4=16. Out of this 2 sates can be detected automatically i.e. if all are heads or all are tails. Now we are left with 14 states that can be similarly detected. There are only 3 cases possible: 1) All 4 of same type 2) 3 of one type 3) 2 of each type I have a startup approach. Lets build upon it to reach to full solution. 1) When all 4 are of same type, flip all once and call heads. If all 4 were tails initially then, you win. If the ans is no, then flip all again and call heads. If all 4 were heads initially, then you win. If still the ans is no, then the original count is maintained coz u flipped the coins twice. 2) For 3 of each type, flip any 1 coin and call heads. If the ans is yes, you win else flip all and call heads again. Explaination: When u have 3H,1T and u happen to flip tail and call heads, you win. Else at the end of above operations, you end up with 2H,2T If you had 3T,1H and you happen to flip heads, u have 4T and with the flip all u will have 4H and u will win. Else, u will end up with 2H,2T. (Try and see it) Now the problem has been reduced to 2H,2T. I'm still buiding upon this idea for 2H,2T. We can clearly see that, if the problem for 2H,2T is solved, then the problem can be solved completely 3) you have 2H&2T, flip the diagonal, and check for all Head, if yes ok, otherwise flip all check for all head.

Notes

Page 104

Abhishek Kumar

Data Structure

So if the diagonal were either 2H or 2T, then we got the solution otherwise we are left with the same 2H and 2T. 4)Now from the above step we know that diagonal coins are opposite, so we try for adjacent, hence we flip the adjacent and check for all head, if yes ok, otherwise flip all check for all head. there is still a possibility that adjacent might be opposite i.e.1H and 1T 5)Now this is the best step, since we have flipped the adjacent coins, this would have made the diagonals 2H and 2T so repeat step 3 58 Binary Matrix problem asked in Amazon interview you are given a M x N matrix with 0's and 1's find the matrix1. find the largest square matrix with all elements 1's 2. Find the largest rectangular matrix with all elements 1's Some Thoughts but not solutioins:

1.

1> any '1' in the matrix is a square of size 1. 2> we build a table say DP , we initialise it with all the '1' in the border of the matrix . i.e if matrix[0][j]='1' or matrix[0]='1' . 3> we check for DP[i-1][j-1] because if matrix[j] completes a square , there must be a subsquare at matrix[i-1][j-1] . if yes : we check if we can build a greater square with the already existing square at matrix[i-1][j-1] . if yes DP[j]=max( DP[j-1] , DP[i-1][j] , 1+ DP[i-1][j-1] ) if no DP[j]=max( DP[j-1] , DP[i-1][j] , DP[i-1][j-1] )

2.

now maintain a variable called maxsq=0 for each row iterate now for a given element in row iterate downward in column until the value of that element . If we are getting 0 before that just jump on next element in row. so , if we are able to iterate up to that much amount in a column than we can say that we have found a square of value w *w if w is value at that element. complexity O(n*n*n)

3.

well any '1' appearing at the border of the binary matrix is a 1 unit length square . We have to build on existing squares . i.e 1 1

Notes

Page 105

Abhishek Kumar

11

Data Structure

is a length '2' square , which we build from the previously computed 1 unit length length . we go on like this ..... if we can build upon a square at (i,j) we write val=1 else 0 makeSquare(i,j)= max( val+ makeSquare(i-1,j-1) , makeSquare(i,j-1) , maxSquare(i-1,j) Now how to decide whether we can build a square from i-1,j-1 m=DP(i-1,j-1) for( p=i-m ; p<=i; p++) if( matrix[p][j]!='1')val=0 ; for( p=j-m ; p<=j; p++) if( matrix[j][p]!='1')val=0 ; 60 There are 'n' arrays of size 'k' each All of them are sorted in ascending or descending order Our task is to pick 1 number from every array ( so that n numbers are selected) such that the range of the selected numbers is minimum. i.e. MAX(selected numbers) - MIN(selected Numbers)=Minimum How can we perform the above task in minimum Time complexity O(nklogn) solution assumption arrays are sorted in increasing order

make the min heap of the first element of each array. Also keep track of the largest element in the heap ( this is easy we can find the greatest element from the first elements of the arrays ) how calculate the difference between the largest element and smallest element( available on heap) . extract(delete) the minimum element from the element, now insert the next element of the same array of which the minimum array was extracted. Update the greatest element if necessary. Again find the difference max-min and proceed in the same way at the end we are in the position to tell what is the minimum difference.

61 Input is a matrix of size n x m of 0's and 1's. eg 1001 0010 0000 If 1 1 1 a location has 1; make all the elements of that row and column = 1. eg 111 111 011

Solution should be with Time complexity = O(n*m) and space complexity = O(1) First:

Notes

Page 106

Abhishek Kumar

Data Structure

1. Traverse the matrix once and if you find a one at position a[.i.][j], put a[0][.i.] and a[j][0] = 2

2. Now traverse the first row. if a[i][0] = 2, then set the ith row. 3. Traverse the first column if a[0][j] = 2, then set the jth column. Just check if 1 is in the first row or not. Time Complexity: O(mn) Space Complexity: O(1). Second:

1. Traverse each col from top to bottom and while traversing down do :

.......a) If a(i)(j)==0 and ( a(i-1)(j)==1 or a(i-1)(j)==2) ...then do a(i)(j)=2 ( 2 is .......used just to distinguish it from the other '1' nodes ) .......b)Traverse down to next element 2) Next traverse each col from bottom to up ....and do the same procedure as in above step ...

3) Traverse each row from left to right and while traversing right do : .....a)If a(i)(j)!=1 and ( a(i)(j-1)==1 or a(i)(j-1)==3 .....then do a(i)(j)='3' ( 3 is used to distinguish it from any '1' or '2' node. ......b)Traverse right to next 4)Next traverse each row from right to left and do same as above in step 3... 5) Finally change all the entries with value '2' and '3' to '1' ... 62You have 1 million co-ordinate in a file and now a point (x,y) is given to you ,you have to calculate the nearest 100 co-ordinates. Sol 1.Broute force : Calculate the distance of all n numbers and sort them . After that return first 100. Time : O(nlogn) Space : O(n) 2.Better Solution: Take first 100 element and store them and sort them , now take every number from million of numbers and replace them if necessary from this array of 100. Time : O(nm) Space : O(m) Where m = 100 in this case. 3.Best solution : Create a Max Heap of 100 and loop through all other elements and remove the maximum element if required and again heapyfy the heap . Time : O(nlogm) Space : O(m) Where m = 100 in this case. 63a city with several gas stations(coordinates in a 2-D axis) is given. We have to lay a pipeline which passes through this plane and perpendicular to it. After the pipe line is built, each of the gas stations is connected to it. The sum of lengths of each pipe should be minimum. solution to minimum distance problem X coordinate of the pipeline is the median of x coordinates of all the gas stations. Proof: imagine a vertical pipeline coming from -infinity , once it passes min(x coordinate of stations) sum of distances start increasing for stations to left of pipeline and it start decreasing for those right to it.Clearly magnitude of increase or decrease is same for all stations.so we keep sliding the pipeline until we have passed half many stations and we stop there. You may refer to Cormen or Aho, hopcroft and Ullman for o(n) time algorithm for finding median by proper partitioning of set.....

Notes

Page 107

Abhishek Kumar

Data Structure

64 write an efficient algorithm for looking up words with missing letters in a dictionary and I want the set of possible words. For example, if I have th??e, I might get back these, those, theme, there.etc. I was wondering if anyone can suggest some data structures or algorithm I should use. Thanks! EDIT: A Trie is too space-inefficient and would make it too slow. Any other ideas modifications? UPDATE: There will be up to TWO question marks and when two question marks do occur, they will occur in sequence. Currently I am using 3 hash tables for when it is an exact match, 1 question mark, and 2 question marks. Given a dictionary I hash all the possible words. For example, if I have the word WORD. I hash WORD, ?ORD, W?RD, WO?D, WOR?, ??RD, W??D, WO??. into the dictionary. Then I use a link list to link the collisions together. So say hash(W?RD) = hash(STR?NG) = 17. hashtab(17) will point to WORD and WORD points to STRING because it is a linked list. The timing on average lookup of one word is about 2e-7s. I am looking to do better, preferably on the order of 1e-9. 65Give a fast way to multiply a number by 7. Multiply by 8 (left shift by 3 bits) and then subtract the number. (x << 3) - x 66Given an eight-bit bitmap graphics file, devise an algorithm to convert the file into a two-bit ASCII approximation. Assume that the file format is one byte for every pixel in the file, and that the approximation will produce one ASCII character of output for each pixel. This problem is easier to solve than it sounds. This is one of the tricks used in technical interview questions. Problems may be obscured or made to sound difficult. Don't be fooled! Take the time to think about the core of the problem. In this case, all you want is an algorithm for reading the values in a file and outputting characters based upon those values. Eight-bit numbers can be in the range from 0 to 255. Two-bit numbers are in the range from 0 to 3. Basically, we want to divide the 256 numbers specified by an eight-bit number into four ranges, which can be indicated by a two-bit number. So, divide the range of 0 to 255 uniformly into four separate ranges: 0 to 63, 64 to 127, 128 to 191, and 192 to 255. You then have to assign an ASCII character to each of those four ranges of numbers. For example, you could use "_", "~", "+", and "#". Then, the algorithm is as follows: 1. Open the file. 2. For every byte in the file: a. Read in one byte. b. If the value is in the range 0..63, we'll print '_'. c. If the value is in the range 64..127, we'll print '~'. d. If the value is in the range 128..191, we'll print '+'. e. If the value is in the range 192..255, we'll print '#'. 3. Close the file. 67 Besides communication cost, what is the other source of inefficiency in RPC?

(answer : context switches, excessive buffer copying). How can you optimise the communication? (ans : communicate through shared memory on same machine, bypassing the kernel _ A Univ. of Wash. thesis) 68What does the term cast refer to? Why is it used? A Casting is a mechanism built into C that allows the programmer to force the conversion of data types. This may be needed because most C functions are very particular about the data types they process. A programmer may wish to override the default way the C compiler promotes data types. 69List C's storage classes and what they signify.

Notes

Page 108

Abhishek Kumar

Data Structure

static - Variables are defined in a nonvolatile region of memory such that they retain their contents though out the program's execution. register - Asks the compiler to devote a processor register to this variable in order to speed the program's execution. The compiler may not comply and the variable looses it contents and identity when the function it which it is defined terminates. extern - Tells the compiler that the variable is defined in another module. volatile - Tells the compiler that other programs will be modifying this variable in addition to the program being compiled. For example, an I/O device might need write directly into a program or data space. Meanwhile, the program itself may never directly access the memory area in question. In such a case, we would not want the compiler to optimize-out this data area that never seems to be used by the program, yet must exist for the program to function correctly in a larger context.

70What is the difference between: #include <stdio.h> and #include "stdio.h" They both specify a file for inclusion into the current source file. The difference is where the file stdio.h is expected to be. In the case of the brackets, the compiler will look in all the default locations. In the case of the quotes, the compiler will only look in the current directory. 71What is the difference between an lvalue and an rvalue? The lvalue refers to the left-hand side of an assignment expression. It must always evaluate to a memory location. The rvalue represents the right-hand side of an assignment expression; it may have any meaningful combination of variables and constants. 72What is the difference between malloc() and calloc()? The malloc() function allocates raw memory given a size in bytes. On the other hand, calloc() clears the requested memory to zeros before return a pointer to it. (It can also compute the request size given the size of the base data structure and the number of them desired.) 73 /* strcpy: copy t to s; pointer version 2 */ void strcpy(char *s, char *t) { while ((*s++ = *t++) != '\0') ; } 74 Why a double pointer can't be used as a 2D array? ------------------------------------------------This is a good example, although the compiler may not complain, it is wrong to declare: "int **mat" and then use "mat" as a 2D array. These are two very different data-types and using them you access different locations in memory. On a good machine (e.g. VAX/VMS) this mistake aborts the program with a "memory access violation" error. This mistake is common because it is easy to forget that the decay convention mustn't be applied recursively (more than once) to the same array, so a 2D array is NOT equivalent to a double pointer. A "pointer to pointer of T" can't serve as a "2D array of T". The 2D array is "equivalent" to a "pointer to row of T", and this is very different from "pointer to pointer of T". When a double pointer that points to the first element of an array, is used with subscript notation "ptr[0][0]", it is fully dereferenced two times (see rule #5). After two full dereferencings the resulting object will have an address equal to whatever value was found INSIDE the first element of the array. Since the first element contains our data, we would have wild memory accesses.

Notes

Page 109

Abhishek Kumar

We could take care of the extra dereferencing by having an intermediary "pointer to T": type mat[m][n], *ptr1, **ptr2; ptr2 = &ptr1; ptr1 = (type *)mat; but that wouldn't work either, the information on the array "width" (n), is lost, and we would get right only the first row, then we will have again wild memory accesses. A possible way to make a double pointer work with a 2D array notation is having an auxiliary array of pointers, each of them points to a row of the original matrix. type mat[m][n], *aux[m], **ptr2; ptr2 = (type **)aux; for (i = 0 ; i < m ; i++) aux[i] = (type *)mat + i * n; Of course the auxiliary array could be dynamic. 75 substring of zero and one [Amazon]

Data Structure

Given and Array of A[1..n] bits, find the length of longest consecutive substring of 0 and 1 such that number of 0s and 1s are equal

Go through http://discuss.techinterview.org/default.asp?interview.11.792102.26

76 Pythogarian triplets - lower bound? Given an array of 'n' integers. We need to find out all pythagorian triplets in the array. I mean numbers like a^2 + b^2 = c^2. What is the best way to do this? # O(n**2) time, O(1) space (after sorting the input) def findtriples(lst): lst.sort() for k in xrange(2, len(lst)): i=0 j=k-1 while True: d = lst[i]**2 + lst[j]**2 - lst[k]**2 if d == 0: print lst[i], lst[j], lst[k] if d <= 0: i += 1 if d >= 0: j -= 1 if i >= j: break if __name__ == '__main__': findtriples(list(xrange(1, 101)))

Notes

Page 110

Abhishek Kumar

#345 # 6 8 10

Data Structure

77Question 1 Reply the solutions if you have any so that we can discuss... Given an array of n elements (containing only positive numbers) and sum, X. Find the first two elements in the array that sum upto X eg: Array of elements - {2, 3,1000, 200, 51, 88, 29, 49, 65, 40, 98, 12, 3} Sum - 100. Eg: Given -- {40, 20, 10, 30, 80} Sum -- 50 The answer for the above sample is 51, 49. There are other possiblities also, but the first two numbers summing upto the given sum, 100 should be taken. How will you do this with minimal space and time complexities?

Possible Solution : 1. Brute Force O(n2) Simple 2 loops would do. 2. O(nLogn) + O(n) Space : Take 1 arrays and store index of the numbers in that array.

Now sort the array and keep other array such that index also move when you sort the first array. Ex. 10 3 20 2 30 4 40 1 80 5

Now start from first element and take one more pointer from thr end .Now find all possible combination (this is well known problem of finding all possible combination in an array) .Check that the index of the comination should be as required.

Question 2 Given two nodes of a binary tree (implemented in a linked list without a parent pointer) and a head pointer, find out the common ancestor of the two nodes with minimal space and time complexities...

Notes

Page 111

Abhishek Kumar

Data Structure

Question 3 This was asked in an interview with GOOGLE. Given an array (dont consider the data type of the element) of 2n elements with first n integer elements and next n character elements. i1 i2 i3 ....in c1 c2 c3 ....cn Write an in-place algorithm to rearrange these elements in the following order. i1 c1 i2 c2 i3 c3....incn. in-place algorithm means that the memory used in the algorithm other than the input array should not be dependent on the size of the input.... Also keep this in mind -- Time and space complexity.

78 Optimising Bit Counting using Iterative, Data-Driven Development Based on several blogs and discussion boards I've seen lately, it seems that the old interview question chestnut of bit counting is still in vogue. I suspect it's more common at companies that seek quantitative skills in their programming staff -like investment banks, Hedge Funds, internet startups run by ex-Googlers, etc. So what is it? Bit counting is the problem of reporting the number of set (1) bits in the binary representation of an integer. In Java there is a built in static method, Integer.bitcount(), that you could use but it is interesting to examine what the internals of such a method would look like. Many people have written on this topic, with varying degrees of accuracy and explanatory comments (a list of references comes later). I don't wish to repeat the good work others have done and I'm not proposing anything new that other smart people haven't already written about. Instead I'd like to outline a pragmatic approach that a sensible C# developer might go through in an interview situation when faced with such a question and where access to a Google search engine and the accompanying "instant-knowledge" is not available but a sensible solution needs to be delivered (hopefully it's close to optimal). By memorising the answer you might be able to rattle off a fast answer when needed, but IMHO it's far more valuable to understand the solution and more importantly the process taken to get there. i.e. Teach a (wo)man to fish, rather than give a (wo)man a fish. Alas, if you are after a fast answer, or a link to great reference material, skip to the end for instant gratification. Motivation You might ask yourself why people ask such interview questions when the answers can be found out using a search engine is no time. It's because they want to: a) examine your logical approach to a problem they hope you haven't seen before; and b) to find out the level of understanding you have about a particular problem if indeed you have seen it before. Bit Counting has many various solutions so even if you've heard this one before your interviewer can push you deeper and deeper into the problem until you hit unfamiliar ground and are forced to think on your feet - which is really the ultimate goal of all interview puzzle problems. Version 1: The "Naive" Solution Firstly, we try to formulate an approach that yields the right answer using the most obvious approach. The solution that most people will think of first is to convert the number to binary notation and then walk through the binary digits summing the ones that are set. Here is some C# code showing an extension method to do just that: public static uint BitsSetCountNaive(this uint input) { uint count = 0; while (input != 0) { count += input & 1; // logical AND on the LSB input >>= 1; // do a bitwise shift to the right to create a new LSB

Notes

Page 112

Abhishek Kumar

} return count; } Notice that:

Data Structure

1. We don't need to do anything explicit to convert the number from an integer into binary

representation.

2. Since pass-by-value is the default in C#, we can re-use the UInt32 variable, input, internally

without affecting the caller's version of this variable.

3. Figuring out if a bit is set or not is accomplished by doing a bitwise AND (&) of the input value

with 1 which has the effect of isolating and returning the least significant bit, and conveniently returning the value we need to increment the counter by. Doing such bitwise AND operations is often called masking.

4. Looping through each binary digit is accomplished by doing a bitwise shift to the right 1 place.

This just pushes all bits to the right 1 position and inserts zeros at the most significant bit (big) end.

5. For 32-bit unsigned integers that have lots of leading zeros in their binary representation, the

loop terminates before iterating through all 32 bits. This is because the right shift operation after counting the leftmost non-zero bit produces zero which triggers the while{} exit condition. In effect, this is a mask-add-shift loop which has no real memory overhead and running time of O(n) where n is the number of binary digits in the binary representation of the given integer. This approach is commonly referred to as the "Iterated Count". Note that running time is said to be O(n) despite the fact that the clever while loop will exit after processing the last non-zero bit. This is because asymptotic analysis refers to worst case (the upper bound), which in this case is all n bits hence O(n). Because of (4) above, this approach would work best when most of the set-bits are among the least significant bits. Version 2: Tightening the Loop Now we have a basic, naive solution we can iterate on it (in an Agile sense) to improve it's performance. The inital solution had running time of O(n) because we have to loop through all n binary digits. We could obviously improve that if we could somehow jump from set bit to set bit. How do we do this? Again we can use a simple trick with bitwise operators: public static uint BitsSetCountWegner(this uint input) { uint count; for (count = 0; input!=0; count++) { input &= input - 1; // turn off the rightmost 1-bit } return count; } Notice that:

1. We use the formula: x = x & (x-1) to turn off the rightmost set (1) bit. 2. The loop now iterates on the counter and we jump from set bit (1) to set bit, exiting when there

are no more set bits.

3. The function is called "...Wegner" after the guy who originally came up with it but more on that

later. Brian Kernighan from K&R C fame also mentions this approach in one of his books so many people incorrectly attribute this to him. This uses no more memory than version 1 (both have O(1) space complexity), but should yield better running times since we don't enumerate through all binary digits, just the set ones. Running time is ~O(b) where b is the number of set (1) bits in the binary representation of the given integer, and common sense tells us that this approach would be useful when there are few set bits in the input. i.e when the input is sparse. For this reason, this approach is sometimes called the "Sparse Ones" approach. Version 2b: From Sparse to Dense The astute programmer will intuitively realise that if there exists an algo optimised for sparse input then there must be a complementary one optimised for dense input. "Dense" input, in this case, refers to a binary number which has a large number of set (1) bits relative to the number of 0 bits. It is shown

Notes

Page 113

Abhishek Kumar

below for the sake of completeness:

Data Structure

public static uint BitsSetCountDense(this uint input) { uint count; for (count = 0; input != UInt32.MaxValue; count++) { input |= input + 1; // turn on the rightmost 0-bit } return 32-count; } Here we repetitively turn ON the rightmost 0-bit until all bits are set to 1 in which case the input would equal UInt32.MaxValue. Here the "magic" bitwise operation used to turn on the rightmost 0-bit is: x =x | (x+1). Alternately, the original number can be complemented (all bits are flipped), and we can count down from the max theoretical bit count, 32 as follows:

public static uint BitsSetCountDenseAlternate(this uint input) { uint count; input = ~input; // complement (flips all bits) for (count = 32; input != 0; count--) { input &= input - 1; // turn off the rightmost 1-bit } return count; } Sparse Ones and Dense Ones were first described by Peter Wegner in A Technique for Counting Ones in a Binary Computer, Communications of the ACM, Volume 3 (1960) Number 5, page 322. Evaluation of V1 and V2 Regardless of what asymptotic anaylsis tells us, it's prudent to measure and compare the alternatives since we are data-driven. Accordingly, we could write a quick test case as follows:

static void Main() { const int LoopMax = 1000000; // test code for bit counting uint xx = 3160637183; // test case 1 Stopwatch sw = new Stopwatch(); sw.Start(); uint ans = 0; for (int l = 0; l < LoopMax; l++) ans = xx.BitsSetCountNaive(); Console.WriteLine(ans); Console.WriteLine(String.Format("Timing for Naive bit count: {0}", sw.ElapsedTicks)); // test case 2 sw = new Stopwatch(); sw.Start(); for (int l = 0; l < LoopMax; l++) ans = xx.BitsSetCountWegner(); Console.WriteLine(ans); Console.WriteLine(String.Format("Timing for Wegner bit count: {0}", sw.ElapsedTicks));

Notes

Page 114

Abhishek Kumar

Data Structure

Console.ReadLine(); } This test is very crude in that it doesn't use a variety of inputs. However, we know that the algorithm proposed in Version 2 should definitely outperform the Ver.1 algo when there is only a few set (1) bits in the binary number. So our choice of 3160637183, which has 23 set bits out of 32 bits in total, should not favour the Ver. 2 algo very much. That said, the test figures we get are good enough to give us a relative measure: 23 Timing for Naive bit count: 588842 23 Timing for Wegner bit count: 451542 The data backs up our asymptotic analysis. We see a 25% improvement from Ver. 1 (Naive) to Ver. 2 (Wegner) which correlates to scanning 23 bits rather than 32 bits. Version 3: The Penny has Dropped - Lookup Tables A 32-bit unsigned integer repesents a finite number of integer values, 2 32 = 4,294,967,296 distinct values. The number of bits set in each of these integer representations does not change so it would be possible to precompute these values, or to use memoization /caching to store them after first use. If we have some sort of lookup table we could get constant run time, at the expense of space complexity. The feasibility of this approach depends on the environment and where the resource bottlenecks are. Often an interviewer will qualify the question with something like "you have unlimited memory storage". That's your queue to employ a lookup table. But what sort of lookup table should be used? We need an 8-bit byte data type in C# to represent values in the range 0-32. Storing over 4 billion of these in a lookup array would consume 4 GB of memory. Since summing the 1-bits in a 32 bit number is the same as summing the 1-bits in both the lower and upper 16 bits separately and then adding them together, we can employ a divide-and-conquer approach here and use a byte array lookup table that only has 216 = 65,536 distinct values. This would consume only 64 KB of memory, a much more acceptable overhead. Here's what the code looks like: static readonly byte[] bitCounts = new byte[UInt16.MaxValue]; private static bool IsInitialized; // will be false by default static public void InitializeBitcounts() { for (uint i = 0; i < UInt16.MaxValue; i++) { // Get the bitcount using any method bitCounts[i] = (byte)i.BitsSetCountWegner(); } IsInitialized = true; } public static uint BitsSetCountUsingLookup(this uint i) { if (!IsInitialized) InitializeBitcounts(); return (uint)(bitCounts[i & 0xFFFF] + bitCounts[(i >> 16) & 0xFFFF]); } Of course, it's possible to precompute 8-bit values and then break the 32-bit input value into 4 blocks of 8, each of which can be looked up from the table. Note that we are assuming that the lookup table can be Version 4: The Quest for (Near) Constant Time without Memory Overhead Getting this far is pretty good - we have a fast method albeit with some memory overhead. But what if the environment does not allow for additional memory being used for lookup tables? Is there a (near) constant time algo that doesn't have the memory overhead required by the previous solution? The clue to this approach is mentioned above: divide-and-conquer, combined with some parallel processing. Many algorithms employ a divide-and-conquer approach and end up with running time ~O(log2 n) because they successively divide the problem in half until it cannot be divided in half any

Notes

Page 115

Abhishek Kumar

Data Structure

more. Such an approach is taken by binary search, balanced binary tree data structures, Fibonacci search, etc. In our case, we know that we can partition the 32-bit integer any way we please and sum the bits of all the parts individually and easily compute the required value by summing these interim counts. Furthermore we know that some counting operations can be done in parallel using cleverly crafted bitwise operators. To see this consider the case where we want to sum adjacent bits. Bits can be 0 or 1, so the value of the sum will be 0, 1 or 2 (in decimal), which is 00, 01 or 10 in binary. In other words, we can sum 2 individual bits and store the resulting sum in those same 2 bits! This can be expressed mathematically as follows: a = (a & 0x55555555) + ( (a >> 1) & 0x55555555) Here, the hexadecimal constant 0x55555555 represents a binary mask (01010101010101010101010101010101) that picks up every even-numbered bit. That is, the 2 0, 22, 24, etc positions in the binary representation. Doing a right shift by one position and applying the mask again picks up all the other, odd-numbered bit positions, 2 1, 23, 25... Hence the above equation sums adjacent bits and stores the results in-place. This can be extended to sum the adjacent 4-bits, then the adjacent 8-bits, and finally the adjacent 16bits to get our answer. All we need to do is adjust the bit mask used at each step and the magnitude of the shift operator. Here's the code to do this in C#:

public static uint BitsSetCountUnOptimised(this uint i) { i = (i & 0x55555555) + ((i >> 1) & 0x55555555); // adjacent bits grouped i = (i & 0x33333333) + ((i >> 2) & 0x33333333); // adjacent 2-bit groups paired i = (i & 0x0F0F0F0F) + ((i >> 4) & 0x0F0F0F0F); // adjacent 4-bit groups paired i = (i & 0x00FF00FF) + ((i >> 8 ) & 0x00FF00FF); // adjacent 8-bit groups paired i = (i & 0x0000FFFF) + ((i >> 16) & 0x0000FFFF); // adjacent 16-bit groups paired return (i & 0x0000003F); // a 32-bit unsigned int can have at most 32 set bits, // which in decimal needs on 6 bits to represent } Version 5: Finesse and Extreme Optimisation We've already made some great leaps forward in efficiency and asymptotic run time but believe it or not there are further optimisations that can be made. First, note that the last logical AND is not necessary since it right shifts 16 places which would leave only 16 bits so the masking is redundant. As well, there are other logical ANDs that can safely be removed when there is no danger that a field will carry over into the adjacent field. In our case, the grouping of adjacent 4-, 8- and 16-bit groups can have redundant logical ANDs removed. Finally, the first line can be trimmed from 4 operations down to 3 by replacing it with: x = x - (( x >> 1) & 0x55555555); The revised code now looks like: // taken from Hacker's Delight, chapter 5, pp66 public static uint BitsSetCountOptimised(this uint i) { i = i - ((i >> 1) & 0x55555555); // adjacent bits grouped i = (i & 0x33333333) + ((i >> 2) & 0x33333333); // adjacent 2-bit groups paired i = i + ((i >> 4) & 0x0F0F0F0F); // adjacent 4-bit groups paired i = i + (i >> 8); // adjacent 8-bit groups paired i = i + (i >> 16); // adjacent 16-bit groups pair return (i & 0x0000003F); // a 32-bit unsigned int can have at most 32 set bits, // which in decimal needs on 6 bits to represent } Putting this through a crude timing check we see the following results for 1 million iterations. The figures are total elapsed ticks: Timing for Lookup bit count UnOptimised: 224970 Timing for Lookup bit count Optimised: 214467 Timing for Lookup-based bit count: 131663

Notes

Page 116

Abhishek Kumar

Data Structure

This shows that the lookup table-based approach is still the fastest but the optimised approach given above is not too far behind. It is elegant in that it is branch-free (branching is usually a more expensive operation) and employs parallel bit counting. If you have an inquisitive mind you are probably already asking yourself if the function can be made even faster by working with adjacent groups bigger than 2-bits. Well, it certainly can. In fact this was the approach taken by HAKMEM Item 196 (see references below). The solution offered there used adjacent 3-bit groups. This was further extended by other researchers to work with - you guessed it adjacent 4-bit groups. Here's some C# code to do just that: // works by counting adjacent 4-bits public static uint BitsSetCountHackMem(this uint i) { uint n = (i >> 1) & 0x77777777; i = i - n; n = (n >> 1) & 0x77777777; i = i - n; n = (n >> 1) & 0x77777777; i = i - n; i = (i + (i >> 4) & 0xF0F0F0F); i = i * 0x01010101; return i >> 24; } So how does it compare? Because we're data-driven rather then theory-driven, let's measure it and let the data tell us: Timing for Lookup bit count UnOptimised: 216632 Timing for Lookup bit count Optimised: 205692 Timing for Lookup-based bit count: 104143 Timing for modified HAKMEM bit count: 83938 Damn, that's fast! It's on par with the lookup-table based approach and it doesn't have any memory overhead! That's good enough for me. At this point we're probably going to see diminishing returns for further optimisation efforts so let's settle on this approach. One final bit of advice: your interviewer more than likely doesn't want a 20 minute sermon on the topic of bit counting. Think carefully before launching into a lengthy discussion upfront. Advanced Topics If you've dazzled the interviewer with your knowledge and reasoning skills it's more than likely they'll realise that you've seen the problem before and you'll get pushed onto the more esoteric issues of this vast topic. Here are a few you can think about in advance:

If you were building a framework for other developers to use, and you needed to expose a BitCount() method, what would your method internals look like? Further Research This problem has several common names:

Why can't we apply these algorithms to signed integers? Can you suggest some architecture-dependent optimisations? How would you determine what constitutes a spare/dense number prior to bit counting? Can you suggest an algo which is cache oblivious? Can you think of some examples where you might need to use bit counting? What is the worst case asymptopic run time of the above mentioned algorithms?

Sideways Sum / Sideways Addition Useful References Hackers Delight, Chapter 5 Bitwise Tricks and Techniques by Don Knuth (The Art of Computer Programming, Part IV) Sean Anderson's Bit Twiddle Hacks HAKMEM Item 169: MIT AI Memo 239, Feb. 29, 1972 Discussion of the Problem on StackOverflow

Notes

Page 117

Abhishek Kumar

Data Structure

Gurmeet Singh's Weblog with C code Jesse Farmer's Blog Magic Algorithms Bit Twiddle Basics All of the below mentioned bit twiddling hacks have been elucidated from a magnificent book called Hacker's Delight by Henry S. Warren Jnr. If you want comprehensive coverage of low-level, highly efficient tricks on various processor architectures this is a book you must read.

1. 2. 3. 4. 5. 6. 7. 8. 9.

To turn off the rightmost 1-bit in x: To isolate the rightmost bit in x: To isolate the rightmost 1-bit in x: To isolate the rightmost 0-bit in x: To create a mask for all trailing zeros: NOT) To check if x is a power of 2:

x & (x-1) x&1 x & (-x) x & (x+1) ~x & (x-1) (~ is the one's complement, aka bitwise

To turn off the k-th bit for the right in x: x & ~(1<<k) x & (x - 1) == 0 ~(T)0 To toggle multiple bits at one: cretae a mask for the bits to toggle, M, and use: x ^ M To find the max value of a data type T, cast zero as T then complement:

79 In-Memory Sorting of Large Lists Problem: You have 1 million objects in memory (it's a big machine). Each object represents a customer and therefore has a Name and AgeInYears property. You need to sort the customers by AgeInYears. Solution: Many technical folks immediately rack their brain over the various sorting algorithms and if they are any good will offer up quickSort, or maybe mergeSort, and tell you it has O(n.log 2n) running time. But you need to stop and think about the problem for a minute and not be constrained by textbook knowledge. What's the age range of the customers? It couldn't be more than a hundred or so right (0-100)? So how about we create 100 lists - one for each age in this range - and then make a single pass through all 1 million customers adding them to the relevant list. That involves precisely n comparisons thus this has O(n) run time complexity. Of course we still need to merge the 100 lists to get the end result but that is trivial to do and doesn't add to the run time complexity. Now that's thinking outside the box! 80 Creating a String Representation of an Integer Question: Without using any system-provided function [eg. Int32.ToString() ], write a function that takes an integer and returns a string of the value. Answer: This is fairly trivial but a seemingly common question to throw at programmers. All you need to do is apply some modular arithmetic to iterate over each digit fetching the string value as you go. The edge cases for negative numbers and 0 still seem to catch many people out! It's trivial to extend this algorithm to bases other than 10. Here's some code in C# to do it: public static string ConvertInt(int i) { var digits = new [] {"0","1","2","3","4","5","6","7","8","9"}; var result = "0"; bool isNegative = (i < 0); if (isNegative) i = i * -1; while (i != 0) { result = digits[i%10] + result; i = (i - i%10) / 10; } if (isNegative) result = "-" + result;

Notes

Page 118

Abhishek Kumar

Data Structure

return result; } 81 Finding the Minimum Without If Question: Write a function (in C#) to find the minimum of 2 integers. You cannot use any inbuilt functions that provide this functionality and you cannot use any flow-of-control statements like "if" and "while". Answer: It would be darn easy if it wasn't for those restrictions in the question! So we need to think outside of the box which is the point of this particular problem. You've probably already guessed that it involves a mathematical trick. Here is a function that uses absolute values... try and see if you can figure out why it works static int Min(int a, int b) { return (int)(0.5 * (Math.Abs(a + b) - Math.Abs(a - b))); } static void Main() { int a = 45; int b = 67; Console.WriteLine( Min(a,b) ); } 82 Swapping Integers static class SwapInts { public static void SwapWithXOR(ref int a, ref int b) { a ^= b; b ^= a; a ^= b; } public static void SwapWithAlgebra(ref int a, ref int b) { checked { a = a+b; b = a-b; a = a-b; } } public static void SwapWithTemp(ref int a, ref int b) { int t = a; a = b; b = t; } unsafe public static void SwapWithXORUnsafe(int* a, int* b) { *a ^= *b; *b ^= *a; *a ^= *b; }

Notes

Page 119

Abhishek Kumar

unsafe public static void SwapWithTempUnsafe(int* a, int* b) { int t = *a; *a = *b; *b = t; }

Data Structure

83 Random Number Generator Question: Given an unbiased PRNG which returns values in the range 0-5, how could you use this function to produce unbiased random numbers in the range 0-7. Answer: Most people immediately think that they could run the Rand5() twice and return the sum mod 8 however this doesn't produce unbiased results. To get unbiased results every possible result needs to have the same probability. Consider what the numbers 0-5 look like in binary: 0 = 000 1 = 001 2 = 010 3 = 011 4 = 100 5 = 101 Notice that we have 6 values in total. This is nice and even and will help us no doubt. Now look at the least-significant bits (LSB) of these 6 values. Three have a one and three have a zero. i.e. 0s and 1s have an equal likelihood of appearing in the LSB of the returned random number. The same cannot be said of the other 2 columns. Now let us add 6 and 7 to our binary table: 0 = 000 1 = 001 2 = 010 3 = 011 4 = 100 5 = 101 6 = 110 7 = 111 This table shows the binary representation of all possible values that could be returned from the function we need to produce - Rand7(). Examining the distribution of zeros and ones in each column we can see that every column have an even distribution of 0s and 1s. So we can produce Rand7() as follows: static uint Rand7() { return (uint)(((Rand5() & 1) << 2) + ((Rand5() & 1) << 1) + (Rand5() & 1)); } What this is doing is calling the Rand5() function 3 times, masking off the LSB which we know will be evenly distributed between 0s and 1s, and shifting them into each of the 3 bit positions, then converting the resulting binary representation to an unsigned integer. The result is an unbiased pseudo-random drawing from the range 0-7 inclusive. OR Call Rand5() 7 times and then divide It by 7. 84 Sum of an Arithmetic Progression An arithmetic progression is a sequence of numbers where the difference between 2 successive numbers is constant. An example is: 1,2,3,4,5,6,7,8... This sequence is increasing by one each time. So, the more general definition for the i-th term in an arithmetic progression, where d is the constant difference, and a0 is the first term in the sequence, is given by the formula:

Notes

Page 120

Abhishek Kumar

Data Structure

There is a formula to find the sum of the first n terms but you need not remember it since you can derive it on the spot with common sense. Assuming the simple case where d=1, if you pick the first and last element in the sequence and sum them you get n+1. Ignoring these 2 items from the list, if you do the same thing again you get 2+(n-1)=n+1, the same result. And if you do it again, you get the same result again: 3 + (n-2) = n+1. So a pattern emerges - selecting and removing the first and last element in the sequence and summing them gives (n+1) so the sum of all of these must be that number, n+1, multiplied by the number of pairs you can extract from the sequence, which is in fact n/2. Hence the sum of the first n terms in this particular sequence is (n+1)*n/2. A similar process can be applied regardless of the value of d. Another way to visualise the result is to write down the sequence of numbers, then just below it write down that same sequence but in reverse order. Now sum each vertical pair and you'll see the same pattern: n/2 lots of (n+1).

It's amazing how many mathematical puzzles and technical interview questions reduce to this simple summation! Don't believe me - well here's a sample of puzzles/questions that are all easily solved if you know the sum of an arithmetic progression:

1. In a sequential search, what is the average number of comparisons it takes to search through

n elements?

2. Given 100 boxes each of which, except one, contains 100 x 10g weights, whilst the remaining

box has 100 x 9g weights in it. You need to identify the box that is lighter than the others but you can only use the scales once. [Thanks to Phil Lancaster, a work colleague of mine for throwing this devious puzzle at me one day.]

3. Prove that the worst case run time for a bubble sort is O(n2) 4. You have an array of size n-1 which is suppose to contain all the integers between 1 and n,

without duplicates, except there is a missing number. You need to find the missing number using a constant-space algorithm

without duplicates, except two numbers are missing. You need to find the missing numbers using a constant-space algorithm Answers

5. You have an array of size n-2 which is suppose to contain all the integers between 1 and n,

1.

If we assume that the answer to our search could be in any position from 1 to n, all with equal likelihood (i.e. a Uniform distribution, aka the distribution of maximum ignorance), then the average number of comparisons is equal to the sum of all possibilities divided by n. That is, the sum of the arithmetic progression above divided by n, which is (n+1)/2 Enumerative combinatorics tells us that we'd need, at most, log 2(n) weightings to identify which box has the lighter blocks - divide all remaining boxes into 2 halves, weigh one half of the boxes as a total unit, pick the lighter half, and repeat the process - but we are allowed only one! So consider redistributing the weights as follows: take all the weights out marking them with the box number they came from. Then, using one empty box, put 1 block from box1, 2 blocks from box2, 3 blocks from box3, ..., n-1 blocks from box(n-1) and n blocks from box(n). From our knowledge of the sum of an arithmetic progression we know what the total weight of the box, if all the blocks were 10g, would be 100/2*(100+1)*10g = 5050g. Now, box(j) contributes j blocks each of which is 10 grams for a total contribution of 10. j grams. If box(j) is the lighter box then it will only contribute 9. j grams, a difference of exactly j grams. Blocks from all other box will not deviate from there expected weights since only 1 box originally had lighter blocks. Given this, it follows that the weight difference between actual weight of the specially prepared box and theoretical total weight using our formula always gives the precise box number of the original light box. The bubble sort uses pairwise comparisons to sort lists. The worst case for the bubble sort is when the list is in strictly descending order when you are trying to put it into ascending order. In this case the algorithm compares the first element to ( n-1) other elements, and then compares the second element to (n-2) elements to it's right, and then compares the third element to (n-3) elements to it's right, etc, etc. So in total the number of comparisons is (n-1) + (n-2) + (n-3) + (n-4) + ...+ 2 + 1 = n/2(n-1). Expanding this out to polynomial form it has it's highest power of n being 2, hence complexity theory classes this as O(n2).

2.

3.

Notes

Page 121

Abhishek Kumar

Data Structure

4.

This seems simple at first - just iterate through the list updating an array to indicate which items you have, but this approach does not meet the constant space limitation imposed in the wording of the question. It would require O(n) space. i.e the space required grows linearly with the size of the list. But if we allocate some space to store a running total which we update as we iterate through the list, once we reach the end of the list we can compare the running total with the theoretical total that we can calculate using, you guessed it, the sum of an arithmetic progression we can easily identify the missing integer. This approach has constant space complexity. This problem is an extension of the previous one. In the previous case we had a single equation with a single unknown that was easily solved. Now we have a single equation with 2 unknown numbers. If you remember anything about solving simultaneous equations you'll know this is not a good situation. However, once again we can make use of some simple algebra to help out. Instead of summing all the terms we can calculate the product of them. The theoretical value for this, n!, is known assuming n isn't too large, so we can compare the calculated value to the theoretical value and now we have 2 equations which we can use to solve for the 2 unknowns, and we've used only constant space complexity to do it. Viola!

1.

85 Loop in a Single Link List: So here is such an argument that they will eventually meet: after the slow and the fast pointers (so to speak) are initialized to i and i+1, it will take exactly n-1 iterations for them to meet. To make sense of it, consider this: after n-1 iterations, the slow pointer will be at (i+n-1)%n th position which is equivalent to (i-1)%n. The fast pointer will be at: (i+1 + 2(n-1))%n = (i+1+2n -2)%n = (i-1+2n)%n = (i-1)%n th position. Hence after n-1 iterations, both will be at (i-1)%n th position which means they both meet. But, this is slower than the alternative where we keep the first pointer static and move only the second pointer. 86Sort an array containing '0' and '1' You have an array containing only '0's and '1's. Sort this array in minimum time. It seems easy, sum all the elements in the array and then reset the array with those many '1's in the end and rest '0's in the beginning. Time complexity is also O(n) with constant space. So it seems the best and easy one. But if you are OK with this, please read the question again - MINIMUM TIME, it doesn't mean the minimum complexity. In the above solutions you are having 2 passes, one for taking sum and another for setting the array. This can be done in a single pass: just start two pointers, ptr1 from starting of the array(I should say from -1) and ptr2 from end of the array(I should say from length of the array). while ptr2>ptr1 increment ptr1 if (array[ptr1] == 0) continue; else decrement ptr2 until array[ptr2] == 1; if(ptr2> ptr1) swap array[ptr1] with array[ptr2]

C code:

Notes

Page 122

Abhishek Kumar

#include <stdio.h> #define MAXSIZE 100 #define SWAP(a,b) a^=b^=a^=b void main() { int array[MAXSIZE], i, j, len=-1; printf("Enter array of 0 and 1, enter -1 to discontinue: "); do{ len++; scanf("%d",&array[len]); }while(array[len] != -1);

Data Structure

i= 0; j = len;

Now the real challenge is to sort an array containing only 0,1,2 in one pass.

87Kth Minimum element in a tree Question: WAP to find Kth minimum element in a Binary Searth Tree.

Notes

Page 123

Abhishek Kumar

Data Structure

Answer:

Count number of nodes in the tree if they are less then k return. If (no_of_nodes(node->left) == k-1) return node->data If (no_of_nodes(node->left) >= k) findkthmin(node->left,k) else findkthmin(node->right, (k - no_of_nodes(node->left) -1))

struct treenode{ int data; struct treenode * left; struct treenode * right; };

int no_of_nodes(struct treenode * node) { if(node == NULL) return 0; else return(1 + no_of_nodes(node->left) + no_of_nodes(node->right)); }

int findkthmin(struct treenode * node, int k) { int no_nodes = no_of_nodes(node->left); if (no_nodes == k-1) return node->data;

Notes

Page 124

Abhishek Kumar

if (no_nodes >= k) return findkthmin(node->left,k); else return findkthmin(node->right, (k - no_nodes - 1)); }

Data Structure

void main() { struct treenode * tree; int k, min; tree = createtree(); /*create a BST*/ scanf("%d",&k); if (no_of_nodes(tree) < k) { printf("Number of nodes in tree are less than %d \n",k); return; } min = findkthmin(tree, k); printf("kth min element = %d\n",min); }

This solution can be optimized by saving the no of nodes in each subtree while calling no_of_nodes(tree) for root.

88Arranging sequence Question: we have an array of 2n elements like "a1 a2...an b1 b2...bn". WAP to rearrange the array as "a1 b1 a2 b2...an bn" time complexity is O(n) no extra array or memory can be taken... Answer: I have an answer but could not meet the complexity requirement.

Notes

Page 125

Abhishek Kumar

C code: #include<stdio.h> #define SWAP(a,b) a^=b^=a^=b int string[100]; int m;

Data Structure

void Replace(int n,int start){ int i,j; for(i=start,j=0;j<n;i++,j++) { SWAP(string[i],string[n+i]); } n=n-1; if(n>0) Replace(n,start+1); } main() { int i; printf("Enter no of elements (max 100): "); scanf("%d",&m); if(m%2){ printf("odd nos\n"); } else{ printf("Enter numbers: "); for(i=0;i<m;i++) scanf("%d",&string[i]); for(i=0;i<m;i++) printf("%d\t",string[i]); printf("\n");

Notes

Page 126

Abhishek Kumar

Data Structure

Output:

Enter no of elements (max 100): 12 Enter numbers: 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 5 6 7 8 9 10 11 12 1 7 2 8 3 9 4 10 5 11 6 12 Output with intermediate steps: Enter no of elements (max 100): 12 Enter numbers: 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 5 6 7 8 9 10 11 12 1 7 8 9 10 11 2 3 4 5 6 12 1 7 2 3 4 5 8 9 10 11 6 12 1 7 2 8 9 10 3 4 5 11 6 12 1 7 2 8 3 4 9 10 5 11 6 12 1 7 2 8 3 9 4 10 5 11 6 12 1 7 2 8 3 9 4 10 5 11 6 12

89 Level order tree traversal in reverse order Question: Write an algorithm to print a binary tree level wise and that too from leaves to root. Example:

Notes

Page 127

Abhishek Kumar

Data Structure

Answer: We all know the algorithm of BREADTH-FIRST-TRAVERSAL: BreadthOrderTraversal(BinaryTree binaryTree) { Queue queue; queue.Enqueue(binaryTree.Root); while(Queue.Size > 0) { Node n = GetFirstNodeInQueue(); queue.Enqueue(n.LeftChild); //Enqueue if exists queue.Enqueue(n.RightChild); //Enqueue if exists queue.Dequeue(); //Visit } } And this will give the answer as 1 2 3 4 5 6 7, one can think of reverse of it, but it will print the nodes at a particular level in reverse order, which won't solve our purpose.

This problem can be solved by Enqueuing Right child prior to left chile, i.e. : BreadthOrderTraversal_1(BinaryTree binaryTree) { Queue queue; queue.Enqueue(binaryTree.Root); while(Queue.Size > 0) { Node n = GetFirstNodeInQueue(); queue.Enqueue(n.RightChild); //Enqueue if exists queue.Enqueue(n.LeftChild); //Enqueue if exists queue.Dequeue(); //Visit

Notes

Page 128

Abhishek Kumar

} } This will give us output as 1 3 2 7 6 5 4 and reversing this will give 4 5 6 7 2 3 1.

Data Structure

So printing those in reverse order can be easily done by putting them in stack and print when complete tree is scanned. BreadthOrderTraversalReverse(BinaryTree binaryTree) { Queue queue; queue.Enqueue(binaryTree.Root); while(Queue.Size > 0) { Node n = GetFirstNodeInQueue(); queue.Enqueue(n.RightChild); //Enqueue if exists queue.Enqueue(n.LeftChild); //Enqueue if exists stack.push() = queue.Dequeue(); //Visit } while(!isEmpty(stack)) print(stack.pop); }

90Valid parenthesis Sequences Question: Print all valid parenthesis Sequences for a given number of pairs of parenthesis. Example: For n =2 {}{}, {{}} For n = 3 {{{}}}, {}{}{}, {{}}{}, {}{{}}, {{}{}} Answer: If you have heard about Catalan number, you might know that valid number of sequences are ((2n)!)/((n+1)! * n!) Algo: func(len) //Input: //length: current length of string formed //Output: //Whenever a result is found display() function is called if(len = 2*n) ____display(out) ____return; out[len] = '(' //Append ( to out if(isValid(s1)) ____func(len+1) out[len] = ')' //Append ( to out if(isValid(s1)) ____func(len+1)

Notes

Page 129

Abhishek Kumar

Data Structure

Code: #include <stdio.h> #define N 4 //Number of parenthesis pair int n; char out[N *2+1] = {0}; int isvalid(int len) { int open, close, i; open = close = 0; for (i = 0; i <= len; i++) { if (out[i] == '(') open++; else close++; if ((open < close) || (open > N)) return 0; } return 1; } void generate(int len) { if (len == (N *2)) { printf("%s\n", out); return ; } out[len] = '('; if (isvalid(len)) generate(len + 1); out[len] = ')'; if (isvalid(len)) generate(len + 1); } int main() { generate(0); return 0; } But this solution has a lot of overhead of isvalid() function. If we can check the validity of sequence in generate() itself by passing 'open' and 'close' count, this overhead can be reduced a lot. Below is a modified and more efficient program for the same. #include <stdio.h> #define N 4 int n = N; char out[2 *N + 1] = { 0 };

Notes

Page 130

Abhishek Kumar

Data Structure

void printbracket(int level, int open, int close) { if (level == 2 *n) { printf("%s\n", out); return ; } out[level] = '('; if ((open + 1) <= n && open >= close) { printbracket(level + 1, open + 1, close); } out[level] = ')'; if ((close + 1) <= n && open >= close) { printbracket(level + 1, open, close + 1); } } main() { printbracket(0, 0, 0); }

91What

This question is asked in many interviews that why we need 'Virtual Destructor'. In which scenarios, it is needed? And What is the advantage/disadvantage of using it? Also if virtual destructor is there why there is nothing like 'Virtual Constructor' in C++ ? So for understanding this, first we need to have a brief idea about virtual keyword. And subsequently we need to go in the concept of polymorphism. What is polymorphism and what are it's advantage? Polymorphism is simply defined as 'Single Interface Multiple Implementations'. It means that we need not to have different interfaces (of names) for same type of functionality in same type of objects. There are two type of polymorphism: 1) Static Polymorphism: Function overloading allows programs to declare multiple functions having the same name (but with different arguments).I don't want to go in that details and assuming that you know that. 2) Dynamic Polymorphism: Lets have an example. Dog and Lion are of same type (Animal) but speaks differently. Lion roars while Dog barks. So if we don't know before hand whether we are dealing with a Dog or with a Lion, we can't predict the actual behavior.

Notes

Page 131

Abhishek Kumar

Data Structure

Code:

class Animal { public: Animal(){}; ~Animal(){}; void speak() { printf("Animal Sound\n"); }; }; class Dog: public Animal { public: Dog(){}; ~Dog(){}; void speak() { printf("Dog Barks\n"); }; }; class Lion: public Animal { public: Lion(){}; ~Lion(){}; void speak() { printf("Lion Roars\n"); }; }; main() { Dog d; Lion l; Animal *p; p = &d; p->speak; //prints "Animal Sound" p = &l; p->speak; //prints "Animal Sound" }

As we saw that this sound function treat Dog(d) and Lion(l) as Animal and prints "Animal Sound" in both the cases but infact we want it to print "Dog Barks" and "Lion Roars" respectively. For this purpose, we need to make speak() as virtual in Animal class. class Animal

Notes

Page 132

Abhishek Kumar

{ public: Animal(){}; ~Animal(){}; virtual void speak() { printf("Animal Sound\n"); }; };

Data Structure

So this 'virtual' keyword does wonder here. Isn't it? It ensures that the correct function is called. And how does it do it? It maintains a ' vtable' for every class (in which virtual function is defined) and from there it comes to know that which implementation needs to be called. Another important thing about virtual function is that it maintains it's virtual behavior in all subsequent inherited classes. Another concern was that what is the cost we pay for having this behavior: 1) The class size increases by the size of an address. This address is actually the address of vtable. No matter how many virtual function are there, it just carry the vtable address. 2) Every time a function is called, we need to refer vtable for finding exact definition. Virtual Destructor: I hope that by now, the need of virtual destructor must be clear to many. For those who are still confused, virtual destructor are there to ensure that correct destructor is called. There is another thing to remember that virtual destructor (and virtual functions) comes is picture only when we construct an object polymorphically. If we are sure that we won't call a function polymorphically, we need not to have virtual functions.

92Reconstruct

Question: An n-ary tree is represented in a matrix form such that A[i,j] = 1 if j is the ancestor of i. Otherwise A[i,j] = 0. Given this construct the tree. Example: Lets start with an example. If we have a tree like this:

Notes

Page 133

Abhishek Kumar

Data Structure

1 1 2 3 4 5 6 7 8 9 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 3 4 5 6 7 8 9 1 0

Answer: Firstly, we need to count ancestors for each of the nodes. So, we add a new column in the table, which will tell us the number of ancestor count for each node. And our new table will look like this: 1 1 2 3 4 5 6 7 8 9 1 0 no of anc 0

Notes

Page 134

Abhishek Kumar 2 3 4 5 6 7 8 9 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 3 2 2 3 2 1 1

Data Structure

Now, we can clearly see from this table that '1' has no ancestor, so it must be the root node. So our initial tree will look like this:

And it is also clear that which the nodes has only one ancestor left they can be added in the partial tree constructed till now by seeing that whose child they are. For example ancestor count for '2' is 1 and in 2's row we can see that '1' is it's only ancestor left so we can add '2' as direct child to '1'. Same can be done with '9' and '10'. Now since all the direct child of 1 has been calculated we can set the 1's column as 'nil' and reconstruct ancestor count column. Now the new matrix and partial tree will look like this:

2 3

6 7

1 0

no of anc 0 0

1 1 1

1 2

Notes

Page 135

Data Structure

The same exercise can be repeated by seeing who all nodes has ancestor count as 1. Nodes '3', '5', '6' and '8' can be added to the partial tree and their parent columns can be removed. Now the new matrix and partial tree will look like this:

6 7 8

1 0

no of anc 0 0

ni l 1 ni l ni l ni l

0 1 0 0

Notes

Page 136

Data Structure

Again the same exercise can be repeated by seeing who all nodes has ancestor count as 1. Nodes '4' and '7' can be added to the partial tree and their parent columns can be removed. Now the new matrix and partial tree will look like this:

3 4

1 0

no of anc 0 0

ni l ni l ni l ni l ni

0 0 0 0

Notes

Page 137

Data Structure

Now, we can see that ancestor count becomes 0 for all the nodes. So we can say that now the tree is complete and this is the answer. Complexity of this exercise is left to the readers. I hope that I'm clear and the solution can be understood easily. I am also leaving the coding part to readers.

93

Question: You are given some denominations of coins in an array (int denom[])and infinite supply of all of them. Given an amount (int amount), find the minimum number of coins required to get the exact amount. Answer: Let's rephrase the question like this: "Given a list of N coins, their values (V1, V2, ... , VN), and the total sum S. Find the minimum number of coins the sum of which is S (we can use as many coins of one type as we want), or report that it's not possible to select coins in such a way that they sum up to S."

Notes

Page 138

Abhishek Kumar

Data Structure

It is simple - for each coin j, Vji, look at the minimum number of coins found for the i-Vjsum (we have already found it previously). Let this number be m. If m+1 is less than the minimum number of coins already found for current sum i, then we write the new result for it. For a better understanding let's take this example: Given coins with values 1, 3, and 5. And the sum S is set to be 11. First of all we mark that for state 0 (sum 0) we have found a solution with a minimum number of 0 coins. We then go to sum 1. First, we mark that we haven't yet found a solution for this one (a value of Infinity would be fine). Then we see that only coin 1 is less than or equal to the current sum. Analyzing it, we see that for sum 1-V1= 0 we have a solution with 0 coins. Because we add one coin to this solution, we'll have a solution with 1 coin for sum 1. It's the only solution yet found for this sum. We write (save) it. Then we proceed to the next state - sum 2. We again see that the only coin which is less or equal to this sum is the first coin, having a value of 1. The optimal solution found for sum (2-1) = 1 is coin 1. This coin 1 plus the first coin will sum up to 2, and thus make a sum of 2 with the help of only 2 coins. This is the best and only solution for sum 2. Now we proceed to sum 3. We now have 2 coins which are to be analyzed - first and second one, having values of 1 and 3. Let's see the first one. There exists a solution for sum 2 (3 1) and therefore we can construct from it a solution for sum 3 by adding the first coin to it. Because the best solution for sum 2 that we found has 2 coins, the new solution for sum 3 will have 3 coins. Now let's take the second coin with value equal to 3. The sum for which this coin needs to be added to make 3 , is 0. We know that sum 0 is made up of 0 coins. Thus we can make a sum of 3 with only one coin - 3. We see that it's better than the previous found solution for sum 3 , which was composed of 3 coins. We update it and mark it as having only 1 coin. The same we do for sum 4, and get a solution of 2 coins - 1+3. And so on. Pseudocode:

Set Min[i] equal to Infinity for all of i Min[0]=0 For i = 1 to S For j = 0 to N - 1 If (Vj<=i AND Min[i-Vj]+1j]+1 Output Min[S] Here are the solutions found for all sums:

Su m 0

Coin value added to a smaller sum to obtain this sum (it is displayed in brackets) -

Notes

Page 139

Abhishek Kumar

Data Structure

1 2 3 4 5 6 7 8 9 10 11

1 2 1 2 1 2 3 2 3 2 3

1 (0) 1 (1) 3 (0) 1 (3) 5 (0) 3 (3) 1 (6) 3 (5) 1 (8) 5 (5) 1 (10)

As a result we have found a solution of 3 coins which sum up to 11. Additionally, by tracking data about how we got to a certain sum from a previous one, we can find what coins were used in building it. For example: to sum 11 we got by adding the coin with value 1 to a sum of 10. To sum 10 we got from 5. To sum 5 we got from 0. Thus, we find the coins used are: 1, 5 and 5.

94Max

Question: There is an integer array consisting positive numbers only. Find maximum possible sum of elements such that there are no 2 consecutive elements present in the sum. Example: If given array is (6, 4, 2, 8, 1), the answer will be 14 (8+6). This is the maximum sum we can obtain without taking two consecutive elements. Answer: To solve these type of question, first thing is to find a recurring relation. In this case our recurring relation will tell the max sum till a given length. It means that we will get the max sum for running length of the array. If we denote the ith element as T(i) and max sum till ith element of the array as S(i), then

Notes Page 140

Abhishek Kumar S(i) = MAX {S(i-1), S(i-2) + T(i) } S(-1) = 0; if i=0, S(i) = T(0);

Data Structure

Note: while developing this relationship, I assume array index is starting from 0. Writing code for this problem is not a big deal now. Below is the code snippet for the same. I have written this on notepad and it's not compiled so please correct me if I am wrong. sum2 = 0; sum = sum1 = array[0];

for(i=1; i<len, i++) { sum = MAX(sum2 + array[i], sum1); sum2 = sum1; sum1 = sum; }

95Number

of bus-stations Question: At a bus-station, you have time-table for buses arrival and departure. You need to find the minimum number of platforms so that all the buses can be accommodated as per their schedule. Example: Time table is like below:

Notes

Page 141

Abhishek Kumar

Data Structure

Then the answer must be 3. Otherwise the bus-station will not be able to accommodate all the buses.

Answer: Lets take the same example as described above. Now we have to calculate the number of bus-station, which will be nothing but the maximum number of buses at the bus-station at any time.

So first sort all the arrival(A) and departure(D) time in an int array. Please save the corresponding arrival or departure in the array also. Either you can use a particular bit for this purpose or make a structure. After sorting our array will look like this:

0900 A

0915 A

1930 D

1030 A

1045 A

1100 D

1145 D

1300 D

Now modify the array as put 1 where you see A and -1 where you see D. So new array will be like this:

-1

-1

-1

-1

Notes

Page 142

Abhishek Kumar Your solution will be the maximum value in this array. Here it is 3.

Data Structure

I think that code for this will not be complex so I am skipping that part. The complexity of this solution depends on the complexity of sorting.

PS: If you have a arriving and another departing at same time then put departure time first in sorted array.

96Find

largest 20 elements from billions of numbers Question: There is a huge file containing billions of integers. Find maximum 20 integers form that file.

Answer: Always remember that when you are asked about these types of question where you have to find max N elements or so, best DS to use id HEAP. Here also, I took some time in thinking but finally decide to use heap.

A solution for this problem may be to divide the data in some sets of 1000 elements (lets say 1000), make a heap of them, and take 20 elements from each heap one by one. Finally heap sort all the sets of 20 elements and take top 20 among those. But the problem in this approach is where to store 20 elements from each heap. That may require a large amount of memory as we have billions of numbers.

Reuse top 20 elements from earlier heap in subsequent elements can solve this problem. I meant to take first block of 1000 elements and subsequent blocks of 980 elements each. Initially heapsort first set of 1000 numbers, took max 20 elements and mix them with 980 elements of 2nd set. Again heapsort these 1000 numbers (20 from first set and 980 from 2nd set), take 20 max element and mix those with 980 elements of 3rd set. Repeat the same exercise till last set of 980 (or less) elements and take max 20 elements from final heap. Those 20 elements will be your answer.

Notes

Page 143

Abhishek Kumar

Data Structure

Since complexity of heap sorting 1000 elements will be a constant so the O(n) = N i.e. linear complexity :)

PS: Meanwhile this problem, interviewer asked me to give him an algorithm to create a heap, removing and element from heap and heapify procedure. So, I again insist to have sound knowledge of any data structure you use.

97 98 99 100

Notes

Page 144

- NetNumen U31 RANЗагружено:Andrea Stefanelli
- c interview questionsЗагружено:Nicholas Williams
- Age3RMgreat LakesЗагружено:kurt_karun
- Age3RMCarolina.dmpЗагружено:Sanjog Vasant
- A01QЗагружено:Edwin Chan
- Data Dissemination Grp4Загружено:Mairos Kunze Bonga
- Lecture 13Загружено:Fred
- UVm how it workЗагружено:عبدالرحمن إحسان
- problemset9sol[1]Загружено:Gobara Dhan
- Data Structres Final FallЗагружено:Crow Tome
- Cplus2.docЗагружено:BenjaminBen
- Functions in cЗагружено:PS Radhika
- Tutorial Interview QuestionsЗагружено:kumarishu125
- IJERA(www.ijera.com)International Journal of Engineering Research and ApplicationsЗагружено:Anonymous 7VPPkWS8O
- Data Structures Algorithms Mock Test iЗагружено:surendhar
- 5. DSA - TreesЗагружено:Debabala Swain
- Trees and GraphsЗагружено:Dr Rushen Singh
- TreeЗагружено:Aseem Kumar Patel
- pgfgantt.pdfЗагружено:nick-boi1578
- Ds Two MarksЗагружено:srcsundar
- artificial intelligence_ch2_search1Загружено:upender_kalwa
- Data Structure ExamplesЗагружено:korombish
- c++interviewquestionsЗагружено:Roshinee
- CommVault Systems Placement Experience - GeeksforGeeksЗагружено:JubinCJose
- Computer Organization Assignment-IsE 4th SemЗагружено:Kiran Kumar
- Traverse HierarchiesЗагружено:Mohammed Anas PM
- Study Guide FinalЗагружено:shivambarca
- Data Structures QuestionBankЗагружено:raghu ram Makkapati
- Data StructureЗагружено:Niranjan Sahoo
- Merge SortЗагружено:Garry Mehrok

- Problems From the BookЗагружено:Geovane Junior
- text7Загружено:saanchit
- Math 780 NotesЗагружено:Vlad Copil
- COT5405Загружено:saanchit
- algorithm lectureЗагружено:Jask-Eagle
- Singular value decompositionЗагружено:saanchit
- Naive Bayes, Histogram approachЗагружено:saanchit
- information retrievalЗагружено:saanchit
- Zabbix Manual v1.6Загружено:Joaquin Alonso
- voronoi diagramЗагружено:saanchit
- Distance-Maximizing-in-Array.pptxЗагружено:saanchit
- Thread PoolsЗагружено:saanchit
- Synchronizing ThreadsЗагружено:saanchit
- Hacking a Google Interview Handout - Common Questions 2Загружено:machinelearner

- JAVAЗагружено:Vidhaya Sagar
- Ale Scenario Development Procedure Good DocumentЗагружено:Upendra Kumar
- XebiaLabs to Offer Jenkins Enterprise by CloudBeesЗагружено:Anonymous uh2YCdVSi
- Learn JavaЗагружено:vijay kumar
- Javascript TutorialЗагружено:Nirmal Mohan
- UNICORNЗагружено:jorgegutierrez81
- Java Script in MirthЗагружено:Rishish Jain
- dsa1Загружено:michaelgodfather
- S.O.L.I.D. Software Development, One Step at a TimeЗагружено:junkyard
- Custom Genil UsageЗагружено:Booker Booker
- M_02_1.00 Database Architecture with Demo and Labs.pdfЗагружено:Edmundo Lozada
- Changelog for ModdingЗагружено:Jan
- res1Загружено:Bhabajit Mishra
- Bhaskar Resume (1)Загружено:kalicharan13
- Assignment 1Загружено:Ritvik Sharma
- Taming TigerЗагружено:api-3745283
- Assignment 1Загружено:rythemkiller
- Tutorial pythonЗагружено:milos
- logcatЗагружено:Mathew Hon Jun Yoon
- jsЗагружено:Max Mustermann
- Superbase OdbcЗагружено:ecllce
- pgfganttЗагружено:Codera Purpa
- TheNewStack_GuideToCloudNativeDevOps.pdfЗагружено:Steve
- SAP Solution Manager 7.0 and Computer Associates - Wily Intro ScopeЗагружено:Kurian Varghese
- Is and Developer 61 Sp 2 Doc SupplementЗагружено:Ivo Zhekov
- Web-programming Course DescriptionЗагружено:ethanz1740
- 9 Using the Maya Python APIЗагружено:Aleksandra Mandic
- BMS Concepts in CICSЗагружено:graghunath2
- Java 8 Update 101_UninstallЗагружено:Chirag Pandit
- Algorithms, Flowcharts, Data Types and Pseudo CodeЗагружено:Lim Jun Xin

## Гораздо больше, чем просто документы.

Откройте для себя все, что может предложить Scribd, включая книги и аудиокниги от крупных издательств.

Отменить можно в любой момент.