Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated singly_linked_list #2477

Merged
merged 15 commits into from
Sep 25, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
* [Sum Of Subsets](https://github.com/TheAlgorithms/Python/blob/master/backtracking/sum_of_subsets.py)

## Bit Manipulation
* [Binary And Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_and_operator.py)
* [Binary Or Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_or_operator.py)
* [Binary Xor Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_xor_operator.py)

## Blockchain
* [Chinese Remainder Theorem](https://github.com/TheAlgorithms/Python/blob/master/blockchain/chinese_remainder_theorem.py)
Expand Down
162 changes: 102 additions & 60 deletions data_structures/linked_list/singly_linked_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,54 @@ def __repr__(self):
class LinkedList:
def __init__(self):
self.head = None # initialize head to None
realDuYuanChao marked this conversation as resolved.
Show resolved Hide resolved
self.size = 0 # length of linked list
Copy link
Member

@cclauss cclauss Sep 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not a fan of this change. This creates duplicate state and opens us up to too many locations where this variable must be accurately updated or we have bugs that are difficult to debug.

Instead, please consider creating an .__iter__() method. Then .__len__() becomes return len(tuple(self)). This could also simplify other methods like .__repr__(), .__str__(), etc.
There are examples of creating this method elsewhere in this repo.

Copy link
Member Author

@realDuYuanChao realDuYuanChao Sep 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please give me an example file link in this repo.

Copy link
Member

@cclauss cclauss Sep 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def __iter__(self):
        node = self.head
        while node:
            yield node.data
            node = node.next

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add this add source file ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is just me but I like to sort the .__xxx__() methods above the other methods so the reader knows the builtin capabilities before reading the custom methods.

Copy link
Member Author

@realDuYuanChao realDuYuanChao Sep 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please clone this this branch and commit what you want. I am not similar to implement __iter__ . Thanks in advance.


def insert_tail(self, data) -> None:
if self.head is None:
self.insert_head(data) # if this is first node, call insert_head
else:
temp = self.head
while temp.next: # traverse to last node
temp = temp.next
temp.next = Node(data) # create node & link to tail
self.insert_nth(self.size, data)

def insert_head(self, data) -> None:
self.insert_nth(0, data)

def insert_nth(self, index: int, data) -> None:
if index < 0 or index > self.size: # test if index is valid.
raise IndexError("list index out of range.")
new_node = Node(data) # create a new node
realDuYuanChao marked this conversation as resolved.
Show resolved Hide resolved
if self.head:
if self.head is None:
self.head = new_node
elif index == 0:
new_node.next = self.head # link new_node to head
self.head = new_node # make NewNode as head
self.head = new_node # make NewNode as head
realDuYuanChao marked this conversation as resolved.
Show resolved Hide resolved
else:
temp = self.head
for _ in range(index - 1):
temp = temp.next
new_node.next = temp.next
temp.next = new_node
self.size += 1

def print_list(self) -> None: # print every node data
temp = self.head
while temp:
print(temp.data)
temp = temp.next
print(self)
cclauss marked this conversation as resolved.
Show resolved Hide resolved

def delete_head(self): # delete from head
realDuYuanChao marked this conversation as resolved.
Show resolved Hide resolved
temp = self.head
if self.head:
self.head = self.head.next
temp.next = None
return temp
return self.delete_nth(0)

def delete_tail(self): # delete from tail
temp = self.head
if self.head:
if self.head.next is None: # if head is the only Node in the Linked List
self.head = None
else:
while temp.next.next: # find the 2nd last element
temp = temp.next
# (2nd last element).next = None and temp = last element
temp.next, temp = None, temp.next
return temp
return self.delete_nth(self.size - 1)

def delete_nth(self, index: int):
realDuYuanChao marked this conversation as resolved.
Show resolved Hide resolved
if index < 0 or index > self.size - 1: # test if index is valid
raise IndexError("list index out of range.")
delete_node = self.head # default first node
if index == 0:
self.head = self.head.next
else:
temp = self.head
for _ in range(index - 1):
temp = temp.next
delete_node = temp.next
temp.next = temp.next.next
self.size -= 1
return delete_node.data

def is_empty(self) -> bool:
return self.head is None # return True if head is none
realDuYuanChao marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -72,12 +80,14 @@ def reverse(self):

def __repr__(self): # String representation/visualization of a Linked Lists
current = self.head
string_repr = ""
string_repr = []
while current:
string_repr += f"{current} --> "
string_repr.append(f"{current.data}")
current = current.next
# END represents end of the LinkedList
return string_repr + "END"
return "->".join(string_repr)

def __str__(self) -> str:
return repr(self)

# Indexing Support. Used to get a node at particular position
def __getitem__(self, index):
Expand All @@ -93,21 +103,21 @@ def __getitem__(self, index):
if current.next is None:
raise IndexError("Index out of range.")
current = current.next
return current
return current.data

# Used to change the data of a particular node
def __setitem__(self, index, data):
current = self.head
# If list is empty
if current is None:
raise IndexError("The Linked List is empty")
raise IndexError("The Linked List is empty.")
for i in range(index):
if current.next is None:
raise IndexError("Index out of range.")
current = current.next
current.data = data

def __len__(self):
def __len__(self) -> int:
"""
Return length of linked list i.e. number of nodes
>>> linked_list = LinkedList()
Expand All @@ -126,45 +136,77 @@ def __len__(self):
>>> len(linked_list)
0
"""
if not self.head:
return 0
return self.size


def test_singly_linked_list() -> None:
"""
>>> test_singly_linked_list()
"""
linked_list = LinkedList()
assert linked_list.is_empty() is True
assert str(linked_list) == ""

count = 0
cur_node = self.head
while cur_node.next:
count += 1
cur_node = cur_node.next
return count + 1
try:
linked_list.delete_head()
assert False # This should not happen.
except IndexError:
assert True # This should happen.

try:
linked_list.delete_tail()
assert False # This should not happen.
except IndexError:
assert True # This should happen.

for i in range(10):
assert len(linked_list) == i
linked_list.insert_nth(i, i + 1)
assert str(linked_list) == "->".join(str(i) for i in range(1, 11))

linked_list.insert_head(0)
linked_list.insert_tail(11)
assert str(linked_list) == "->".join(str(i) for i in range(0, 12))

assert linked_list.delete_head() == 0
assert linked_list.delete_nth(9) == 10
assert linked_list.delete_tail() == 11
assert str(linked_list) == "->".join(str(i) for i in range(1, 10))


def main():
A = LinkedList()
A.insert_head(input("Inserting 1st at head ").strip())
A.insert_head(input("Inserting 2nd at head ").strip())
from doctest import testmod

testmod()
test_singly_linked_list()

linked_list = LinkedList()
linked_list.insert_head(input("Inserting 1st at head ").strip())
linked_list.insert_head(input("Inserting 2nd at head ").strip())
print("\nPrint list:")
A.print_list()
A.insert_tail(input("\nInserting 1st at tail ").strip())
A.insert_tail(input("Inserting 2nd at tail ").strip())
linked_list.print_list()
linked_list.insert_tail(input("\nInserting 1st at tail ").strip())
linked_list.insert_tail(input("Inserting 2nd at tail ").strip())
print("\nPrint list:")
A.print_list()
linked_list.print_list()
print("\nDelete head")
A.delete_head()
linked_list.delete_head()
print("Delete tail")
A.delete_tail()
linked_list.delete_tail()
print("\nPrint list:")
A.print_list()
linked_list.print_list()
print("\nReverse linked list")
A.reverse()
linked_list.reverse()
print("\nPrint list:")
A.print_list()
linked_list.print_list()
print("\nString representation of linked list:")
print(A)
print(linked_list)
print("\nReading/changing Node data using indexing:")
print(f"Element at Position 1: {A[1]}")
A[1] = input("Enter New Value: ").strip()
print(f"Element at Position 1: {linked_list[1]}")
linked_list[1] = input("Enter New Value: ").strip()
print("New list:")
print(A)
print(f"length of A is : {len(A)}")
print(linked_list)
print(f"length of linked_list is : {len(linked_list)}")


if __name__ == "__main__":
Expand Down