QUICK SORT: Thuật toán sắp xếp nhanh

Ngày đăng: 08/09/2020   -    Cập nhật: 23/10/2020


Thuật toán Quick Sort (Sắp xếp nhanh) còn có một tên gọi khác là sắp xếp phân chia (Part Sort).

Nó được phát minh lần đầu bởi C.A.Hoare vào năm 1960. Có lẽ đây là thuật toán được nghiên cứu và sử dụng rộng rãi nhất trong các thuật toán sắp xếp.

Thuật toán Quick sort cũng là thuật toán đệ quy. Ngược với Mergesort gọi đệ quy rồi mới xử lý, Quick sort xử lý xong mới gọi đệ quy.



1. Thuật toán QUICK SORT là gì?


Thuật toán Quick sort

Thuật toán Quick sort

Thuật toán QUICK SORT giống như cái tên gọi của nó.

Ý tưởng của thuật toán này dựa trên phương pháp chia để trị, nghĩa là chia dãy cần sắp xếp thành 2 phần, sau đó thực hiện việc sắp xếp cho mỗi phần độc lập nhau.

Để làm việc này thì ta cần phải làm các bước sau:

Bước 1:

Chọn ngẫu nhiên một phần tử nào đó của dãy làm phần tử khóa (pivot).

Kĩ thuật chọn phần tử khóa rất quan trọng vì nếu không may bạn có thể bị rơi vào vòng lặp vô hạn đối với các trường hợp đặc biệt.

Tốt nhất là chọn phần tử ở vị trí trung tâm của dãy. Khi đó, sau log2(n) lần phân chia ta sẽ đạt tới kích thước danh sách bằng 1.

Tuy nhiên điều đó rất khó. Có các cách chọn phần tử khóa như sau:


  • Chọn phần tử đứng đầu hoặc đứng cuối làm phần tử khóa.
  • Chọn phần tử đứng giữa danh sách làm phần tử khóa.
  • Chọn phần tử trung gian trong 3 phần tử đứng đầu, đứng giữa và đứng cuối làm phần tử khóa.
  • Chọn phần tử ngẫu nhiên làm phần tử khóa. (Cách này có thể dẫn đến khả năng rơi vào các trường hợp đặc biệt)

Bước 2:

Xếp các phần tử nhỏ hơn phần tử chốt ở phía trước phần tử khóa.

Bước 3:

Xếp các phần tử lớn hơn phần tử chốt ở phía sau phần tử khóa.

Để có được sự phân loại này thì ở 2 bước trên, các phần tử sẽ được so sánh với khóa và hoán đổi vị trí cho nhau hoặc cho khóa nếu nó lớn hơn khóa mà lại nằm trước khóa, hoặc nhỏ hơn mà lại nằm sau khóa.

Áp dụng kĩ thuật như trên cho mỗi đoạn đó và tiếp tục làm vậy cho đến khi mỗi đoạn chỉ còn 2 phần tử. Khi đó toàn bộ dãy đã được sắp xếp.

Quick sort là một thuật toán dễ cài đặt, hiệu quả trong hầu hết các trường hợp và tiêu tốn ít tài nguyên hơn so với các thuật toán khác.

Độ phức tạp trung bình của giải thuật là O(NlogN).


2. Minh họa quá trình chia tách mảng


Có thể có nhiều cách để thực hiện phân vùng.

Logic rất đơn giản thôi, ta bắt đầu từ phần tử ngoài cùng bên trái và theo dõi chỉ số của các phần tử nhỏ hơn (hoặc bằng) là i.

Trong khi duyệt, nếu ta tìm thấy một phần tử nhỏ hơn, ta hoán đổi phần tử hiện tại với arr[i]. Nếu không, bỏ qua phần tử hiện tại.



quickSort(arr[], low, high) {
    if (low < high) {
        
        // pi  là chỉ mục, arr [pi] là vị trí của chốt       
        pi = partition(arr, low, high);

        quickSort(arr, low, pi - 1);  // trước pi
        quickSort(arr, pi + 1, high); // sau pi
    }
}
 


Dưới đây là một diễn giải thủ công:


1 arr[] = {10, 80, 30, 90, 40, 50, 70}
Các chỉ mục tương ứng:  0   1   2   3   4   5   6
2 low = 0, high =  6, pivot = arr[h] = 70
Khởi tạo chỉ mục ban đầu, i = -1
Di chuyển các phần tử từ j = low đến hight - 1
3 j = 0 : vì arr[j] <= pivot, i++ và swap(arr[i], arr[j])
i = 0
arr[] = {10, 80, 30, 90, 40, 50, 70}
==> Không thay đổi vì i và j tương tự nhau

 
4 j = 1 : vì arr[j] > pivot
==> Không làm gì cả - không có thay đổi ở i và arr[]

 
5 j = 2 : vì arr[j] <= pivot, i++ và swap(arr[i], arr[j])
i = 1
arr[] = {10, 30, 80, 90, 40, 50, 70}
==> Hoán đổi 80 và 30

 
6 j = 3 : vì arr[j] > pivot
==> không làm gì cả - không có thay đổi ở i và arr[]

 
7 j = 4 : vì arr[j] <= pivot, i++ và swap(arr[i], arr[j])
i = 2
arr[] = {10, 30, 40, 90, 80, 50, 70}
==> 80 và 40 được hoán đổi.

j = 5 : vì arr[j] <= pivot, à i++ và swap arr[i] với arr[j]
i = 3
arr[] = {10, 30, 40, 50, 80, 90, 70}
==> 90 và 50 được hoán đổi.

 
8 Đến đấy sẽ thoát ra khỏi vòng lặp vì j = high - 1
Cuối cùng, ta đặt chốt ở vị trí chính xác bằng cách hoán đổi arr[i+1] và arr[high] (hoặc pivot)
arr[] = {10, 30, 40, 50, 70, 90, 80}
==> 80 và 70 được hoán đổi

 
9 Bây giờ, 70 đã ở đúng vị trí của nó.
Các phần tử nhỏ hơn 70 đều ở trước và các phần tử lớn hơn đều ở sau nó.

 

3. Triển khai thuật toán Quick sort trong Java

Từ quá trình minh hoạ bên trên, ta hãy thử triển khai thuật toán Quick sort trong Java xem như thế nào nhé.

Có vẻ sẽ vất vả đây!!!



package quick.sort.algo;

public class QuickSort {

    // Hàm nhận phần tử cuối cùng làm chốt,
    // đặt các phần tử nhỏ hơn chốt ở trước
    // và lớn hơn ở sau nó
    int partition(int arr[], int lowint high) {
        int pivot = arr[high];
        int i = (low - 1); // index of smaller element
        for (int j = lowj < highj++) {

            // Nếu phần tử hiện tại nhỏ hơn chốt
            if (arr[j] < pivot) {
                i++;

                // swap arr[i] và arr[j]
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }

        // swap arr[i+1] và arr[high] (hoặc pivot)
        int temp = arr[i + 1];
        arr[i + 1] = arr[high];
        arr[high] = temp;

        return i + 1;
    }

    // arr[] --> Mảng cần được sắp xếp,
    // low --> chỉ mục bắt đầu,
    // high --> chỉ mục kết thúc
    void sort(int arr[], int lowint high) {
        if (low < high) {

            // pi là chỉ mục của chốt, arr[pi] vị trí của chốt
            int pi = partition(arrlowhigh);

            // Sắp xếp đệ quy các phần tử
            // trướcphân vùng và sau phân vùng
            sort(arrlowpi - 1);
            sort(arrpi + 1high);
        }
    }

    // In các phần tử của mảng
    static void printArray(int arr[]) {
        int n = arr.length;
        for (int i = 0i < n; ++i)
            System.out.print(arr[i] + " ");
        System.out.println();
    }

    public static void main(String args[]) {
        int arr[] = { 10803090405070 };
        int n = arr.length;
        
        System.out.println("Mảng ban đầu:");
        printArray(arr);

        QuickSort ob = new QuickSort();
        ob.sort(arr0n - 1);

        System.out.println("Mảng sau khi sắp xếp:");
        printArray(arr);
    }
}
 

Phù!!!

Bây giờ thì thử chạy nó xem nào.



Mảng ban đầu:
10 80 30 90 40 50 70
Mảng sau khi sắp xếp:
10 30 40 50 70 80 90
 

May quá, cũng ra kết quả đúng. :D

Thuật toán Quick sort chạy rất nhanh nhưng nó cũng có nhược điểm đó là: Không hiệu quả trên những dãy đã được sắp xếp sẵn.

Khi đó ta phải mất N lần gọi đệ quy và mỗi lần chỉ loại được 1 phần tử.

Thời gian thực hiện thuật toán trong trường hợp xấu nhất này là O(n2).

Trong trường hợp tốt nhất, mỗi lần phân chia sẽ được 2 nửa dãy bằng nhau, khi đó thời gian thực hiện thuật toán là: T(N) = 2T(N/2) + N

Khi đó T(N) xấp xỉ bằng NlogN.

Như vậy Quick sort là thuật toán hiệu quả trong đa số các trường hợp. Nhưng đối với các trường hợp việc sắp xếp chỉ phải thực hiện ít và lượng dữ liệu lớn thì chúng ta nên sử dụng một số thuật toán khác.

Đừng lo lắng nếu lần đầu bạn gặp thuật toán này mà chưa hiểu, kể cả có được giải thích kỹ càng như thế nào đi nữa.

Mình cũng vậy, nhưng hãy kiên trì suy nghĩ về nó, tự code lại thuật toán quick sort này nhiều lần. Dần dần bạn sẽ nắm vững thuật toán này mà thôi.

> Nếu bạn đang cảm thấy khó khăn khi học lập trình thì hãy tham khảo KHÓA HỌC LẬP TRÌNH FULL STACK này ngay để được học bài bản, vững chắc về lập trình. Đi làm ngon lành ngay sau 12 tháng.

> Tham khảo: Hãy thử trực quan hóa từng bước của thuật toán Quick sort tại đây.


---
HỌC VIỆN ĐÀO TẠO CNTT NIIT - ICT HÀ NỘI
Học Lập trình chất lượng cao (Since 2002). Học làm Lập trình viên. Hành động ngay!
Đc: Tầng 3, 25T2, N05, Nguyễn Thị Thập, Cầu Giấy, Hà Nội
SĐT: 02435574074 - 0914939543
Email: hello@niithanoi.edu.vn
Fanpage: https://facebook.com/NIIT.ICT/
 
#niit #niithanoi #niiticthanoi #hoclaptrinh #khoahoclaptrinh #hoclaptrinhjava #hoclaptrinhphp #python #java #php
Bình luận Facebook
Đăng ký tư vấn
Nhân viên gọi điện tư vấn miễn phí sau khi đăng ký
Được cập nhật các ưu đãi sớm nhất
Hotline: 0383180086
Tên không được để trống
Số điện thoại không được để trống
Email không được để trống
Hãy đăng ký để nhận những thông tin mới nhất về học bổng mới nhất tại NIIT - ICT Hà Nội
top
Đóng lại Đăng ký học tại NIIT - ICT Hà Nội
6260+ học viên đã theo học tại NIIT - ICT Hà Nội và có việc làm tốt trong ngành lập trình. Nắm lấy cơ hội ngay hôm nay!
Chọn khóa học
  • KHÓA HỌC LẬP TRÌNH FRONT END VỚI REACT.JS
  • KHÓA HỌC LẬP TRÌNH PHP WEB
  • Khóa học PHP Full stack [2023] cho người mới bắt đầu
  • Khóa học BIG DATA với Hadoop và Spark
  • Khóa học Lập trình Android tại Hà Nội
  • [Tuyển sinh 2023] Lập trình viên Quốc tế DigiNxt
  • Khóa học Tiền lương & Phúc lợi (C&B Excel) tại Hà Nội
  • LẬP TRÌNH GAME
    • Khóa học Lập trình Game Unity
  • LẬP TRÌNH WEB FRONT END
    • KHÓA HỌC PYTHON HƯỚNG ĐỐI TƯỢNG
    • KHÓA HỌC ANGULAR & TYPESCRIPT (FRONT END)
  • LẬP TRÌNH WEB BACK END
    • LẬP TRÌNH JAVA WEB VỚI FRAME WORK
    • Lập trình Web với Django
    • Lập trình PHP với Laravel Framework
  • CHƯƠNG TRÌNH ĐÀO TẠO ỨNG DỤNG CÔNG NGHỆ
    • Khóa học Tiền lương & Phúc lợi (C&B Excel) tại TP HCM
  • LẬP TRÌNH WEB FULL STACK
    • Khóa học Java Full stack (IJFD)
  • LẬP TRÌNH MOBILE
    • FRONT-END VỚI REACTJS VÀ REACT NATIVE
    • Lập trình Android Nâng cao
  • ĐÀO TẠO CHO DOANH NGHIỆP
    • KHÓA HỌC BUSINESS ANALYSIC TỪ CƠ BẢN ĐẾN NÂNG CAO 2023
    • Khóa học Magento: Làm chủ CMS TMĐT lớn nhất
    • Khóa học IOT: Xây dựng Sản phẩm IOT với Raspberry Pi
    • Khóa học Automation Testing Chuyên nghiệp
  • KHÓA HỌC DỰ ÁN
    • Học sử dụng bộ Office: Word, Excel, Power Point, Mail chuyên nghiệp
  • KHÓA HỌC KHÁC
    • VBA Excel Toàn Tập (Cơ Bản - Nâng Cao)
    • VBA Excel Nâng cao
    • Khóa học JMeter: Performance Testing
    • Khóa học Tester đạt chuẩn Quốc tế ISTQB Foundation Level
    • Khoá Học Tester đạt chuẩn quốc tế ISTQB Advanced Level
Bạn chưa chọn khóa học cần đăng ký
Tên không được để trống
Số điện thoại không được để trống
Email không được để trống
Đăng ký học thành công!
Cảm ơn bạn đã đăng ký học tại NIIT - ICT HÀ NỘI!