The time complexity of array insertion and deletion is high for elements shifting.
Linked List:
A collection of nodes that collectively form a linear sequence, an alternative to an array-based structure.
Including Singly Linked List, Circularly Linked List, and Doubly Linked List.
2 Singly Linked List
One kind of Linked List whose each node stores a reference to an object that is next node of the list.
head: the first node of the linked list
the linked list instance must keep a reference to its head;
tail: the last node of the linked list
can be found by traversing the linked list;
the next reference of tail is null;
Alternatively, the next reference of the tail can be itself, which can also help us to find the end of the list by checking whether the next reference of a node is itself or not.
it is usual efficiency to store an explicit reference to the tail node for avoiding such traversal;
It is usual for a linked list instance to keep a count of the total number of nodes that is the size of the list, to avoid traversing the list to count the nodes.
// Node class nested privatestaticclassNode<E> { // ... } }
2.1 Insertion of Singly Linked List
Insert at the head
Create a new node as the new head;
Set the next node of the new head to point to the current head;
Set the head of the list to point to the new head;
Java implementation:
1 2 3 4 5 6 7 8 9
publicvoidaddAtHead(E inData) { Node<E> newHead = newNode<>(inData); // Create a new head, newHead.setNext(head); // and set its next to point to the current head head = newHead; // Set the head of the list to point to the new head; if (size == 0) { // If the head is only node in the list tail = head; // The head is also the tail } size++; // Increase the size }
Insert at the tail
Create a new node as the new tail;
Set the next node of the new head to point to the null;
Set the tail of the list to point to the new tail;
Java implementation:
1 2 3 4 5 6 7 8 9 10 11 12
publicvoidaddAtTail(E inData) { Node<E> newTail = newNode<>(inData); // Create a new tail, newTail.setNext(null); // and set its next to point to null if (size == 0) { // if the tail is only node in the list head = newTail; // The tail is also the head } else { tail.setNext(newTail); // else the new tail is the next node of current tail } tail = newTail; // Set the tail of the list to point to the new tail; size++; // Increase the size }
Insert at the index
If the index is 0, then call addAtHead();
If the index is size, then call addAtTail();
Create a new node as new node;
Access the node before the index node by traversal as current node;
Set the next node of the new node to point to the next node of the current node;
Set the next node of the current node to point to the new node.
publicvoidaddAtIndex(E inData, int index) { if (index == 0) { // If the index is 0, then call addAtHead(); addAtHead(inData); } elseif (index == size) { // If the index is size, then call addAtTail(); addAtTail(inData); } else { Node<E> newNode = newNode<>(inData); // Create a new node as new node Node<E> current = head; // Access the node before the index node by traversal for (inti=0; i < index - 1; i++) { current = current.getNext(); } // Set the next node of the new node to point to the next node of the current node newNode.setNext(current.getNext()); // Set the next node of the current node to point to the new node current.setNext(newNode); size++; } }
2.2 Removing of Singly Linked List
Remove the head
Just let head point to the next node of the head.
Remove at the index
Access the node before the index node by traversal as current node;
Set the next node of current node to point to the next node of next node of the current node.
Java implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
publicvoiddeleteAtIndex(int index) { if (size == 0) { return; } if (index == 0) { // Just set head point to the next node of the head head = head.getNext(); } elseif (index >0 && index < size) { Node<E> current = head; // Access the node before the index node by traversal as current node for (inti=0; i < index - 1; i++) { current = current.getNext(); } // Set the next node of current node to point to the next node of next node of the current node current.setNext(current.getNext().getNext()); } size--; // decrease the size }
2.3 Implementation of the Singly Linked List Class
Using generics datatype in Java to define the data stored in nodes.
public E getTail() { return tail.getData(); } // get node from an index public Node<E> getAtIndex(int index) { if (index < 0 || index >= size) { returnnull; } Node<E> current = head; for (inti=0; i < index; i++) { current = current.getNext(); } return current; }
// insert node at head publicvoidaddAtHead(E inData) { Node<E> newHead = newNode<>(inData); newHead.setNext(head); head = newHead; if (size == 0) { tail = head; } size++; }
// insert node at an index publicvoidaddAtIndex(E inData, int index) { if (index < 0 || index > size) { return; } if (index == 0) { addAtHead(inData); } elseif (index == size) { addAtTail(inData); } else { Node<E> newNode = newNode<>(inData); Node<E> current = head; for (inti=0; i < index - 1; i++) { current = current.getNext(); } newNode.setNext(current.getNext()); current.setNext(newNode); size++; } } // remove and return a node at head public E deleteAtHead() { if (size == 0) { returnnull; } Edata= head.getData(); head = head.getNext(); size--; if (size == 0) { tail = head; } return data; }
// remove and return a node at an index public E deleteAtIndex(int index) { Edata=null; if (size == 0) { returnnull; } if (index == 0) { return deleteAtHead(); } elseif (index >0 && index < size) { Node<E> current = head; for (inti=0; i < index - 1; i++) { current = current.getNext(); } data = current.getNext().getData(); current.setNext(current.getNext().getNext()); } size--; return data; }
// Node class privatestaticclassNode<E> { private E data; private Node<E> next;
publicNode() { data = null; next = null; }
publicNode(E data) { this.data = data; next = null; }
The main idea is move the node after the old head(as the new head) to the front of the list(as the head) one by one till the head become the tail of the list.
Java implementation:
1 2 3 4 5 6 7 8 9
publicvoidreverse() { Node<E> oldHead = this.head; // initial the oldHead to the head while (oldHead != null && oldHead.next != null) { // loop till lodHead become the tail Node<E> newHead = oldHead.next; // Set the newHead as the next of oldHead oldHead.next = newHead.next; // Set next node of the old one to new one's next newHead.next = this.head; // Main idea: Move the newHead to the front of the list, to become the current head this.head = newHead; // Let head point to the newHead } }
3 Circularly Linked List
Circularly Linked List is a special case of Singly Linked List, which the next node of the tail points back to the head of the list, rather than null;
NO maintain of the head reference;
Once we have the reference to the tail, tail.getNext() refers to the head of the list;
It saves a bit of memory storage;
And simplify the code and make it more efficient, since it no need to maintain an additional head reference;
3.1 Rotate a Circularly Linked List
Just let the current tail node point to its next node:
In the Singly Linked List, it is not efficient to insert and delete a arbitrary node except the head node, because we cannot determine the node that immediately precedes the arbitrary node, since we only know the next node of it.
Solution:
Based on the Singly Linked List, we add an additional reference in each node to point to the node before it, which is the Doubly Linked List.
Using next to point to the next node. Using prev to point to the previous node.
Java implementation of the Node in Doubly Linked List:
The two neighbors of the node to be deleted are linked directly to each other, thereby bypassing the original node.
Set the next of the prev node of deleted node to point to the deleted node’s next node;
Set the prev of the next node of deleted node to point to the deleted node’s prev node;
In the following example, the neighbors of the node 3, which are node 2 and 4, are linked directly to each other, so node 3 is skipped when traversing the list, seems that the node 3 is deleted.
public Node<E> getHead() { return header.getNext(); }
public Node<E> getTail() { return trailer.getPrev(); }
public String toString() { StringBuildersb=newStringBuilder(); Node<E> current = this.getHead(); while (current != trailer) { sb.append(current.getData() + " "); current = current.getNext(); } return sb.toString(); }
// Get a node at an index public Node<E> getAtIndex(int index) { if (index < 0 || index >= size) { returnnull; } Node<E> current = this.getHead(); for (inti=0; i < index; i++) { current = current.getNext(); } return current; }
// Add a node between two nodes publicvoidaddBetween(E data, Node<E> prev, Node<E> next) { Node<E> newNode = newNode<>(data); newNode.setPrev(prev); newNode.setNext(next); prev.setNext(newNode); next.setPrev(newNode); size++; }
// Add a node at the head of list publicvoidaddAtHead(E data) { addBetween(data, header, header.getNext()); }
// Add a node at the tail of list publicvoidaddAtTail(E data) { addBetween(data, trailer.getPrev(), trailer); }
// Add a node at an index of list publicvoidaddAtIndex(E data, int index) { if (index < 0 || index > size) { return; } if (index == 0) { addAtHead(data); } elseif (index == size) { addAtTail(data); } else { Node<E> prev = getAtIndex(index - 1); addBetween(data, prev, prev.getNext()); } }
// Remove a special node public Node<E> delete(Node<E> node) { if (node == null) { returnnull; } node.getPrev().setNext(node.getNext()); node.getNext().setPrev(node.getPrev()); size--; return node; }
// Remove the head node public Node<E> deleteAtHead() { if (isEmpty()) { returnnull; } return delete(header.getNext()); }
// Remove the tail node public Node<E> deleteAtTail() { if (isEmpty()) { returnnull; } return delete(trailer.getPrev()); }
// Remove a node at an index public Node<E> deleteAtIndex(int index) { if (index < 0 || index >= size) { returnnull; } if (index == 0) { return deleteAtHead(); } elseif (index == size - 1) { return deleteAtTail(); } else { Node<E> prev = getAtIndex(index - 1); return delete(prev.getNext()); } }
// Node class privatestaticclassNode<E> { private E data; private Node<E> next; private Node<E> prev;
publicNode() { data = null; next = null; prev = null; }
Goodrich, M., Tamassia, R., & O’reilly, A. (2014). Data Structures and Algorithms in Java, 6th Edition. John Wiley & Sons. Linked List - Explore - LeetCode
写在最后
Linked List 相关的知识会继续学习,继续更新. 最后,希望大家一起交流,分享,指出问题,谢谢!