Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge pairs on common integer with restrictions

I need an efficient way to merge a list of nodes (pairs of integers).
The merge should happen only if there is one common number in the pair and the common number is on the first or the last position (otherwise its allready connected).

For example:

mylist = [[4, 3], [6, 3]]
merge_links(mylist) # should output [4, 3, 6]

another example:

mylist = [[4, 3], [6, 3], [6, 4]]
merge_links(mylist) 
# should output again [4, 3, 6] because both 6 and 4 allready exist in array.

and yet another example:

mylist = [[4, 3], [6, 3], [6, 4], [6, 2], [7, 4], [4, 9]]
merge_links(mylist) 
# should output [7, 4, 3, 6, 2]

# [4, 3] ✔ 
# [4, 3] + [6, 3] ✔ -> [4, 3, 6]
# [4, 3, 6] + [6, 3] ✘ both 6 and 3 exist in [4, 3, 6] 
# [4, 3, 6] + [6, 4] ✘ both 6 and 4 exist in [4, 3, 6]
# [4, 3, 6] + [6, 2] ✔ -> [4, 3, 6, 2]
# [4, 3, 6, 2] + [7, 4] ✔ -> [7, 4, 3, 6, 2]
# [7, 4, 3, 6, 2] + [4, 9] ✘ 4 is allready connected "7-4-3"!

Currently I'm using:

def merge_links(a, b):
    inter = np.intersect1d(a, b, True)
    a = set(a) - set(inter)
    b = set(b) - set(inter)
    n = np.unique(list(a) + list(inter) + list(b))
    return n

But it doesnt work well for the above restrictions

like image 303
Panos Kal. Avatar asked Aug 28 '18 18:08

Panos Kal.


1 Answers

The code below works as follows:

  1. Create a new list with size of old list + 1 (The maximum size the output can reach).
  2. Start with the first pair in your list of pairs with its first value at the end of your new list, and its second value at the start of your new list. And mark them as visited using a python dictionary for example.
  3. Maintain two indexes of your first and last positions pointing to the end and start of your new list respectively initially.
  4. Iterate on the rest of pairs, if for pair i its both values exist or don't exist in the dictionary, skip it. Else, merge the not visited value to element at the first index or the last index (depending on the position of the visited value), and update your index. Note that no merging will happen if the visited value is not at the first or the last index (if it is somewhere in the middle).
  5. Return a concatenation of new list[first index to end] with new list[start to the last index].

Note: every merging operation takes O(1). Also you can use an array of booleans instead of dictionary if you know the range of pairs values.

#The function takes a list of pairs as an argument
def merge_lst(lst):

  #Dictionary to check if value is already in array
  visited = dict()

  #Length of old list
  lst_len = len(lst)

  #New list will have at most lst_len+1 elements
  new_lst = [0]*(lst_len+1)

  #Put the first pair values in last and first elements of the new list repectively and mark them as visited
  new_lst[lst_len], new_lst[0] = lst[0][0], lst[0][1]
  visited[lst[0][0]], visited[lst[0][1]] = True, True

  #Maintain the positions of your first and last elements, which are the the last index and 0 respectively now
  first_index, last_index = lst_len, 0

  #Iterate on the rest of pairs
  for i in range(1, lst_len):

    #Check if pair[a, b] are already visited
    a_exists, b_exists = lst[i][0] in visited, lst[i][1] in visited

    #Skip if they both exist or don't exist
    if(a_exists == b_exists):
      continue

    #Assume a was the common one
    common, to_merge = lst[i][0], lst[i][1]

    #If b exists (b is the common not a), then just swap
    if(b_exists):
      common, to_merge = lst[i][1], lst[i][0]

    #If the common element is at the first index, the first element and index are updated
    if(new_lst[first_index] == common):
      first_index-=1
      new_lst[first_index] = to_merge
      visited[to_merge] = True

    #If the common element is at the last index, the last element and index are updated
    elif(new_lst[last_index] == common):
      last_index+=1
      new_lst[last_index] = to_merge
      visited[to_merge] = True

    #Else, the common element is somewhre in the middle (already connected)

  #Return concatenation of new_lst[first_index to the end] with new_lst[0 to the last_index] 
  return new_lst[first_index:lst_len+1]+new_lst[0:last_index+1]

This code gives correct output for all of your mentioned test cases.

like image 99
Mohamed EL Tair Avatar answered Nov 18 '22 02:11

Mohamed EL Tair