0% found this document useful (0 votes)
35 views

5 Array LinkedLists

The document discusses arrays and linked lists. It provides details about how arrays store elements in contiguous memory locations and can be accessed using indexes, while linked lists connect elements logically using pointers. Linked lists allow dynamic sizes and easy insertion/deletion in the middle compared to arrays. Each linked list node contains a data field and a pointer to the next node. Common linked list operations like insertion, deletion, traversal and searching are also summarized.

Uploaded by

Amit Pathak
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
35 views

5 Array LinkedLists

The document discusses arrays and linked lists. It provides details about how arrays store elements in contiguous memory locations and can be accessed using indexes, while linked lists connect elements logically using pointers. Linked lists allow dynamic sizes and easy insertion/deletion in the middle compared to arrays. Each linked list node contains a data field and a pointer to the next node. Common linked list operations like insertion, deletion, traversal and searching are also summarized.

Uploaded by

Amit Pathak
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 68

Array and Linked List

Array
• Storing data in a sequential memory locations
• Access each element using integer index
• Very basic, popular, and simple
• int a[10]; int *a = new int(10);

Memory

a b c d

start

2
Array: Problems
• New insertion and deletion: difficult
• Need to shift to make space for insertion
• Need to fill empty positions after deletion

• Why don’t we connect all elements just


“logically” not “physically”?
• Linked List

3
Linked List Basics
• Linked lists and arrays are similar since they
both store collections of data.
• The array's features all follow from its strategy
of allocating the memory for all its elements in
one block of memory.
• Linked lists use an entirely different strategy:
linked lists allocate memory for each element
separately and only when necessary.
Linked List Basics
• Linked lists are used to store a collection of
information (like arrays)
• A linked list is made of nodes that are pointing
to each other
• We only know the address of the first node
(head)
• Other nodes are reached by following the
“next” pointers
• The last node points to NULL

5
Linked Lists
head

next next next next

a b c d

First node Last node


Empty List
Empty Linked list is a single pointer having the
value NULL.

head = NULL;

head
Linked List Basics
• Each node has (at least) two fields:
– Data
– Pointer to the next node

data ptr

8
Linked List vs. Array
• In a linked list, nodes are not necessarily
contiguous in memory (each node is allocated
with a separate “new” call)
• Compare this to arrays which are contiguous

Array

head Linked List


NULL
9
Linked List vs. Array
• Advantages of Arrays
– Can directly select any element
– No memory wasted for storing pointers
• Disadvantages of Arrays:
– Fixed size (cannot grow or shrink dynamically)
– Need to shift elements to insert an element to the middle
– Memory wasted due to unused elements
• Advantages of Linked Lists:
– Dynamic size (can grow and shrink as needed)
– No need to shift elements to insert into the middle
– Size can exactly match the number of elements (no wasted memory)
• Disadvantages of Linked Lists
– Cannot directly select any element (need to follow ptrs)
– Extra memory usage for storing pointers
10
Linked List vs. Array
• In general, we use linked lists if:
– The number of elements that will be stored cannot be
predicted at compile time
– Elements may be inserted in the middle or deleted
from the middle
– We are less likely to make random access into the
data structure (because random access is expensive
for linked lists)

11
Linked List Implementation

• A linked list node can be represented as follows:

template <class T>


class Node {
public:
T element;
Node *next;
};

12
Linked List Node Implementation

• We can add a constructor to simplify creating a


new node:
template <class T>
class Node {
public:
Node(const T& e = T(), Node *n = NULL) :
element(e), next(n) { }

T element;
Node *next;
};

13
Constructor
Node(const T& e = T(), Node *n = NULL) :
element(e), next(n) { }
• (const T& e = T(), Node *n = NULL) is the constructor for the
Node class. It takes two parameters, e and n, with default
values.
• const T& e = T(): This parameter e is of type T, which is a template
parameter. It represents the element that the node will store.
• The & symbol indicates that a reference to an object of type T is
being passed.
• The = T() part means that if no value is provided for e when
creating a node, it will be initialized with a default value of T().
• Node *n = NULL:
• This parameter n is a pointer to another Node object. It
represents the next node in the linked list. If no value is provided
for n, it is initialized to NULL. This is common for the last node in
the list, indicating the end of the list.

14
Constructor
Node(const T& e = T(), Node *n = NULL) :
element(e), next(n) { }

• element(e), next(n) { }
• element(e), next(n) { } is the
constructor's initialization list.

• It initializes the member variables of the


Node class.

• element(e): This initializes the element


member variable with the value of the e
parameter that was passed to the
constructor.

• next(n): This initializes the next member


variable with the value of the n parameter
that was passed to the constructor.
15
Linked List Implementation
• A linked list can be defined as follows
• Note that the constructor creates an empty list
by making the head point to NULL

template <class T>


class List {
private:
Node<T> *head;
public:
List() : head(NULL) {}
};

16
Basic Linked List Operations
• Insert a node
• Delete a node
• List Traversal
• Searching a node
• Is Empty
Linked List Operations
• isEmpty(): returns true if the list is empty, false
otherwise

template <class T>


class List {
private:
Node<T> *head;
public:
List() : head(NULL) {}
bool isEmpty() const;
};

18
Linked List Operations
• isEmpty(): returns true if the list is empty, false
otherwise

template <class T>


bool List<T>::isEmpty() const {
return head == NULL;
}

19
Linked List Operations
• first(): returns the first node of the list

template <class T>


class List {
private:
Node<T> *head;
public:
List() : head(NULL) {}
bool isEmpty() const;
Node<T>* first();
};

20
Linked List Operations

template <class T>


Node<T>* List<T>::first() {
return head;
}

21
Insertion in a linked list

a b
… …

p
x

newNode
Insertion
• For insertion, we have to consider two cases:
1. Insertion to the middle
2. Insertion before the head (or to an empty list)
• In the second case, we have to update the head
pointer as well

23
Linked List Operations : insert
• insert(const T& data, Node<T>* p): inserts a
new element containing data after node p
template <class T>
class List {
private:
Node<T> *head;
public:
...
void insert(const T& data, Node<T>* p);
...
};

24
Linked List Operations
template <class T>
void List<T>::insert(const T& data, Node<T>* p) {

if (p != NULL) { // case 1
Node<T>* newNode = new Node<T>(data, p->next);
p->next = newNode;
}
else { // case 2
Node<T>* newNode = new Node<T>(data, head);
head = newNode;
}
}

25
Linked List Operations
• To avoid this if-check at every insertion, we can add a
dummy head to our list (also useful for deletion)
• This dummy head will be our zeroth node and its next
pointer will point to the actual head (first node)

dummyHead (refer to this as `head’ in code – next slides)

next next next next

X a b c

First node 26
Linked List Operations
• An empty list will look like this (the contents of
the node is irrelevant):

dummyHead

next

27
Linked List Operations
• Now, insertion code is simplified:
template <class T>
void List<T>::insert(const T& data, Node<T>* p) {

// now p should not be NULL. To insert to the


// first position, it should point to dummy head

Node<T>* newNode = new Node<T>(data, p->next);


p->next = newNode;

28
Linked List Operations
• We must make some changes to support the dummy
head version:
template <class T>
class List {
private:
Node<T> *dummyHead;
public:
List() {
dummyHead = new Node<T>(T(), NULL);
}
};

29
Linked List Operations
template <class T>
class List {
private: “Note that if we don't
Node<T> *dummyHead; have a constant first()
public: function, we cannot
Node<T>* zeroth() { make isEmpty const as
return dummyHead; well”
}
Node<T>* first() {
return dummyHead->next;
}
const Node<T>* first() const {
return dummyHead->next;
}
bool isEmpty() const {first() == NULL;}
}; 30
Searching for an Element
• To find an element, we must loop through all elements
until we find the element or we reach the end:

template <class T>


class List {
private:
Node<T> *dummyHead;
public:
...
Node<T>* find(const T& data);
...
};
31
Searching for an Element

• To find an element, we must loop through all elements


until we find the element or we reach the end:
template <class T>
Node<T>* List<T>::find(const T& data) {
Node<T>* p = first();

while (p) {
if (p->element == data)
return p;
p = p->next;
}
return NULL;
} 32
Removing a node from a linked list

tmp

a x b
… …
p
Removing an Element
• To remove a node containing an element, we must find
the previous node of that node. So we need a
findPrevious function
template <class T>
class List {
private:
Node<T> *dummyHead;
public:
...
Node<T>* findPrevious(const T& data);
...
};
34
Finding Previous Node

template <class T>


Node<T>* List<T>::findPrevious(const T& data)
{
Node<T>* p = zeroth();

while (p->next) {
if (p->next->element == data)
return p;
p = p->next;
}
return NULL;
}
35
Removing an Element
• Now we can implement the remove function:

template <class T>


class List {
private:
Node<T> *dummyHead;
public:
...
void remove(const T& data);
...
};

36
Removing an Element
• Note that, because we have a dummy head, removal of
an element is simplified as well
template <class T>
void List<T>::remove(const T& data) {
Node<T>* p = findPrevious(data);

if (p) {
Node<T>* tmp = p->next;
p->next = tmp->next;
delete tmp;
}
}
37
Removing an Element
• Implement an algorithm to delete a node in the middle
of a single linked list, given only access to that node
(no findPrevious).

38
Printing All Elements (Traversal)
• List traversal is straightforward:

template <class T>


class List {
private:
Node<T> *dummyHead;
public:
...
void print() const;
...
};

39
Printing All Elements (Traversal)
• List traversal is straightforward: “Again, the constant
first() function allows
template <class T> print() to be const as
void List<T>::print() const well”
{
const Node<T>* p = first();

while(p) {
std::cout << p->element << std::endl;
p = p->next;
}
}
40
Removing All Elements
• We can make the list empty by deleting all nodes
(except the dummy head)
template <class T>
class List {
private:
Node<T> *dummyHead;
public:
...
void makeEmpty();
...
};

41
Removing All Elements

template <class T>


void List<T>::makeEmpty()
{
while(!isEmpty()) {
remove(first()->element());
}
}

42
Destructor
• We must release allocated memory in the destructor

template <class T>


class List {
private:
Node<T> *dummyHead;
public:
...
~List();
...
};

43
Destructor

template <class T>


List<T>::~List()
{
makeEmpty();

delete dummyHead;
}

44
Assignment Operator
• As the list has pointer members, we must provide an
assignment operator
template <class T>
class List {
private:
Node<T> *dummyHead;
public:
...
List& operator=(const List& rhs);
...
};

45
Assignment Operator
template <class T>
List<T>& List<T>::operator=(const List& rhs)
{
if (this != &rhs) {
makeEmpty();
const Node<T>* r = rhs.first();
Node<T>* p = zeroth();

while (r) {
insert(r->element, p);
r = r->next; Uses the const
p = p->next; version
}
}
return *this;
46
}
Copy Constructor
• Finally, we must implement the copy constructor

template <class T>


class List {
private:
Node<T> *dummyHead;
public:
...
List(const List& rhs);
...
};

47
Copy Constructor
template <class T>
List<T>::List(const List& rhs)
{
dummyHead = new Node<T>(T(), NULL);

*this = rhs; // use operator=


}

48
Testing the Linked List Class
• Let's check if our implementation works by
implementing a test driver file
int main() {
List<int> list;
list.insert(0, list.zeroth());
Node<int>* p = list.first();

for (int i = 1; i <= 10; ++i)


{
list.insert(i, p);
p = p->next;
}

std::cout << "printing original list" << std::endl;


list.print(); 49
Testing the Linked List Class

for (int i = 0; i <= 10; ++i)


{
if (i % 2 == 0)
list.remove(i);
}

std::cout << "printing odd number list" << std::endl;


list.print();

50
Testing the Linked List Class

List<int> list2 = list;


cout << "printing copy constructed list" << endl;
list2.print();

List<int> list3;
list3 = list;
cout << "printing assigned list" << endl;
list3.print();

list.makeEmpty();
cout << "printing emptied list" << endl;
list.print();

51
Testing the Linked List Class
for (int i = 0; i <= 10; ++i) {
if (i % 2 == 0) {
if (list2.find(i) == NULL)
cout << "could not find element " << i << endl;
}
else {
if (list2.find(i) != NULL)
cout << "found element " << i << endl;
}
}

52
Variations of Linked Lists
• The linked list that we studied so far is called singly
linked list
• Other types of linked lists exist, namely:
– Circular linked linked list
– Doubly linked list
– Circular doubly linked list
• Each type of linked list may be suitable for a
different kind of application
• They may also use a dummy head for simplifying
insertions and deletions
53
Circular Linked Lists
• Last node references the first node
• Every node has a successor
• No node in a circular linked list contains NULL
• E.g. a turn-based game may use a circular linked
list to switch between players

54
Doubly Linked Lists
pHead

a b c

Advantages:
• Convenient to traverse the list backwards.
• E.g. printing the contents of the list in backward order
Disadvantage:
• Increase in space requirements due to storing two pointers
instead of one
55
Deletion
oldNode

oldNode->prev->next = oldNode->next;
oldNode->next->prev = oldNode->prev;
delete oldNode;
Insertion
current

newNode = new Node(x,NULL);


newNode->prev = current;
newNode->next = current->next;
newNode->prev->next = newNode; newNode
newNode->next->prev = newNode;
Circular Doubly Linked Lists
• Circular doubly linked list
– prev pointer of the dummy head node points to the
last node
– next reference of the last node points to the dummy
head node
– No special cases for insertions and deletions

58
Circular Doubly Linked Lists

(a) A circular doubly linked list with a dummy head node


(b) An empty list with a dummy head node
59
Linked List Problems
• See http://cslibrary.stanford.edu/105/ for some
• Here are some cool interview questions
• Find nth-to-last element with one for-loop
• Keep pointers A & B. Move A n times. Then move both until A ends.
• Find if linked list has a loop
• A is half speed of B. They will collide if there is a loop.
• Print linked list in reverse order
• Reverse pointers, then print new list: 1-> 2 -> 3 becomes 1 <- 2 <- 3
• After each recursion, print out the first node void printRev(Node* n) {
if (! n) return;
printRev(1->2->3) printRev(n->next);
printf("%d\n", n->val);
}
printRev(2->3)
printRev(3) //print out 3 (and so on, read to top)
printRev(NULL) //print out nothing – do nothing
• No additional stack space in non-rec, but needs 2 passes. Both O(N).
Linked List Problems
• More on reversing; assume no Stack around
• Cre­ate 3 nodes, cur­rN­ode, Pre­vN­ode and nextNode
• Ini­tial­ize them as cur­rN­ode=head; nextN­ode=null;pre­vN­ode = null;
• Now keep revers­ing the point­ers one by one till currNode!=null

• In the end set head = prevNode


Linked List Problems
• Merge linked list L2 into L1 at alternate positions

void List<T>::alternateWith(List L2) {


Node<T>* p = first();
while (p) {
insert(L2.first()->element, p);
L2.remove(L2.first()->element);
p = p->next->next; } //end of while
}
Linked List Problems
• Split linked list L into L1 and L2. New linked lists will
con­tain the alter­nate nodes from the given linked list

void List<T>::alternateSplit(List<T> L1, List<T> L2) {


Node<T>* p = first(), * A = L1.zeroth(), * B = L2.zeroth();
while (p) {
L1.insert(p->element, A); A = A->next;
p = p->next;
if (p) {
L2.insert(p->element, B); B = B->next; }
p = p->next; } //end of while
}
Linked List Problems
• Add two numbers represented by linked a linked list

void List<T>::add(List<T> L1, List<T> L2) {


if (L1.length() > L2.length()) L2.pad(L1.length() - L2.length());
if (L1.length() < L2.length()) L1.pad(L2.length() – L1.length());
L1.setPrevs(); L2.setPrevs();
Node<T>* p = L1.first(), * q = L2.first();
while(p->next) p = p->next; while(q->next) q = q->next;
int c = 0; List<T> R; //carry and resulting list
while (p) {//or while (q)
int v = p->element + q->element + c;
if (v < 10) { R.insert(v, R.zeroth()); c = 0; }
else { R.insert(v%10, R.zeroth()); c = 1; }
p = p->prev; q = q->prev; }
if (c > 0) R.insert(1, R.zeroth()); }
Linked List Problems
• Add two numbers represented by linked a linked list

void List<T>::pad(int n) {
for (int i = 0; i < n; i++)
insert(0, zeroth());
}

void List<T>::setPrevs() {
Node<T>* p = first(), pr = NULL;
while (p) {
p->prev = pr;
pr = p;
p = p->next; }
}
Linked List Problems
• Add two numbers represented by linked a linked list
• Solve using Stack

• Traverse L1 and L2 and push elements into S1 and S2


• S1.topAndPop(e1); S2.topAndPop(e2); //call-by-reference
• v = (e1==-1?0:e1) + (e2==-1?0:e2) + carry;
• R.insert(v < 10 ? v : v % 10); carry = (v < 10 ?0 :1);
Linked List Problems
• Fill in the boxes:
• You are not allowed to change any existing
node’s value field, but you are allowed to use
temporary node(s) if necessary. I filled the first
entry for you in bold. All lists are terminated by null
and the variables a and b have value null when they
do not point to anything Use next pointers and not
the ADT functions. Size of the given space is
arbitrary and does not give a hint about the solution.
References
• CENG707 - Data Structures and Algorithms by
Yusuf Sahillioğlu

You might also like