cs240
play

CS240 Mike Lam, Professor Linked Lists Retrospective Arrays are - PowerPoint PPT Presentation

CS240 Mike Lam, Professor Linked Lists Retrospective Arrays are great O(1) access time to any element Amortized O(1) insertion and removal Referential arrays allow arbitrary-sized objects There are still disadvantages


  1. CS240 Mike Lam, Professor Linked Lists

  2. Retrospective ● Arrays are great – O(1) access time to any element – Amortized O(1) insertion and removal – Referential arrays allow arbitrary-sized objects ● There are still disadvantages – Occasional O(n) worst-case costs – Requires large chunks of reserved memory – Insertion/removal in the middle is expensive

  3. Retrospective ● Goal: Do less work when inserting and removing in the middle of our lists 2 3 5 8

  4. Retrospective ● Goal: Do less work when inserting and removing in the middle of our lists 2 3 5 8 ● Let's "pull apart" the array 2 3 5 8

  5. Retrospective ● Goal: Do less work when inserting and removing in the middle of our lists 2 3 5 8 ● Let's "pull apart" the array 2 3 5 8 ● And add links between all the items 2 3 5 8

  6. Linked Lists ● This is a "linked list" 2 3 5 8 ● Every item has a "next" pointer/reference – Last item has a NULL "next" pointer ● Add and remove items by manipulating the pointers ● Keep external pointers to the beginning ("head") and end ("tail") of the list

  7. Singly-Linked Lists ● S i n g l y - l i n k e d l i s t : tail head "a" "b" "c" NULL single link per node

  8. Singly-Linked Lists ● Node: typedef struct linknode { data_t data; struct linknode *next; } linknode_t; ● Linked list: typedef struct { linknode_t *head; linknode_t *tail; size_t size; } linklist_t;

  9. Singly-Linked Lists ● Creating new linked-list nodes: linknode_t* malloc_node(data_t value) { linknode_t *new_node = (linknode_t*)malloc(sizeof(linknode_t)); // TODO: check for null new_node->value = value; new_node->next = NULL; return new_node; }

  10. Singly-Linked Lists ● Inserting at the head: l i n k n o d e _ t * n e w e s t = m a l l o c _ n o d e ( x ) ; newest->next = head; head = newest;

  11. Singly-Linked Lists ● Inserting at the tail: linknode_t *newest = malloc_node(x); newest->next = NULL; tail->next = newest; tail = newest;

  12. Singly-Linked Lists ● Removing from the head: i f ( h e a d = = N U L L ) ERROR! old_head = head; head = head->next; free(old_head);

  13. Singly-Linked Lists ● Removing from the tail: if (tail == NULL) ERROR old_tail = tail; tail = ??? ● Problem: Can't access previous node

  14. Singly-Linked Lists ● Removing from the tail: if (tail == NULL) ERROR old_tail = tail; tail = ??? ● Problem: Can't access previous node – Solution: Track previous nodes as well ● (doubly-linked lists)

  15. Challenge ● Given a singly-linked list called "data", write a snippet of code that will print out all of the values in the list

  16. Singly-Linked Lists ● Insert: O(1) – i f y o u h a v e a r e f e r e n c e t o t h e l o c a t i o n – O(n) if the new location is index-based or the list needs to be sorted ● Delete: O(1) – if you have a reference to the item – O(n) if you have to look for it ● Indexed access or search: O(n)

  17. Linked Stack ● Consider stack implementation using a singly- linked list

  18. Linked Stack ● Consider stack implementation using a singly- linked list – Insert and remove at the head – Push, pop, and top are O(1)

  19. Linked Queue ● Consider queue implementation using a singly- linked list

  20. Linked Queue ● Consider queue implementation using a singly- linked list – Insert at tail, remove from head ● C a n ' t r e m o v e f r o m t h e t a i l ! – Enqueue, dequeue, and first are O(1)

  21. Looking ahead ● What if we kept two pointers? – "next" and "prev" – This is a "doubly-linked list" ● What if tail.next pointed to the head? – This is a "circularly-linked list" ● What if we kept multiple pointers to places further down the list? – This is a "skip list"

  22. Doubly-Linked Lists ● Two references: prev and next – To predecessor and successor nodes ● Allows insert and remove at both ends – Can now implement stacks, queues, and deques – “Deque” = “double-ended queue”

  23. Circularly-Linked Lists ● Keep a single node reference ● Useful for round-robin scheduling – New operation: r o t a t e ( ) ● Can be used to implement regular list – No need to track both head and tail – head = tail.next

  24. Sentinels ● Placeholder (“fake”) nodes at head and/or tail Empty list: head tail After append(2): head 2 tail After append(3): head 2 3 tail After append(5): head 2 3 5 tail

  25. Sentinels ● Simplifies logic of insertion and removal Empty list: head tail Populated list: head 2 3 5 tail void list_append(list_t *a, x) void list_append(list_t *a, x) { { linknode_t *new = malloc_node(x); linknode_t *new = malloc_node(x); new->prev = a->tail; new->prev = a->tail->prev; new->next = NULL; new->next = a->tail; if (list->head == NULL) { a->tail->prev->next = new; a->head = new; a->tail->prev = new; } else { } a->tail->next = new; } a->tail = new; }

  26. Deques ● Double-ended queue ● Two sets of insert/remove methods: – addfirst and removefirst – addlast and removelast ● Implementation using doubly-linked list w/ sentinels

  27. Tradeoffs ● Advantages of linked lists – Worst-case O(1) bounds ● No amortized bounds – O(1) insertions and removals at arbitrary positions ● No need to shift elements ● This is a HUGE advantage!

  28. Tradeoffs ● Advantages of arrays – O(1) access to elements by index – Proportionally fewer actual operations ● Calculation and dereference vs. memory allocation and reference re-arranging – Proportionally less memory usage ● Both arrays and linked lists can be referential ● Arrays require at most 2n space overhead, while linked lists are at least 2n (or 3n for doubly-linked lists)

Recommend


More recommend