Lists
ADT (brief intro):
Abstract Data Type A DESCRIPTION of a data type
The data type can be anything: lists, sets, trees, stacks, etc.
What we want to do at the ADT level is describe what it is and what it should do
We don’t worry about HOW it does it
There’s no definite rule for what operations must be supported for each type
Use what makes sense.
Lists:
Things we know about lists:
The items have an order
One comes after another - this doesn’t mean they’re “ordered” in any purposeful way, but there’s a built in order to the elements in a list
The list has a size (n elements in the list)
Data in a list can be duplicated
All elements in the list are of the same data type
List operations we might want: push(x)-add to end of list insert(x,k) adds item x to list at kth position remove (Node *node) – removes a node from the list removekth(int kth) – removes the node at the kth position from the
list pop() – removes the last element from the list size() - gives you number of elements in list isEmpty() – returns true iff the list is empty find(x) – return the position of x in the list (usually -1 if not in list) findkth(k) – return the item at the kth position in the list printList() – you figure it out
… I’m sure there are other things you’d want to be able to do with a list.
Linked List (versus Array): Every element in a linked list consists of at a minimum 2 parts:
The data
A pointer to (aka the address of ) the element coming next in the list
No fixed size (no limit on the number of elements in the list)
No “wasted space”
No empty spaces in between elements in the list
Sequential access (as opposed to random access, like an array)
3
7
4 2 NULL
Nodes in a linked list
Node implementation:class Node {
friend class LL; // allows another class to access privage members of the Node class
int data;Node *next;
public:Node(int x);~Node();
}; //Node
Node::Node(int x){data = x;next = NULL;
} //ConstructorNode::~Node() {
if (next != NULL) {cout << “deleting may cause memory leak.” << endl;
} //if} //destuctor
A list class (A list of nodes):#include "Node.hpp“
class LL { Node *first; int currsize;public: LL(); ~LL(); void push(); void insert(x,k); void remove (int pos); int pop(); int size(); bool isEmpty(); int find(x); Node * findkth(k);};
Push: //adds node to end of list If the list is empty?
Create the new node
Set the first pointer to point to the new node
Set the currsize to 1
void LL::push() {int x = rand()%10;if (first == NULL) {
first = new Node(x);} //ifcurrsize=1;
} //push
class LL { Node *first; int currsize;public: … void push(); …};
7 NULLfirst
Push: //adds node to end of list If List is not empty?
class LL { Node *first; int currsize;public: … void push(); …};firs
t7 4 3 NUL
L
temp
NULL
8
new Node(8)
Node *temp = first;
while (temp->next != NULL) { //Why isn’t the condition while (temp != NULL) { ?
temp = temp->next;
} //while
temp->next = new Node(x);
currsize++;
How many steps to push onto the list? Is there a more efficient way? (Hint: yes, there is!!!)
Adding a last node pointer:When we start list:last = first;
When we’re pushing onto the end of an existing list:last->next = new Node(8);last = last->next;
Now what is the count for push()?
7 NULLfirst
NULL
first
7 4 3
8
NULL
last
last
class LL { Node *first; Node *last; int currsize;public: … void push(); …};
7 4 3 8 NULL
9
void insert(x,k);Inserting node with data, x= 9 at k = 2;
lastfirst
How do we get to the kth position in the list?
Loop!
Do we loop to the kth position or the k-1th position? Why?
Is there a better way? What if we did this with an array?
Node *n = new Node(9);n->next = node4->next;node4->next = n;
insert(x,k)
bool LLC::insert(int x, int k) {if (k > currsize) { //illegal insert
return false;} //ifelse { // inserting a new node
NodeC *n = new NodeC(x);if (k == 0) { //inserting at front of list
if (first == NULL) { //empty listlast = n;
} //ifelse {
n->next = first;} //elsefirst = n;
}// ifelse if (k != 0){
NodeC *temp = first;for (int j = 0; j < k-1; j++) {
temp = temp->next;} //forn->next = temp->next;temp->next = n;if (k == currsize) { //inserting at end of list
last = temp->next;} //if
} // else if} //elsecurrsize++;return true;
} //insert
Problems with Linked List (as we’ve seen so far…)
We can insert a node only after a node we have a pointer to.
We can remove a node only if we have a pointer to its predecessor node
We can traverse the list in only the forward direction
Solution?
Stack Subset of list
Only 2 operations: push() and pop()
Stick something on the end of the stack, or take something off the end of the stack
Last-in, first-out (LIFO)
Think of a pile of … almost anything at all …
you can’t take anything off the BOTTOM of the pile, only off the top. Equally, you can’t add to the pile at the bottom, only at the top.
Applications:
Reversing a word
The “undo” option
Backgracking in a maze
Memory for functions
(the easiest way to visualize this is recursion)
Linked list or array??
Hint: oh please…
Pop() How do we implement pop?
How many steps?
Now pop again…
Where is the last pointer still pointing?
Is there a better way?
What about arrays – will that work better?
first
7 4 3 NULL
8
last
Doubly-linked list Now what do we need to do?
Node *temp = last;last = last->prev;delete temp;last->next = NULL;
Now how many steps?
class Node {friend class LL; int data;Node *next;Node *prev;
public:Node(int x);~Node();
}; //Node
first 7 4 3 NUL
L8
last
NULL
temp
NULL
Doubly-linked list: Disadvantages:
A bit more memory (now we’ve got that prev pointer space for each node)
Must manage more pointers when performing operations on the linked list (e.g., insert, remove, etc.)
Advantages:
Makes pop() easier (O(1))
Makes traversing the list in reverse order easier
Reversing the list is easy now
Can go backwards and forwards from a node in a list
We may need surrounding nodes
We may need data that occurred “close to” a node with certain data
What about inserting into an ordered list?
Inserting 6 into the ordered list:
Node *temp = first;while (temp->data < 6) { // left out check for end of list
temp = temp->next;}
Node *n = new Node(6);n->prev = temp->prev;temp->prev->next = n;n->next = temp;
Easier with a doubly-linked list?
first 2 4 7 NUL
L8
last
NULL
6 NULL
NULL
temp
n
Circular List Can be singly or doubly linked list Make the “last” node’s next pointer point to the first node.
No NULL pointers Don’t have to worry about “falling off the end” of the list Can hit all items in the list, even if we start looking in the middle
tail->next = head;
head->prev = tail; // if doubly-linked
Do we still need a head and tail pointer?
first
7 4 3 8
last
Circular linked lists: Works nicely with queues:
FIFO (or LILO)
First in, first out
Think of the check-out line in a store
To-do lists
The printing jobs coming into a printer
Now to insert new job:
Node *n = new Node(20);
n->next = last->next;
last->next = n;
To remove a job:
Node *n = last->next;
last->next = last->next->next;
delete n;
What if there’s only one node in the list?
What about searching for a particular node in the list? What if the node isn’t in the list?
How about Find in a linked list?
Find 3 in the list
Find 6 in the list
O(n)
Is there a better way?
Binary search tree!
7 4 3 8 NULL
lastfirst
23
Tree (new ADT)Terminology: A tree is a collection of elements (nodes) Each node may have 0 or more successors (called children)
How many does a list have?
Each node has exactly one predecessor (called the parent) Except the starting node, called the root
Links from node to its successors are called branches Nodes with same parent are siblings Nodes with no children are called leaves
24
Tree We also use words like ancestor and descendent
• Pets is the parent of Dogs and Cats• Poodle and Beagle are the children of Dogs• Poodle, Beagle, Persian, and Siamese are descendents of Pets, • Pets is the ancestor of Poodle, Beagle, Persian, and Siamese
25
Tree Terminology
Subtree of a node:
A tree whose root is a child of that node
Depth of a node:
A measure of its distance from the root:
Depth of the root = 0
Depth of other nodes = 1 + depth of parent
2
1
3
4