# advantages: can be traversed forward and backwards
#   delete operation is more efficient
#   can quickly insert a node before a given node
# disadvantages: needs extra space for previous pointer
#   all operations require extra pointer (previous) to be maintained