You are on page 1of 32

Class K63A2 {

Thành Viên Nhóm {

int Tạ Duy An = 18000948;


int Phạm Văn Thành = 18002000;
int Phan Duy Ánh = 18002001;
}

O S ALGORITH
R T I N G Giảng viên hướng dẫn {

M TS.Nguyễn Thị Hồng Minh;


TS.Phạm Huy Thông;
}
DATA STRUCTURE & ALGORITHMS }
Class K63A2 {
Thành Viên Nhóm {

int Tạ Duy An = 18000948;


int Phạm Văn Thành = 18002000;
int Phan Duy Ánh = 18002001;
}

O S ALGORITH
R T I N G Giảng viên hướng dẫn {

M TS.Nguyễn Thị Hồng Minh;


TS.Phạm Huy Thông;
}
DATA STRUCTURE & ALGORITHMS }
Class K63A2 {
Thành Viên Nhóm {

int Tạ Duy An = 18000948;


int Phạm Văn Thành = 18002000;
int Phan Duy Ánh = 18002001;
}

O S ALGORITH
R T I N G Giảng viên hướng dẫn {

M TS.Nguyễn Thị Hồng Minh;


TS.Phạm Huy Thông;
}
DATA STRUCTURE & ALGORITHMS }
1.Giới thiệu

Như ví dụ ở phần giới thiệu , chúng ta có thể thấy được sự quan trọng của sự sắp xếp

Sự sắp xếp giúp chúng ta hiểu được nội dung vấn đề, dễ dàng hơn
trong công việc
1.Giới thiệu

Trong cuộc sống hàng ngày các đối tượng luôn đuợc sắp xếp theo trật tự nào đó: Theo thời gian,
theo chiều cao, cận nặng, theo học lực, theo chất lượng v.v... Các tiêu chí sắp xếp đó là khoá của
sắp xếp.
1.Giới thiệu
Nhưng trong thực tế việc sắp xếp mất rất nhiều thời gian nhất là với những công việc sắp xếp bao gồm rất nhiều dữ
liệu lớn , hơn nữa việc sắp xếp thủ công còn rất dễ xảy ra sai sót nên cần 1 giải pháp nào đó cho công việc này ? ? ? ?
???

Vì vậy các giải thuật sắp xếp đã được phát minh ra , và các thuật
toán này hoàn toàn dựa vào ý tưởng của con người trong cuộc
sống hàng ngày.
2.Các thuật toán sắp xếp
2.1 thuật toán sắp xếp nổi bọt (bubble Sort)
Ý tưởng của thuật toán
• Bubble Sort là thuật toán sắp xếp đơn giản nhất hoạt động bằng cách hoán đổi nhiều lần các phần tử liền kề nếu chúng sai thứ tự.

Ví dụ về cài đặt thuật toán bằng mảng

Void bubbleSort(int[] arr) {


for(int i =0; I < arr.length - 1; i++) {
for(int j = 0; j < arr.length – I – 1; j++) {
if(arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}

}
}

Độ phức tạp và ưu nhược điểm của thuật toán:


• Trường hợp tốt : n
• Trường hợp xấu : n^2
• Ưu điểm : dễ triển khai thuật toán
• Nhược điểm : Kém hiệu quả khi thực hiện, hiệu xuất thấp
2.Các thuật toán sắp xếp
2.2 thuật toán sắp xếp chọn (seletionSort)
Thuật toán sắp xếp chọn sẽ sắp xếp một mảng bằng cách đi tìm phần tử có giá trị nhỏ nhất (giả sử với sắp xếp mảng tăng dần) trong đoạn đoạn
chưa được sắp xếp và đổi cho phần tử nhỏ nhất đó với phần tử ở đầu đoạn chưa được sắp xếp (không phải đầu mảng). Thuật toán sẽ chia mảng
làm 2 mảng con ,một mảng con đã được sắp xếp và một mảng con chưa được sắp xếp
Tại mỗi bước lặp của thuật toán, phần tử nhỏ nhất ở mảng con chưa được sắp xếp sẽ được di chuyển về đoạn đã sắp xếp.

Ví dụ về cài đặt thuật toán bằng mảng

void selectionSort(int[] arr){  
        for(int i = 0; i < arr.length - 1; i++)  
        {  
            int index = i;  
            for (int j = i + 1; j < arr.length; j++){  
                if (arr[j] < arr[index]){  
                    index = j;  
                }  
            }  
            int min= arr[index];   
            arr[index] = arr[i];  
            arr[i] = min;  
        }  
    }  

• Độ phức tạp thuật toán: O(n2) (là trường hợp trung bình) và cũng là trường hợp xấu

• Ưu điểm : dễ triển khai thuật toán, Thuật toán ít phải đổi chỗ các phần tử nhất trong số các thuật toán sắp xếp(n lần hoán vị)

• Nhược điểm : khi phân tích thuật toán trường hợp trung bình có cùng độ phức tạp với trường hợp xấu nhất
2.Các thuật toán sắp xếp
2.2 thuật toán sắp xếp chèn (insertionSort)
• Sắp xếp chèn (insertion sort) là một thuật toán sắp xếp bắt chước cách sắp xếp quân bài của những người chơi bài. Muốn
sắp một bộ bài theo trật tự người chơi bài rút lần lượt từ quân thứ 2, so với các quân đứng trước nó để chèn vào vị trí thích
hợp.
Ví dụ về cài đặt thuật toán bằng mảng

void insertionSort(int[] arr){  
        for(int i = 1; i < arr.length; ++i)  

             int key = arr[i];  
             int j= i – 1;
while(j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j -1;
}
arr[j + 1] = key;
        }  
    }  

Độ phức tạp và ưu nhược điểm của thuật toán


• Trường hợp tốt: O(n)
• Trung bình và xấu đều là: O(n^2)
• Ưu điểm : dễ triển khai thuật toán, Chạy nhanh khi mảng nhỏ hay được sắp xếp một phần
• Nhược điểm: Hiệu suất thấp
2.Các thuật toán sắp xếp
2.2 thuật toán sắp xếp nhanh (QuickSort)
• Thuật toán Quick Sort là một thuật toán sắp xếp, còn được gọi là sắp xếp kiểu phân chia (Part Sort). Là một thuật toán hiệu
quả dựa trên việc phân chia mảng dữ liệu thành các nhóm phần tử nhỏ hơn.
• Ý tưởng và cách thực thi thuật toán QuickSort
1. Chọn phần tử chốt (khóa).
(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 )
2. Chọn một phần tử để so sánh, gọi là phần tử Key (Pivot), từ trong mảng đầu tiên.
3. Phân vùng và sắp xếp mảng con trong phân vùng làm sao cho các phần tử lớn hơn từ phần tử Key nằm sau (bên phải) và các phần
tử bé hơn phần tử Key nằm trước (bên trái). Đây được gọi là quá trình phân vùng.
4. Cuối cùng là đệ quy sử dụng các bước trên cho các mảng với phần tử bé hơn và phân tách với các phần tử lớn hơn sau khi phân
vùng.

• Độ  phức tạp và ưu nhược điểm thuật


toán của quick sort
1. Trường hợp tốt: O(nlog(n))
2. Trung bình: O(nlog(n))
3. Trường hợp xấu: O(n^2)

4. Ưu điểm: Tuỳ cách chọn pivot mà tốc


độ của thuật toán nhanh hay chậm
5. Nhược điểm: Code khá phức tạp, với
dữ liệu nhỏ thì không nhanh bằng thuật
toán insertionSort
//code minh họa thuật toán quick sort
public static int partition(int arr[], int low, int high) {
int pivot = arr[high];
int i = (low - 1); // index of smaller element
for (int j = low; j < high; j++) {

if (arr[j] <= pivot) {


i++;
// swap arr[i] and arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// swap arr[i+1] and arr[high] (or pivot)
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;

return i + 1;
}
public static void quickSort(int arr[], int low, int high) {
if (low < high) {
/*
* pi is partitioning index, arr[pi] is now at right place
*/
int pi = partition(arr, low, high);

// Recursively sort elements before


// partition and after partition
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
2.Các thuật toán sắp xếp
2.3 thuật toán sắp xếp trộn (mergeSort)
• Thuật toán sắp xếp merge sort là một trong những thuật toán có độ phức tạp ở mức trung bình và cùng sử dùng phương
pháp chia để trị giống thuật toán sắp xếp nhanh quick sort.
Ý tưởng của thuật toán
• Giống như Quick sort, Merge sort là một thuật toán chia để trị. Thuật toán này chia mảng cần sắp xếp thành 2 nửa. Tiếp tục
lặp lại việc này ở các nửa mảng đã chia. Sau cùng gộp các nửa đó thành mảng đã sắp xếp. Hàm merge() được sử dụng để
gộp hai nửa mảng. Hàm merge(arr, l, m, r) là tiến trình quan trọng nhất sẽ gộp hai nửa mảng thành 1 mảng sắp xếp, các nửa
mảng là arr[l…m] và arr[m+1…r] sau khi gộp sẽ thành một mảng duy nhất đã sắp xếp.

Độ  phức tạp và ưu nhược điểm của thuật


toán

• Trường hợp tốt: O(nlog(n))


• Trung bình: O(nlog(n))
• Trường hợp xấu: O(nlog(n))
• Không gian bộ nhớ sử dụng: O(n)
• Ưu điểm: Hiệu suất của merge sort rất cao
• Nhược điểm: Code thuật toán này khá phức
tạp

ý tưởng triển khai code


public static void merge(int arr[], int beg, int mid, int end) {
int l = mid - beg + 1;
int r = end - mid;
int LeftArray[] = new int[l];
int RightArray[] = new int[r];

for (int i = 0; i < l; ++i)


LeftArray[i] = arr[beg + i];

for (int j = 0; j < r; ++j)


RightArray[j] = arr[mid + 1 + j];
int i = 0, j = 0;
int k = beg;
while (i < l && j < r) {
if (LeftArray[i] <= RightArray[j]) {
arr[k] = LeftArray[i];
i++;
} else {
arr[k] = RightArray[j];
j++;
}
k++;
}
while (i < l) {
arr[k] = LeftArray[i];
i++;
k++;
}
while (j < r) {
arr[k] = RightArray[j];
j++;
k++;
}
}
public static void mergeSort(int arr[], int beg, int end) {
if (beg < end) {
int mid = (beg + end) / 2;
mergeSort(arr, beg, mid);
mergeSort(arr, mid + 1, end);
merge(arr, beg, mid, end);
}
}
2.Các thuật toán sắp xếp
2.3 thuật toán sắp xếp vun đống (heapSort)

• Heapsort dựa trên một cấu trúc dữ liệu được gọi là đống nhị phân (binary heap), gọi đơn giản là đống.
Ý tưởng của thuật toán
• Khái niệm đống nhị phân: Mỗi mảng a[1..n] có thể xem như một cây nhị phân gần đầy
• vun đống:Việc sắp xếp lại các phần tử của một mảng ban đầu sao cho nó trở thành đống được gọi là vun đống.

Độ  phức tạp và ưu nhược điểm của thuật toán

• Trường hợp tốt: O(nlog(n))


• Trường hợp xấu: O(nlog(n))

• Ưu điểm: Hiệu suất của rất cao


• Nhược điểm: Code thuật toán này khá phức tạp

Thủ tục Heapify chỉ có thể được áp dụng cho một nút
nếu các nút con của nó được “vun đống”. Vì vậy việc
vun đống phải được thực hiện theo thứ tự từ dưới lên.
//code minh họa thuật toán heapSort
public static void heapSort(int arr[]) {
int n = arr.length;

// Build heap (rearrange array)


for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);

// One by one extract an element from heap


for (int i = n - 1; i > 0; i--) {
// Move current root to end
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;

// call max heapify on the reduced heap


heapify(arr, i, 0);
}
}

public static void heapify(int arr[], int n, int i) {


int largest = i; // Initialize largest as root
int l = 2 * i + 1; // left = 2*i + 1
int r = 2 * i + 2; // right = 2*i + 2

if (l < n && arr[l] > arr[largest])


largest = l;
if (r < n && arr[r] > arr[largest])
largest = r;

if (largest != i) {
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;

// Recursively heapify the affected sub-tree


heapify(arr, n, largest);
}
}
}
3. So sánh thời gian chạy của các thuật toán

Chúng ta có thể được cài đặt các thuật toán sắp xếp trên bằng các kiểu dữ liệu khác nhau, đó là các kiểu :

. Array: Với mảng trong java chúng ta chỉ có thể lưu trữ một tập các phần tử có số lượng phần tử cố định.Mảng trong java lưu
các phần tử theo chỉ số, chỉ số của phần tử đầu tiên là 0.
.ArrayList: là một lớp kế thừa lớp AbstractList và triển khai của List Interface trong Collections Framewor
.LinkedList: là một lớp kế thừa lớp AbstractSequentialList và triển khai của List
Ta thấy như bảng dưới thì thời gian cài đặt của các   thêm vào chèn Lấy ra xóa Set
kiểu dữ liệu khác nhau vì thế khi chúng ta sử dụng các
Array O(1) O(n) O(1) O(n) O(1)
kiểu dữ liệu khác nhau sẽ cho ra thời gian khác nhau
trên cùng một số phần tử cần sắp xếp Arraylist O(1) O(n) O(1) O(n) O(1)

Vd: việc cài đặt kiểu Array sẽ nhanh hơn kiểu LinkList O(1) O(n) O(n) O(n) O(n)
Arraylist bởi vì kích thước cố định còn thao tác thay
đổi kích thước trong arraylist làm chậm hiệu suất của

Vì Vậy chúng ta sẽ cùng xem biểu đồ thể hiện thời gian chạy của các thuật toán sắp xếp được cài đặt bằng các kiểu dữ
liệu khác nhau sau đây
Bảng số liệu thời gian khi chạy thuật toán sắp xếp cài đặt bằng Mảng
3. So sánh thời gian chạy của các thuật toán
13,6

THỜI GIAN CHẠY CÁC THUẬT TOÁN KHI CÀI ĐẶT BẰNG
2,43
MẢNG

3,31 1,13
11
0,6

0.0769

2 53 0,278
0.0
Time(s)

0.0146

644
0.00 0.0118
0.000613

0 3
.0 003 0.00281
0.000319 0
15
0.0002
0.0000658
87
0.0000555 0.00003 0.000133
0.000154
0.0000198 0.0000374
0.0000112
0 50 100 500 1000 5000 10000 50000 100000
Element size

BubbleSort InsertionSort SelectionSort QuickSort MergeSort heapSort


Bảng số liệu thời gian khi chạy ba thuật toán sắp xếp nhanh nhất được cài đặt bằng Mảng
3. So sánh thời gian chạy của các thuật toán

1,16
THỜI GIAN CHẠY CÁC THUẬT TOÁN KHI CÀI ĐẶT BẰNG
MẢNG

0,812

56
0,3
Time(s)

5
0,541

30
0,
53
0,1

48
0,1 04
718 0,2
0.0
04
0.07 8
0.01
12 0.097
6
0.012 27 0.0467
0.0000598 0.00537
0.000108 0.000912 0.008
0.00389
0.000117 0.00102 0.000657 0.00616
0 1000 10000 50000 100000 500000 1000000 2000000 5000000
Element size

QuickSort MergeSort heapSort


Bảng số liệu thời gian khi chạy thuật toán sắp xếp cài đặt bằng ArrayList
3. So sánh thời gian chạy của các thuật toán
52,3
THỜI GIAN CHẠY CÁC THUẬT TOÁN KHI CÀI ĐẶT BẰNG ARRAYLIST

,6
12
10
Time(s)

4,87
8
2,2

1,11

1
49

9
7

0,3
0. 0

0.0829
0.00254 0.00349 0.0188
0.000178 0.000568 0.00785 8
0.000398 0.00168 0. 030
0.0000847 0.000185 0.00105 0.00638 0.00757
0.0000539

0 50 100 500 1000 5000 10000 50000 100000


Element size

BubbleSort InsertionSort SelectionSort QuickSort MergeSort heapSort


Bảng số liệu thời gian khi chạy ba thuật toán sắp xếp nhanh nhất được cài đặt bằng ArrayList
3. So sánh thời gian chạy của các thuật toán

THỜI GIAN CHẠY CÁC THUẬT TOÁN KHI CÀI ĐẶT BẰNG 8,73

ARRAYLIST

5,25

3,72
Time(s)

3
2,1
5 8
0,9
2
0,84

0,265

22
5 8
0,3

0, 6
0,120
0 .0 5
3
0.024 0,2 70
0.0197 0.0680 0,121
0.000321 0.00316 0.0213
0.00168 0.0653
0.00525 0.00227
0.00038
0 1000 10000 50000 100000 500000 1000000 2000000 5000000
Element size

QuickSort MergeSort heapSort


Bảng số liệu thời gian khi chạy thuật toán sắp xếp cài đặt bằng LinkedList
3. So sánh thời gian chạy của các thuật toán

THỜI GIAN CHẠY CÁC THUẬT TOÁN KHI CÀI ĐẶT BẰNG
LINKEDLIST
Time(s)

0 10 50 100 500 1000 2000 3000 5000


Element size

selectionSort insertionSort bubbleSort QuickSort MergeSort heapSort


Bảng số liệu thời gian khi chạy ba thuật toán sắp xếp nhanh nhất được cài đặt bằng LinkedList
3. So sánh thời gian chạy của các thuật toán

THỜI GIAN CHẠY CÁC THUẬT TOÁN KHI CÀI ĐẶT BẰNG
LINKEDLIST
Time(s)

0 50 100 1000 5000 10000 50000 70000 100000


Element size

QuickSort MergeSort heapSort


Bảng số liệu thời gian so sánh khi chạy thuật toán sắp xếp cài đặt bằng Mảng, arraylist và linkedlist
3. So sánh thời gian chạy của các thuật toán
82,2
SO SÁNH THỜI GIAN CỦA 1 THUẬT TOÁN SẮP XẾP ĐƯỢC CÀI BẰNG BA KIỂU
DỮ LIỆU KHÁC NHAU
Time(s)

10,9
6
2.2
0.0137
0,522 5 24
0.00
0.00275 0.0121 0.00318 0.0055
0.000443 0.00124
0.00066
0.0000558 0.000155 0.000488
0.0000251
0 0.00000253 0.0000378 0.0000537 0.000117 0.000442 0.000801 0.00194

10 100 500 1000 5000 10000 20000 500000


Element size

Quick_Array Quick_ArrayList Quick_LinkedList


4. kết luận
Từ bảng số liệu em thấy rằng "Không có 1 bất kỳ thuật toán sắp xếp nào cụ thể cả, nó còn phụ thuộc vào nhiều yếu
tố" Và "phụ thuộc vào nhiều yếu tố" cũng là lý do mà có rất nhiều loại thuật toán sắp xếp khác nhau ra đời. Chúng
ta nhìn vào 1 vài ví dụ cụ thể dưới đây để thấy những yếu tố nào sẽ ảnh hưởng việc lựa chọn thuật toán
• Quick Sort sẽ là tốt nhất nếu ...

1. Không lo lắng về các case đầu vào kể cả trường hợp xấu nhất (trật tự nói chung là ngẫu nhiên)
2. Không quan tâm đến dung lượng bộ nhớ, bộ nhớ là hoàn toàn lý tưởng và phù hợp ở đây
• Nếu dữ liệu đã được sắp xếp sẵn, thì nên chọn Insertion Sort hoặc Shell Sort sẽ tốt hơn.
• Nếu chúng ta thực sự phải loại bỏ case xấu nhất, có thể sử dụng Heap (hoặc ít nhất là Quick3) với độ phức tạp NlogN
• Tim Sort sẽ có độ phức tạp thấp hơn Quick Sort ở cả Best Case lẫn Worse Case, Tim Sort là sự kết hợp của Merge Sort và
Insertion Sort. Python sử dụng thuật toán sắp xếp này là mặc định của họ
• Trong trường hợp, dữ liệu rất ít phần tử (10-20 phần tử), lựa chọn Selection Sort sẽ nhanh hơn Quick Sort

Tóm lại, về lý thuyết thì Quick Sort thật sự là thuật toán sắp xếp nhanh nhất trong phần lớn các trường hợp. Tuy
nhiên, trên thực tế, việc lựa chọn thuật toán sắp xếp dựa vào nhiều yếu tố như dữ liệu đầu vào số lượng như thế
nào, có sắp xếp sẵn hay không, dung lượng bộ nhớ ra sao, tốc độ xử lý CPU...
Cảm ơn thầy , cô và các bạn đã lắng nghe bài thuyết trình của nhóm em

You might also like