Linked Lists LAST TODAY NEXT Interfaces Implementing stacks and - - PowerPoint PPT Presentation
Linked Lists LAST TODAY NEXT Interfaces Implementing stacks and - - PowerPoint PPT Presentation
Linked Lists LAST TODAY NEXT Interfaces Implementing stacks and queues Better arrays Stacks Linked lists Queues How to implement queues Data structure with a flexible size Recursive structure Base case: empty list
LAST Interfaces
- Stacks
- Queues
TODAY Implementing stacks and queues Linked lists NEXT Better arrays
How to implement queues
- Data structure with a flexible size
- Recursive structure
- Base case: empty list
- Recursive case: something followed by a list
Linked list implementation (for integers)
What if there was no pointer here?
struct list_node { int data; struct list_node* next; };
Linked list implementation (for integers)
struct list_node { int data; struct list_node* next; };
Let us introduce a shorter name for this structure type
Linked list implementation (for integers)
typedef struct list_node list; struct list_node { int data; list* next; };
Class activity
There is an embedded linked list in the class!
Fixed element type
typedef struct list_node list; struct list_node { int data; list* next; };
Linked list implementation (general)
typedef string elem; // This goes in the client's code
How should we indicate the end of a linked list?
typedef struct list_node list; struct list_node { elem data; list* next; };
List segments [start, end)
typedef struct list_node list; struct list_node { elem data; list* next; }; bool is_segment(list* start, list* end)
List segments [start, end)
bool is_segment(list* start, list* end) { if (start == NULL) return false; if (start == end) return true; return is_segment(start->next, end); }
recursive implementation
typedef struct list_node list; struct list_node { elem data; list* next; };
List segments [start, end)
bool is_segment(list* start, list* end){ for (list* p = start; p != NULL; p = p->next) { if (p == end) return true; } return false; } typedef struct list_node list; struct list_node { elem data; list* next; };
iterative implementation
Observations about is_segment
- Always returns true when [start, end) is a valid segment
- Does it always return false when it is [start, end) is not a
valid segment?
- Only if start is NULL-terminated
- Loops forever if it contains a cycle
How can we detect a cycle?
Tortoise and hare
bool is_acyclic(list* start);
tortoise hare
Adding and removing elements
Delete (remove) from the start of a list segment
Cost: O(1)
Consider a non-empty list segment [start, end)
elem x = start->data; start = start->next; return x;
Delete (remove) from the end of a list segment
Cost: O(n)
Consider a non-empty list segment [start, end) 3 4 5
before
3 4
after
start end
Insertion at the start of a list segment
Consider a list segment [start, end)
list* l = alloc(list); l->data = x; l->next = start; start = l;
Cost: O(1)
Insertion at the end of a list segment
Cost: O(1)
set end to it
end start
3 4 5
start end new node
3 4 5 6
set the data field of end to the value to add
3 4 5 6
set its next field to a new dummy node
Insertion at the end of a list segment (code)
end->data = x; list* new_dummy = alloc(list); end->next = new_dummy; end = new_dummy;
Cost: O(1)
Consider a list segment [start, end)
Insertion at the end of a list segment
Consider the alternative sequence of steps
- create a new node
- set its data field to the value to add
- set its next field to end
- point the old last node to it
Insertion at the end of a list segment
3 4 5 3 4 5
start end
6
new node
Consider the alternative sequence of steps to add 6
Cost: O(n)
Implementing Queues with Linked Lists
Queue implementation
// typedef ______* queue_t; bool queue_empty(queue_t Q) /* O(1) */ /*@requires Q != NULL; @*/; queue_t queue_new() /* O(1) */ /*@ensures \result != NULL; @*/ /*@ensures queue_empty(\result); @*/; void enq(queue_t Q, string e) /* O(1) */ /*@requires Q != NULL; @*/; string deq(queue_t Q) /* O(1) */ /*@requires Q != NULL; @*/ /*@requires !queue_empty(Q); @*/ ;
Interface Implementation Code using linked lists
- Enqueue from back
- Dequeue from front
struct queue_header { list* front; list* back; };
header {
Considering cost of insert and delete for linked lists what should be front and back?
// typedef ______* queue_t; bool queue_empty(queue_t Q) /* O(1) */ /*@requires Q != NULL; @*/; queue_t queue_new() /* O(1) */ /*@ensures \result != NULL; @*/ /*@ensures queue_empty(\result); @*/; void enq(queue_t Q, string e) /* O(1) */ /*@requires Q != NULL; @*/; string deq(queue_t Q) /* O(1) */ /*@requires Q != NULL; @*/ /*@requires !queue_empty(Q); @*/ ;
Implementing queues as list segments
- Insert at the back and remove from front
[start, end) of a list segment becomes [front, back) of a queue
Queue implementation
// typedef ______* queue_t; bool queue_empty(queue_t Q) /* O(1) */ /*@requires Q != NULL; @*/; queue_t queue_new() /* O(1) */ /*@ensures \result != NULL; @*/ /*@ensures queue_empty(\result); @*/; void enq(queue_t Q, string e) /* O(1) */ /*@requires Q != NULL; @*/; string deq(queue_t Q) /* O(1) */ /*@requires Q != NULL; @*/ /*@requires !queue_empty(Q); @*/ ;
Interface Implementation
struct queue_header { list* front; list* back; }; typedef struct queue_header queue; typedef queue* queue_t; … queue_t queue_new() //@ensures is_queue(\result); //@ensures queue_empty(\result); …
Representation invariant
bool is_queue(queue* Q) { return Q != NULL && is_acyclic … && is_segment … } bool is_segment(list* start, list* end);
Q
Detecting emptiness
bool queue_empty(queue* Q) //@requires is_queue(Q); { }
Cost: O(1)
return Q->front == Q->back; struct queue_header { list* front; list* back; }; typedef struct queue_header queue; typedef queue* queue_t;
// typedef ______* queue_t; bool queue_empty(queue_t Q) /* O(1) */ /*@requires Q != NULL; @*/; queue_t queue_new() /* O(1) */ /*@ensures \result != NULL; @*/ /*@ensures queue_empty(\result); @*/; void enq(queue_t Q, string e) /* O(1) */ /*@requires Q != NULL; @*/; string deq(queue_t Q) /* O(1) */ /*@requires Q != NULL; @*/ /*@requires !queue_empty(Q); @*/ ;
Interface
Creating a new queue
queue* queue_new() //@ensures is_queue(\result); //@ensures queue_empty(\result); { }
// typedef ______* queue_t; bool queue_empty(queue_t Q) /* O(1) */ /*@requires Q != NULL; @*/; queue_t queue_new() /* O(1) */ /*@ensures \result != NULL; @*/ /*@ensures queue_empty(\result); @*/; void enq(queue_t Q, string e) /* O(1) */ /*@requires Q != NULL; @*/; string deq(queue_t Q) /* O(1) */ /*@requires Q != NULL; @*/ /*@requires !queue_empty(Q); @*/ ;
Interface
Cost: O(1)
queue* Q = alloc(queue); list* l = alloc(list); Q->front = l; Q->back = l; return Q; struct queue_header { list* front; list* back; }; typedef struct queue_header queue; typedef queue* queue_t;
Implementing stacks with Linked Lists
Implementing stacks as list segments
- Insert and remove from the beginning
[start, end) of a list segment becomes [top, floor) of a stack
[start, end) of a list segment becomes [top, floor) of a stack
typedef struct stack_header stack; struct stack_header { list* top; list* floor; };
Linked Lists versus Arrays
UNSORTED ARRAYS LINKED LISTS PROS
- O(1) access
- built in support
- self-resizing
- O(1) insertion (given
right pointers)
- O(1) deletion (given
right pointers) CONS
- Fixed size
- O(n) insertion
- No built-in support
- O(n) access