Chapter 2: Linked List
Foundamentals
Storage
链表中的节点在内存中不是连续分配的
Definition
struct ListNode
{
int val;
ListNode* next;
ListNode(int x): val(x), next(nullptr) {}
}
initialize a node
# way 1
ListNode* head = new ListNode();
head->val = 1;
# way 2
ListNode* head = new ListNode(1);
Time Complexity
insert/delete | access | |
---|---|---|
Array | O(n) | O(1) |
LinkedList | O(1) | O(n) |
Leetcodes
Leetcode 203 Remove LinkedList Elements
Given the head of a linked list and an integer val, remove all the nodes of the linked list that has Node.val == val, and return the new head.
Example 1:
Input: head = [1,2,6,3,4,5,6], val = 6
Output: [1,2,3,4,5]
Example 2:
Input: head = [], val = 1
Output: []
Example 3:
Input: head = [7,7,7,7], val = 7
Output: []
Solutions:
two approaches:
- double pointers: current and previous
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if (head == nullptr)
{
return head;
}
// handle head
while(head && head->val == val)
{
head = head->next;
}
// use double pointers: current and previous
ListNode* ptr = head, * pptr = head;
while (ptr)
{
if (ptr->val == val)
{
pptr->next = ptr->next;
}
else
{
pptr = ptr;
}
ptr = ptr->next;
}
return head;
}
};
- single pointer:
Note:
after ptr->next = ptr->next->next, Do Not Update!!!
in case: 1->2->2->3, val = 2
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if (head == nullptr)
{
return head;
}
// handle head
while(head && head->val == val)
{
head = head->next;
}
// use single pointer
ListNode* ptr = head;
while (ptr && ptr->next)
{
if (ptr->next->val == val)
{
ptr->next = ptr->next->next;
// after doing this, do not update the ptr, in case the following situation occurs:
// 1->2->2->3, val = 2
}
else
{
ptr = ptr->next;
}
}
return head;
}
};
Leetcode 707 Design LinkedList
Design your implementation of the linked list. You can choose to use a singly or doubly linked list.
A node in a singly linked list should have two attributes: val and next. val is the value of the current node, and next is a pointer/reference to the next node.
If you want to use the doubly linked list, you will need one more attribute prev to indicate the previous node in the linked list. Assume all nodes in the linked list are 0-indexed.
Implement the MyLinkedList class:
MyLinkedList() Initializes the MyLinkedList object.
int get(int index) Get the value of the indexth node in the linked list. If the index is invalid, return -1.
void addAtHead(int val) Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
void addAtTail(int val) Append a node of value val as the last element of the linked list.
void addAtIndex(int index, int val) Add a node of value val before the indexth node in the linked list. If index equals the length of the linked list, the node will be appended to the end of the linked list. If index is greater than the length, the node will not be inserted.
void deleteAtIndex(int index) Delete the indexth node in the linked list, if the index is valid.
Example 1:
Input
[“MyLinkedList”, “addAtHead”, “addAtTail”, “addAtIndex”, “get”, “deleteAtIndex”, “get”]
[[], [1], [3], [1, 2], [1], [1], [1]]
Output
[null, null, null, null, 2, null, 3]
Explanation
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2); // linked list becomes 1->2->3
myLinkedList.get(1); // return 2
myLinkedList.deleteAtIndex(1); // now the linked list is 1->3
myLinkedList.get(1); // return 3
class MyLinkedList {
public:
struct ListNode
{
int val;
ListNode* next;
ListNode(int val): val(val), next(nullptr){}
};
ListNode* head;
int size;
// Constructor
MyLinkedList() {
head = nullptr;
size = 0;
}
int get(int index) {
if (index >= size)
{
return -1;
}
ListNode* ptr = head;
while (index)
{
ptr = ptr->next;
index --;
}
return ptr->val;
}
void addAtHead(int val) {
addAtIndex(0, val);
}
void addAtTail(int val) {
addAtIndex(size, val);
}
void addAtIndex(int index, int val) {
// if index == length of linked list, append at tail
if (index > size)
{
return;
}
ListNode* ptr = head, * pptr = head;
if (index == 0) // insert at head
{
ListNode* new_head = new ListNode(val);
new_head->next = head;
head = new_head;
size ++;
return;
}
while (index)
{
index --;
pptr = ptr;
ptr = ptr->next;
}
pptr->next = new ListNode(val);
pptr->next->next = ptr;
size ++;
}
void deleteAtIndex(int index) {
// invalid index
if (index >= size)
{
return;
}
ListNode* ptr = head, * pptr = head;
if (index == 0) // insert at head
{
head = head->next;
size --;
return;
}
while (index)
{
index --;
pptr = ptr;
ptr = ptr->next;
}
pptr->next = ptr->next;
size --;
}
};
Leetcode 206 Reverse LinkedList
Given the head of a singly linked list, reverse the list, and return the reversed list.
Example 1:
Input: head = [1,2,3,4,5]
Output: [5,4,3,2,1]
Example 2:
Input: head = [1,2]
Output: [2,1]
Example 3:
Input: head = []
Output: []
Solutions:
2 approaches:
- recursive: start from the last function call to construct a new list, insert at tail
class Solution {
public:
ListNode* reverseList(ListNode* head) {
// recursive
if (head == nullptr || head->next == nullptr)
{
return head;
}
ListNode* tmp = head->next;
head->next = nullptr;
ListNode* new_head = reverseList(tmp);
ListNode* ptr = new_head;
while (ptr->next)
{
ptr = ptr->next;
}
ptr->next = head;
return new_head;
}
};
another recursive way:
First reverse all nodes after head, then set head to be the final node in the reversed list. To do that, we just set its next node in the original list(head->next
) to point to it and set its next
to nullptr
ListNode* reverseList(ListNode* head)
{
if (head == nullptr || head->next == nullptr)
{
return head;
}
ListNode* node = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return node;
}
- iterative: insert at head
class Solution {
public:
ListNode* reverseList(ListNode* head) {
// iterative
if (head == nullptr || head->next == nullptr)
{
return head;
}
ListNode* new_head = head;
head = head->next;
new_head->next = nullptr;
while (head)
{
ListNode* tmp = head;
head = head->next;
tmp->next = new_head;
new_head = tmp;
}
return new_head;
}
};