Chapter 3

¡Supera tus tareas y exámenes ahora con Quizwiz!

what is a sentinel in a LinkedList?

"sentinel" -- an extra node that is placed in a linked list at creation time. It's a place where you can store useful information about the list (such as "count") in some languages (but this isn't really applicable to python). But for our uses, it also makes some list methods simpler. Gets rid of the need to check if you are looking at the first node for remove. Previous will just be set to the sentinel.

Abstract data type

An abstract data type, sometimes abbreviated ADT, is a logical description of how we view the data and the operations that are allowed without regard to how they will be implemented. This means that we are concerned only with what the data is representing and not with how it will eventually be constructed. By providing this level of abstraction, we are creating an encapsulation around the data.

Examples of concrete and abstract data types

Concrete: Ints, Floats, strings, arrays, booleans Abstract: User defined classes, lists, stacks, queues, dequeues, unordered lists

Which method is better?

Append and pop() operations are both O(1). This means that the first implementation will perform push and pop in constant time no matter how many items are on the stack. The performance of the second implementation suffers in that the insert(0) and pop(0) operations will both require O(n) for a stack of size n. Clearly, even though the implementations are logically equivalent, they would have very different timings when performing benchmark testing.

Concrete data type

Concrete data types are data types tied to the hardware of the computer. They are already defined and cannot be changed by a programmer.

Hot Potato program using queue

def hotPotato(nameList,count): ~q = Queue() ~for name in nameList: ~~q.enqueue(name) ~while q.size() > 1: ~~for i in range(count-1): ~~~q.enqueue(q.dequeue()) ~~print("Deleting",q.peek()) ~~q.dequeue() ~return q.dequeue() def main(): ~caveDwellers = ["Mireya", "Dennis", "Juan", "Charissa", "Marcy", "Bill" ] ~random.shuffle(caveDwellers) ~print("Initial cave dwellers: ",caveDwellers) ~pause = input("pausing. . .") ~survivor = hotPotato(caveDwellers,4) ~print("survivor is: ",survivor) main()

Palindrome checker

def palChecker(aString): ~charDeque = Deque() ~for ch in aString: ~~charDeque.addRear(ch) ~stillEqual = True ~while charDeque.size() > 1 and stillEqual: ~~first = charDeque.removeFront() ~~last = charDeque.removeRear() ~~if first != last: ~~~stillEqual = False ~return stillEqual

Is prefix or postfix more efficient?

Postfix

What is a deque?

A deque, also known as a double-ended queue, is an ordered collection of items similar to the queue. It has two ends, a front and a rear, and the items remain positioned in the collection. What makes a deque different is the unrestrictive nature of adding and removing items. New items can be added at either the front or the rear. Likewise, existing items can be removed from either end. In a sense, this hybrid linear structure provides all the capabilities of stacks and queues in a single data structure.

How are nodes stored in memory?

A node is an object with two places in memory. One space has the data value for that node, and the next space holds the address for the next node in the memory.

Queue

A queue is an ordered collection of items where the addition of new items happens at one end, called the "rear," and the removal of existing items occurs at the other end, commonly called the "front." As an element enters the queue it starts at the rear and makes its way toward the front, waiting until that time when it is the next element to be removed.

Stack

A stack (sometimes called a "push-down stack") is an ordered collection of items where the addition of new items and the removal of existing items always takes place at the same end. This end is commonly referred to as the "top." The end opposite the top is known as the "base."

linear data structures.

Data collections whose items are ordered depending on how they are added or removed. Once an item is added, it stays in that position relative to the other elements that came before and came after it. Linear structures can be thought of as having two ends. Sometimes these ends are referred to as the "left" and the "right" or in some cases the "front" and the "rear." You could also call them the "top" and the "bottom." The names given to the ends are not significant. What distinguishes one linear structure from another is the way in which items are added and removed, in particular the location where these additions and removals occur.

Deque methods

Deque() creates a new deque that is empty. Parameters: none. Returns: an empty deque. addFront(item) adds a new item to the front of the deque. Parameters: the item. Returns: nothing. addRear(item) adds a new item to the rear of the deque. Parameters: the item. Returns: nothing. removeFront() removes the front item from the deque. Parameters: None. Returns: the item AND the deque is modified. removeRear() removes the rear item from the deque. Parameters: None. Returns: the item AND the deque is modified. isEmpty() tests to see whether the deque is empty. Parameters: None. Returns: a boolean size() returns the number of items in the deque. Parameters: None. Returns: an int

Is a stack LIFO or FIFO?

LIFO The most recently added item is the one that is in position to be removed first. It provides an ordering based on length of time in the collection. Newer items are near the top, while older items are near the base. The base of the stack is significant since items stored in the stack that are closer to the base represent those that have been in the stack the longest.

Why are linked lists useful?

Linked lists became really useful because you can add one more space in memory without having to make a whole other linked list, whereas with arrays, if you did not declare the array to be large enough, you would have to declare a whole other array and copy your previous data over.

Memory is really like 8 gigabytes of memory would be represented by 1 address in memory can store how much? How big are ints and strings? How are they stored?

Memory is a big array 8 Gigabytes of memory would be an array with 0 to 8 billion - 1 indices 1 place in memory can store 1 byte (1 byte = 8 bits) Ints and strings can take up multiple bytes, so will have multiple addresses in memory (and those addresses should be next to each other)

infix, prefix, and postfix expression

Only infix notation requires precedence or parentheses to clarify. The order of operations within prefix and postfix expressions is completely determined by the position of the operator and nothing else. In many ways, this makes infix the least desirable notation to use.

Examples of infix to prefix and postfix A + B * C + D (A + B) * (C + D) A * B + C * D A + B + C + D

Pre: + + A * B C D Post: A B C * + D + Pre: * + A B + C D Post: A B + C D + * Pre: + * A B * C D Post: A B * C D * + Pre: + + + A B C D Post: A B + C + D +

Queue methods

Queue() creates a new queue that is empty. Parameters: none. Returns: an empty queue. q.enqueue(item) adds a new item to the rear of the queue. Parameters: item to be added. Returns: nothing. q.dequeue() removes the front item from the queue. Parameters: none. Returns: the item AND the queue is modified. q.isEmpty() Returns True if the queue is empty, False otherwise. Parameters: None. Returns: a boolean value. q.size() returns the number of items in the queue. Parameters: none. Returns: an integer. q.peek() looks at the top of the queue without modifying the queue. Parameters: none. Returns: the top item of the queue (without modifying the queue).

Parentheses method for converting from infix to postfix and prefix

Recall that A + B * C can be written as (A + (B * C)) to show explicitly that the multiplication has precedence over the addition. On closer observation, however, you can see that each parenthesis pair also denotes the beginning and the end of an operand pair with the corresponding operator in the middle. Look at the right parenthesis in the subexpression (B * C), if we were to move the multiplication symbol to that position and remove the matching left parenthesis, giving us B C *, we would in effect have converted the subexpression to postfix notation. If the addition operator were also moved to its corresponding right parenthesis position and the matching left parenthesis were removed, the complete postfix expression would result, A B C * + If we do the same thing but instead of moving the symbol to the position of the right parenthesis, we move it to the left, we get prefix notation, + A * B C The position of the parenthesis pair is actually a clue to the final position of the enclosed operator.

Stack methods

Stack() creates a new stack that is empty. Parameters: none. Returns: an empty stack. s.push(item) adds a new item to the top of the stack. Parameter: item to be added. Returns: none s.pop() removes the top item from the stack. Parameters: none. Returns: the item. AND the stack is modified. s.peek() returns the top item from the stack but does not remove it. Parameters: none. Returns: the top item of the stack. The stack is not modified. s.isEmpty() tests to see whether the stack is empty. Parameters: none. Returns: a boolean value. s.size() returns the number of items in the stack. Parameters: none. Returns: an integer.

Symbol tables show...

Symbol tables show a variable acting as a pointer to an address in memory for the value of that variable

Is a queue LIFO or FIFO?

The most recently added item in the queue must wait at the end of the collection. The item that has been in the collection the longest is at the front. This ordering principle is sometimes called FIFO, first-in first-out. It is also known as "first-come first-served."

Relationship between insertion order and removal order for stacks

The order of insertion is the reverse of the order of removal. Stacks are fundamentally important, as they can be used to reverse the order of items.

In what way does a pointer relate to the concept of abstraction?

There is a level of abstraction with high-level languages Under the covers the compiler or interpreter takes what you write and converts it to machine language and actually interacts with memory to allocate space. An address is really the same thing as a pointer. The pointer is an abstraction of an address.

Given the following sequence of stack operations, what is the top item on the stack when the sequence is complete? m = Stack() m.push('x') m.push('y') m.push('z') while not m.isEmpty(): ~m.pop() ~m.pop()

an error will occur

What is an array?

an object used to store multiple values in a single variable. Main points, they are the main collection data type for other languages, and you have to declare a set length of them. They are equivalent to the python list objects, but python lists do not require set length

deque implementation

class Deque: ~def __init__(self): ~~self.items = [] ~def isEmpty(self): ~~return self.items == [] ~def addFront(self, item): ~~self.items.append(item) ~def addRear(self, item): ~~self.items.insert(0,item) ~def removeFront(self): ~~return self.items.pop() ~def removeRear(self): ~~return self.items.pop(0) ~def size(self): ~~return len(self.items)

Node class implementation

class Node: ~def __init__(self,initdata): ~~self.data = initdata ~~self.next = None ~def getData(self): ~~return self.data ~def getNext(self): ~~return self.next ~def setData(self,newdata): ~~self.data = newdata ~def setNext(self,newnext): ~~self.next = newnext

Queue Implementation

class Queue: ~def __init__(self): ~~self.items = [] ~def isEmpty(self): ~~return self.items == [] ~def enqueue(self, item): ~~self.items.insert(0,item) ~def dequeue(self): ~~return self.items.pop() ~def size(self): ~~return len(self.items) ~def peek(self): ~~return self.items[-1]

Stack implementation assuming that the end of the list will hold the top element of the stack.

class Stack: ~def __init__(self): ~~self.items = [] ~def isEmpty(self): ~~return self.items == [] ~def push(self, item): ~~self.items.append(item) ~def pop(self): ~~return self.items.pop() ~def peek(self): ~~return self.items[len(self.items)-1] #OR return self.items[-1] ~def size(self): ~~return len(self.items)

Stack implementation assuming that the beginning of the list will hold the top element of the stack.

class Stack: ~def __init__(self): ~~self.items = [] ~def isEmpty(self): ~~return self.items == [] ~def push(self, item): ~~self.items.insert(0,item) ~def pop(self): ~~return self.items.pop(0) ~def peek(self): ~~return self.items[0] ~def size(self): ~~return len(self.items)

UnorderedList class implementation

class UnorderedList: ~def __init__(self): ~~self.head = None ~def isEmpty(self): O(1) ~~return self.head == None ~def add(self,item): O(1) ~~temp = Node(item) ~~temp.setNext(self.head) ~~self.head = temp ~def size(self): O(n) ~~current = self.head ~~count = 0 ~~while current != None: ~~~count = count + 1 ~~~current = current.getNext() ~~return count ~def search(self,item): O(n) ~~current = self.head ~~found = False ~~while current != None and not found: ~~~if current.getData() == item: ~~~~found = True ~~~else: ~~~~current = current.getNext() ~~return found ~def remove(self,item): O(n) ~~current = self.head ~~previous = None ~~found = False ~~while not found and current != None: ~~~if current.getData() == item: ~~~~found = True ~~~else: ~~~~previous = current ~~~~current = current.getNext() ~~if current == None: ~~~print("Sorry, item not found") ~~elif previous == None: ~~~self.head = current.getNext() ~~else: ~~~previous.setNext(current.getNext())

UnorderedList with sentinels

class UnorderedList: # with sentinels ~def __init__(self): ~~sentinel = Node(None) ~~self.head = sentinel ~def isEmpty(self): ~~return self.head.getNext() == None ~def size(self): ~~current = self.head.getNext() ~~count = 0 ~~while current != None: ~~~count += 1 ~~~current = current.getNext() ~~return count ~def search(self,item): ~~found = False ~~current = self.head.getNext() ~~while current != None and not found: ~~~if current.getData() == item: ~~~~found = True ~~~else: ~~~~current = current.getNext() ~~return found ~def remove(self,item): # and we're assuming item MUST be present ~~found = False ~~previous = self.head ~~current = self.head.getNext() ~~while not found: ~~~if current.getData() == item: ~~~~found = True ~~~else: ~~~~previous = current ~~~~current = current.getNext() # if I get to this point, "current" points to the # item I want to delete ~~previous.setNext(current.getNext()) ~def add(self,item): ~~temp = Node(item) ~~temp.setNext(self.head.getNext()) ~~self.head.setNext(temp)

Convert infix to postfix

def infixToPostfix(infixexpr): ~prec = {} ~prec["*"] = 3 ~prec["/"] = 3 ~prec["+"] = 2 ~prec["-"] = 2 ~prec["("] = 1 ~opStack = Stack() ~postfixList = [] ~tokenList = infixexpr.split() ~for token in tokenList: ~~if token in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or token in "0123456789": ~~~postfixList.append(token) ~~elif token == '(': opStack.push(token) elif token == ')': ~~~topToken = opStack.pop() ~~~while topToken != '(': ~~~~postfixList.append(topToken) ~~~~topToken = opStack.pop() ~~else: ~~~while (not opStack.isEmpty()) and \ (prec[opStack.peek()] >= prec[token]): ~~~~postfixList.append(opStack.pop()) ~~~opStack.push(token) ~while not opStack.isEmpty(): ~~postfixList.append(opStack.pop()) ~return " ".join(postfixList)

Convert infix to prefix

def infixToPrefix(infixexpr): #same as infix to post up through opStack = Stack() ~prefixList = [] ~tokenList = infixexpr.split() ~tokenList.reverse() ~for token in tokenList: ~~if token in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or token in "0123456789": ~~~prefixList.insert(0,token) ~~elif token == ')': ~~~opStack.push(token) ~~elif token == '(': ~~~topToken = opStack.pop() ~~~while topToken != ')': ~~~~prefixList.insert(0,topToken) ~~~~topToken = opStack.pop() ~~else: ~~~while (not opStack.isEmpty()) and \ (prec[opStack.peek()] >= prec[token]): ~~~~prefixList.insert(0,opStack.pop()) ~~~opStack.push(token) ~while not opStack.isEmpty(): ~~prefixList.insert(0,opStack.pop()) ~return " ".join(prefixList)

Parentheses checker using stack

def parChecker(symbolString): ~s = Stack() ~balanced = True ~index = 0 ~while index < len(symbolString) and balanced: ~~symbol = symbolString[index] ~~if symbol in "{[(": ~~#add left-facing symbol to the stack ~~~s.push(symbol) ~~elif symbol in "}])": ~~# it's a right side, and it had better match the top of the stack! ~~~if s.isEmpty(): ~~~#checks for unclosed parantheses and recognizes as wrong ~~~~balanced = False ~~~else: ~~~#sets top of stack to "top" and recognizes error such as (} ~~~~top = s.pop() ~~~~if not matches(top,symbol): ~~~~~balanced = False ~~else: # it's a letter, number, or operation ~~~pass ~~index += 1 ~if balanced and s.isEmpty(): ~~return True ~else: ~~return False def matches(openP,closeP): #P is for parantheses to avoid using "open" ~opens = "{[(" ~closes = "}])" #make sure they are the same order as in opens ~return opens.index(openP) == closes.index(closeP) #returns True if the indices match and False if they don't def main(): ~example1 = "(a+b)[(c+d)]" ~print(example1, ":", parChecker(example1)) ~example2 = "([])({})" ~print(example2, ":", parChecker(example2)) ~example3 = "{]()" ~print(example3, ":", parChecker(example3)) main()

evaluate postfix

def postfixEval(postfixExpr): ~operandStack = Stack() ~tokenList = postfixExpr.split() ~for token in tokenList: ~~if token in "0123456789": ~~~operandStack.push(int(token)) ~~else: ~~~operand2 = operandStack.pop() ~~~operand1 = operandStack.pop() ~~~result = doMath(token,operand1,operand2) ~~~operandStack.push(result) ~return operandStack.pop() def doMath(op, op1, op2): ~if op == "*": ~~return op1 * op2 ~elif op == "/": ~~return op1 / op2 ~elif op == "+": ~~return op1 + op2 ~else: ~~return op1 - op2

Evaluate prefix

def prefixEval(prefixExpr): ~operandStack = Stack() ~tokenList = prefixExpr.split() ~tokenList.reverse() ~for token in tokenList: ~~if token in "0123456789": ~~~operandStack.push(int(token)) ~~else: ~~~operand1 = operandStack.pop() ~~~operand2 = operandStack.pop() ~~~result = doMath(token,operand1,operand2) ~~~operandStack.push(result) ~return operandStack.pop() def doMath(op, op1, op2): ~if op == "*": ~~return op1 * op2 ~elif op == "/": ~~return op1 / op2 ~elif op == "+": ~~return op1 + op2 ~else: ~~return op1 - op2

Unlike with a stack, if you have items in a queue, they are removed...

in the same order they were added.

append, insert, index, and pop for unordered list

one append method requires adding a tail pointer (this would also affect add and remove methods when removing the last item from a list or adding to the end of the list ~def __init__(self): ~~self.head = None ~~self.tail = None ~def sappend(self, item): # This is O(n) time complexity ~~current = self.head ~~if current != None: ~~~while current.getNext() is not None: ~~~~current = current.getNext() ~~~current.setNext(Node(item)) ~~else: ~~~self.head = Node(item) ~def append(self, item): # This is O(1) time complexity ~~temp = Node(item) ~~last = self.tail ~~if last != None: ~~~last.setNext(temp) ~~else: ~~~self.head = temp ~~self.tail = temp ~def index(self, item): #O(1) ~~pos = 0 ~~current = self.head ~~found = False ~~while current is not None and not found: ~~~if current.getData() == item: ~~~~found = True ~~~else: ~~~~current = current.getNext() ~~~~pos += 1 ~~if not found: ~~~raise ValueError('Value not present in the List') ~~return pos ~def insert(self, index, item): #O(index) ~~temp = Node(item) ~~current = self.head ~~previous = None ~~count = 0 ~~found = False ~~if index > self.length-1: ~~~raise IndexError('List Index Out Of Range') ~~while current is not None and not found: ~~~if count == index: ~~~~found = True ~~~else: ~~~~previous = current ~~~~current = current.getNext() ~~~~count += 1 ~~if previous is None: ~~~temp.setNext(self.head) ~~~self.head = temp ~~else: ~~~temp.setNext(current) ~~~previous.setNext(temp) def pop(self, index=None): if index is None: index = self.length-1 if index > self.length-1: raise IndexError('List Index Out Of Range') current = self.head previous = None found = False if current: count = 0 while current.getNext() is not None and not found: if count == index: found = True else: previous = current current = current.getNext() count += 1 if previous is None: self.head = current.getNext() if current.getNext() is None: self.tail = current.getNext() else: self.tail = previous previous.setNext(current.getNext()) self.length -= 1 return current.getData()

algorithmic complexity of queue methods

~def __init__(self): ~~self.items = [] O(1) ~def isEmpty(self): ~~return self.items == [] O(1) ~def enqueue(self, item): ~~self.items.insert(0,item) O(n) ~def dequeue(self): ~~return self.items.pop() O(1) ~def size(self): ~~return len(self.items) O(1) ~def peek(self): ~~return self.items[-1] O(1)

algorithmic complexity of stack methods assuming that the end of the list will hold the top element of the stack.

~def __init__(self): ~~self.items = [] O(1) ~def isEmpty(self): ~~return self.items == [] O(1) ~def push(self, item): ~~self.items.append(item) O(1) ~def pop(self): ~~return self.items.pop() O(1) ~def peek(self): ~~return self.items[len(self.items)-1] #OR return self.items[-1] O(1) ~def size(self): ~~return len(self.items) O(1)

OrderedList implementation (only search and add are different)

~def search(self,item): O(n) ~~current = self.head ~~found = False ~~stop = False ~~while current != None and not found and not stop: ~~~if current.getData() == item: ~~~~found = True ~~~else: ~~~~if current.getData() > item: ~~~~~stop = True ~~~~else: ~~~~~current = current.getNext() ~~return found ~def add(self,item): O(n) ~~current = self.head ~~previous = None ~~stop = False ~~while current != None and not stop: ~~~if current.getData() > item: ~~~~stop = True ~~~else: ~~~~previous = current ~~~~current = current.getNext() ~~temp = Node(item) ~~if previous == None: ~~~temp.setNext(self.head) ~~~self.head = temp ~~else: ~~~temp.setNext(current) ~~~previous.setNext(temp)


Conjuntos de estudio relacionados

PHIL 1001 HW 1 [A = Statement/Sentence/Neither] [C = Validity]

View Set

Leçon 7A | Roman-photo De retour au P' tit Bistrot

View Set

FINAL DNA FORENSIC EXAM (DNA FORENSIC EXAM 1, DNA FORENSICS EXAM 2, DNA Forensics Exam 3)

View Set

Epidemiology Exam 2 Quiz Questions

View Set

Science Quiz: Cell Division and Cancer

View Set

1.1 Write the short form (she's / we aren't etc.)

View Set

Ch 5 EMS Communications/ Ch 6 Documentation

View Set