Guest User

Circular Array

a guest
Mar 30th, 2022
76
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.46 KB | None | 0 0
  1. /*
  2. * Problem description: https://ibb.co/N2cnXG1
  3. */
  4. /*
  5. * It's a circular array, for each pair [endNode[i],endNode[i+1]] 0 < i < m-1 we
  6. * visit all nodes from endNode[i] to endNode[i+1] by incrementing count inclusive.
  7. * If endNode[i+1] < endNode[i] it means it a circular path so we traverse so
  8. * we visit both [endNode[i], n] and [1, to endNode[i+1]]
  9. * Finally we iterate from 1 to n to find the most visited node also it needs to be smallest
  10. *
  11. * Time Complexity O(|n|*m), Space Complexity O(|n|)
  12. */
  13. int circularArrayBruteForce(int n, vector<int> endNode) {
  14. vector<int> count(n + 1);
  15. for (int i = 0; i < endNode.size() - 1; i++) {
  16. int start = endNode[i];
  17. int end = endNode[i + 1];
  18.  
  19. //if the range is in middle of the array
  20. if (start <= end) {
  21. for (int j = start; j <= end; j++)
  22. count[j]++;
  23. } else { // if the range is composed of the tail and head
  24. for (int j = start; j <= n; j++)
  25. count[j]++;
  26. for (int j = 1; j <= end; j++)
  27. count[j]++;
  28. }
  29. }
  30.  
  31. int max = 0, most = 0;
  32. for (int i = 1; i <= n; i++) {
  33. if (max < count[i]) {
  34. max = count[i];
  35. most = i;
  36. }
  37. }
  38. return most;
  39. }
  40.  
  41. /*
  42. * We turn endNode into ranges keeping track of starts and ends separately.
  43. * We can think split circular ranges into two ranges [i, j] i>j -> [i,n][1,j]
  44. * the same way as brute force approach.
  45. *
  46. * Then we look for most deepest overlapping range. We use two pointer left and right.
  47. * When starts[right] <= ends[left] it implies that we hit an overlap otherwise we increment left
  48. * The window size just grows so when starts[right] <= ends[left] it means that we have hit the
  49. * deepest overlap so 'most' 1. the most visited node 2. the smallest node in the most visited nodes
  50. *
  51. * Time Complexity O(m*log(m)), Space Complexity O(m)
  52. */
  53. int circularArray1(int n, vector<int> endNode) {
  54. int m = endNode.size();
  55. vector<int> starts, ends;
  56. for (int i = 0; i < m - 1; i++) {
  57. auto start = endNode[i];
  58. auto end = endNode[i + 1];
  59. starts.push_back(start);
  60. ends.push_back(end);
  61. if (start > end) {
  62. starts.push_back(1);
  63. ends.push_back(n);
  64. }
  65. }
  66.  
  67. sort(starts.begin(), starts.end());
  68. sort(ends.begin(), ends.end());
  69.  
  70. int most = 0;
  71. for (int left = 0, right = 0; right < starts.size(); right++) {
  72. if (starts[right] <= ends[left]) {
  73. most = starts[right];
  74. } else {
  75. left++;
  76. }
  77. }
  78.  
  79. return most;
  80. }
  81. /*
  82. * The same as solution1 except:
  83. * 1. We do not need to add n to ends, if n is end range, n will never be selected
  84. * 2. We do not need to add 1 to start but we should set most=1 as default value
  85. */
  86. int circularArray2(int n, vector<int> endNode) {
  87. int m = endNode.size();
  88. vector<int> starts, ends;
  89. for (int i = 0; i < m - 1; i++) {
  90. auto start = endNode[i];
  91. auto end = endNode[i + 1];
  92. starts.push_back(start);
  93. ends.push_back(end);
  94. }
  95.  
  96. sort(starts.begin(), starts.end());
  97. sort(ends.begin(), ends.end());
  98.  
  99. int most = 1;
  100. for (int left = 0, right = 0; right < starts.size(); right++) {
  101. if (starts[right] <= ends[left]) {
  102. most = starts[right];
  103. } else {
  104. left++;
  105. }
  106. }
  107.  
  108. return most;
  109. }
  110. /*
  111. * We do not need two extra arrays since starts as these two arrays are almost the same
  112. * except in two elements, 'starts' misses nodeList[m - 1] and 'ends' misses nodeList[0]. We can use
  113. * a single array (or even endNode itself) to perform the same algorithm but we need to take care
  114. * of missing elements. In case of endNode[right] == end we ignore the iteration only once and
  115. * in case of endNode[left] = start we increment last by one once. We should be careful to ignore
  116. * start and end only once as there might be duplicates of start and end in the array.
  117. *
  118. * Time Complexity O(m*log(m)), Space Complexity O(1)
  119. */
  120. int circularArray3(int n, vector<int> endNode) {
  121. int m = endNode.size();
  122. int start = endNode[0], end = endNode[m - 1];
  123.  
  124. sort(endNode.begin(), endNode.end());
  125.  
  126. int most = 1;
  127. bool skipStartOnce = false, skipEndOnce = false;
  128. for (int right = 0, left = 0; right < endNode.size(); right++) {
  129. // ignore start on the left side
  130. if (!skipEndOnce && endNode[left] == start) {
  131. skipEndOnce = true;
  132. left++;
  133. }
  134. // ignore end on the right side
  135. if (!skipStartOnce && endNode[right] == end) {
  136. skipStartOnce = true;
  137. continue;
  138. }
  139. if (endNode[right] <= endNode[left]) {
  140. most = endNode[right];
  141. } else {
  142. left++;
  143. }
  144. }
  145. return most;
  146. }
  147.  
  148. /*
  149. * If we look at this code snippet from solution3 code
  150. * for (int right = 0, left = 0; right < endNode.size(); right++) {
  151. * ...
  152. * if (endNode[right] <= endNode[left]) {
  153. * most = endNode[right];
  154. * } else {
  155. * left++;
  156. * }
  157. * }
  158. * We notice that we are basically selecting the most frequent item here.
  159. * So similar results can be achieved with maps and counting without sorting in O(n)
  160. * But there are two problems:
  161. * 1. if (!skipEndOnce && endNode[left] == start) {
  162. * skipEndOnce = true;
  163. * left++;
  164. * }
  165. *
  166. * Which basically shrinks the window once we hit endNode[left] == start.
  167. *
  168. * endNode ASAAAAAAA ASAAAAAAA
  169. * window <----> => <--->
  170. * (A: some element, S: start, the element one the left side that we want to ignore)
  171. * As you can see we have expanded the window by 1 so that means that every element after
  172. * appearance of S the gets one score more to be selected as the most frequent item.
  173. * In the counting approach we can increment all the items that appear after start by 1
  174. *
  175. *
  176. *
  177. * 2. if (!skipStartOnce && endNode[right] == end) {
  178. * skipStartOnce = true;
  179. * continue;
  180. * }
  181. * Which basically expands the window for values larger or equal to end os we increment
  182. * the count map by one the same way.
  183. *
  184. * endNode AAAAAAEAA AAAAAAEAA
  185. * window <----> => <----->
  186. * (A: some element, S: start, the element one the left side that we want to ignore)
  187. * As you can see we have shrinked the window by 1 so that means that every element after
  188. * appearance of S the gets one score less to be selected as the most frequent item.
  189. * In the counting approach we can decrement all the items that appear after start by 1
  190. *
  191. * Time Complexity O(m), Space Complexity O(m)
  192. */
  193. int circularArray4(int n, vector<int> endNode) {
  194. int m = endNode.size();
  195. unordered_map<int, int> count;
  196. int start = endNode[0], end = endNode[m - 1];
  197.  
  198. for (auto &e: endNode)
  199. count[e]++;
  200.  
  201. for (auto &[k, _]: count) {
  202. if (k >= end)
  203. count[k]--;
  204. if (k > start)
  205. count[k]++;
  206. }
  207.  
  208. int max = 0, most = 1;
  209. for (auto &[k, v]: count) {
  210. // we are dealing with unsorted map so we need to
  211. // make sure we select the smallest item in the group
  212. if (max < v || max == v && most > k) {
  213. max = v;
  214. most = k;
  215. }
  216. }
  217. return most;
  218. }
Advertisement
Add Comment
Please, Sign In to add comment