You are on page 1of 15

Question: Reverse a Linked-list. Write code in C. Answer: There are multiple ways to go about this.

Lets first look at a recursive solution. Node * reverse( Node * ptr , Node * previous) { Node * temp; if(ptr->next == NULL) { ptr->next = previous; return ptr; } else { temp = reverse(ptr->next, ptr); ptr->next = previous; return temp; } } reversedHead = reverse(head, NULL); Now for a non-recursive solution. Node * reverse( Node * ptr ) { Node * temp; Node * previous = NULL; while(ptr != NULL) { temp = ptr->next; ptr->next = previous; previous = ptr; ptr = temp; } return previous; }

Another solution : Node * reverse(Node * cur) { if(cur->next==NULL) return cur; Node *tmp = rreverse(cur->next); cur->next->next = cur; return tmp; }

Worked fine: Node* reverse(node *hed, node *prev) { node *tmp; tmp=hed->next; hed->next=prev; if(tmp!=NULL) { hed=reverse(tmp,hed); } return hed; }

2. Question: You are given a pointer to a node (not the tail node) in a singly linked list. Delete that node from the linked list. Write code in C. Answer: To delete a node, you have to redirect the next pointer of the previous node to point to the next node instead of the current one. Since we dont have a pointer to the previous node, we cant redirect its next pointer. So what do we do? We can easily get away by moving the data from the next node into the current node and then deleting the next node. This solution has O(1) runtime. Heres some code to illustrate this simple logic. void deleteNode( Node * node ) { Node * temp = node->next; node->data = node->next->data; node->next = temp->next; free(temp); }

Some things to consider. This method could pose potential problems. For instance, lets consider we have a linked list A -> B -> C -> D and we are given a pointer to B to delete it. Theoretically, you would expect B to be deleted and all pointers which were pointing to B to become invalid. However, if we use this function to delete B, all pointers which were pointing to B will still be valid. Furthermore, node B now will contain the value C and node C will be invalid. Any previous pointers to C will become invalid, which may not be expected behavior in general. This is not a problem if there are no external links to any of the items in the linked list. But this would definitely be something you should consider asking your interviewer to show your thinking process.

uestion: Devise an algorithm to determine the Nth-to-Last element in a singly linked list of unknown length. If N = 0, then your algorithm must return the last element. Answer: Given that the only way to traverse a singly linked list is forwards from the head, it is not possible to just count N elements from the end of the linked list. Furthermore, the length of the linked list is unknown. We could traverse the list once to determine the number of elements and then traverse the list again to stop at the Nth element from the last element. But this seems a little inefficient. What if we could use 2 pointers? So what if we use a current pointer and another pointer that is N elements behind the current pointer. When the current pointer reaches the end of the list, the second pointer will be pointing to the element N elements from the end of the list. Heres some code to illustrate this logic. Node * findNToLastNode( Node *head, int N ) { int i = 0; Node *current, *behind; current = head; for( i = 0; i < N; i++ ) { if( current->next ) { current = current->next; } else { return NULL; than N } // Length of the list is less

behind = head; while( current->next ) { current = current->next; behind = behind->next; }

return behind; } int nthtolast(list_t* list, int n) { int len=0; list_t* i=list; while(list) { if(len>n) i=i->next; len++; list=list->next; } return i->key; }

Recursive solution Node* findNToLastNode ( Node* head, int n ) { static int _count ; static Node* _pNode = NULL ; if ( !head ) { _count = 0 ; return head ; } findNToLastNode(head->next, n) ; if ( _count++ <= n) _pNode = head ;

return _pNode ; }

Question: Print all the permutations of a string. Write code in C. Answer: Here is a recursive solution to print all the permutations of a string. However, this solution does not take care of duplicates. It is assumed that there are no duplicates in the string. I left out the implementation of the swap method since that implementation is not important here. The idea is to keep the first character constant and generate permutations with the rest. Then keep the first two characters constant and generate permutations with the rest until you are out of characters void permutate( char[] str, int index ) { int i = 0; if( index == strlen(str) ) { // We have a permutation so print it printf(str); return; } for( i = index; i < strlen(str); i++ ) { swap( str[index], str[i] ); // It doesn't matter how you swap. permutate( str, index + 1 ); swap( str[index], str[i] ); } } permutate( "abcdefgh", 0 ); Now what do we do if there are duplicates in the string? The trick is to sort the characters in the alphabetical order first. We can then ignore the duplicates easily when generate the permutation. void permutate( char[] str, int index ) { int i = 0;

static lastChar = 0; if( index == strlen(str) ) { // We have a permutation so print it printf(str); return; } for( i = index; i < strlen(str); i++ ) { if( lastChar == str[i] ) { continue; } else { lastChar = str[i]; } swap( str[index], str[i] ); // It doesn't matter how you swap. permutate( str, index + 1 ); swap( str[index], str[i] ); } } permutate( sort("Hello World"), 0 ); Question: Write an algorithm to find the first non-repeated character in a string. For example, the first non-repeated character in the string abcdab is c. Answer: Seems trivial enough right? If a character is repeated, we should be able to search the string to determine if that character appears again. So if we go about doing this for every character in the string, our worst case run time would O(n^2). Here is some code to show this logic. char returnFirstNonRepeatedChar( char * str ) { int i, repeated = 0; int len = strlen(str);

for( i = 0; i < len; i++ )

{ repeated = 0; for( j = 0; j < len; j++ ) { if( i != j && str[i] == str[j] ) { repeated = 1; break; } }

if( repeated == 0 ) { return str[i]; } }

// Found the first non-repeated character

return ''; } This approach seems to work perfectly well. But can we do better? Of course we can! With the use of other data structures we can reduce the run time of this algorithm. Any guesses? A hash table could work really handy here. Although, with this approach, we do sacrifice space for runtime improvement. So we can create a hash table by sequentially reading all the characters in the string and keeping count of the number of times each character appears. Once weve created the hash table, we can sequentially read the entries to see which one has a count of one. What is the runtime of this algorithm? We have O(n) to create the hash table and another O(n) to read the entries. This results in a runtime of O(n) + O(n) = O(2n) = O(n). Have a better solution to this problem? Let us know through the comments section.

Keywords:

Static

Static keyword can be used for both variables and functions.

By default, functions and global variables defined in a file are available for use in other files. However, if the static keyword is used, they are restricted to the current file only. They will not be available in other files even if the extern keyword is used. Local variables by default will be unavailable once the program leaves the scope in which it was defined. If the static keyword is used with a local variable, the local variable is preserved even when the program leaves the scope in which it was defined. This is commonly used to preserve local variable values between successive function calls. Static variables are initialized during compile time. volatile

Volatile indicates that the variable can be changed in unpredictable ways. A volatile variable has to be
retrieved from memory each time it is used rather using a copy if it is available in a register or cache. int itr_rx; // Interrupt Address ... while( !itr_rx ) { // Do something } In this case, if the optimization is turned on, the compiler will convert this to an infinite while loop since the value of itr_rx could not have changed due to the program flow. The compiler doesnt know that the variable is an interrupt address so it will treat it like a normal variable. We dont want that, do we? volatile int itr_rx; // Interrupt Address ... while( !itr_rx ) { // Do something } If the variable is declared as volatile, it will be reloaded from memory each time. It will not be optimized into an infinite while loop. It can be used for peripheral addresses and variables which are used to synchronize multiple threads. const const int a; The value of a can not be changed. It will generate a compile error if you try to do it. However, there is a way you can change it but it is not recommended. *(int *) &a = 123;

const int * b; The address to which the pointer is pointing to can not be changed however the value in that address can be changed. int * const b; The value in the address, b is pointing to, can not be changed. Question: Reverse a string in C using as little additional memory as possible. Answer: The first solution needs the size of a char and size of two integers, all of which will be allocated from the stack. This solution is the most commonly accepted good solution. Here is the code. void reverseString(char* str) { int i, j; char temp; i=j=temp=0;

j=strlen(str)-1; for (i=0; i<j; i++, j--) { temp=str[i]; str[i]=str[j]; str[j]=temp; } } The second solution is slightly better than the first as it does not need the char space. It uses bitmanipulation (XOR in this case) to swap the chars in place.

void reverseStringBetter(char* str) { int i, j; i=j=0;

j=strlen(str)-1; for (i=0; i<j; i++, j--) { str[i] ^= str[j] ; str[j] ^= str[i] ; str[i] ^= str[j] ; } }

Even though the second solution uses less space, the first one is probably a better solution in terms of readability and maintainability. The compilers are very advanced now and are able to reduce the swap instructions in the first solution into a single execution step. However, the same might not happen for the second solution so in real life situations, it is better to use the first solution although the second solution seems smarter and better theoretically. If you have any questions, please feel free to send

Question: How would you check if a binary tree is balanced? Answer: A tree is considered balanced when the difference between the min depth and max depth does not exceed 1. Recursive algorithms always work well on trees, so heres some code. int min_depth( Node * root ) { if( !root ) { return 0; } return 1 + min( min_depth( root->left ), min_depth( root->right )); }

int max_depth( Node * root ) { if( !root ) {

return 0; } return 1 + max( max_depth( root->left ), max_depth( root->right )); }

bool is_balanced( Node * root ) { return ( max_depth( root ) - min_depth( root ) ) <= 1 } Question: How would you find the first common ancestor of two nodes in a binary search tree? First as in the lowest in the tree. Another way to ask is to find the lowest common ancestor of two nodes. Challenge: Do you know the answer to this question? Post in the comments. Answers will be posted March 6th. Meanwhile, check out the challenges from previous weeks here. Answer: TreeNode findFirstCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {

if (root == null) { return null; }

if (root == p || root == q) { return root; }

TreeNode left = findFirstCommonAncestor(root.left, p, q); TreeNode right = findFirstCommonAncestor(root.right, p, q);

if ((left == p && right == q) ||

(left == q && right == q)) { return root; }

return (left != null) ? left : right; } Alternate: TreeNode findFirstCommonAncestor(TreeNode root, int p, int q) {

if (root == null) { return null; }

if (root.value == p || root.value == q) { return root; }

if (root.value > p && root.value > q ) { return findFirstCommonAncestor(root.left, p, q); } else if (root.value < p && root.value < q ) { return findFirstCommonAncestor(root.right, p, q); } else { return root; } } uestion: Reverse the order of words in a string in C using as little additional memory as possible.

Answer: The solution for this questions uses the solution of Reverse a String. I am going to make a small change to that solution by adding a length parameter. It will probably be better for you to read through that question before proceeding further with this one. Note that a space is being used to delimit the words in the sentence. Lets look at an example before we dive into the code. Original Sentence: My car is very fast Modifies Sentence: fast very is car My void reverseWords( char * str ) { int i = 0, j = 0; reverseString( str, strlen(str) ); // tsaf yrev si rac yM while( 1 ) // Loop forever { if( *(str+j) == ' ' || *(str+j) == '\0') // Found a word or reached the end of sentence { reverseString( str+i, j-i ); i = j+1; } if( *(str+j) == '\0') { break; } j++; } } void reverseString(char* str, int len) { int i, j; char temp;

i=j=temp=0;

j=len-1; for (i=0; i<j; i++, j--) { temp=str[i]; str[i]=str[j]; str[j]=temp; } } I did not have time to test this a lot so if there are a : Is your machine little or big endian? Answer: Let first take a look at the solution before we discuss about it. int isLittleEndian( void ) { unsigned int temp = 0x12345678; char * tempAddress = &temp; if( *tempAddress == 0x12 ) { return 0; // Indicating False } else { return 1; // Indicating True } } To answer this, you have to obviously know what little and big endian are. Also you need some basic understanding of variable assignments for data types of varying sizes. A major mistake which you could make is with the temp variable. When I was asked this question, I answered it by taking a fixed address for temp which is insanely dangerous (I know I was dumb). The address could have been used for something else or even been a reserved address for all you know. By creating a local variable, temp will be placed on the stack so we dont have to worry about whether it will be safe or not.

You might also like