500 likes | 583 Views
Computer Science 112. Fundamentals of Programming II Introduction to Linked Structures. Basic Array Operations. Constructor: Array(size, fillValue = None ) Length: len (a) Access: a[index] Replacement: a[index] = newValue Iteration: for value in a :
E N D
Computer Science 112 Fundamentals of Programming II Introduction to Linked Structures
Basic Array Operations Constructor: Array(size, fillValue = None) Length: len(a) Access: a[index] Replacement: a[index] = newValue Iteration: for value in a: Search: value in a Elements can be of any type [] has a precondition on the index
Problems With Arrays • Insertions and deletions require shifting elements, an O(n) operation • Length is fixed at instantiation, resizing requires new memory and transfer of elements
Why We Have These Problems • Array cells map to adjacent cells in memory, to support constant-time access • There is a one-to-one correspondence between the logical structure of the sequence of elements and the underlying memory storage • If we can decouple the logical sequence and the memory representation, we might solve these problems
Linked Structures D1 D2 D3 D4 A linked structure allows us to represent a sequence of elements independently of their positions in memory Linked structures support insertions, deletions, and traversals, but not random access Storage is allocated for each new element, as needed, from an area of memory called the system heap
Linked Structures A linked structure consists of a unique external link.
Linked Structures D1 D2 D3 • A linked structure consists of a unique external link. • This link is either empty or points to a node. • A node contains two parts: • a data element • a link to the next node
Linked Structures D1 D2 D3 D4 • A linked structure consists of a unique external link. • This link is either empty or points to a node. • A node contains two parts: • a data element • a link to the next node • The last node in a linked structure contains an empty link
Arrays and Computer Memory The variable numbers contains the address of the first cell in the array The array cells are adjacent in memory 0 1 2 2 0 656 ... ... numbers 656 657 658 659 660 3 6 5 3 0 1 2 3 4 6 5 Memory address of numbers[2] = base address in numbers + 2 = 656 + 2 = 658 0 2 ... ... 65534 65535 10 9
Nodes and Computer Memory 0 1 2 2 numbers 3 6 5 1 0 1 2 65534 ... ... 656 657 658 659 660 6 658 5 The variable numbers contains the address of the first node in the linked structure 0 2 ... ... 65534 65535 3 656
Nodes and Computer Memory 0 1 2 2 numbers 3 6 5 1 0 1 2 65534 ... ... 656 657 658 659 660 6 658 5 The address of the second node is contained in the previous node’s next link 0 2 ... ... 65534 65535 3 656
Nodes and Computer Memory 0 1 2 2 numbers 3 6 5 1 0 1 2 65534 ... ... 656 657 658 659 660 6 658 5 The last node’s next link is None, which is the memory address 0 0 2 ... ... 65534 65535 3 656
An Empty External link When there are no data, there are no nodes and the external link is empty Links are sometimes called pointers or references References in Python are either None or refer to objects A node will be a new type of object
Linked Structures D1 When a data element is inserted, we create a new node, set its data field to the data element, and set its link to None.
A Node Class class Node(object): def__init__(self, data, next = None): self.data = data self.next = next Usually a utility class to be used within a client class No need for accessor or mutator methods The data can be any Python object The next is always either another node or None
Declaring an External Pointer class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = None head just contains an empty link or None pointer head
Adding a Node class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = Node("A", None) head contains a link or reference to a node head “A”
Display the Data Element class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = Node("A", None) print(head.data) Displays the letter “A” head “A”
Adding and Accessing a Second Node class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = Node("A", Node("B", None)) print(head.next.data) Displays the letter “B” head “A” “B”
Type Exception on None class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = Node("A", None) print(head.next.data) Raises an exception: None has no data field head “A”
Prepend 5 Data Elements class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = None for iin range(1, 6): head = Node(i, head) head
Prepend 5 Data Elements class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = None for iin range(1, 6): head = Node(i, head) head 1
Prepend 5 Data Elements class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = None for iin range(1, 6): head = Node(i, head) head 2 1
Prepend 5 Data Elements class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = None for iin range(1, 6): head = Node(i, head) head 3 2 1
Prepend 5 Data Elements class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = None for iin range(1, 6): head = Node(i, head) head 4 3 2 1
Prepend 5 Data Elements class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = None for iin range(1, 6): head = Node(i, head) head 5 4 3 2 1
Traverse the Structure class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = <prepend 5 elements> probe = head while probe != None: print(probe.data) probe = probe.next head probe 5 4 3 2 1
Traverse the Structure class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = <prepend 5 elements> probe = head while probe != None: print(probe.data) probe = probe.next head probe 5 4 3 2 1
Traverse the Structure class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = <prepend 5 elements> probe = head while probe != None: print(probe.data) probe = probe.next head probe 5 4 3 2 1
Traverse the Structure class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = <prepend 5 elements> probe = head while probe != None: print(probe.data) probe = probe.next head probe 5 4 3 2 1
Traverse the Structure class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = <prepend 5 elements> probe = head while probe != None: print(probe.data) probe = probe.next head probe 5 4 3 2 1
Traverse the Structure class Node(object): def__init__(self, data, next = None): self.data = data self.next = next head = <prepend 5 elements> probe = head while probe != None: print(probe.data) probe = probe.next head probe 5 4 3 2 1
Delete the First Element (Node) class Node(object): def__init__(self, data, next = None): self.data = data self.next = next if head != None: head = head.next head 5 4 3 2 1
Delete the First Element (Node) class Node(object): def__init__(self, data, next = None): self.data = data self.next = next The deleted node cannot be referenced, so the garbage collector will return it to the system memory if head != None: head = head.next head 5 4 3 2 1
Clear the Structure class Node(object): def__init__(self, data, next = None): self.data = data self.next = next The deleted nodes cannot be referenced, so the garbage collector will return them all to the system memory head = None head 5 4 3 2 1
Memory as Needed • The logical size and the physical size of the structure are always the same • Memory is allocated for new nodes only when needed • Storage for deleted nodes is reclaimed by the system
Analysis of Insert and Remove at the Head • Use of memory is always O(1) • No movement of data within the structure, just rearrange a couple of links, O(1) running time • Quite the opposite behavior of arrays
Insert at the End (Append an Element) • If the head pointer is empty, just assign it a new node • Otherwise, • chain down the links until you reach the pointer to the last node • Set the last node’s next pointer to a new node
Insert at the End (Append an Element) newNode = Node(1, None) if head == None: head = newNode else: probe = head whileprobe.next != None: probe = probe.next probe.next = newNode newNode 1 head probe 5 4 3 2
Insert at the End (Append an Element) newNode = Node(1, None) if head == None: head = newNode else: probe = head whileprobe.next != None: probe = probe.next probe.next = newNode newNode 1 head probe 5 4 3 2
Insert at the End (Append an Element) newNode = Node(1, None) if head == None: head = newNode else: probe = head whileprobe.next != None: probe = probe.next probe.next = newNode newNode 1 head probe 5 4 3 2
Insert at the End (Append an Element) newNode = Node(1, None) if head == None: head = newNode else: probe = head whileprobe.next != None: probe = probe.next probe.next = newNode newNode 1 head probe 5 4 3 2
Insert at the End (Append an Element) newNode = Node(1, None) if head == None: head = newNode else: probe = head whileprobe.next != None: probe = probe.next probe.next = newNode newNode 1 head probe 5 4 3 2
Insert at (before) the ith Position i = 2 if head == Noneori == 0: head = Node(1, head) else: probe = head whilei > 1 andprobe.next != None: probe = probe.next i -= 1 probe.next = Node(1, probe.next) head probe 5 4 3 2 0 1 2 3
Insert at the ith Position i = 2 if head == Noneori == 0: head = Node(1, head) else: probe = head whilei > 1 andprobe.next != None: probe = probe.next i -= 1 probe.next = Node(1, probe.next) head probe 5 4 3 2 0 1 2 3
Insert at the ith Position i = 2 if head == Noneori == 0: head = Node(1, head) else: probe = head whilei > 1 andprobe.next != None: probe = probe.next i -= 1 probe.next = Node(1, probe.next) newNode 1 head probe 5 4 3 2 0 1 2 3
Analysis of Insertions • Use of memory is O(1) • No movement of data within the structure, just rearrange a couple of links • Append at the end and insert at the ith position are both O(n), because we must chain down the links • Same behavior for removals, accesses, and replacements at the ith position
Tradeoffs: Array or Linked Structure? numbers 5 4 3 2 0 1 2 3 4 numCount 4 numbers 5 4 3 2
Tradeoffs • Arrays: • Constant-time access is good for binary search, implementing a dictionary, etc. • Uses less memory if the array is close to full • Linked structures: • Uses memory only as needed, never have to resize • Insertions and removals need no extra overhead to shift data elements • But accesses, replacements, insertions, and removals at arbitrary positions are linear time operations
For Wednesday Interfaces and Implementations Chapter 5