This book is very useful for the engineering degree students and master degree students during their academic preparations. The data structures and algorithms made easy by narasimha karumanch book and chapter are mainly emphasizing problems and their analysis rather than on theory. In each chapter there is required basic theory, which is followed by a section on problem sets.
It is useful for Engineering degree students and Masters degree students for their academic preparations. The special part of this made easy books pdf book is that it also consists of multiple solutions with different levels of complexity.
Also, around 58 international universities were using these books as reference for academic courses. Step-1 : Read the Book Name and author Name thoroughly. Step-4 : Click the Download link provided below to save your material in your local drive. LearnEngineering team try to Helping the students and others who cannot afford buying books is our aim. Once they enter the loop they are expected to meet, which denotes that there is a loop.
This works because the only way a faster moving pointer would point to the same location as a slower moving pointer is if somehow the entire list or a part of it is circular. Think of a tortoise and a hare running on a track.
The faster running hare will catch up with the tortoise if they are running in a loop. As an example, consider the following example and trace out the Floyd algorithm. From the diagrams below we can see that after the final step they are meeting at some point in the loop which may not be the starting point of the loop.
Note: slowPtr tortoise moves one pointer at a time and fastPtr hare moves two pointers at a time. There are two possibilities for L: it either ends snake or its last element points back to one of the earlier elements in the list snail.
Give an algorithm that tests whether a given list L is a snake or a snail. Solution: It is the same as Problem If there is a cycle find the start node of the loop. Solution: The solution is an extension to the solution in Problem After finding the loop in the linked list, we initialize the slowPtr to the head of the linked list. From that point onwards both slowPtr and fastPtr move only one node at a time. The point at which they meet is the start of the loop.
Generally we use this method for removing the loops. Solution: This problem is at the heart of number theory. Furthermore, the tortoise is at the midpoint between the hare and the beginning of the sequence because of the way they move. Solution: Yes, but the complexity might be high. Trace out an example. If there is a cycle, find the length of the loop.
Solution: This solution is also an extension of the basic cycle detection problem. After finding the loop in the linked list, keep the slowPtr as it is. The fastPtr keeps on moving until it again comes back to slowPtr. While moving fastPtr, use a counter variable which increments at the rate of 1. Solution: Traverse the list and find a position for the element and insert it.
The element itself. The reverse of the second element followed by the first element. Space Complexity: O n ,for recursive stack. The head or start pointers of both the lists are known, but the intersecting node is not known. Also, the number of nodes in each of the lists before they intersect is unknown and may be different in each list. Give an algorithm for finding the merging point. Solution: Brute-Force Approach: One easy solution is to compare every node pointer in the first list with every other node pointer in the second list by which the matching node pointers will lead us to the intersecting node.
But, the time complexity in this case will be O mn which will be high. Time Complexity: O mn. Consider the following algorithm which is based on sorting and see why this algorithm fails. Any problem with the above algorithm? In the algorithm, we are storing all the node pointers of both the lists and sorting. But we are forgetting the fact that there can be many repeated elements.
This is because after the merging point, all node pointers are the same for both the lists. The algorithm works fine only in one case and it is when both lists have the ending node at their merge point. Space Complexity: O n or O m. By combining sorting and search techniques we can reduce the complexity. Space Complexity: O Max m, n. Solution: Brute-Force Approach: For each of the node, count how many nodes are there in the list, and see whether it is the middle node of the list.
The reasoning is the same as that of Problem Time Complexity: Time for creating the hash table. Space Complexity: O n. Since we need to create a hash table of size n. Solution: Efficient Approach: Use two pointers. Move one pointer at twice the speed of the second. When the first pointer reaches the end of the list, the second pointer will be pointing to the middle node. Solution: Traverse recursively till the end of the linked list.
While coming back, start printing the elements. Solution: Use a 2x pointer. Take a pointer that moves at 2x [two nodes at a time]. At the end, if the length is even, then the pointer will be NULL; otherwise it will point to the last node. Solution: Assume the sizes of lists are m and n. Solution: Refer Trees chapter. Solution: Refer Sorting chapter. If the number of nodes in the list are odd then make first list one node extra than second list. As an example, consider the following circular list.
Solution: Algorithm: 1. Get the middle of the linked list. Reverse the second half of the linked list. Compare the first half and second half.
Construct the original linked list by reversing the second half again and attaching it back to the first half.
Else return. Otherwise, we can return the head. Create a linked list and at the same time keep it in a hash table. For n elements we have to keep all the elements in a hash table which gives a preprocessing time of O n.
Hence by using amortized analysis we can say that element access can be performed within O 1 time. Time Complexity — O 1 [Amortized].
Space Complexity - O n for Hash Table. Find which person will be the last one remaining with rank 1. Solution: Assume the input is a circular linked list with N nodes and each node has a number range 1 to N associated with it. The head node has number 1 as data.
Give an algorithm for cloning the list. Solution: We can use a hash table to associate newly created nodes with the instances of node in the given list. We scan the original list again and set the pointers building the new list. Delete that node from the linked list. 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.
Time Complexity: O 1. Solution: To solve this problem, we can use the splitting logic. While traversing the list, split the linked list into two: one contains all even nodes and the other contains all odd nodes. Now, to get the final list, we can simply append the odd node linked list after the even node linked list.
To split the linked list, traverse the original linked list and move all odd nodes to a separate linked list of all odd nodes. At the end of the loop, the original list will have all the even nodes and the odd node list will have all the odd nodes. To keep the ordering of all nodes the same, we must insert all the odd nodes at the end of the odd node list.
Solution: For this problem the value of n is not known in advance. Solution: For this problem the value of n is not known in advance and it is the same as finding the kth element from the end of the the linked list. Assume the value of n is not known in advance. The other steps run in O 1. Therefore the total time complexity is O min n,m.
If we have an even number of elements, the median is the average of two middle numbers in a sorted list of numbers. We can solve this problem with linked lists with both sorted and unsorted linked lists. First, let us try with an unsorted linked list. In an unsorted linked list, we can insert the element either at the head or at the tail.
The disadvantage with this approach is that finding the median takes O n. Also, the insertion operation takes O 1. Now, let us try with a sorted linked list. Insertion to a particular location is also O 1 in any linked list. Note: For an efficient algorithm refer to the Priority Queues and Heaps chapter. The result should be stored in the third linked list. Also note that the head node contains the most significant digit of the number.
Solution: Since the integer addition starts from the least significant digit, we first need to visit the last node of both lists and add them up, create a new node to store the result, take care of the carry if any, and link the resulting node to the node which will be added to the second least significant node and continue.
First of all, we need to take into account the difference in the number of digits in the two numbers. So before starting recursion, we need to do some calculation and move the longer list pointer to the appropriate place so that we need the last node of both lists at the same time. The other thing we need to take care of is carry. If two digits add up to more than 10, we need to forward the carry to the next node and add it. If the most significant digit addition results in a carry, we need to create an extra node to store the carry.
The function below is actually a wrapper function which does all the housekeeping like calculating lengths of lists, calling recursive implementation, creating an extra node for the carry in the most significant digit, and adding any remaining nodes left in the longer list.
Time Complexity: O max List1 length,List2 length. Space Complexity: O min List1 length, List1 length for recursive stack. Note: It can also be solved using stacks. Solution: Simple Insertion sort is easily adabtable to singly linked lists. To insert an element, the linked list is traversed until the proper position is found, or until the end of the list is reached. It is inserted into the list by merely adjusting the pointers without shifting any elements, unlike in the array.
This reduces the time required for insertion but not the time required for searching for the proper position. Solution: Find the middle of the linked list. We can do it by slow and fast pointer approach. After finding the middle node, we reverse the right halfl then we do a in place merge of the two halves of the linked list.
Solution: The solution is based on merge sort logic. Assume the given two linked lists are: list1 and list2. Since the elements are in sorted order, we run a loop till we reach the end of either of the list. We compare the values of list1 and list2. If the values are equal, we add it to the common list. A stack is a simple data structure used for storing data similar to Linked Lists.
In a stack, the order in which the data arrives is important. A pile of plates in a cafeteria is a good example of a stack. The plates are added to the stack as they are cleaned and they are placed on the top. When a plate, is required it is taken from the top of the stack. The first plate placed on the stack is the last one to be used. Definition: A stack is an ordered list in which insertion and deletion are done at one end, called top. The last element inserted is the first one to be deleted.
Special names are given to the two changes that can be made to a stack. When an element is inserted in a stack, the concept is called push, and when an element is removed from the stack, the concept is called pop. Trying to pop out an empty stack is called underflow and trying to push an element in a full stack is called overflow. Generally, we treat them as exceptions. Let us assume a developer is working on a long-term project. The manager then gives the developer a new task which is more important.
The developer puts the long-term project aside and begins work on the new task. The phone rings, and this is the highest priority as it must be answered immediately. The developer pushes the present task into the pending tray and answers the phone. When the call is complete the task that was abandoned to answer the phone is retrieved from the pending tray and work progresses.
To take another call, it may have to be handled in the same manner, but eventually the new task will be finished, and the developer can draw the long-term project from the pending tray and continue with that. For simplicity, assume the data is an integer type. Exceptions Attempting the execution of an operation may sometimes cause an error condition, called an exception. In the Stack ADT, operations pop and top cannot be performed if the stack is empty.
Attempting the execution of pop top on an empty stack throws an exception. Trying to push an element in a full stack throws an exception. In the array, we add elements from left to right and use a variable to keep track of the index of the top element.
The array storing the stack elements may become full. A push operation will then throw a full stack exception. Similarly, if we try deleting an element from an empty stack it will throw stack empty exception. Trying to push a new element into a full stack causes an implementation-specific exception. We took one index variable top which points to the index of the most recently inserted element in the stack. To insert or push an element, we increment top index and then place the new element at that index.
Similarly, to delete or pop an element we take the element at top index and then decrement the top index. We represent an empty queue with top value equal to —1. The issue that still needs to be resolved is what we do when all the slots in the fixed size array stack are occupied?
First try: What if we increment the size of the array by 1 every time the stack is full? This way of incrementing the array size is too expensive. Let us see the reason for this. Alternative Approach: Repeated Doubling Let us improve the complexity by using the array doubling technique.
If the array is full, create a new array of twice the size, and copy the items. With this approach, pushing n items takes time proportional to n not n2. That means, we do the doubling at 1,2,4,8, If we observe carefully, we are doing the doubling operation logn times.
Now, let us generalize the discussion. For n push operations we double the array size logn times. That means, we will have logn terms in the expression below. The total time T n of a series of n push operations is proportional to T n is O n and the amortized time of a push operation is O 1. Performance Let n be the number of elements in the stack.
Linked List Implementation The other way of implementing stacks is by using Linked lists. Push operation is implemented by inserting element at the beginning of the list. We start with an empty stack represented by an array of size 1. Note: For analysis, refer to the Implementation section. Solution: Stacks can be used to check whether the given expression has balanced symbols. This algorithm is very useful in compilers. Each time the parser reads one character at a time.
The opening and closing delimiters are then compared. If they match, the parsing of the string continues. If they do not match, the parser indicates that there is an error on the line. A linear-time O n algorithm based on stack can be given as: Algorithm: a Create a stack. Otherwise pop the stack. Since we are scanning the input only once. Space Complexity: O n [for stack]. Solution: Before discussing the algorithm, first let us see the definitions of infix, prefix and postfix expressions.
Infix: An infix expression is a single letter, or an operator, proceeded by one infix string and followed by another Infix string. Prefix: A prefix expression is a single letter, or an operator, followed by two prefix strings. Every prefix string longer than a single variable contains an operator, first operand and second operand.
Postfix: A postfix expression also called Reverse Polish Notation is a single letter or an operator, preceded by two postfix strings. Every postfix string longer than a single variable contains first and second operands followed by an operator. Prefix and postfix notions are methods of writing mathematical expressions without parenthesis. Time to evaluate a postfix and prefix expression is O n , where n is the number of elements in the array. Now, let us focus on the algorithm.
Therefore, for the infix to postfix conversion algorithm we have to define the operator precedence or priority inside the algorithm. The table shows the precedence and their associativity order of evaluation among operators. Notice that between infix and postfix the order of the numbers or operands is unchanged. It is 2 3 4 in both cases. The stack that we use in the algorithm will be used to change the order of operators from infix to postfix.
Postfix expressions do not contain parentheses. We shall not output the parentheses in the postfix output. Solution: Algorithm: 1 Scan the Postfix string from left to right. If the operator is a binary operator, then pop two elements from the stack. After popping the elements, apply the operator to those popped elements. Let the result of this operation be retVal onto the stack. Example: Let us see how the above-mentioned algorithm works using an example. Initially the stack is empty. Now, the first three characters scanned are 1, 2 and 3, which are operands.
They will be pushed into the stack in that order. The second operand will be the first element that is popped. The value of the expression that has been evaluated 23 is pushed into the stack. Now, since all the characters are scanned, the remaining element in the stack there will be only one element in the stack will be returned. Solution: Using 2 stacks we can evaluate an infix expression in 1 pass without converting to postfix.
Get the next token in the infix string b. If next token is an operand, place it on the operand stack c. If next token is an operator i. Solution: Take an auxiliary stack that maintains the minimum of all values in the stack.
Also, assume that each element of the stack is less than its below elements. For simplicity let us call the auxiliary stack min stack. When we pop the main stack, pop the min stack too. When we push the main stack, push either the new element or the current minimum, whichever is lower. At any point, if we want to get the minimum, then we just need to return the top element from the min stack. Let us take an example and trace it out. Initially let us assume that we have pushed 2, 6, 4, 1 and 5.
Time complexity: O 1. Space complexity: O n [for Min stack]. The main problem of the previous approach is, for each push operation we are pushing the element on to min stack also either the new element or existing minimum element.
That means, we are pushing the duplicate minimum elements on to the stack. Now, let us change the algorithm to improve the space complexity. We still have the min stack, but we only pop from it when the value we pop from the main stack is equal to the one on the min stack.
We only push to the min stack when the value being pushed onto the main stack is less than or equal to the current min value. In this modified algorithm also, if we want to get the minimum then we just need to return the top element from the min stack. Solution: The number of stack permutations with n symbols is represented by Catalan number and we will discuss this in the Dynamic Programming chapter. The string is marked with special character X which represents the middle of the list for example: ababa Check whether the string is palindrome.
Solution: This is one of the simplest algorithms. What we do is, start two indexes, one at the beginning of the string and the other at the end of the string. Each time compare whether the values at both the indexes are the same or not. If the values are not the same then we say that the given string is not a palindrome. If the values are the same then increment the left index and decrement the right index. Continue this process until both the indexes meet at the middle at X or if the string is not palindrome.
Solution: Refer Linked Lists chapter. If they are the same then pop the stack and go to the next element in the input list.
Space Complexity: O n , for recursive stack. Analyze the running time of the queue operations. Solution: Refer Queues chapter. You will find this book useful for interview preparation, competitive exams preparation, and campus interview preparations.
As a job seeker, if you read the complete book, I am sure you will be able to challenge the interviewers. This book is also useful for Engineering degree students and Masters degree students during their academic preparations. In all the chapters you will see that there is more emphasis on problems and their analysis rather than on theory. In each chapter, you will first read about the basic required theory, which is then followed by a section on problem sets.
0コメント