Computer Science 112 Fundamentals of Programming II Linked

  • Slides: 29
Download presentation
Computer Science 112 Fundamentals of Programming II Linked Lists

Computer Science 112 Fundamentals of Programming II Linked Lists

The List Clan Abstract. Collection Abstract. List. Interface Two. Way. Node * Linked. List

The List Clan Abstract. Collection Abstract. List. Interface Two. Way. Node * Linked. List Array

Insert into An Array List def insert(self, i, item: ( """ Inserts the item

Insert into An Array List def insert(self, i, item: ( """ Inserts the item at position i""". # Resize array if necessary here. if i < 0: i = 0 elif i > len(self): i = len(self( if i < len(self: ( for j in range(len(self), i, -1: ( self. _items[j] = self. _items[j - 1[ self. _items[i] = item self. _size += 1 self. inc. Mod. Count() No precondition on the index, can insert after the last item The index is normalized: 0 <= i <= len(self)

Problems with Arrays • Must waste memory, especially if the structure is less than

Problems with Arrays • Must waste memory, especially if the structure is less than half full • When the structure becomes full, must grow memory to resize, which is O(n) in memory and running time • Insertions and removals are O(n) on average, due to shifting of items

Linked Structures to the Rescue • Uses memory only as needed, physical size grows

Linked Structures to the Rescue • Uses memory only as needed, physical size grows or shrinks with logical size in constant time • Insertions and removals require no shifting of items

Problems with Linked Structures Appending, which is constant time for arrays, is linear for

Problems with Linked Structures Appending, which is constant time for arrays, is linear for a singly linked structure new. Node head probe 5 4 3 2 1

Solution: Add a Tail Pointer Maintain a tail pointer, which is either None or

Solution: Add a Tail Pointer Maintain a tail pointer, which is either None or points to the last node in the structure tail is a second external pointer, which is None when the structure is empty new. Node head tail 5 4 3 2 1

Solution: A Tail Pointer new. Node = Node(1, None) if head == None: head

Solution: A Tail Pointer new. Node = Node(1, None) if head == None: head = new. Node else: tail. next = new. Node tail = new. Node head tail 5 4 3 2 1

Other Problems • Links only go one in one direction, so movement in the

Other Problems • Links only go one in one direction, so movement in the other direction is difficult and expensive • Insertions and removals require access to the previous node’s next field • tail pointer won’t help to remove the last node • Insertions and removals at the head of the structure are special cases that complicate the code

Other Linked Structures head D 1 D 2 D 3 D 4 A singly

Other Linked Structures head D 1 D 2 D 3 D 4 A singly linked structure permits movement in one direction only head D 1 D 2 D 3 A doubly linked structure permits movement in both directions and simplifies some operations

Doubly Linked Structures head D 1 D 2 D 3 A circular, doubly linked

Doubly Linked Structures head D 1 D 2 D 3 A circular, doubly linked structure with a dummy header node • permits movement in both directions • allows constant-time access to the head or tail • eliminates special cases in code when access is at the beginning or the end of the structure

An Empty Structure head When there are no data, there is a single dummy

An Empty Structure head When there are no data, there is a single dummy header node Its two links point ahead and back to itself Its data field is None

The Node Class class Two. Way. Node(object): def __init__(self, data, previous = None, next

The Node Class class Two. Way. Node(object): def __init__(self, data, previous = None, next = None): self. data = data self. previous = previous self. next = next P D N Strictly a utility class No need for accessor or mutator methods

Declaring an External Pointer class Two. Way. Node(object): def __init__(self, data, previous = None,

Declaring an External Pointer class Two. Way. Node(object): def __init__(self, data, previous = None, next = None): self. data = data self. previous = previous self. next = next head = Two. Way. Node(None) head. previous = head. next = head

Appending a Node head. previous always points to the last node The last node’s

Appending a Node head. previous always points to the last node The last node’s next pointer always points back to head temp = Two. Way. Node("A", head. previous, head) # Step 1 temp head “A”

Appending a Node head. previous always points to the last node The last node’s

Appending a Node head. previous always points to the last node The last node’s next pointer always points back to head temp = Two. Way. Node("A", head. previous, head) head. previous. next = temp # Step 1 # Step 2 temp head “A”

Appending a Node head. previous always points to the last node The last node’s

Appending a Node head. previous always points to the last node The last node’s next pointer always points back to head temp = Two. Way. Node("A", head. previous, head) head. previous. next = temp head. previous = temp # Step 1 # Step 2 # Step 3 temp head “A”

Analysis No loop is needed to locate the last node Append is a constant

Analysis No loop is needed to locate the last node Append is a constant time operation! No if statements are needed to check for special cases temp = Two. Way. Node("A", head. previous, head) head. previous. next = temp head. previous = temp # Step 1 # Step 2 # Step 3 temp head “A”

Data for Linked. List from node import Two. Way. Node from abstractlist import Abstract.

Data for Linked. List from node import Two. Way. Node from abstractlist import Abstract. List class Linked. List(Abstract. List): """A linked list implementation. """ # Constructor def __init__(self, source. Collection = None): """Sets the initial state of self, which includes the contents of source. Collection, if it's present. """ self. _head = Two. Way. Node(None) self. _head. previous = self. _head. next = self. _head Abstract. List. __init__(self, source. Collection) self. _head

Index-Based Operations • For __getitem__, __setitem__, insert, and pop • Need to locate the

Index-Based Operations • For __getitem__, __setitem__, insert, and pop • Need to locate the ith node in the linked structure • Define a helper method named _get. Node • Takes an index as an argument and returns the node at that position

Defining _get. Node def _get. Node(self, i): """Helper method: returns a pointer to the

Defining _get. Node def _get. Node(self, i): """Helper method: returns a pointer to the node at position i. """ probe = self. _head. next while i > 0: probe = probe. next i -= 1 return probe Usage: ith. Node = self. _get. Node(i) # Then access data, next, or previous in ith. Node

Defining [] (subscript for access) def __getitem__(self, i): """Precondition: 0 <= i < len(self)

Defining [] (subscript for access) def __getitem__(self, i): """Precondition: 0 <= i < len(self) Returns the item at position i. Raises: Index. Error. """ if i < 0 or i >= len(self): raise Index. Error("List index out of range") ith. Node = self. _get. Node(i) return ith. Node. data Usage: item = lyst[i] What is the running time of [] for a linked list?

Insertion into a Linked List 2 self. _head 0 1 D 2 lyst. insert(1,

Insertion into a Linked List 2 self. _head 0 1 D 2 lyst. insert(1, D 3) The data nodes have virtual index positions, starting at 0 The header node has a virtual index position of the length of the list No special cases of insertions at head or tail, no if statements!

Insertion into a Linked List 2 self. _head lyst. insert(1, D 3) 1. Get

Insertion into a Linked List 2 self. _head lyst. insert(1, D 3) 1. Get a pointer to the ith node 0 1 D 2 ith. Node

Insertion into a Linked List 2 self. _head lyst. insert(1, D 3) new. Node

Insertion into a Linked List 2 self. _head lyst. insert(1, D 3) new. Node 0 1 D 2 D 3 ith. Node 1. Get a pointer to the ith node 2. Create a new node with the ith node’s previous as the previous and the ith node as the next

Insertion into a Linked List 2 self. _head lyst. insert(1, D 3) new. Node

Insertion into a Linked List 2 self. _head lyst. insert(1, D 3) new. Node 0 1 D 2 D 3 ith. Node 1. Get a pointer to the ith node 2. Create a new node with the ith node’s previous as the previous and the ith node as the next 3. Reset the ith node’s previous. next to the new node

Insertion into a Linked List 2 self. _head lyst. insert(1, D 3) new. Node

Insertion into a Linked List 2 self. _head lyst. insert(1, D 3) new. Node 0 1 D 2 D 3 ith. Node 1. Get a pointer to the ith node 2. Create a new node with the ith node’s previous as the previous and the ith node as the next 3. Reset the ith node’s previous. next to the new node 4. Reset the ith node’s previous to the new node

Defining insert def insert(self, i, item): """Inserts the item at position i. """ if

Defining insert def insert(self, i, item): """Inserts the item at position i. """ if i < 0: i = 0 elif i > len(self): i = len(self) ith. Node = self. _get. Node(i) ith. Node = Two. Way. Node(item, ith. Node. previous, ith. Node) ith. Node. previous. next = ith. Node. previous = ith. Node self. _size += 1 self. inc. Mod. Count() What is the running time of insert for a linked list?

For Wednesday List iterators

For Wednesday List iterators