I've seen many types of linked-list implementations of binary search trees, and I am wondering how I would go about implementing one in an array. Is this possible? And how would it look like if it is?
Thank you so much!
Here is an array implementation of a queue!
class Queue:
MAX = 6
def __init__(self):
self.queue = [None for x in range(self.MAX)]
self.front = 0
self.rear = 0
def isEmpty(self):
return self.front == self.rear
def size(self):
if self.isEmpty():
return 0
elif self.front < self.rear:
return self.rear - self.front
else:
return self.rear + self.MAX - self.front
def isFull(self):
return self.size() == self.MAX - 1
def insert(self, data):
if self.isFull():
print("Cannot insert to full queue")
else:
self.queue[self.rear] = data
self.rear = (self.rear + 1) % self.MAX
return data
def delete(self):
if self.isEmpty():
print("Cannot delete from empty queue")
else:
data = self.queue[self.front]
self.queue[self.front] = None
self.front = (self.front + 1) % self.MAX
return data
def peek(self):
if self.isEmpty():
return None
else:
return self.queue[self.front]
def display(self):
if self.isEmpty():
print("Empty Queue")
elif self.front < self.rear:
for i in range(self.front, self.rear):
print(self.queue[i], end = ' ')
else:
for i in range(self.front, self.MAX):
print(self.queue[i], end = ' ')
for j in range(self.rear):
print(self.queue[j], end = ' ')
Your question is a little bit confused. A Queue is an abstract data type that can be implemented in more than one way. Implementing it in an array or list data structure is a standard implementation and straightforward as you can see.
A Binary Search Tree is already an implementation - typically an implementation of an abstract data type like an Ordered Map Container abstract data type. It depends on the ability to (efficiently) create and delete nodes with links to other nodes in them. You typically need to code this implementation in terms of primitives in your language that implement that sort of creation and deletion. Restricting yourself to the array type rules out those primitives.
However, most languages implement those primitives on top of a more primitive layer, your computer process address space (its memory). So you could pretend that the array is like a memory and implement your own allocation and deallocation mechanism on top of that array. Have a look at typical memory allocation algorithms to see what I am talking about.
Of course this is not a good idea in practice, but perhaps you are doing it as a learning experience. It will certainly require some learning!
One other note. You may be thinking about a Heap (as implemented in the Python heapq module). A Heap is not a Binary Search Tree but it has some similarities and is worth learning about.
I've written BSTs, but I don't know how to do it using "array" or "linked list". Here's what me and other people normally do:
class TreeNode:
def __init__(self, val):
self.val = val
self.left = self.right = None
class Tree:
def __init__(self):
self.root = None
def add_node(self, val):
# traversing the tree by comparing val with existing node value
...
def remove_node(self, val):
# whatever...
You store TreeNode in a Tree.
Related
class Node:
def __init__(self, data, left_child=None, right_child=None):
self.data = data
self.left_child = left_child
self.right_child = right_child
self.height = -1
class BinarySearchTree:
def __init__(self):
self.root = None
self.height = -1
self.balance = 0
def inorder(self):
return self.inorder_helper(self.root)
def inorder_helper(self, cur_node):
lyst = []
if cur_node is not None:
lyst += self.inorder_helper(cur_node.left_child)
lyst.append(str(cur_node.data))
lyst += self.inorder_helper(cur_node.right_child)
final_lyst = list(map(int, lyst))
return final_lyst
def rebalance_tree(self):
arr = self.inorder()
return self.rebalance_tree_helper(arr)
def rebalance_tree_helper(self, arr):
if not arr:
return None
mid = (len(arr)) // 2
root = Node(arr[mid])
root.left_child = self.rebalance_tree_helper(arr[:root])
root.right_child = self.rebalance_tree_helper(arr[root + 1:])
return root
bst.add(5)
bst.add(10)
bst.add(12)
bst.add(20)
bst.rebalance_tree()
print(bst)
Having issues trying to convert a binary search tree into a balanced search tree. So i currently have a binary search tree and it works correctly, 100%. However, I am trying to add a method that will balance the tree (aka turn it into an avl tree).
So i tried using https://www.geeksforgeeks.org/sorted-array-to-balanced-bst/ and tried making it a method into a tree, but i am having a few issues.
it keeps saying that i am reaching the recursion limit, and this is worrying me as i added these few lines of code that makes the recursion limit a lot higher then it should be needed:
resource.setrlimit(resource.RLIMIT_STACK, (2**29,-1))
sys.setrecursionlimit(10**6)
So now I am wondering if my method is running infinitely and causing the recursion limit, but i took a long look at the rebalance_helper and it looks like its not infinite.
If anyone can give me any insight i would really appreciate it. Thank you!
I have provided a working example where I have a dynamic array implemented as custom type in Python 3. I wish to use a single instance of this dynamic array as a common resource for implementing, say, N stacks. How do I do that?
I imagine I would like to give each stack an access to only a certain part of the DynamicArray by making demarcation points _start and _end. In order to have _start and _end for each stack, I would like to wrap them in a helper class _StackRecord. In case I am successful in providing a modifiable view of DynamicArray, I want _StackRecord to do all the heavy lifting of poping and pushing such that the stacks don't collide while the underlying DynamicArray expands/shrinks as per the need. I know I am asking for too much, but I might learn some useless skills while I fail to do this.
Any suggestions/criticism towards modularity, maintainability and good practices are wholeheartedly welcome.
import ctypes
class DynamicArray:
"""Expandable array class similar to Python list"""
def __init__(self, size=0):
self._n = size
self._capacity = size + 1
self._A = self._make_low_level_array(self._capacity)
def _make_low_level_array(self, capacity):
return (capacity*ctypes.py_object)()
# following two methods are needed for Python to implement __iter__
def __len__(self):
return self._n
def __getitem__(self, index_key):
if isinstance(index_key, slice):
start, stop, step = index_key.indices(len(self))
return [self._A[i] for i in range(start, stop, step)]
elif isinstance(index_key, int):
if 0 <= index_key < self._n :
return self._A[index_key]
else:
raise IndexError("index out of bounds")
elif isinstance(index_key, tuple):
raise NotImplementedError('Tuple as index')
else:
raise TypeError('Invalid argument type: {}'.format(type(key)))
def __setitem__(self, index_k, value):
if 0 <= index_k < self._n :
self._A[index_k] = value
else:
raise IndexError("index out of bounds")
###################################################################
class FixedMultiStack:
class _StackRecord(DynamicArray):
def __init__(self, array: DynamicArray, stack_number=0, size_of_each=10):
self._stack = stack_number
self._start = stack_number*size_of_each
self._end = self._start + size_of_each
# try commenting the following lines
self._n = size_of_each
self._A = DynamicArray(self._n)
# If I have to use self._A then I would like it to point
# to array[self._start:self._end]
for i in range(self._start, self._end):
array[i] = i
for i in range(self._n):
self._A[i] = array[self._start+ i]
def __init__(self, numStack=1, sizeEach=10):
self._stacks = []
self._items = DynamicArray(numStack*sizeEach)
for i in range(numStack):
self._stacks.append(self._StackRecord(self._items, i, sizeEach))
def __getitem__(self, stack_number):
return self._stacks[stack_number]
if __name__ == "__main__":
fms = FixedMultiStack(3,10)
print(list(fms[0]))
print(list(fms[1]))
print(list(fms[2]))
print(list(fms._items))
Issues
I am doing the wasteful act of making a local copy called self._A. How do I avoid that? Why can't I just work on the global dynamic array passed to my local record keeper _StackRecord?
What do I expect?
fms = FixedMultiStack(3,10), A fixed multi stack packing 3 stacks of size 10 each such that
I would like, if self._A is necessary, the local self._A to refer to that part of DynamicArray which corresponds to the given stack number.
So that print(list(fms[n])) gives me the contents of nth stack
While print(list(fms._items)) should give me the combine state of all the stacks. Yikes! print(list(fms._items)) is ugly. How about print(list(fms))?
I should be able to write something like self._items[n].push(val), self._items[n].pop() to push and pop on n-th stack.
You can use memoryview for creating the different views over the entire array:
class FixedMultiStack:
def __init__(self, m, n):
self.data = bytearray(m*n)
view = memoryview(self.data)
self.stacks = [view[i*n:(i+1)*n] for i in range(m)]
def __getitem__(self, index):
return self.stacks[index]
BFS requires O(b^d) memory, whereas IDDFS is known to run in only O(bd) memory. However, when I profile these two implementations they turn out to use exactly the same amount of RAM - what am I missing?
I'm using a Tree class with a branching factor or 10 to run the tests:
class Tree(object):
def __init__(self, value):
self.key = value
self.children = [ ]
def insert(self, value):
if len(self.children) == 0:
self.children = [ Tree(value) for x in range(10) ]
else:
for ch in self.children:
ch.insert(value)
My implementation of iddfs:
def iddfs(t):
for i in range(0,8):
printGivenLevel(t, i)
def printGivenLevel(t, level):
if not t:
return
if level == 1:
pass
elif level > 1:
for ch in t.children:
printGivenLevel(ch, level - 1)
BFS is
def bfs(t):
currLevel = [t]
nextLevel = []
while currLevel:
for node in currLevel:
if node:
nextLevel.extend([ x for x in node.children ])
currLevel = nextLevel
nextLevel = []
The code is not really doing anything, just looping through the whole tree.
I'm using https://github.com/fabianp/memory_profiler to profile the code.
IDDFS's memory benefits only apply to an implicit tree, where nodes are generated as they're reached and discarded soon after. With a tree represented completely in memory, the tree itself already takes O(b^d) memory, and the memory required for either IDDFS or BFS is minor in comparison.
I have a dictionary their key is a word an I want in the elements save a linked list, something like this.
Dictionary
key element
hi linkedlist1
hello linkedlist2
i already make it with arrays
dictionari={}
for textoactual in texto:
archi = open(textoactual,'r')
lines = archi.readlines()
for row in lines:
for word in row.split(' '):
if word in dictionari:
aux = dictionari[word]
aux_txt = textoactual.replace('.txt','')
if not(aux_txt in aux):
aux.append(aux_txt)
dictionari[word]=aux
else:
aux_txt = textoactual.replace('.txt','')
dictionari[word] = makelist(aux_txt)
EDIT3 this might come too late to the show, since the question was accepted over a month ago, yet I've got a thing to add.
In fact Python has a standard C-ish linked list implementation that is the deque class in the collections module. Here is the source
A dequeobject is composed of a doubly-linked list of block nodes.
So if you need a fast linked list in Python stick with deque.
EDIT2 based on OP's comment.
...because i want to see what is faster linked list or arrays when i
search information
Search complexity in a linked list is equal to that in an array (or array-based structures) and is approximately O(n), where n is the number of elements in your container. But since Python built-in data structures are heavily optimized and C-loaded, they will run a lot faster in real-life usage. Linked lists are helpful when you need constant time insertion/deletion at any position of the list or when you don't want to mess with dynamically sized arrays, but it doesn't seem like your case. Since you are actually looking for fast search, you need a hash-table, hence use sets to store file names. In order to do this replace the following line in the match_words_and_files
res.setdefault(word, llist.LinkedList()).insert_with_lookup(file_title)
with
res.setdefault(word, set()).add(file_title)
EDIT. OP updated the request. Provided the LinkedList stuff is saved in a separate module named llist:
import os
import llist
def match_words_and_files(directory):
directory = os.path.abspath(directory)
res = {}
for file_name in filter(os.path.isfile, os.listdir(directory)):
file_title = os.path.splitext(file_name)[0]
with open(os.path.join(directory, file_name)) as inp:
for line in inp:
parsed_line = line.rstrip().split()
for word in parsed_line:
res.setdefault(word, llist.LinkedList()).insert_with_lookup(file_title)
return res
Original post.
If you want a linked list in Python it can be implemented this way (obviously this is not the only way to do it)
class Node(object):
__slots__ = ["_data", "_next_node"]
def __init__(self, data, next_node=None):
self._data = data
self._next_node = next_node
def __str__(self):
return str(self._data)
def __repr__(self):
return repr(self._data)
#property
def data(self):
return self._data
#property
def next_node(self):
return self._next_node
def link_node(self, next_node):
if not hasattr(next_node, "_next_node"):
self._next_node = Node(next_node)
self._next_node = next_node
class LinkedList(object):
def __init__(self, head=None):
if head is not None and not isinstance(head, Node):
self._head = Node(head)
else:
self._head = head
def __repr__(self):
return repr([repr(node) for node in self.iter_links()])
def __str__(self):
return ','.join(str(node) for node in self.iter_links())
def __len__(self):
return sum(1 for _ in self.iter_links())
def set_head(self, head):
self._head = head
def insert(self, node):
if not isinstance(node, Node):
node = Node(node)
node.link_node(self._head)
self._head = node
def insert_with_lookup(self, node):
"""
Inserts a node if the data it contains is not equal to the one
stored in the the head node.
"""
if not isinstance(node, Node):
node = Node(node)
if node.data != self._head.data:
self.insert(node)
def iter_links(self):
current_node = self._head
while current_node:
yield current_node
current_node = current_node.next_node
linked_list = LinkedList(1)
linked_list.insert(2)
linked_list.insert(3)
Let's create one and grow it a little
print(list(linked_list.iter_links()))
The output:
[3, 2, 1]
P.S.
I don't see a single reason to use a linked list in your case.
Below code is a simple implementation of BFS in Python. I able to print the values level by level from a tree. However when I want to search a element and print it . I am not able to do it. Whts is the error?
def search_bfs(self,root,key):
q=QueueClass()
q.enqueue(root)
while q.size() > 0:
curr_node = q.dequeue()
#print curr_node
#print key
if curr_node == key:
print curr_node
break
if curr_node.left is not None:
q.enqueue(curr_node.left)
if curr_node.right is not None:
q.enqueue(curr_node.right)
from QueueClass import QueueClass
class Node:
def __init__(self,data):
self.data=data
self.left=None
self.right=None
def __str__(self):
return str(self.data)
class searchtree:
def __init__(self):
self.root = None
def create(self,val):
if self.root == None:
self.root=Node(val)
else:
current=self.root
while 1:
if val < current.data:
if current.left:
current=current.left
else:
current.left=Node(val)
break
if val > current.data:
if current.right:
current=current.right
else:
current.right=Node(val)
break
else:
break
tree=searchtree()
lst=[3,1,2,6,4,5,8,12]
for i in lst:
tree.create(i)
tree.search_bfs(tree.root, 3)
You really make it hard to reproduce your problem! So here's what I did:
class QueueClass(object):
def __init__(self):
self.l = []
def size(self): return len(self.l)
def enqueue(self, it): self.l.append(it)
def dequeue(self): return self.l.pop()
class Node:
def __init__(self, name, left=None, right=None):
self.name = name
self.left = left
self.right = right
def __str__(self):
return '{}:{}/{}'.format(self.name, self.left, self.right)
root = Node('root')
adding all the code you omitted (more than you supplied!-).
And now, adding your code exactly as reported, the call:
search_bfs(None, root, root)
emits
root:None/None
exactly as desired and contrary to your report.
It follows that your bug is in some of code you didn't show us, not in the coded you did show.
You either have a buggy queue-class, or are building a different tree than you thought, or searching for a node that is not actually in the tree.
Hard to debug code you're now showing, you know.
Added: so now I've integrated the extra code per your edit and at the end I have:
st = searchtree()
st.create('imtheroot')
st.search_bfs(st.root, st.root)
and of course it prints imtheroot as expected.
Is your bug perhaps STILL hiding in parts you're not yet showing, e.g instead of looking for a node you may be looking for something else?
E.g, if the final call was erroneously st.search_bfs(st.root, 'imtheroot') then obviously the search would fail -- you're checking equality of the key parameter with a node, so key clearly must be a node. not a string or other things (unless the Node class defines a very, very peculiar __eq__ method, which the one you've shown fortunately doesn't:-).
I think the issue is that when you do if curr_node == key, curr_node is a Node object, which has an integer .data attribute, but key is the integer value.
So I think you just need to use if curr_node.data == key.