0% found this document useful (0 votes)
53 views57 pages

L14. Singly and Doubly Linked List

The document discusses linked lists and how they are implemented using nodes that contain data and a reference to the next node. It explains that a linked list overcomes some of the limitations of arrays by allowing dynamic sizes and efficient insertions/deletions. The key aspects are that each node contains data and a next pointer, and the linked list is traversed by following the next pointers between nodes. It provides examples of creating a linked list with nodes, traversing it to print the data, and introduces the concept of a LinkedList class to encapsulate the list functionality.

Uploaded by

haladaher5555
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
53 views57 pages

L14. Singly and Doubly Linked List

The document discusses linked lists and how they are implemented using nodes that contain data and a reference to the next node. It explains that a linked list overcomes some of the limitations of arrays by allowing dynamic sizes and efficient insertions/deletions. The key aspects are that each node contains data and a next pointer, and the linked list is traversed by following the next pointers between nodes. It provides examples of creating a linked list with nodes, traversing it to print the data, and introduces the concept of a LinkedList class to encapsulate the list functionality.

Uploaded by

haladaher5555
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 57

Intro to Singly and Doubly

Linked List
Reading: Chapter 3
Data Structures and
Algorithms in JAVA
Singly Linked List
• Arrays are nice and simple for storing things in a certain order, but
they have drawbacks:
– They are not very adaptable
– For instance, we have to fix the size n of an array in advance, which makes
resizing an array difficult
– Insertions and deletions are difficult because elements need to be shifted
around
• We now explore the implementation of an alternate sequence,
known as the singly linked list
• A linked list, in its simplest form, is a collection of nodes that
together form a linear ordering

16-2
References to same type
• Objects can point or refer to different type objects as fields
• Also, objects can refer to same type objects as fields

• What would happen if we had a class that declared one of its own
type as a field?

public class Strange {


string name;
name Other
Strange other;
“Adam”
}
• Strange object has two fields:
− One can store a single string name
− One is a reference to another Strange object.
• We can use it to store a list of string values
A list node class
• Node: a single element/object of a list structure.
• Each node contains its own data.
• Each list node object stores data and reference(s):
– data can be of any primitive types or object of any class, etc.
– reference (usually called next) is an object address that refers to the next node
in the list.

Pseudocode
Class Node Data portion of Node
dataType data1;
dataType data2;

Node next; link to another Node object

dat nex dat nex dat nex data next


a t a t a t 9 null
42 -3 17
What is a linked list ele
m
next

• A simple IntNode class : 18


public class IntNode {
– member elem stores the element stored in int elem;
this node, which in this case is a integer IntNode next;
}
– member next stores a reference to the next
node
• Linked lists gather objects of the same type in sequence, accessible
using references.
– These objects are usually called Nodes
• first, head, front, or list is used to keep track of the linked list.
– It is not a Node but a reference to a node
– If you keep a reference to the front of the list, then you can get to anything in
it.
head ele nex ele nex ele nex elem next
m t m t m t 9 null
42 -3 17
List node client example
• Let us construct a list that stores the sequence of values [42, -3, 17].
• There are three values then we need three nodes that are linked together.
IntNode list;
• The variable list is not itself a node but a variable capable of referring to a
node.
list ?

• ? Will be replaced with a reference to a node which means this box does not have
data or next field.
• We do not have the actual node yet. To have it, we should call new:
elem next
list = new IntNode(); list 0

• .
IntNode default construct initializes the fields as:
– Data initialized to 0
– Next initialized to null ( in these slides / indicates null)
public class ConstructList1 {
public static void main(String[] args) {
IntNode list = new IntNode();
List node
list.elem = 42; tester
list.next = new IntNode();
list.next.elem = -3; example
list.next.next = new IntNode();
list.next.next.elem = 17;
list.next.next.next = null;
System.out.print(list.elem);
System.out.print(" " + list.next.elem);
System.out.println(" " + list.next.next.elem);
}
}

elem next elem next elem next


list 0 0 017
.

42 -3 null

Output
42 -3 17
Node with constructor
public class IntNode {
int elem;
IntNode next;

public IntNode() {
elem = 0; // this(0, null)
next = null;
}

public IntNode(int data) {


this.elem = data; // this(data, null)
this.next = null;
}

public IntNode(int data, IntNode next) {


this.elem = data;
this.next = next;
}
}
List node w/ constructor
• Exercise: Modify the previous client to use these constructors.
public class ConstructList2 {
public static void main(String[] args) {
IntNode list =
new new IntNode(-3, new InttNode(17) )
IntNode(42, );
System.out.print(list.elem + " " + list.next.elem +"
"
+ list.next.next.elem);
} elem next elem next elem next
} list 0 0 017
.

42 -3 null

Output
42 -3 17
References vs. objects
variable = value;
a variable (left side of = ) is an arrow (the base of an arrow)
a value (right side of = ) is an object (a box; what an arrow points at)
• For the list at right: dat nex
– q = a.next; p a t
means to make q point at 2 20
– a.next = p; dat nex 2 dat nex
a
means to adjust where 1 points a t1 a t
Reassigning references 10 20
q
• a.next = b.next;
– "Make the variable a.next data next data next
a
refer to the same value as
10 20
b.next."
– Or, "Make a.next point to the data next data next
same place that b.next points." b
30 40
Traversing a list?
• Suppose we have a long chain of list nodes:

ele next ele next ele next


head m m ... m
10 20 990
• How would we print the data values in all the nodes?
– Start at the head of the list.
– While (there are more nodes to print):
• Print the current node's element.
• Go to the next node.
• How do we walk through the nodes of the list?
while (head != null) {
System.out.println(head.elem);
head = head.next; // move to next node
}
• What's wrong with this approach?
– it loses the linked list as it prints it!
Traversing a list with a current reference
• Don't change list. Make another variable, and change that.
– A IntNode variable is NOT a IntNode object
IntNode current = head;
• What happens to the picture below when we write:
current = current.next;

ele nex ele nex ele nex


head m m m
t t ... t
current 10 20 990

• The correct way to print every value in the list:


IntNode current = head;
while (current != null) {
System.out.println(current.elem);
current = current.next; // move to next node
}
– Changing current does not damage the list.
A IntLinkedList class
• Let's write a class named IntLinkedList with the following interface
isEmpty(); // is list empty?
addFirst(int value); // add to front of list
first(); // get front element
removeFirst(); // remove front item list
toString(); //toString
//we can add more methods when needed
– The list is internally implemented as a chain of linked nodes
• The IntLinkedList keeps a reference to its head as a field
• NULL is the end of the list; a NULL head signifies an empty list

InLinkedtList
IntNode IntNode IntNode
head ele nex ele nex ele nex
empty() m t m t m t
addFirst(value)
first() 42 -3 17
removeFirst()
toString()
A IntLinkedList class
public class IntLinkedList {
private IntNode head;
public IntLinkedList() {…}
public void addFirst(int value){…}// add to front of list
public boolean isEmpty(){…} // is list empty?
public int first(){…} // get front element
public void removeFirst(){…} // remove front item list
public String toString(){…}
};
Adding to an empty list or to its front
• Before adding -3, list is empty:
head ele next
m
head -3
ele next
v m
42
– To add -3, we must create a new node and attach it to the list.
head = new IntNode(-3);
– To add 42 to the front, we must create a new node and attach it
to the list.
IntNode v = new IntNode(42);
v.next = head;
head = v; public void addFirst(int value){
IntNode v = new IntNode(value);
v.next = head;
head = v;
}
Getting/removing the front node && isEmpty
• To get the front node element: return head.elem
ele next ele next ele next
m m m
head
42 -3
public int first()17{
return head.elem;
}

• To remove the front node: public void removeFirst(){


head = head.next; head = head.next;
}

ele next ele next ele next


m m m
head
42 -3 17

• To check if a list is empty or not just we check the head


return (head==null); public boolean isEmpty() {
return (head==null);
}
toString() Method
// Returns a String representation of the list.
public String toString() {
if (head == null) {
return "[]";
} else {
String result = "[" + head.elem;
IntNode current = head.next;
while( current != null) {
result += ", " + current.elem;
current = current.next;
}
result += "]";
return result;
}
}

result [42 , -3, 17 ]

ele next ele next ele next


m m m
head
42 -3 17
current
16-17
Construct
• To check if a list is empty or not just we check the head
public IntLinkedList() { head = null; }

IntLinkedList list;
list = new IntLinkedList();
List.addFirst(5);

list head ele next


m
empty()
addFirst(value) 5
first()
removeFirst(i)
toString()
public class IntLinkedList {

private IntNode head;

public IntLinkedList() { head = null;}

public void addFirst(int value) {


IntNode v = new IntNode(value);
v.next = head;
head = v;
}

public int first() { return head.elem;}

public void removeFirst() { head = head.next;}

public boolean isEmpty() { return head == null;}

public String toString() {


if (head == null) {
return "[]";
} else {
String result = "[" + head.elem;
IntNode current = head.next;
while (current != null) {
result += ", " + current.elem;
current = current.next;
}
result += "]";
return result;
}
}
}
public class ListClient {
public static void main(String[] args) {
IntLinkedList list = new IntLinkedList();
list.addFirst(43);
list.addFirst(143);
list.addFirst(243);
System.out.println(list);
list.removeFirst();
System.out.println(list);
}
}

list
head
ele next
ele next m
empty() ele next m
addFirst(value)
first() m 43
removeFirst(i) 143
toString()
243

[243, 143, 43]


[143, 43]
Arrays vs. Linked Lists

• How would you keep a linked list sorted?


• How would you search for an elements (by value or by index)?
• How would you insert an element at a specific position?
– We are going to add more methods/operations/ behaviors

– Improve the list to have links in both directions

– Introduce Tree later on in this course

21
Adding to the tail of the list
• Consider following list:
data next data next
head 42 -3
data next
current v 17
– To add a node, we must modify the next reference of the node before the
place you want to add/change
IntNode v = new IntNode(17);
IntNode current = head;
while (current.next != null) {
current = current.next;
}
public void addLast(int value){
Current.next = v; IntNode v = new IntNode(value);
if (head==null)
head=v;
else{
IntNode current = head;
while (current.next!=null){
current=current.next;
O(N) }
current.next=v;
}
}
Adding to the tail of the list using a tail reference
public class IntLinkedList {
• Another less costly approach is to private IntNode head;
add a tail reference that keeps private IntNode tail;
track of the last node: …
}
v
head tail tail

ele next ele next data next


m m 17
42 -3

public void addLast(int value){


IntNode v = new IntNode(value); O(1)
if (head==null)
head=v;
else
tail.next=v;
tail = v;
}
Remove from the tail of the list
• Consider following list:
data next data next data next
head 42 -3 17

v
• In order to delete a node, we need to update the next link of the
node immediately preceding the deleted node

public void removeLast(){


if (head.next==null)
removeFirst();
else{
IntNode v = head;
while (v.next.next!=null){ O(N)
v=v.next;
}
v.next = null;
}
}
The size()method
data next data next data next
count 3
01
2 head 42 -3 17
current

// Returns current number of elements


public int size(){
int count = 0;
IntNode current = head;
while( current != null) {
current = O(N)
current.next;
count++;
}
return count;
}
public class IntLinkedList {
• Another less costly approach is to add private IntNode head;
a data member to count the number of private IntNode tail;

nodes in the list (increment it when private int n;


public int size (){ return n;}
adding a new node and decrement it …
when removing an old node). }
O(1)
Adding a node between two nodes
• Consider the following list:
element 0 element 1 element 2
head data next data next data next
42 -3 17
before data next
5
v

• To add a node between two nodes, we first point to the node before
the location of the new node (Example: to add at index 2)
IntNode v = new IntNode(5);
IntNode before = head;
for (int i = 0; i < index - 1; i++)
before = before.next;
v.next = before.next;
before.next = v;
remove a node between two nodes
// Removes value at given index from list.
// Precondition: 0 <= index < size()
public void remove(int index){
if (index == 0) {
// special case: removing first element
head = head.next;
} else {
IntNode current = head;
// removing from elsewhere in the list
for (int i = 0; i < index - 1; i++) {
current = current.next;
}
current.next = current.next.next;
}
}

data next data next data next


front 42 -3 17
current
Doubly Linked Lists
• Removing/adding an element at the tail of a singly linked list is not
easy.
– Indeed, it is time consuming to remove/add any node other than the head in a
singly linked list,
– we do not have a quick way of accessing the node immediately preceding the
one we want to remove or add.
– For some applications, it would be nice to have a way of going both directions
in a linked list.
• How to see a doubly linked list?
• A sequence of nodes « back to back ».
• Each node gives one hand to its successor and one hand to its predecessor.
Doubly Linked List
• Doubly linked list allows to traverse backward as well as forward
through the list.
–Each node has two references to other nodes instead of one
next: refer to the next node in the list
• Last node has no next node (no successor)
 prev: refer to the previous node in the list
• first node has no previous node (no predecessor)
–Each list uses two references to access its first and last nodes
 header : refers to the first node
trailer : refers to the last node

trailer

header

JFK PVD SFO AAA

next next next next null

null prev prev prev prev


Doubly Linked List- DNode class
public class DNode { // doubly linked list node
public String elem; // node element value
public DNode prev; // previous node in list
public DNode next; // next node in list
public DNode() { }
public DNode(String data){
elem = data;
}
//other methods go here

};

trailer

header

JFK PVD SFO AAA

next next next next null

null prev prev prev prev

30
Doubly Linked List- DLinkedList
public class DLinkedList { // doubly linked list
private DNode header;
private DNode trailer;
public DLinkedList(){ header = null; trailer = null;} // constructor
public boolean isEmpty(){ return ((header==null) && (trailer==null));}
public String front(){ return header.elem; }
public String back(){ return trailer.elem; }
public void addFront(String e){} // add to front of list
public addBack(String e){} // add to back of list
public removeFront(){} // remove from front
public removeBack(){} // remove from back
};

trailer

header

JFK PVD SFO AAA

next next next next null

null prev prev prev prev


Doubly Linked List- DLinkedList
public void addFront(String value){
DNode v = new DNode(value);
if (header == null) //check if it is an empty list
trailer = v;
else trailer null
header.prev = v; header
v.next = header; null
header = v;
} v

value

next null

null prev

trailer

header
v

value AAA BBB CCC DDD


null next next next next
next null
null prev prev prev prev 32
null prev
Doubly Linked List
public void addBack(const String) { // add to back of list
DNode v = new DNode(e);
if (trailer == null) //check if the list is empty
header = v;
else
tailer.next = v;
v.prev = trailer;
trailer = v;
}

trailer
v
header

aaa bbb ccc ddd


e
next next next next null
next null
null prev prev prev prev
null prev

33
Doubly Linked List-Deletion
public void removeFront()
{
if (header.next == NULL) //if only one item in the list
trailer = NULL;
else
header.next.prev = NULL;
header = header.next;
}

trailer

header

ssss aaaa bbbb dddd

next next next next null

null prev prev prev prev


null
Doubly Linked List-Deletion
public void removeBack()
{
if (trailer.prev == null) //if only one item in the list
header = null;
else
trailer.prev.next = null;
trailer = trailer.prev;
}

trailer

header

aaaa bbbb nnnn null gggg


next next next next null

null prev prev prev prev


Doubly Linked List
•Forward Traversal: start at the first node until the end of the list
DNode current = header;
while (current != null)
current = current.next;

trailer

header

aaa nnnn jjjj frfff

next next next next null


null prev prev prev prev

current

36
Doubly Linked List
•Backward Traversal: start at the last node until the beginning of the
list
DNode current =trailer;
while (current != NULL)
current = current.prev;

trailer

header

aaa bbb vvv xxxx

next next next next null


null prev prev prev prev

current

37
Doubly Linked List
Add a node at a specific insertion point in the list
DNode v = new DNode(e);
DNode current = header;

while (current != null && key != current.elem)


current = current.next;
current.prev.next = v;
v.prev = current.prev;
v.next = current;
current.prev = v;

trailer
header current

aaaa bbbb target ddd

next next next next null

null prev prev prev prev


e

next

prev
v 38
Implementing Singly Linked List of String Nodes
• Similar to what we have done with IntNode and IntLinkedNode,
we define two classes StringNode and StringLinkedList
– member elem in StringNode stores the element stored in this node, which
in this case is a string and memember next stores a pointer to the next node

• We make StringLinkedList keeps reference to the tail node as well to


the head node

39
Inner Classes
• We make StringNode class an inner class in StringLinkedList
• inner classes are convenient for insignificant classes.
– If inner class is defined inside an enclosing class, but outside its methods, it is
available to all methods of enclosing class
– Trivial classes can be defined inside a method
– Compiler turns an inner class into a regular class file
– Methods of inner classes can access variables and fields from the surrounding
scope.
Declared inside a method
Declared inside the class class OuterClassName {
class OuterClassName { method signature {
// methods . . .
// fields class InnerClassName
accessSpecifier class InnerClassName {
// methods
{ // fields
// methods }
// fields . . .
} }
. . . . . .
} 40
}
Implementing a Singly String Linked List
public class StringLinkedList {
private static class StringNode {
private String element; // reference to the element stored at this node
private StringNode next; // reference to the subsequent node in the list

public StringNode(String e, Node n) {


element = e;
next = n;
}
public String getElement() {
return element;
}
public StringNode getNext() {
return next;
}
public void setNext(StringNode n) {
next = n;
}
} // ----------- end of nested Node class -----------

private StringNode head = null; // head node of the list (or null if empty)
private StringNode tail = null; // last node of the list (or null if empty)
private int size = 0; // number of nodes in the list
41
Inserting an Element at the Head of a Singly Linked List

Algorithm addFirst(e):
newest = Node(e) {create new node instance storing reference to element e}
newest.next = head {set new node’s next to reference the old head node}
head = newest {set variable head to reference the new node}
size = size+1 {increment the node count}

42
Inserting an Element at the Tail of a Singly Linked List

Algorithm addLast(e):
newest = Node(e) {create new node instance storing reference to element e}
newest.next = null {set new node’s next to reference the null object}
tail.next = newest {make old tail node point to new
node}
tail = newest {set variable tail to reference the new node}
size = size+1 {increment the node count}
43
Implementing a Singly String Linked List
/** Constructs an initially empty list. */
public StringLinkedList() {}

public boolean isEmpty() { return size == 0; }

public int size() { return size;}

public void addFirst(String e) { // adds element e to the front of the list


head = new Node(e, head); // create and link a new node
if (size == 0)
tail = head; // special case: new node becomes tail also
size++;
}

public void addLast(String e) { // adds element e to the end of the list


Node newest = new Node(e, null); // node will eventually be the tail
if (isEmpty())
head = newest; // special case: previously empty list
else
tail.setNext(newest); // new node after existing tail
tail = newest; // new node becomes the tail
size++;
}
44
Removing an Element from a Singly String Linked List

Algorithm removeFirst():
if head == null then
the list is empty.
head = head.next {make head point to next node (or
null)}
size = size−1 {decrement the node count}
• We cannot delete the last node using a tail reference.
• we must access the node before the last node in order to remove the last node.
• We cannot reach the node before the tail by following next links from the tail.
45
Implementing a Singly String Linked List
public String removeFirst() { // removes and returns the first element
if (isEmpty())
return null; // nothing to remove
String answer = head.getElement();
head = head.getNext(); // will become null if list had only one node
size--;
if (size == 0)
tail = null; // special case as list is now empty
return answer;
}

public String first() { // returns (but does not remove) the first element
if (isEmpty())
return null;
return head.getElement();
}

public String last() { // returns (but does not remove) the last element
if (isEmpty())
return null;
return tail.getElement();
}

46
public String toString() {
if (isEmpty()) { Implementing a
return "[]";
} else { Singly String
String result = "[" + first();
Node current = head.getNext(); Linked List
while (current != null) {
result += ", " + current.getElement();
current = current.getNext();
}
result += "]";
return result;
}
}
} // end of StringLinkedList class

public class ListClient {


public static void main(String[] args) {
StringLinkedList list = new StringLinkedList();
list.addFirst("Alex");
list.addLast("Omar");
list.addFirst("Alice");
System.out.println(list);
list.removeFirst();
System.out.println(list);
} 47
}
Type Parameters (Generics)
ArrayList<Type> name = new ArrayList<Type>();

• Recall: When constructing a java.util.ArrayList, you


specify the type of elements it will contain between < and >.
– We say that the ArrayList class accepts a type parameter,
or that it is a generic class.

ArrayList<String> names = new ArrayList<String>();


names.add("Marty Stepp");
names.add("Stuart Reges");
Implementing Generics
// a parameterized (generic) class
public class name<Type> {
...
}

– By putting the Type in < >, you are demanding that any client that
constructs your object must supply a type parameter.

– The rest of your class's code can refer to that type by name.

• Exercise: Convert our list classes to use generics.


Generics and arrays
public class Foo<T> {
private T myField; // ok
public void method1(T param) {
myField = new T(); // error
T[] a = new T[10]; // error
}
}

• You cannot create objects or arrays of a parameterized type.


• But you can create variables of that type, accept them as parameters,
return them, or create arrays by casting Object[].
public class Foo<T> {
private T myField; // ok
public void method1(T param) {
myField = param; // ok
T[] a2 = (T[]) (new Object[10]); // ok
}
}
Comparing generic objects
public class ArrayList<E> {
...
public int indexOf(E value) {
for (int i = 0; i < size; i++) {
if (elementData[i].equals(value)) {
return i;
}
}
return -1;
}
}

• When testing objects of type E for equality, must use


equals
Generic linked list nodes

public class Node<E> {


public E data;
public Node<E> next;

...
}

• For a generic linked list, the node class must also accept the type
parameter E
Implementing a Generic Linked List
public class LinkedList<E> {
private static class Node<E> {
private E element; // reference to the element stored at this node
private Node<E> next; // reference to the subsequent node in the list

public Node(E e, Node<E> n) {


element = e;
next = n;
}
public E getElement() {
return element;
}
public Node<E> getNext() {
return next;
}
public void setNext(Node<E> n) {
next = n;
}
} // ----------- end of nested Node class -----------

private Node<E> head = null; // head node of the list (or null if empty)
private Node<E> tail = null; // last node of the list (or null if empty)
private int size = 0; // number of nodes in the list
53
Implementing a Generic Linked List
/** Constructs an initially empty list. */
public LinkedList() {}

public boolean isEmpty() { return size == 0; }

public int size() { return size;}

public void addFirst(E e) { // adds element e to the front of the list


head = new Node<>(e, head); // create and link a new node
if (size == 0)
tail = head; // special case: new node becomes tail also
size++;
}

public void addLast(E e) { // adds element e to the end of the list


Node<E> newest = new Node<>(e, null);
// node will eventually be the tail
if (isEmpty())
head = newest; // special case: previously empty list
else
tail.setNext(newest); // new node after existing tail
tail = newest; // new node becomes the tail
size++;
} 54
Implementing a Generic Linked List
public E removeFirst() { // removes and returns the first element
if (isEmpty())
return null; // nothing to remove
E answer = head.getElement();
head = head.getNext(); // will become null if list had only one node
size--;
if (size == 0)
tail = null; // special case as list is now empty
return answer;
}

public E first() { // returns (but does not remove) the first element
if (isEmpty())
return null;
return head.getElement();
}

public E last() { // returns (but does not remove) the last element
if (isEmpty())
return null;
return tail.getElement();
}

55
Implementing a Generic Linked List
public String toString() {
if (isEmpty()) {
return "[]";
} else {
String result = "[" + first();
Node<E> current = head.getNext();
while (current != null) {
result += ", " + current.getElement();
current = current.getNext();
}
result += "]";
return result;
}
}
}

56
Implementing a Singly String Linked List
public class ListClient {

public static void main(String[] args) {

LinkedList<Integer> list2 = new LinkedList<Integer>();


list2.addFirst(43);
list2.addFirst(143);
list2.addFirst(243);
System.out.println(list2);
list2.removeFirst();
System.out.println(list2);

LinkedList<String> list3 = new LinkedList<String>();


list3.addFirst("AL");
list3.addFirst("OM");
list3.addFirst("alice");
System.out.println(list3);
list3.removeFirst();
System.out.println(list3);
}

57

You might also like