Chào ace, bài này chúng ta sẽ tìm hiểu về Đảo ngược một linked list(Danh sách liên kết đơn) trong series tự học về cấu trúc dữ liệu và giải thuật, sau đây cafedev sẽ giới thiệu và chia sẻ chi tiết(khái niệm, độ phức tạp, ứng dụng của nó, code ví dụ…) về nó thông qua các phần sau.

Trong bài học này, chúng ta sẽ được cho trước một con trỏ trỏ đến node head của một linked list, nhiệm vụ cần làm là hãy đảo ngược lại linked list này. Bạn thấy đó, để có thể đảo ngược được linked list, chúng ta sẽ cần phải thay đổi các liên kết giữa các nodes.

Ví dụ:

Input: con trỏ Head của linked list sau đây

1->2->3->4->NULL

Output: Linked list cần phải được thay đổi thành,

4->3->2->1->NULL

Input: con trỏ Head của linked list sau đây

1->2->3->4->5->NULL

Output: Linked list cần phải được thay đổi thành,

5->4->3->2->1->NULL

Input: NULL

Output: NULL

Input: 1->NULL

Output: 1->NULL

1. Đảo ngược linked list bằng cách sử dụng vòng lặp

1. Khởi tạo ba con trỏ: prev bằng với NULL, curr bằng với head, và next bằng với NULL.

2. Lặp/duyệt qua toàn bộ linked list. Bên trong vòng lặp, làm những việc sau:

// Trước khi thay đổi con trỏ next của con trỏ current,

// hãy lưu lại node next

next = curr -> next

// Bây giờ, thay đổi con trỏ next của con trỏ current

// Đây là nơi mà sự đảo ngược diễn ra

curr -> next = prev

// Di chuyển con trỏ prev và con trỏ curr một bước về phía trước

prev = curr

curr = next

Sau đây sẽ là đoạn code cài đặt cái thuật toán đảo ngược một linked list sử dụng vòng lặp đã nêu ở trên, được viết bằng nhiều ngôn ngữ lập trình:

CODE VÍ DỤ ĐƯỢC VIẾT BẰNG C++ C Java Python C#

C


// Iterative C program to reverse a linked list 
#include <stdio.h> 
#include <stdlib.h> 
  
/* Link list node */
struct Node { 
    int data; 
    struct Node* next; 
}; 
  
/* Function to reverse the linked list */
static void reverse(struct Node** head_ref) 
{ 
    struct Node* prev = NULL; 
    struct Node* current = *head_ref; 
    struct Node* next = NULL; 
    while (current != NULL) { 
        // Store next 
        next = current->next; 
  
        // Reverse current node's pointer 
        current->next = prev; 
  
        // Move pointers one position ahead. 
        prev = current; 
        current = next; 
    } 
    *head_ref = prev; 
} 
  
/* Function to push a node */
void push(struct Node** head_ref, int new_data) 
{ 
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node)); 
    new_node->data = new_data; 
    new_node->next = (*head_ref); 
    (*head_ref) = new_node; 
} 
  
/* Function to print linked list */
void printList(struct Node* head) 
{ 
    struct Node* temp = head; 
    while (temp != NULL) { 
        printf("%d  ", temp->data); 
        temp = temp->next; 
    } 
} 
  
/* Driver program to test above function*/
int main() 
{ 
    /* Start with the empty list */
    struct Node* head = NULL; 
  
    push(&head, 20); 
    push(&head, 4); 
    push(&head, 15); 
    push(&head, 85); 
  
    printf("Given linked list\n"); 
    printList(head); 
    reverse(&head); 
    printf("\nReversed Linked list \n"); 
    printList(head); 
    getchar(); 
} 

C++


// Iterative C++ program to reverse 
// a linked list 
#include <iostream> 
using namespace std; 
  
/* Link list node */
struct Node { 
    int data; 
    struct Node* next; 
    Node(int data) 
    { 
        this->data = data; 
        next = NULL; 
    } 
}; 
  
struct LinkedList { 
    Node* head; 
    LinkedList() 
    { 
        head = NULL; 
    } 
  
    /* Function to reverse the linked list */
    void reverse() 
    { 
        // Initialize current, previous and 
        // next pointers 
        Node* current = head; 
        Node *prev = NULL, *next = NULL; 
  
        while (current != NULL) { 
            // Store next 
            next = current->next; 
  
            // Reverse current node's pointer 
            current->next = prev; 
  
            // Move pointers one position ahead. 
            prev = current; 
            current = next; 
        } 
        head = prev; 
    } 
  
    /* Function to print linked list */
    void print() 
    { 
        struct Node* temp = head; 
        while (temp != NULL) { 
            cout << temp->data << " "; 
            temp = temp->next; 
        } 
    } 
  
    void push(int data) 
    { 
        Node* temp = new Node(data); 
        temp->next = head; 
        head = temp; 
    } 
}; 
  
/* Driver program to test above function*/
int main() 
{ 
    /* Start with the empty list */
    LinkedList ll; 
    ll.push(20); 
    ll.push(4); 
    ll.push(15); 
    ll.push(85); 
  
    cout << "Given linked list\n"; 
    ll.print(); 
  
    ll.reverse(); 
  
    cout << "\nReversed Linked list \n"; 
    ll.print(); 
    return 0; 
} 

Java

// Java program for reversing the linked list 
  
class LinkedList { 
  
    static Node head; 
  
    static class Node { 
  
        int data; 
        Node next; 
  
        Node(int d) 
        { 
            data = d; 
            next = null; 
        } 
    } 
  
    /* Function to reverse the linked list */
    Node reverse(Node node) 
    { 
        Node prev = null; 
        Node current = node; 
        Node next = null; 
        while (current != null) { 
            next = current.next; 
            current.next = prev; 
            prev = current; 
            current = next; 
        } 
        node = prev; 
        return node; 
    } 
  
    // prints content of double linked list 
    void printList(Node node) 
    { 
        while (node != null) { 
            System.out.print(node.data + " "); 
            node = node.next; 
        } 
    } 
  
    public static void main(String[] args) 
    { 
        LinkedList list = new LinkedList(); 
        list.head = new Node(85); 
        list.head.next = new Node(15); 
        list.head.next.next = new Node(4); 
        list.head.next.next.next = new Node(20); 
  
        System.out.println("Given Linked list"); 
        list.printList(head); 
        head = list.reverse(head); 
        System.out.println(""); 
        System.out.println("Reversed linked list "); 
        list.printList(head); 
    } 
} 

Python

# Python program to reverse a linked list  
# Time Complexity : O(n) 
# Space Complexity : O(1) 
  
# Node class  
class Node: 
  
    # Constructor to initialize the node object 
    def __init__(self, data): 
        self.data = data 
        self.next = None
  
class LinkedList: 
  
    # Function to initialize head 
    def __init__(self): 
        self.head = None
  
    # Function to reverse the linked list 
    def reverse(self): 
        prev = None
        current = self.head 
        while(current is not None): 
            next = current.next
            current.next = prev 
            prev = current 
            current = next
        self.head = prev 
          
    # Function to insert a new node at the beginning 
    def push(self, new_data): 
        new_node = Node(new_data) 
        new_node.next = self.head 
        self.head = new_node 
  
    # Utility function to print the linked LinkedList 
    def printList(self): 
        temp = self.head 
        while(temp): 
            print temp.data, 
            temp = temp.next
  
  
# Driver program to test above functions 
llist = LinkedList() 
llist.push(20) 
llist.push(4) 
llist.push(15) 
llist.push(85) 
  
print "Given Linked List"
llist.printList() 
llist.reverse() 
print "\nReversed Linked List"
llist.printList() 

C#

// C# program for reversing the linked list 
using System; 
  
class GFG { 
    static void Main(string[] args) 
    { 
        LinkedList list = new LinkedList(); 
        list.AddNode(new LinkedList.Node(85)); 
        list.AddNode(new LinkedList.Node(15)); 
        list.AddNode(new LinkedList.Node(4)); 
        list.AddNode(new LinkedList.Node(20)); 
  
        // List before reversal 
        Console.WriteLine("Given linked list:"); 
        list.PrintList(); 
  
        // Reverse the list 
        list.ReverseList(); 
  
        // List after reversal 
        Console.WriteLine("Reversed linked list:"); 
        list.PrintList(); 
    } 
} 
  
class LinkedList { 
    Node head; 
  
    public class Node { 
        public int data; 
        public Node next; 
  
        public Node(int d) 
        { 
            data = d; 
            next = null; 
        } 
    } 
  
    // function to add a new node at 
    // the end of the list 
    public void AddNode(Node node) 
    { 
        if (head == null) 
            head = node; 
        else { 
            Node temp = head; 
            while (temp.next != null) { 
                temp = temp.next; 
            } 
            temp.next = node; 
        } 
    } 
  
    // function to reverse the list 
    public void ReverseList() 
    { 
        Node prev = null, current = head, next = null; 
        while (current != null) { 
            next = current.next; 
            current.next = prev; 
            prev = current; 
            current = next; 
        } 
        head = prev; 
    } 
  
    // function to print the list data 
    public void PrintList() 
    { 
        Node current = head; 
        while (current != null) { 
            Console.Write(current.data + " "); 
            current = current.next; 
        } 
        Console.WriteLine(); 
    } 
} 

Kết quả in ra là:

Given linked list
85 15 4 20 
Reversed Linked list 
20 4 15 85 

Độ phức tạp về thời gian: O (n)
Độ phức tạp không gian: O (1)

2. Đảo ngược linked list bằng cách sử dụng đệ quy

1. Chia linked list thành hai phần – node đầu tiên và phần còn lại của linked list

2. Gọi đến hàm reverse cho phần còn lại của linked list

3. Kết nối phần còn lại của linked list với node đầu tiên của linked list

4. Thay đổi con trỏ head

Sau đây sẽ là đoạn code cài đặt cái thuật toán đảo ngược một linked list sử dụng đệ quy đã nêu ở trên, được viết bằng nhiều ngôn ngữ lập trình:

ĐOẠN CODE VÍ DỤ ĐƯỢC VIẾT BẰNG C++ Java Python3

C++


// Recursive C++ program to reverse 
// a linked list 
#include <iostream> 
using namespace std; 
  
/* Link list node */
struct Node { 
    int data; 
    struct Node* next; 
    Node(int data) 
    { 
        this->data = data; 
        next = NULL; 
    } 
}; 
  
struct LinkedList { 
    Node* head; 
    LinkedList() 
    { 
        head = NULL; 
    } 
  
    Node* reverse(Node* head) 
    { 
        if (head == NULL || head->next == NULL) 
            return head; 
  
        /* reverse the rest list and put  
          the first element at the end */
        Node* rest = reverse(head->next); 
        head->next->next = head; 
  
        /* tricky step -- see the diagram */
        head->next = NULL; 
  
        /* fix the head pointer */
        return rest; 
    } 
  
    /* Function to print linked list */
    void print() 
    { 
        struct Node* temp = head; 
        while (temp != NULL) { 
            cout << temp->data << " "; 
            temp = temp->next; 
        } 
    } 
  
    void push(int data) 
    { 
        Node* temp = new Node(data); 
        temp->next = head; 
        head = temp; 
    } 
}; 
  
/* Driver program to test above function*/
int main() 
{ 
    /* Start with the empty list */
    LinkedList ll; 
    ll.push(20); 
    ll.push(4); 
    ll.push(15); 
    ll.push(85); 
  
    cout << "Given linked list\n"; 
    ll.print(); 
  
    ll.head = ll.reverse(ll.head); 
  
    cout << "\nReversed Linked list \n"; 
    ll.print(); 
    return 0; 
} 

Java

// Recursive Java program to reverse 
// a linked list 
class recursion {  
    static Node head; // head of list  
      
    static class Node {  
        int data;  
        Node next;  
        Node(int d)  
        {  
            data = d;  
            next = null;  
        }  
    }  
  
    static Node reverse(Node head)  
    {  
        if (head == null || head.next == null)  
            return head;  
  
        /* reverse the rest list and put  
        the first element at the end */
        Node rest = reverse(head.next);  
        head.next.next = head;  
  
        /* tricky step -- see the diagram */
        head.next = null;  
  
        /* fix the head pointer */
        return rest;  
    }  
  
    /* Function to print linked list */
    static void print()  
    {  
        Node temp = head;  
        while (temp != null) {  
            System.out.print(temp.data + " ");  
            temp = temp.next;  
        }  
        System.out.println(); 
    }  
  
    static void push(int data)  
    {  
        Node temp = new Node(data);  
        temp.next = head;  
        head = temp;  
    }  
   
  
/* Driver program to test above function*/
public static void main(String args[])  
{  
    /* Start with the empty list */
       
    push(20);  
    push(4);  
    push(15);  
    push(85);  
  
    System.out.println("Given linked list");  
    print();  
  
    head = reverse(head);  
  
    System.out.println("Reversed Linked list");  
    print();  
} 
} 

Python3

"""Python3 program to reverse linked list 
using recursive method"""
  
# Linked List Node 
class Node: 
    def __init__(self, data): 
        self.data = data 
        self.next = None
  
# Create and Handle list operations 
class LinkedList: 
    def __init__(self): 
        self.head = None # Head of list 
  
    # Method to reverse the list 
    def reverse(self, head): 
  
        # If head is empty or has reached the list end 
        if head is None or head.next is None: 
            return head 
  
        # Reverse the rest list 
        rest = self.reverse(head.next) 
  
        # Put first element at the end 
        head.next.next = head 
        head.next = None
  
        # Fix the header pointer 
        return rest 
  
    # Returns the linked list in display format 
    def __str__(self): 
        linkedListStr = "" 
        temp = self.head 
        while temp: 
            linkedListStr = (linkedListStr + 
                            str(temp.data) + " ") 
            temp = temp.next
        return linkedListStr 
  
    # Pushes new data to the head of the list 
    def push(self, data): 
        temp = Node(data) 
        temp.next = self.head 
        self.head = temp 
  
# Driver code 
linkedList = LinkedList() 
linkedList.push(20) 
linkedList.push(4) 
linkedList.push(15) 
linkedList.push(85) 
  
print("Given linked list") 
print(linkedList) 
  
linkedList.head = linkedList.reverse(linkedList.head) 
  
print("Reversed linked list") 
print(linkedList) 

Kết quả in ra là:

Given linked list
85 15 4 20 
Reversed Linked list
20 4 15 85 

Độ phức tạp về thời gian: O(n)

Độ phức tạp về không gian: O(1)

3. Đảo ngược linked list một cách đơn giản hơn bằng Tail Recursive – đệ quy đuôi

Dưới đây sẽ là phần code cài đặt cho cách làm này:

ĐOẠN CODE VÍ DỤ ĐƯỢC VIẾT BẰNG C++ Java Python3 C#

C++


// A simple and tail recursive C++ program to reverse 
// a linked list 
#include <bits/stdc++.h> 
using namespace std; 
  
struct Node { 
    int data; 
    struct Node* next; 
}; 
  
void reverseUtil(Node* curr, Node* prev, Node** head); 
  
// This function mainly calls reverseUtil() 
// with prev as NULL 
void reverse(Node** head) 
{ 
    if (!head) 
        return; 
    reverseUtil(*head, NULL, head); 
} 
  
// A simple and tail recursive function to reverse 
// a linked list.  prev is passed as NULL initially. 
void reverseUtil(Node* curr, Node* prev, Node** head) 
{ 
    /* If last node mark it head*/
    if (!curr->next) { 
        *head = curr; 
  
        /* Update next to prev node */
        curr->next = prev; 
        return; 
    } 
  
    /* Save curr->next node for recursive call */
    Node* next = curr->next; 
  
    /* and update next ..*/
    curr->next = prev; 
  
    reverseUtil(next, curr, head); 
} 
  
// A utility function to create a new node 
Node* newNode(int key) 
{ 
    Node* temp = new Node; 
    temp->data = key; 
    temp->next = NULL; 
    return temp; 
} 
  
// A utility function to print a linked list 
void printlist(Node* head) 
{ 
    while (head != NULL) { 
        cout << head->data << " "; 
        head = head->next; 
    } 
    cout << endl; 
} 
  
// Driver program to test above functions 
int main() 
{ 
    Node* head1 = newNode(1); 
    head1->next = newNode(2); 
    head1->next->next = newNode(3); 
    head1->next->next->next = newNode(4); 
    head1->next->next->next->next = newNode(5); 
    head1->next->next->next->next->next = newNode(6); 
    head1->next->next->next->next->next->next = newNode(7); 
    head1->next->next->next->next->next->next->next = newNode(8); 
    cout << "Given linked list\n"; 
    printlist(head1); 
    reverse(&head1); 
    cout << "\nReversed linked list\n"; 
    printlist(head1); 
    return 0; 
} 

Java

// Java program for reversing the Linked list 
  
class LinkedList { 
  
    static Node head; 
  
    static class Node { 
  
        int data; 
        Node next; 
  
        Node(int d) 
        { 
            data = d; 
            next = null; 
        } 
    } 
  
    // A simple and tail recursive function to reverse 
    // a linked list.  prev is passed as NULL initially. 
    Node reverseUtil(Node curr, Node prev) 
    { 
  
        /* If last node mark it head*/
        if (curr.next == null) { 
            head = curr; 
  
            /* Update next to prev node */
            curr.next = prev; 
  
            return head; 
        } 
  
        /* Save curr->next node for recursive call */
        Node next1 = curr.next; 
  
        /* and update next ..*/
        curr.next = prev; 
  
        reverseUtil(next1, curr); 
        return head; 
    } 
  
    // prints content of double linked list 
    void printList(Node node) 
    { 
        while (node != null) { 
            System.out.print(node.data + " "); 
            node = node.next; 
        } 
    } 
  
    public static void main(String[] args) 
    { 
        LinkedList list = new LinkedList(); 
        list.head = new Node(1); 
        list.head.next = new Node(2); 
        list.head.next.next = new Node(3); 
        list.head.next.next.next = new Node(4); 
        list.head.next.next.next.next = new Node(5); 
        list.head.next.next.next.next.next = new Node(6); 
        list.head.next.next.next.next.next.next = new Node(7); 
        list.head.next.next.next.next.next.next.next = new Node(8); 
  
        System.out.println("Original Linked list "); 
        list.printList(head); 
        Node res = list.reverseUtil(head, null); 
        System.out.println(""); 
        System.out.println(""); 
        System.out.println("Reversed linked list "); 
        list.printList(res); 
    } 
} 
  

Python

# Simple and tail recursive Python program to  
# reverse a linked list 
  
# Node class  
class Node: 
  
    # Constructor to initialize the node object 
    def __init__(self, data): 
        self.data = data 
        self.next = None
  
class LinkedList: 
  
    # Function to initialize head 
    def __init__(self): 
        self.head = None
  
  
    def reverseUtil(self, curr, prev): 
          
        # If last node mark it head 
        if curr.next is None : 
            self.head = curr  
              
            # Update next to prev node 
            curr.next = prev 
            return 
          
        # Save curr.next node for recursive call 
        next = curr.next
  
        # And update next  
        curr.next = prev 
      
        self.reverseUtil(next, curr)  
  
  
    # This function mainly calls reverseUtil() 
    # with previous as None 
    def reverse(self): 
        if self.head is None: 
            return 
        self.reverseUtil(self.head, None) 
  
  
    # Function to insert a new node at the beginning 
    def push(self, new_data): 
        new_node = Node(new_data) 
        new_node.next = self.head 
        self.head = new_node 
  
    # Utility function to print the linked LinkedList 
    def printList(self): 
        temp = self.head 
        while(temp): 
            print temp.data, 
            temp = temp.next
  
  
# Driver program 
llist = LinkedList() 
llist.push(8) 
llist.push(7) 
llist.push(6) 
llist.push(5) 
llist.push(4) 
llist.push(3) 
llist.push(2) 
llist.push(1) 
  
print "Given linked list"
llist.printList() 
  
llist.reverse() 
  
print "\nReverse linked list"
llist.printList() 

C#

// C# program for reversing the Linked list 
using System; 
  
public class LinkedList { 
    Node head; 
    public class Node { 
  
        public int data; 
        public Node next; 
  
        public Node(int d) 
        { 
            data = d; 
            next = null; 
        } 
    } 
  
    // A simple and tail recursive function to reverse 
    // a linked list. prev is passed as NULL initially. 
    Node reverseUtil(Node curr, Node prev) 
    { 
  
        /* If last node mark it head*/
        if (curr.next == null) { 
            head = curr; 
  
            /* Update next to prev node */
            curr.next = prev; 
  
            return head; 
        } 
  
        /* Save curr->next node for recursive call */
        Node next1 = curr.next; 
  
        /* and update next ..*/
        curr.next = prev; 
  
        reverseUtil(next1, curr); 
        return head; 
    } 
  
    // prints content of double linked list 
    void printList(Node node) 
    { 
        while (node != null) { 
            Console.Write(node.data + " "); 
            node = node.next; 
        } 
    } 
  
    // Driver code 
    public static void Main(String[] args) 
    { 
        LinkedList list = new LinkedList(); 
        list.head = new Node(1); 
        list.head.next = new Node(2); 
        list.head.next.next = new Node(3); 
        list.head.next.next.next = new Node(4); 
        list.head.next.next.next.next = new Node(5); 
        list.head.next.next.next.next.next = new Node(6); 
        list.head.next.next.next.next.next.next = new Node(7); 
        list.head.next.next.next.next.next.next.next = new Node(8); 
  
        Console.WriteLine("Original Linked list "); 
        list.printList(list.head); 
        Node res = list.reverseUtil(list.head, null); 
        Console.WriteLine(""); 
        Console.WriteLine(""); 
        Console.WriteLine("Reversed linked list "); 
        list.printList(res); 
    } 
} 

Kết quả in ra là:

Given linked list
1 2 3 4 5 6 7 8

Reversed linked list
8 7 6 5 4 3 2 1

Nguồn và Tài liệu tiếng anh tham khảo:

Tài liệu từ cafedev:

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!