Algorithm Questions and Answers
Algorithm Questions and Answers
Algorithm Questions and Answers
Here is the list of some most asked algorithm interview questions and
their answer. These questions are also beneficial for academic and
competitive exams perspective.
The algorithm provides the basic idea of the problem and an approach to
solve it. Some reasons to use an algorithm are as follows.
Time complexity
Space complexity
Worst-case: f(n)
Best-case: f(n)
It is defined by the minimum number of steps taken on any instance of size
n.
Average-case: f(n)
Step1: start
Step6: Increment i by 1
Step7: Increment j by 1
Step9: Stop
Case1:
Check if the linked list is empty then set the node as head and return it.
Case3:
θ Notation
The Big O notation bounds a function from above, it defines an upper bound
of an algorithm. Let's consider the case of insertion sort; it takes linear time
in the best case and quadratic time in the worst case. The time complexity of
insertion sort is O(n2). It is useful when we only have upper bound on time
complexity of an algorithm.
Ω Notation
e.g.
Pass1:
Pass2:
(25378) -> (25378) algorithm does not swap 2 and 5 because 2<5.
(25378) -> (23578) swap 3 and 5.
(23578) -> (23578) algorithm does not swap 5 and 7 because 5<7.
(23578) -> (23578) algorithm does not swap 7 and 8 because 7<8.
8. // swapping
9. // a = (initial_a - initial_b)
10. a = a - b;
18. return 0;
19. }
Output
Enter a: 10.25
Enter b: -12.5
After swapping, a = -12.50
After swapping, b = 10.25
8) What is a Hash Table? How can we use this structure to find all
anagrams in a dictionary?
A Hash table is a data structure for storing values to keys of arbitrary type.
The Hash table consists of an index into an array by using a Hash function.
Indexes are used to store the elements. We assign each possible element to
a bucket by using a hash function. Multiple keys can be assigned to the same
bucket, so all the key and value pairs are stored in lists within their
respective buckets. Right hashing function has a great impact on
performance.
To find all anagrams in a dictionary, we have to group all words that contain
the same set of letters in them. So, if we map words to strings representing
their sorted letters, then we could group words into lists by using their sorted
letters as a key.
1. FUNCTION find_anagrams(words)
2. word_groups = HashTable<String, List>
3. FOR word IN words
4. word_groups.get_or_default(sort(word), []).push(word)
5. END FOR
6. anagrams = List
7. FOR key, value IN word_groups
8. anagrams.push(value)
9. END FOR
10. RETURN anagrams
The hash table contains lists mapped to strings. For each word, we add it to
the list at the suitable key, or create a new list and add it to it.
Divide: In this section, the algorithm divides the original problem into a set
of subproblems.
Combine: In this section, the algorithm puts together the solutions of the
subproblems to get the solution to the whole problem.
Algorithm
Step2: Queue the starting node A and set its status=2, i.e. (waiting state)
Step4: Dequeue a node N and process it and set its status=3, i.e.
(processed state)
Step5: Queue all the neighbors of N that are in the ready state (status=1)
and set their status =2 (waiting state)
[Stop Loop]
Step6: Exit
11) What is Dijkstra's shortest path algorithm?
Dijkstra's algorithm is an algorithm for finding the shortest path from a
starting node to the target node in a weighted graph. The algorithm makes a
tree of shortest paths from the starting vertex and source vertex to all other
nodes in the graph.
Suppose you want to go from home to office in the shortest possible way.
You know some roads are heavily congested and challenging to use this,
means these edges have a large weight. In Dijkstra's algorithm, the shortest
path tree found by the algorithm will try to avoid edges with larger weights.
o Merge Sort
o Quick Sort
o Binary Search
o Strassen's Matrix Multiplication
o Closest pair (points)
In other words, an algorithm that always takes the best immediate, or local,
solution while finding an answer.
Greedy algorithms find the overall, ideal solution for some idealistic
problems, but may discover less-than-ideal solutions for some instances of
other problems.
Below is a list of algorithms that finds their solution with the use of the
Greedy algorithm.
For example, suppose an array of with some integer elements. You should
find and print the position of all the elements with their value. Here, the
linear search acts in a flow like matching each element from the beginning of
the list to the end of the list with the integer, and if the condition is `True
then printing the position of the element.'
Step2: In every iteration, compare the target value with the current value of
the array
Step3: If the values match, return the current index of the array
Step4: If the values do not match, shift on to the next array element.
o Nodes which are less than root will be in the left subtree.
o Nodes which are greater than root (i.e., contains more value) will be
right subtree.
o A binary search tree should not have duplicate nodes.
o Both sides subtree (i.e., left and right) also should be a binary search
tree.
No. of leaf nodes= no of leaf nodes in left subtree + number of leaf nodes in
the right subtree.
Example:
1. dictionary[] = {"Java", "Point","Quiz"};
2. Array[][] = {{'J', 'T', 'P',},
3. {'U', 'A', 'A'},
4. {'Q', 'S', 'V'}};
5. isWord(str): returns true if str is present in dictionary
6. else false.
Output:
o Check If the Linked list does not have any value then make the node as
head and return it
o Check if the value of the node to be inserted is less than the value of
the head node, then insert the node at the start and make it head.
o In a loop, find the appropriate node after which the input node is to be
inserted. To find the just node start from the head, keep forwarding
until you reach a node whose value is greater than the input node. The
node just before is the appropriate node.
o Insert the node after the proper node found in step 3.
o The function must accept a pointer to the start node as the first
argument and node to be deleted as the second argument, i.e., a
pointer to head node is not global.
o The function should not return a pointer to the head node.
o The function should not accept pointer to pointer to head node.
We will handle the case when the first node to be deleted then we copy the
data of the next node to head and delete the next node. In other cases when
a deleted node is not the head node can be handled generally by finding the
previous node.
1. #include <stdio.h>
2. #include <stdlib.h>
3. struct Node
4. {
5. int data;
6. struct Node *next;
7. };
8.
9. void delNode(struct Node *head, struct Node *n)
10. {
11. if(head == n)
12. {
13. if(head->next == NULL)
14. {
15. printf("list can't be made empty because there is only on
e node. ");
16. return;
17. }
18. head->data = head->next->data;
19. n = head->next;
20. head->next = head->next->next;
21. free(n);
22. return;
23. }
24. struct Node *prev = head;
25. while(prev->next != NULL && prev->next != n)
26. prev = prev->next;
27. if(prev->next == NULL)
28. {
29. printf("\n This node is not present in List");
30. return;
31. }
32. prev->next = prev->next->next;
33. free(n);
34. return;
35. }
36. void push(struct Node **head_ref, int new_data)
37. {
38. struct Node *new_node =
39. (struct Node *)malloc(sizeof(struct Node));
40. new_node->data = new_data;
41. new_node->next = *head_ref;
42. *head_ref = new_node;
43. }
44. void printList(struct Node *head)
45. {
46. while(head!=NULL)
47. {
48. printf("%d ",head->data);
49. head=head->next;
50. }
51. printf("\n");
52. }
53. int main()
54. {
55. struct Node *head = NULL;
56. push(&head,3);
57. push(&head,2);
58. push(&head,6);
59. push(&head,5);
60. push(&head,11);
61. push(&head,10);
62. push(&head,15);
63. push(&head,12);
64. printf("Available Link list: ");
65. printList(head);
66. printf("\nDelete node %d: ", head->next->next->data);
67. delNode(head, head->next->next);
68.
69. printf("\nUpdated Linked List: ");
70. printList(head);
71.
72. /* Let us delete the the first node */
73. printf("\nDelete first node ");
74. delNode(head, head);
75.
76. printf("\nUpdated Linked List: ");
77. printList(head);
78.
79. getchar();
80. return 0;
81. }
Output:
Example
if first list is 1->2->3 and second is 12->10->2->4->6, the first list should
become 1->12->2->10->17->3->2->4->6 and second list should become
empty. The nodes of the second list should only be inserted when there are
positions available.
Use of extra space is not allowed i.e., insertion must be done in a place.
Predictable time complexity is O(n) where n is number of nodes in first list.
1. #include <stdio.h>
2. #include <stdlib.h>
3. struct Node
4. {
5. int data;
6. struct Node *next;
7. };
8. void push(struct Node ** head_ref, int new_data)
9. {
10. struct Node* new_node =
11. (struct Node*) malloc(sizeof(struct Node));
12. new_node->data = new_data;
13. new_node->next = (*head_ref);
14. (*head_ref) = new_node;
15. }
16. void printList(struct Node *head)
17. {
18. struct Node *temp = head;
19. while (temp != NULL)
20. {
21. printf("%d ", temp->data);
22. temp = temp->next;
23. }
24. printf("\n");
25. }
26. void merge(struct Node *p, struct Node **q)
27. {
28. struct Node *p_curr = p, *q_curr = *q;
29. struct Node *p_next, *q_next;
30. while (p_curr != NULL && q_curr != NULL)
31. {
32. p_next = p_curr->next;
33. q_next = q_curr->next;
34. q_curr->next = p_next;
35. p_curr->next = q_curr;
36. p_curr = p_next;
37. q_curr = q_next;
38. }
39.
40. *q = q_curr;
41. }
42. int main()
43. {
44. struct Node *p = NULL, *q = NULL;
45. push(&p, 3);
46. push(&p, 2);
47. push(&p, 1);
48. printf("I Linked List:\n");
49. printList(p);
50.
51. push(&q, 8);
52. push(&q, 7);
53. push(&q, 6);
54. push(&q, 5);
55. push(&q, 4);
56. printf("II Linked List:\n");
57. printList(q);
58.
59. merge(p, &q);
60.
61. printf("Updated I Linked List:\n");
62. printList(p);
63.
64. printf("Updated II Linked List:\n");
65. printList(q);
66. getchar();
67. return 0;
68. }
Output:
I Linked List:
1 2 3
II Linked List:
4 5 6 7 8
Updated I Linked List:
1 4 2 5 3 6
Updated II Linked List:
7 8
Time complexity deals with the quantification of the amount of time taken
by a set of code or algorithm to process or run as a function of the amount of
input. In other words, the time complexity is efficiency or how long a
program function takes to process a given input.
Working principle
The significant difference between stack and queue is that stack uses LIFO
(Last in First Out) method to access and add data elements whereas Queue
uses FIFO (First in first out) method to obtain data member.
Structure
In Stack, the same end is used to store and delete elements, but in Queue,
one end is used for insertion, i.e., rear end and another end is used for
deletion of elements.
Stack uses one pointer whereas Queue uses two pointers (in the simple
case).
Operations performed
Stack operates as Push and pop while Queue operates as Enqueue and
dequeuer.
Variants
Stack does not have variants while Queue has variants like a circular queue,
Priority queue, doubly ended Queue.
Implementation
25) What is the difference between the Singly Linked List and
Doubly Linked List data structure?
This is a traditional interview question on the data structure. The major
difference between the singly linked list and the doubly linked list is the
ability to traverse.
You cannot traverse back in a singly linked list because in it a node only
points towards the next node and there is no pointer to the previous node.
On the other hand, the doubly linked list allows you to navigate in both
directions in any linked list because it maintains two pointers towards the
next and previous node.