Stuck at an algo which searches indexes of a list - python

The function searches the index of a value and if it is called again then it returns the next index of that value. Here is what i tried, where I'm getting error is that my list is not getting overridden and after every call I am getting same result.
Note: I have not included linked list classes here
If anyone knows a better way to make this function please suggest
class Node:
def __init__(self,value):
self.value = value
self.next = None
self.previous = None
class LinkedList:
def __init__(self):
self.head = None
self.tail = None
class DynamicSharpSearch(LinkedList):
def __init__(self, data):
super().__init__()
self.data = data
self.count = 0
self.lim = 0 # using in search function
for i in self.data:
self.InsertatEnd(i)
self.count += 1
self.demlst = [-1 for i in range(self.count)] # using in Search function only
self.seclist = self.demlst
def Search(self,n):
x = self.head
#demlst = [-1 for i in range(self.count)]
y = 0
tst = -1 # checks value if not in our data
##############################
for i in range(self.count):
if n == x.value:
# demlst.append(i)
self.demlst[y] = i
tst = 0
x = x.next
y += 1
##############################
if tst == -1:
return -1
else:
pass
"""Demlist containes "indexes" """
for i in range(self.count):
if self.seclist[i] >= 0:
#y = self.seclist[i]
self.seclist[i] = -1
self.lim = i
return i
obj = DynamicSharpSearch([53,4,52,7,5,4,5,5,5,6,4,2,4,5,459]) # c = 6
print(obj.Search(5))
print(obj.Search(5))
print(obj.Search(5))
What output I am getting:
4
4
4

I would use a dictionary to keep track of the last returned index for each value.
When the Search method is called, it looks to see if it's looked for that value before: start_index = self.last_returned.get(n, 0). If start_index is >0, then you fast-forward to that index before initiating the search. Once you've found what you're looking for, update the dictionary: self.last_returned[n] = returned_index + 1. Why "+1"? Because otherwise you'll get what you're getting right now, the same index returned over and over. You need to start searching after the last index you returned. (You'd want to make sure you're not storing a value which would result in an IndexError, however.)

Related

Building a Binary Search Tree from a file

I have a text file of lines in the format
2 0 0
7 0 0
4 1 1
10 0 0
9 0 1
8 1 1
These lines represent the data in a binary search tree where the first element is the node data, the second is whether or not a left child exists ( 0 if no, 1 if yes) and the third is whether or not a right child exists (0 if no, 1 if yes)
I have a class called "BinarySearchTree" which has the following initialization function
def __init__(self, value=None):
# Initializes the tree with a value node, a left child and a right child
self.leftChild = None
self.rightChild = None
self.height = 1
self.value = value
I also have a stack class with the following "push" and "pop" functions:
def push(self, item):
# Adds an item to the beginning of the stack
ending = self.stack
self.stack = [item] + [ending]
def pop(self):
# Removes and returns the first element from the stack
if self.isEmpty():
return None
top_element = self.stack[0]
self.stack = self.stack[1:]
return top_element
I am trying to create a binary search tree instance from the lines in the text file and using the stack class. So far I have:
def loadTreeFromFile(filename):
binarySearchTree = stack.Stack()
with open(filename) as file:
# gets a list containing only the elements in the txt file
for level in file.readlines():
nodeInfo = level.rstrip().split()
data, lc, rc = int(nodeInfo[0]), int(nodeInfo[1]), int(nodeInfo[2])
print(data, lc, rc)
if rc == 1:
right_tree = binarySearchTree.pop()
if lc == 1:
left_tree = binarySearchTree.pop()
newTree = BinarySearchTree(data)
if rc == 1:
newTree.rightChild = right_tree
if lc == 1:
newTree.leftChild = left_tree
binarySearchTree.push(newTree)
return newTree
I am running into the problem when I try to display the BST, I get 8: [[[<__main__.BinarySearchTree object at 0x1033e4390>, []]], 9: [None, 10: [None, None]]] (I have a display function written for the BST class so this is not the problem) AND when I try to do anything with this newly created BST (such as get the depth, search it, etc), I get errors. Any help is much appreciated, thanks .

printing stack in a new line within a class

I have a linked stack class and I'm having the problem of printing the elements in the stack, with each element in a new line. The str function in the linked stack class is printing every element in a new line which is what i wanted but it even prints an extra new line at the end.
class Node:
def __init__(self,item,the_next = None):
self.item = item
self.next = the_next
def __str__(self):
return str(self.item)
class LinkedStack:
def __init__(self):
self.top = None
self.count = 0
def __len__(self):
return self.count
def is_empty(self):
return self.count == 0
def isFull(self):
return False
def reset(self):
self.top = None
self.count = 0
def __str__(self): #im having the issue here whereby it prints a newline even after printing the last element
current = self.top
ans = ""
while not (current is None):
ans += str(current)
ans += '\n'
current = current.next
return ans
if __name__ == "__main__":
L = LinkedStack()
L.push(1)
L.push(2)
L.push(3)
print(L)
The output i get is:
3
2
1
#an extra newline printed here which is not wanted
I'm looking for a way to improvise the str function in the Linked Stack class in order to get rid of the redundant new line at the end. Any help is much appreciated.
Why not simply trim the return value of __str__ like so return ans[:-1] ? Since you always append a new line after adding an element to the string.
On a side note, it could be nicer to write your function like so:
def __str__(self):
strs = []
cur = self.top
while cur is not None:
strs = [str(cur)] + strs
cur = cur.next
return '\n'.join(strs)
You could move the addition of the newline until after you get the next item, and check it is not None before you then append it.
A further optimisation then is to move the break condition to that point:
while True:
ans += str(current)
current = current.next
if current is None:
break
ans += '\n'

Append method for linked list

class _ListNode:
def __init__(self, value, next_):
self._data = value
self._next = next_
return
class List:
def __init__(self):
self._front = None
self._count = 0
return
def _linear_search(self,key):
previous = None
current = self._front
index = 0
while current is not None and key > current._data:
previous = current
current = current._next
index += 1
if current._data != key:
previous = None
current = None
index = -1
return previous, current, index
def __contains__(self, key):
_, _, i = self._linear_search(key)
return i != -1
def append(self, value):
if self._front is None:
self._front = _ListNode(value,None)
else:
self._front._next = _ListNode(value,None)
self._count += 1
l = List()
lst = [1,2,3,4]
i = 0
n = len(lst)
while i < n:
l.append(lst[i])
i += 1
print("{}".format(l.__contains(3))
To explain more, I implement the linear search method and the contains method. The contains method check if the number is in the list or not (returns true or false). Now when I need to check that #3 in the list using contains method, the answer is false!! i cant know what's the problem
Your append method does not walk down the list. It simply always appends to self._front._next if self.front is already present. Meaning the contents at the end of the append loop are the first thing you appended, the last thing you appended and nothing in between.
To correct it walk the list looking for a _next equal to None and append there.
def append(self, value):
if self._front is None:
self._front = _ListNode(value, None)
else:
n = self._front
while n._next is not None:
n = n._next
n._next = _ListNode(value, None)
self._count += 1
You could also define a _str__ method to print the content of the List
e.g.
def __str__(self):
res = []
n = self._front
while n is not None:
res.append(str(n._data))
n = n._next
return ', '.join(res)
This is not a particularly efficient implementation as it builds an intermediate builtin list object.
You also don't need those bare return statements in your methods. You can remove those.

Code tracing and logical errors

class MyHashTable:
def __init__(self, capacity):
self.capacity = capacity
self.slots = [None] * self.capacity
def __str__(self):
return str(self.slots )
def __len__(self):
count = 0
for i in self.slots:
if i != None:
count += 1
return count
def hash_function(self, key):
slot = key % len(self.slots)
if key in self.slots:
return slot
elif (not key in self.slots) and len(self.slots) == self.capacity:
return slot
else:
for i in self.slots:
count = 0
if i == None:
return count
count += 1
def insert(self, key):
print(len(self.slots)) #Why does this show an output of 2?
if key in self.slots:
return -2
elif (not key in self.slots) and (len(self.slots) != self.capacity): #Now this cant execute
num = hash_function(key)
self.slots[num] = key
return num
elif (not key in self.slots) and len(self.slots) == self.capacity:
return -1
Im wondering why the commented part above in the insert(self, key) the print statement gives (2) instead of (0). The elif statement underneath wont execute since its giving a result of (2) instead of (0)
A function call of
x = MyHashTable(2)
print(len(x))
Should give: 0
You're initializing self.slots = [None] * self.capacity, so with capacity = 2, self.slots is [None, None], which is of length 2.
Your __len__ method doesn't run because len(self.slot) calls self.slot.__len__, not self.__len__. If you'd like to use your override method, you should be calling len(self) instead.
You have to call your __len__ function (by calling self.__len__() ) if you want the length of the elements which are not None. For lists None are valid entries.
By the way. It is always best to compare with None by a is None or a is not None instead of == or !=.

Deleting a node from Linked List having "sentinels"

I am trying to write a Linked List having sentinels as per the CLRS book. My remove function for some reason removes a chunk of LL upto the node to be deleted. Attached is my code. Any suggestion will be deeply appreciated.
class Node():
def __init__(self,v):
self.value = v
self.next = None
self.prev = None
def getValue(self):
return self.value
def changeValue(self,v):
self.value = v
def getNext(self):
return self.next
def getPrev(self):
return self.prev
def setNext(self,newNext):
self.next = newNext
def setPrev(self,newPrev):
self.prev = newPrev
class List(Node):
def __init__(self):
self.nil = Node(None)
def addNode(self,v):
a = Node(v)
a.setNext(self.nil.next)
a.setPrev(self.nil)
self.nil.next = a
def length(self):
count = 0
a = self.nil
while(a.next != None):
count += 1
a = a.getNext()
return count
def search(self,v):
a = self.nil
while(a.next != None):
if (a.value == v):
return True
a = a.getNext()
return False
def remove(self,v):
a = self.nil.next
breakloop = 0
while((a.next != None) and (breakloop == 0)):
if (a.value == v):
a.prev.next = a.next
a.next.prev = a.prev
breakloop = 1
a = a.getNext()
def printList(self):
a = self.nil.next
while(a.next != None):
print(a.value)
a =a.getNext()
print(a.value)
a = List()
a.addNode(4)
a.addNode(7)
a.addNode(2)
a.addNode(6)
a.addNode(5)
a.addNode(8)
a.addNode(1)
a.addNode(14)
a.addNode(13)
a.addNode(17)
a.addNode(18)
a.printList()
a.remove(13)
a.printList()
The output will be
18 17 13 14 1 8 5 6 2 7 4
14 1 8 5 6 2 7 4
#tcaswell has correctly diagnosed the problem with the code: you're not setting the prev links on the node that used to be self.nil.next correctly. However, I think his solution is not ideal. Here's what I suggest instead:
Here's the immediate fix to the issue:
def addNode(self, v):
a = Node(v)
a.setNext(self.nil.next)
self.nil.next.setPrev(a) # this is the link that was previously missing
a.setPrev(self.nil)
self.nil.setNext(a)
However, that won't work correctly when the list is empty, since self.nil.next is None at the start. We can fix it though, by making self.nil link to itself when we create it in the List constructor:
def __init__(self):
self.nil = Node(None)
self.nil.next = self.nil.prev = self.nil # set up circular links!
Now, self.nil will always have a valid node as it's next and prev values.
You will need to change your removeNode and printList loops to check for self.nil rather than None.
The bug is in your addNode function, the .prev node for all of your nodes is self.nil
Using the following:
def addNode(self,v):
a = Node(v)
a.setNext(self.nil.next)
if self.nil.next is not None:
self.nil.next.setPrev(a)
a.setPrev(self.nil)
self.nil.next = a
will fix your problem. You probably want to put this logic in the setPrev and setNext functions (to make sure a == a.next.prev and a == a.prev.next at all times for all a except the ends).

Categories