What is the time complexity for this proposed solution - algorithm

Question
Given an integer array nums, return the maximum result of nums[i] XOR nums[j], where 0 <= i <= j < n.
Example 1:
Input: nums = [3,10,5,25,2,8]
Output: 28
Explanation: The maximum result is 5 XOR 25 = 28.
Constraints:
1 <= nums.length <= 2 * 105
0 <= nums[i] <= 231 - 1
Approach:
I have solved using Triewhere I add each elements and after adding I search for the number which can result in maximum xor.
Solution:
class TreeNode:
def __init__(self, value = '-1'):
self.val = value
self.left = None
self.right = None
class Solution(object):
def findMaximumXOR(self, a):
root = TreeNode()
msf = float('-inf')
for i in a:
bits = format(i, 'b').zfill(31)
node = root
for bit in bits:
if bit == '0' and node.left:
node = node.left
elif bit == '0':
new_node = TreeNode('0')
node.left = new_node
node = new_node
if bit == '1' and node.right:
node = node.right
elif bit == '1':
new_node = TreeNode('1')
node.right = new_node
node = new_node
ssf=''
node = root
for bit in bits:
if bit == '0' and node.right:
ssf += '1'
node = node.right
elif bit == '1' and node.left:
ssf += '1'
node = node.left
elif node.left:
ssf += '0'
node = node.left
else:
ssf += '0'
node = node.right
val = int(ssf, 2)
msf = max(msf, val)
return msf
Question: What is the time complexity of the above solution? In my opinion it is O(N) where N is the number of element in nums.

your time complexity is O(N*Log2(max(nums))).

It is actually O(n*(log2(max_num)**2)), because for each bit you do string concatenation, which is not O(1) in Python:
for bit in bits: # this entire for loop is O(bits**2)
if bit == '0' and node.right:
ssf += '1' # this is not O(1), it is O(len(bits))
...
Avoid strings, use bitwise operators to get true O(n*log(max_num)).

Related

Comparison of Integer with nil failed (ArgumentError) while solving Insertion Sort with Ruby

Below is the updated code:
def insertion_sort(list)
num = list.length
for i in (0..(num-2))
if list[i] > list[i+1] && i == 0
list[i], list[i+1] = list[i+1], list[i]
i+=1
elsif list[i] == list[i+1]
i+=1
elsif list[i] > list[i+1] && i > 0
len = (list[0..(i+1)].length)
list2 = list[0..(i+1)]
list = list - list2
count = 0
while count <= len+1
if list2[len-1] < list2[len-2]
list2[len-2],list2[len-1]= list2[len-1],list2[len-2]
elsif list2[len-1] == list2[len-2]
count+=1
len-=len
else
count+=1
len-=1
end
end
list = list2 + list
end
end
list
end
p insertion_sort([2,1,4,8,7,3,100,99,8])
p insertion_sort([2,1,4,8,8,7,3,100,99])
p insertion_sort([3790,780,780,1,55])
Summary:
code works if two identical integers are right next to each other :[2,1,4,8,8,7,3,100,99] and the array size > 5.
if the two identical integer are at random positions: [2,1,4,8,7,3,100,99,8]. Below error would occur
aaa.rb:4:in `>': comparison of Integer with nil failed (ArgumentError)
with Line 4 code being: if list[i] > list[i+1] && i == 0
To solve 1. I changed the while loop to "while count <= len+1",
so when array size is smaller than 5 the code would work. But not when identical integers are at random positions.
Does anyone know how to solve this?
Thanks in advance!
Thanks for the clarification in the comments. I see the problem now.
In the swap algorithm here
elsif list[i] > list[i+1] && i > 0
len = (list[0..(i+1)].length)
list2 = list[0..(i+1)]
list = list - list2
count = 0
while count <= len+1
...
you are trying to split the array in two. You get list2 which is the first half of the array and then try and get the second half by subtracting list2 from list.
The problem with using subtract here is that if you have duplicates it will remove them and make your lists too short.
In the example [3790,1,780,55,23,50,1111,60,50] you should have a 50 in the first array and a 50 in the second half.
But using subtract removes one of those 50.
When you add the two temporary lists back together you are now one element short (the missing 50) and you get an out of bounds error when you get to the end of the array and try and access this 9th element which no longer exists.
Instead of using subtract here simply use the same method you used to make list2.
list2 = list[0..(i+1)] # All elements in list from position 0 to i+1
list = list[(i+2)..-1] # All elements in list from position i+2 to end of list
Now list and list2 are just the original list split, and when you add them back together are the end they should be the same length
def insertion_sort(list)
num = list.length
for i in (0..(num-2))
if list[i] > list[i+1] && i == 0
list[i], list[i+1] = list[i+1], list[i]
i+=1
elsif list[i] == list[i+1]
i+=1
elsif list[i] > list[i+1] && i > 0
len = (list[0..(i+1)].length)
list2 = list[0..(i+1)]
list = list[(i+2)..-1]
count = 0
while count <= len+1
if list2[len-1] < list2[len-2]
list2[len-2],list2[len-1]= list2[len-1],list2[len-2]
elsif list2[len-1] == list2[len-2]
count+=1
len-=len
else
count+=1
len-=1
end
end
list = list2 + list
end
end
list
end
p insertion_sort([2,1,4,8,7,3,100,99,8])
p insertion_sort([2,1,4,8,8,7,3,100,99])
p insertion_sort([3790,780,780,1,55])

Coding Challenge 2

Find the element in an array which has consecutive plus or minus 1. I can achieve in O(n) time by doing a linear search. Is there any efficient way to achieve in less than O(n) time.
What I mean by plus or minus 1 is 2 consecutive number in an array will have either -1 or 1 as the difference
For example sample array inputs
Consecutive element have +1 of difference
arr = [1,2,3,4,5]
Consecutive elements have -1 of difference
arr = [ -1,-2, -3, -4 , -5, ]
Elements have either +1 or -1 of difference
arr = [-5,-4,-3,-4, -3,-2,-1,0,1,2,3,4,3,2,1]
Note: Array is unsorted
Example
arr = [-5,-4,-3,-4,-3,-2,-1,0,1,2,3,2,1,2,3,4,5,6,5,4,3,2,1,0,-1,-2,-3,-4,-3,-2,-1]
while i <= arr.length do
if num == a[i]
puts "found"
break
end
i += 1
end
2nd approach which in my mind is instead of starting from 0 index I have done some custom logic
num = gets.to_i
i = 0
if (num < 0 && arr[0] < 0) || (num > 0 && arr[0] < 0)
i = (num + arr[0].abs).abs
elsif num < 0 && arr[0] > 0
i = num.abs + arr[0]
elsif num > 0 && arr[0] > 0
i = (num > arr[0] ? (num - arr[0]) : (arr[0] - num))
end
while i <= arr.length do
if num == a[i]
puts "found"
break
end
i += 1
end
As Nico Schertler said, O(n) is the best you can do. This is a find operation, and find operations on unsorted data are always O(n), because in the worst case they must check every element. This would be different if your data were sorted. See binary search.
Note also that your example code does not do what you're asking - it simply finds some number num in the list. Your example code is equivalent to:
arr.find { |i| i == num }
What you appear to be asking for is
arr.find { |i| i == num - 1 or i == num + 1 }

Leetcode #501 - Find mode in binary tree: Trouble with recursion and pass-by-reference

I believe my code works in one scenario and breaks in another for the Leetcode problem #501 due to gaps in my understanding of recursion and pass-by-reference concepts in Ruby.
Using instance variables, the following code works:
def find_mode(root)
return [] if root.nil?
#modes = []
#counter = 1
#max = 0
#prev = nil
inorder(root)
#modes
end
def inorder(node)
return if node.nil?
inorder(node.left)
if !#prev.nil?
if #prev.val == node.val
#counter += 1
else
#counter = 1
end
end
if #counter > #max
#max = #counter
#modes = [node.val]
elsif #counter == #max
#modes << node.val
end
#prev = node
inorder(node.right)
end
However, the following alternate version does not work. Instead it times out.
def find_mode(root)
return [] if root.nil?
modes = []
counter = 1
max = 0
prev = nil
inorder(root, modes, prev, counter, max)
modes
end
def inorder(node, modes, prev, counter, max)
return if node.nil?
inorder(node.left, modes, node, counter, max)
if !prev.nil?
if prev.val == node.val
counter += 1
else
counter = 1
end
end
if counter > max
max = counter
modes = [node.val]
elsif counter == max
modes << node.val
end
prev = node
inorder(node.right, modes, node, counter, max)
end
What am I failing to understand in the second approach?

how to create graph (data structure) using linked lists

I have create my linked list and I need to create a graph without using dictionaries and normal lists, i have to use linked list, how anyone recommended me to build it? my linked list looks like this:
class LinkList:
def __init__(self):
self.head = None
self.tail = None
self.count = int()
def add(self, value):
if not self.head:
self.head = Node(value)
self.tail = self.head
else:
self.tail.next = Node(value)
self.tail = self.tail.next
self.count += 1
def get(self, position):
node = self.head
for i in range(position):
if node:
node = node.next
if not node:
return "posicion no encontrada"
else:
return node.value
def delete_first(self):
if self.head is None:
print("lista vacia, imposible eliminar primer elemento")
elif self.head == self.tail:
self.head = None
self.tail = None
self.count -= 1
print("habia un elemento, lista vacia")
else:
self.head = self.head.next
self.count -= 1
def delete_last(self):
if self.tail is None:
print("lista vacia, imposible eliminar ultimo elemento")
elif self.head == self.tail:
self.head = None
self.tail = None
self.count -= 1
print("habia un elemento, lista vacia")
else:
validate = True
temp = self.head
while validate:
if temp.next == self.tail:
self.tail = temp
self.count -= 1
validate = False
else:
temp = temp.next
def delete(self, number):
temp = self.head
temp2 = self.head
if self.head is None:
print("Lista vacia, imposible eliminar elemento")
else:
if self.head.value == number:
self.delete_first()
elif self.tail.value == number:
self.delete_last()
else:
validate = True
while validate:
temp = temp.next
if temp.value == number:
temp2.next = temp.next
self.count -= 1
validate = False
elif self.tail == temp:
print("El elemento no esta en la lista")
validate = False
temp2 = temp2.next
def __repr__(self):
if self.head is None:
return str("[]")
rep = '['
actual_node = self.head
while actual_node:
if actual_node != self.tail:
rep += '{0},'.format(actual_node.value)
else:
rep += '{0}]'.format(actual_node.value)
break
actual_node = actual_node.next
return rep
To create a graph data structure, you need to modify your linked list so that each node has a pointer to multiple nodes. Since you have a max of 7 connections, this could be an array with the list of connections.
Instead of just having node.next, you might have an array of connections, like node.links where you would add a link to connect a node to any other node in the graph.

How do I write a merge sort?

I am trying to implement a merge sort and am getting stack level too deep (SystemStackError) error when I run my code. I am not sure what the issue may be.
def merge_sort(lists)
lists if lists.count == 1
middle = lists[0..(lists.count / 2) - 1 ]
left = lists[0..middle.count - 1]
right = lists[middle.count..lists.count]
x = merge_sort(left)
y = merge_sort(right)
end
merge_sort [1,2,3,4,5,6,7,8]
Any help would be great!
From wikipedia:
def mergesort(list)
return list if list.size <= 1
mid = list.size / 2
left = list[0...mid]
right = list[mid...list.size]
merge(mergesort(left), mergesort(right))
end
def merge(left, right)
sorted = []
until left.empty? || right.empty?
if left.first <= right.first
sorted << left.shift
else
sorted << right.shift
end
end
sorted.concat(left).concat(right)
end
write this
return lists if lists.count == 1
instead of
lists if lists.count == 1
In Ruby, from a method last statement is always returned by default. But if you want to return from the middle of any lines except the last line conditionally, you must need to use return keyword explicitly.
A simplified version with comments
def merge_sort(arr)
# 0. Base case
return arr if arr.length <= 1
# 1. Divide
mid = arr.length / 2
arr0 = merge_sort(arr[0, mid])
arr1 = merge_sort(arr[mid, arr.length])
# 2. Conquer
output = merge(arr0, arr1)
end
def merge(l, r)
output = []
until l.empty? || r.empty?
output << if l.first <= r.first
l.shift
else
r.shift
end
end
# The order of `concat(l)` or `concat(r)` does not matters
output.concat(l).concat(r)
end
https://www.khanacademy.org/computing/computer-science/algorithms/merge-sort/a/divide-and-conquer-algorithms
This is a good way to do it. Tis a bit tricky at first, but stay at it.
def merge_sort list
if list.length <= 1
list
else
mid = (list.length / 2).floor
left = merge_sort(list[0..mid - 1])
right = merge_sort(list[mid..list.length])
merge(left, right)
end
end
def merge(left, right)
if left.empty?
right
elsif right.empty?
left
elsif left.first < right.first
[left.first] + merge(left[1..left.length], right)
else
[right.first] + merge(left, right[1..right.length])
end
end

Resources