CS3334 Lecture 3

Arrays, Linked Lists, Stacks & Queues 

Introduction

How to store and organize data in a computer so that the data can be managed efficiently.

-Representation of data

-Algorithms (methods) for managing data (usually include search, insert, delete, and update) 

Efficiency is important

Focus on main memory data storage; data storage in secondary storage (e.g., hard disks and databases) is usually called indexing structures 

 


Array

Array is a data structure that arranges items at equally spaced addresses in computer memory 

int foo[7]

Unsorted Arrays

Pros:

  • Array elements can be accessed by specifying the array name followed by the index in square brackets. Eg foo[2] is 8
  • Efficient for insertion by appending a new element.

Cons:

  • Have to scan through the whole array to determine for sure if an item is not there 

Sorted Arrays

Pros:

  • Efficient for searching

Cons:

  • Have to move all items behind the point of insertion to make room for the new one 

 


Linked Lists

A linked list is a data structure that allows both efficient searching and insertion/deletion.

A collection of items linked in a sequence: 

Pros:

  • Easy to insert/delete items in the middle, provided we know where to insert/delete (a sorted linked list can easily be maintained.) 

Cons:

  • Difficult to access the i-th item, given an arbitrary i

 

Linked Lists: Node 

struct Node {
   Node(): next(NULL) {}
   Node(int newData): data(newData), next(NULL) {}
   Item data;    // Item is a generic data type
   Node* next;
};

Linked Lists: Traversing (non-circular) 

// Suppose head points to the 1st node of a list Node* p = head;
while (p!=0)
{
    // process p->data
    p = p->next; 
}

 

Linked Lists: Insert a Node 

// To insert a node after the node pointed to by p.
// newData is a variable of type Item
1. Node *temp = new Node(newData);
2. temp->next = p->next;
3. p->next = temp;
 

1.2.3. 

Linked Lists: Delete a Node 

// To delete a node after the node pointed to by p
// var. retData to retrieve content of deleted node
1. Node *temp = p->next;
2. retData = temp->data;
3. p->next = temp->next;
4. delete temp;

1.3.

 

Variations of Linked Lists

  • Singly- / doubly-linked
  • With / without dummy head node
  • Circular / non-circular 

E.g., a circular doubly-linked list with a dummy head node 

 

E.g., a non-circular doubly-linked list without a dummy head node 

E.g., a non-circular singly-linked list without a dummy head node 

 

 


Stacks

A stack is a sequence of elements in which update can only happen at one end of the sequence, called the top.

Operations supported:

– push(x): add an element x to the top

– pop(x): remove the top element and return it in x, i.e., first-in-last-out (FILO) 

Array implementation of stack:

– Maintain size, i.e., the number of elements in stack

– Elements stored in A[0..size-1]

  • The oldest one at A[0] is called bottom of stack
  • The newest one at A[size-1] is called top of stack

– push(x):

  • Store x at A[size]; then increase size by 1

– pop(x):

  • If size = 0, return “Empty Stack”, otherwise decrease size by 1 and store A[size] in x

The method of choosing the size of array A[] (As we insert more and more, eventually the array will be full)    -     A dynamic array

  • Maintain capacity of A[]
  • Double capacity when capacity=size (i.e. full)
  • Half capacity when size≤capacity/4 

Stacks: C++ Code 

class Stack {
   public:
      Stack(int initCap=100);
      Stack(const Stack& rhs);
      ~Stack();
   
void push(Item x);
    void pop(Item& x); private: void realloc(int newCap); Item* array; int size; int cap; };
void Stack::push(Item x)
{
   if (size==cap) realloc(2*cap);
   array[size++]=x;
}
// An internal func. to support resizing of array
void Stack::realloc(int newCap)
{
   if (newCap < size) return;
  Item *oldarray = array; //oldarray is “point to” array
  array = new Item[newCap]; //create new space for array //with a size of newCap
   for (int i=0; i<size; i++)
      array[i] = oldarray[i];
  cap = newCap;
  delete [] oldarray; 
}
void Stack::pop(Item& x)
{
    if (size==0)
        x=EmptyStack;
        // assume EmptyStack is a special value
    else {
        x=array[--size];
        if (size <= cap/4)
            realloc(cap/2);
    } 
}   

Stack: Time complexity

  • Let n = current number of elements in the stack; n changes as pushes/pops are performed

  • Excluding the time for expanding/shrinking the array, push() and pop() need O(1) time in the worst case 

  • Array expanding/shrinking is costly (O(n) time) but is needed once after at least n/2 operations

  • E.g., initial cap is 4, after 5 push operations
    • size = n = 5, cap = 8, n/2 = 2.5
    • Need ≥ 4 more push operations to trigger array expanding
    • Need ≥ 3 more pop operations to trigger array shrinking 

Stacks: Space Complexity

  • When there are n elements in a stack, the largest required capacity of the stack is 4n. E.g., when size=n=2, the largest required capacity cap=8 (just before shrinking).

  • Space allocated for array A[0..cap-1] is: ≤ max{100, 4n}

    Therefore, the space complexity is S(n) = O(n).

Linked list implementation of stack:

  • May use a circular doubly-linked list
  • push(x): call insertFront(x)
  • pop(x): if stack not empty, call deleteFront(x) 

 

Conclusion:

Both array and linked-list implementations are efficient:

  • Excluding array expansion/shrinking, all operations take O(1) time in the worst case 

 


Queues

A queue is a list of elements in which insertion can only be done at one end and deletion at the other.

Operations supported:

– EnQueue(x): insert element x at the end

– DeQueue(x): delete the front element and return it in x, i.e., first-in-first-out (FIFO) 

 

Circular array implementation of queue:

  • Maintain 2 variables: front and size
  • Elements stored in A[front..front+size-1], the oldest element at A[front], the newest element at A[front+size-1]
  • Wrap around the array, if necessary, using “%capacity”. (front+size)%capacity
  • EnQueue(x):
    • Store x at A[(front+size)%capacity] and increase size by 1
  • DeQueue(x):
    • If queue is not empty, copy A[front] to x, increase front by 1 and decrease size by 1
  • Whenever front becomes ≥ capacityof A[], subtract front by capacity, i.e., front=front-capacity. 

 

Linked list implementation of queue:

  • May use a circular doubly-linked list
  • EnQueue(x): call insertBack(x)
  • DeQueue(x): if queue is not empty, call deleteFront(x) 

 

 

 

 

 

 

 

 

posted @ 2018-02-04 14:47  Charonnnnn  阅读(241)  评论(0编辑  收藏  举报