Linked List Structure

Download as pdf or txt
Download as pdf or txt
You are on page 1of 6

CSC-300 Luis Pineda

LINKED                LISTS
LINKED LIST STRUCTURE
FIRST NULL

Each Node has data. This is accessed with


nodeName.data. REMEMBER,
Each node has a pointer to the node next to it.
This is accessed with nodeName.next.
NO INDEXING
The first node is always designated as first.

The last node has a pointer to null. If you ever get here, then
you know you are at the end of the Linked List

ADDING A NEW NODE TO THE


FRONT OF A LIST
N FIRST NULL

Adding a new node to the beginning of the list is very common (ie. Stacks).
To add N, first we must make it point to first. This is done by making
N.next = FIRST.
Finally, we must make N the first node. This is done by making N = FIRST.
FIRST

N NULL

DELETING A NODE
FIRST N
Notice N still has
an N.next pointer.
Warmth
If we want to delete N, we need to
make the node before it point to
However, since
footwear nothing is
N.next
referencing N, it
This means you must be able to
will be picked up
access the previous node. (Make a
by garbage
new node)
collection.
FIRST N If no node
remembers you,
you don't exist!

DELETION AND ADDITION EXTRAS


When adding or deleting anywhere other than the
beginning of the linked list, you should keep track of the
previous node. For example, adding at the kth position
requires the k-1th postion
K

FIRST K-1
ITERATION The Holy Trinity of Iteration 
Unlike Arrays, Linked Lists do not have a built-in
Initialization
inspirational
length function sayings
When looping, you typically start at first, much Run Condition
like index 0 in arrays
Instead of being bounded by the length, looping
continues until you reach NULL Incrementer
Instead of adding 1 to the index, you move on
the next node
For(Node n = first; n != null; n = n.next)

PREV NODES
FOR EXAMPLE
while(currNode != null) {
Sometimes you'll need to
if(something)
keep track of the previous
then something;
node.
prevNode = currNode;
When iterating, either
currNode = currNode.next;
through a while or for
}
loop,make sure to update
it at the end of the loop
and prior to incrementing Prev Curr
your current node.
If you increment current When your loop repeats, it should
first, both prev and current look like this:
will point to the same
node!

New New Curr


Prev

Two common uses of Linked Lists are for implementing Stacks and

S
Queues. Both are simply collections of items that operate under
certain rules. FIRST
1 2 3 4

T
Much like a stack of
plates, stacks operate In a Linked List
under the Last-In- FIRST
implementation of a

A
First-Out policy. This Stack, the top of the 1
means that when an stack is represented
item is added, it goes by First.

C
at the front-- where it 2
will then be the first Adding a new element
thing to be removed. is done with the

K
push(item) function
Removing a plate 3
from the bottom of the Removing is done
stack would not be a with the pop()

S
good move. function. This function
returns the element
4
removed.

Queues on the other hand, live up to their name in that they


operate under the First-In-First-Out Policy. Under this policy,
items will be removed in the same order they were added. Think
of waiting in a line to be served at a fast food restaurant.
FIRST
1 2 3 4
Linked list queues are ordered from newest to oldest, with the
newest element being stored in First.
The enqueue(item) method adds an element in the
First postion.
The dequeue() method removes the first item in the
list and returns it.

 Queues
LINKED LISTS TRADEOFFS 
 ARRAYS VS. LINKED LISTS
ARRAYS LINKED LISTS
+ Instant access to
+ No size limit. Can add
elements through
nodes as long as there is
[indexing]
memory space available

- Size is immutable.
+ Good for continuously
When initializing an
growing data
array, a size must be
given and cannot be
- No indexing, so no instant
changed. Requires
access.
rezising.

RUN  TIME
Basics
Computers work one operation at a time
Consider having an input size of N for a function that
prints out all the elements in an array. The computer
will do the print operation the same amount of times
as there are elements
Thus, the run time would be N

BIG-O COMPLEXITY

BAD OKAY GOOD BETTER BEST


SPOTTING RUN TIME
Consider this code for counting the creating a variable is constant
number of distinct elements in an array: time
the first loop will repeat
    int count = 0; however many items are in
          for (int i = 0; i < list.length; i++) { the list (N)
           boolean seenThisIntBefore = false; the second loop will repeat j
              for(int j = 0; j < i; j++) times
                   if ( list[i] == list[j] ) adding to a variable is also
                       seenThisIntBefore = true; constant
              if(!seenThisIntBefore) count++;
     return count;

SORTING
Selection Sort
Find the smallest item in the array, and
exchange it with the first entry
Find the next smallest item and exchange it
with the second entry
Continue until the entire array is sorted
Repeatedly selects the smallest remaining item
N^2/2 compares and N exchanges to sort
an array of length N

Insertion Sort
Compares the item before it and continues
to swap backwards if the item is greater
N^2/4 compares and N^2/4 exchanges on
average
The worst case is N^2/2 compares and
N^2/2 exchanges and the best case is N-1
compares and 0 exchanges.

Merge Sort
Divide array into two halves until only single items
remain
Sort the two halves recursively
Then merge the results.
Between 1/2 N lg N and N lg N compares and at
most 6 N lg N array accesses to sort any array of
length N.

0
UNiONS
Connected components are simply
the items that are either connected or
1 8 2 7 4 alone. In this case, there are 4
connected components.
Depending on the implementation,
these components will be stored in an
6 5 9 3 array and will be identified by the
parent element

Quick-Find
IMPLEMENTATION & API 

In the Quick-Find implementation, the ID


4 10 of a given element gets the value of its
root. The corresponding array to the
1 8 structure on the left, then, would be:
5
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
0 4 | 4 | 5 | 6 | 4 | 5 | 6 | 6 | 4 | 6 | 10
6 2
Notice that from this array, we can extract some useful
information. For one, we can scan the array for any element
3 7 whose value is the same as its index to find out how many
roots --and subsequently, connected components-- there are.
In this array, we can infer that there are four connected
components with the roots, 4, 6, 5, and 10.
9
True to its name, Quick-Find is very fast at finding the value of any given
element's root, since its value in the array stores the root's value itself.
Inversely, connecting two components would require one to iterate through
the array and change the old values of all the elements in one component
to the value of the new root.

For example, if we wanted to call union(5,4), we would get:

0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
4 4 | 4 | 4 | 6 | 4 | 4 | 6 | 6 | 4 | 6 | 10
1 5 Notice that every value of 5, the old root, was
8 changed to 2, the new root.
The Union function would be written like this:

0 2 public void union(int p, int q) {


      int pID = id[p];
      int qID = id[q];  
Like mentioned before,      if (pID == qID) return;
      for (int i = 0; i < id.length; i++)
because each element          if (id[i] == pID) id[i] = qID;
in the array stores its       count--;
root, the find() function
is very fast. All one has    public int find(int p) {
to do is simply return        return id[p]; If we ran find(2) or find(3) for
   }
the value of the example, we would get 4 and 6,
element given. respectively.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
4 | 4 | 4 | 6 | 4 | 4 | 6 | 6 | 4 | 6 | 10
Quick-Union
6
Like Quick-Find, an array will also be used to
4 3 7 store the values of each element except this
time, each site in the array will store the value of
its parent, or the node above it, instead of its
1 8 9 root.
The array for this component then, would be:

0 5 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
1 | 4 | 5 | 6 | 4 | 5 | 6 | 6 | 4 | 7 | 10
10 2
Because each site holds the value of its parent and not
the root, implementing the find() function will require more
work, since we will have to essentially "climb" up the  public int find(int p) {
component tree until we find the root. In the case of        while (p != parent[p])
find(0) for example, we would first have to go to 1, and            p = parent[p];
then to 4 before returning the value. This extra work is        return p;
reflected through the use of a loop in the code.    }
Observe how find(0) would run on our array:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
1 | 4 | 5 | 6 | 4 | 5 | 6 | 6 | 4 | 7 | 10

Luckily, connecting two components is a lot easier;


again, due to the fact that now it is only required to
change the value of the old root with the new root.  public void union(int p, int q) {
       int rootP = find(p);
If we ran union(5,4), in this example, the tree would look       int rootQ = find(q);
the same as with Quick-Find (though this won't always        if (rootP == rootQ) return;
       parent[rootP] = rootQ;
be the case) but the array will not :
       count--;

4
1 5 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
8 1 | 4 | 5 | 6 | 4 | 4 | 6 | 6 | 4 | 7 | 10
0 2

Weighted Quick-Union
Although Quick-Union is a helpful To solve this issue, we create a new array that holds the
implementation, it can lead to a sizes of each tree and compares them when connecting
two components. In the case of union(4,5), the program
tree increasing in depth after
would compare the sizes of 4 and 5, and upon determining
connecting two structures. See the that 4 was the larger of the two, it would make that the
case of union(4,5) for example: parent. The only difference in code will come in the union()
function.

5
  public void union(int p, int q) {
       int rootP = find(p);
4 2        int rootQ = find(q);
4        if (rootP == rootQ) return;
       if (size[rootP] < size[rootQ]) {
1 8 1 5            parent[rootP] = rootQ;
8            size[rootQ] += size[rootP]; }
       else {
2            parent[rootQ] = rootP;
0 0            size[rootP] += size[rootQ]; }
   }

You might also like