Tải bản đầy đủ (.pdf) (17 trang)

Về các thuật toán và độ phức tạp của chúng

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.66 MB, 17 trang )

<span class="text_page_counter">Trang 1</span><div class="page_container" data-page="1">

ĐẠI HỌC QUỐC GIA THÀNH PHỐ HỒ CHÍ MINH TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN

KHOA CÔNG NGH THÔNG TIN Ệ

NGUYỄN VĂN TRÍ - 21120345

MƠN H C: C U TRÚC DỌ Ấ Ữ LIỆU VÀ GI I THU T Ả Ậ GIẢNG VIÊN:NGUYỄN TR N DUY MINH Ầ

Thành ph H Chí Minh - 2022ố ồ

BÁO CÁO

</div><span class="text_page_counter">Trang 2</span><div class="page_container" data-page="2">

MỤC L C Ụ

<small>MỤC LỤ ... 1 C</small>

<small>I. VỀ CÁC THUẬT TOÁN VÀ ĐỘ PHỨC TẠP CỦA CHÚNG. ... 2 </small>

<small>1.1.Thuật toán Selection Sort ... 2 </small>

<small>1.2.Thuật toán Insertion Sort ... 2 </small>

<small>1.3.Thuật toán Bubble Sort ... 3 </small>

<small>1.4.Thuật toán Shell Sort ... 4 </small>

<small>1.5.Thuật toán Heap Sort ... 5 </small>

<small>1.6.Thuật toán Merge Sort ... 7 </small>

<small>1.7.Thuật toán Quick Sort ... 9 </small>

<small>1.8.Thuật toán Radix Sort ... 10 </small>

<small>II. THỰC NGHI M CÁC THU T TOÁNỆẬ ... 13</small>

<small>1. Đối v i các m ng s p x p ng u nhiênớảắếẫ ... 13 </small>

<small>2. Đối v i thu t toán gớậần như được sắp xếp tăng dần ... 14</small>

<small>3. Đối v i mớảng đã được sắp xếp tăng dần ... 15 </small>

<small>4. Đối v i mớảng được sắp x p gi m dếảần ... 16</small>

</div><span class="text_page_counter">Trang 3</span><div class="page_container" data-page="3">

I. VỀ CÁC THUẬT TOÁN VÀ ĐỘ PHỨC TẠP CỦA CHÚNG.

1.1. Thuật toán Selection Sort

- Ý tưởng: thu t toán s p x p Selection Sort là mậ ắ ế ột thuật toán s p x p các mắ ế ảng bằng cách đi tìm phần tử có giá trị nhỏ nhất trong đoạn và thay đổi chúng v i ph n ớ ầ tử ở đoạn đầu đoạn 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 c a mủ ảng con chưa đượ ắc s p x p sế ẽ được di chuy n vể ề đoạn đã đượ ắc s p xế p. - Về code (hàm Swap mặc định đã đượ ử ục s d ng và chèn vào mã nguồn):

void SelectionSort(int a[], int n){

//nếu như phần tử ứth j nh ỏhơn phần tử ứ min_idx thì gán th min_idx = j và đổi chỗ phần tử nhỏ nhất với

phần tử đầu tiên

if (a[j]< a[min_idx]) min_idx=j; if( min_idx!=i) Swap(a[min_idx],a[i ]); }

}

- Về độ ph c t p: ứ ạ

Thời gian: vì thu t tốn Selection Sort có hai phép l p lòng vào nhau ậ ặ với m i phép lỗ ặp là n bước nên về ph c t p v độ ứ ạ ề thời gian là:O(n*n) = O(n <small>2</small>).

Khơng gian: vì các mảng là như nhau sau mỗi bước nên độ ph c tứ ạp về không gian là O(1).

1.2. Thuật toán Insertion Sort

- Ý tưởng: Thuật toán Insertion Sort là 1 trong những thuật toán dễ thực hiện nhất và phù h p v i các dợ ớ ữ liệu có độ lớn nh . Vỏ ề thuật tốn, chúng ta xem nó như việc s p x p m t bắ ế ộ ộ bài tây, đầu tiên ta cho lá bài đầu tiên làm gốc, đánh d u lá bài ấ thứ 2 và so sánh độ lớn v i lá bài th nh t, n u nhớ ứ ấ ế ỏ hơn thì ta sẽ thay đổ ịi v trí 2 lá

</div><span class="text_page_counter">Trang 4</span><div class="page_container" data-page="4">

bài, n u khơng thì ta gi nguyên và chuy n d u cho lá bài tiế ữ ể ấ ếp theo cho đến lá cuối cùng. Cũng như vậy, thuật toán Insertion Sort cũng duyệt

void InsertionSort(int a[], int n){

//tạo vòng l p duy t m ng và cho ph n tặ ệ ả ầ ử thứ 2 làm g c ố for (int i=1;i<=n;i++){

int key=a[i];

j=i-1;//j là bi n duy t qua các mế ệ ảng đã được sắp xếp //Di chuy n m ng a[0,..i-1], lể ả ớn hơn phầ ửn t key lùi v phía ề

while(j>=0 && a[j]>a[key]){

Thời gian: tương tự như Selection Sort là O(n<small>2</small>). Không gian: tương tự như Selection Sort là O(1). 1.3. Thuật toán Bubble Sort

- Ý tưởng: Thuật toán Bubble Sort hoạt động như việc chỉnh đốn hang ngũ trong hang, ta so sánh chi u cao c a 2 cá nhân trong hàng, n u b n bên ph i thề ủ ế ạ ả ấp hơn thì ta đổi chỗ 2 bạn cho nhau và tiếp tục như thế. Trong lập trình, ta duyệt 1 mảng và đưa các số lớn nhất dần về phía dưới bằng cách so sánh 2 ph n t t ng cầ ử ừ ặp với nhau, n u ph n tế ầ ử bên ph i nhả ỏ hơn phầ ử bên trái thì ta ti p tn t ế ục đổi chỗ hoặc không đổi chỗ nếu chúng đã theo thứ tự và tiếp tục xét tiếp cặp tiếp theo,như thế, phần tử nhỏ hơn sẽ "nổi" lên, còn ph n tầ ử lớn hơn sẽ "chìm" d n và v bên phầ ề ải, đó là lý do vì sao thu t tốn tên là Bubble Sort. C p tậ ứ tiế ục cho đến khi ph n t l n nhầ ử ớ ất được đưa về cuối và ta xét ti p vòng th hai, th ế ứ ứ ba,.. cho đến khi mảng đã được sắp xếp hoàn toàn.

</div><span class="text_page_counter">Trang 5</span><div class="page_container" data-page="5">

void BubbleSort(int a[], int n){ int temp;

//ta duy t vào m ng v i vệ ả ớ ị trí đầu tiên là 0 for (int i = 0; i < n; i++){

//ta duy t m ng l n thệ ả ầ ứ 2 để ắ ặ b t c p v i ph n tớ ầ ử đầu tiên để so sánh và đổi ch vỗ ới

Thời gian:tương tự như 2 thuật toán trên là O(n <small>2</small>). Khơng gian:tương tự như 2 thuật tốn trên là O(1). 1.4. Thuật toán Shell Sort

- Ý tưởng: Shell Sort cũng có ý tưởng hoạt động tương tự như thuật toán Insertion Sort, th m chí cịn là b n nâng c p c a Insertion Sort nh m tránh vi c tráo ậ ả ấ ủ ằ ệ đổi v trí c a 2 ph n tị ủ ầ ử cách xa nhau b ng cách so sánh các ph n t ằ ầ ử ở các kho ng(gap ả hay interval) v i các kho ng nh n giá trớ ả ậ ị là n/2, n/4, n/8,… cho đến khi các kho ng ả bằng 1. Gi i thu t khá hi u qu v i các dả ậ ệ ả ớ ữ liệu có độ ớn trung bình và có độ l phức tạp trung bình nhỏ hơn rất nhi u so v i thu t toán Insertion Sort. ề ớ ậ

</div><span class="text_page_counter">Trang 6</span><div class="page_container" data-page="6">

void ShellSort(int a[], int n){

//tạo vòng l p v i biặ ớ ến interval để chia m ng ra thành các khoả ảng để so sánh

for(int interval = n/2 ; interval >0; interval/=2){

//tạo vòng l p cho bi n i ch y trong khoặ ế ạ ảng đã được chia để ến hành Insertion ti Sort cho kho ng ả

for(int i= interval; i<n;i++){

temp =a[i];//lưu các giá trị trong khoảng vào biến tạm để lưu giá trị vào v ị trí chuẩn khi s p x p ắ ế

//cho bi n j ch y trong khoế ạ ảng để tìm v trí chính xác cho giá trị ị a[i] for (int j=i, j>=interval && a[j-interval] > temp; j-=interval){

Thời gian: độ phức tạp của thuật toán Shell Sort phụ thuộc vào việc kho ng ả chia c a b n cho thu t toán ủ ạ ậ như thế nào. Nếu trong trường hợp tốt nhất, khi mà mảng đã được s p x p thì tắ ế ổng độ chia c a thu t toán bủ ậ ằng đúng dữ liệu của mảng, độ ph c t p c a Shell Sort là O(n*logứ ạ ủ <small>2</small>(n)) còn trong trường hợp xấu nh t, thu t tốn Shell Sort sấ ậ ẽ có độ ph c tứ ạp tương tự hư Insertion Sort n là O(n ), và trung bình thu t tốn nh<small>2</small> ậ ận độ ph c t p là O(n*ứ ạ log<small>2</small>(n) ).<small>2</small> Khơng gian: O(1).

1.5. Thuật tốn Heap Sort

- Ý tưởng: Thuật toán Heap Sort là thuật toán được dựa trên cấu trúc dữ liệu cây nh phân Binary Heap. Thu t toán giúp s p x p các ph n t l n nh t trong danh ị ậ ắ ế ầ ử ớ ấ sách về cuố ữ liệi d u v i tớ ốc độ chạy nhanh và cài đặt không quá ph c t p. Thuứ ạ ật toán Heap Sort thường sẽ sử dụng cấu trúc dữ liệu Max heap là chủ yếu với phần t ử lớn nh t s luôn là nút còn các ph n tấ ẽ ầ ử nh nh t sỏ ấ ẽ là lá, điều này giúp cho vi c sệ ắp xếp các ph n t l n nh t vầ ử ớ ấ ề cuối

- Về code, chúng ta sẽ chia ra làm 2 hàm, là hàm Heapify dùng để tạo ra kiểu dữ liệu Binary Max Heap và hàm thứ 2 là Heap Sort dùng để ắ s p xếp:

</div><span class="text_page_counter">Trang 7</span><div class="page_container" data-page="7">

void Heapify( int a[], int n, int i){ int largest = i;

int left = 2*i+1;//xét v trí node bên trái ị int right=2*i+2;//xét v trí node bên ph i ị ả

//Nếu như cây con bên trái lớn hơn gốc thì ta sẽ cho gốc là node bên trái

if(left<n && a[left]>a[largest]) largest=left;

//Tương tự như cây con bên phải if(right<n && a[right]>a[largest])

largest=right;

//Nếu như phần tử lớn nhất khơng ph i là g c thì ta sả ố ẽ thay đổ ị trí của i v node đó với rễ sao cho phần tử lớn nhất ln là gốc

if(largest!=i)

Swap(a[i],a[largest]);

//Ta ti p tế ục đệ quy ti n trình trên v i các nodế ớ e ở hàng dưới cho đến khi ra một cây đệ quy lớn nhất hoàn chỉnh

Heapify(a,n,largest); }

void HeapSort(int a[], int n){

// cho i ch y tạ ừ giữa mảng để ạ t o thành m t cây nh phân ộ ị for(int i=n/2-1;i>=0;i--)

//Đổi ch giữa g c c a cây(thành phần lớn nh t) và vỗ ố ủ ấ ị trí cuối cùng của mảng, sau đó cho tạ ại cây mới sau khi cắo l t m t phần gốc để rút ấ gọn cây lại cho đến khi không cịn ph n tầ ử nào trong cây thì mảng đã

</div><span class="text_page_counter">Trang 8</span><div class="page_container" data-page="8">

Về thời gian: vì độ dài c a mủ ột cây nh phân khi có n ph n t là log(n), ị ầ ử kể cả là trường h p x u nh t khi ta ph i di chuy n gợ ấ ấ ả ể ốc t i ph n node xa nhớ ầ ất thì v n là log(n). Và vì khi s p x p ta c n ph i duy t qua m ng n lẫ ắ ế ầ ả ệ ả ần nên độ phức t p lúc này sạ ẽ là O(n*log<small>2</small>(n))

Về không gian: O(1). 1.6. Thuật toán Merge Sort

- Ý tưởng: Thu t toán Merge Sort là thu t toán áp dậ ậ ụng phương pháp “Chia để trị” có độ phức tạp khi cài đặt ở mức trung bình và tốc độ khá nhanh trong các thuật toán s p x p. ắ ế Ở thuật tốn, ta sẽ chia mảng chính thành 2 mảng con, sau đấy ti p tế ục chia 2 mảng con thành 4 m ng con khác nhau,... ti p tả ế ục cho đến khi m ng ti p theo ả ế chỉ còn 1 ph n t thì ta s ầ ử ẽ so sánh và đổi chỗ phần t c a các m ng khác nhau. Cuử ủ ả ối cùng ta tr n các m ng l i theo quy tộ ả ạ ắc:

So sánh các ph n tầ ử đứng đầu, n u chúng nhế ỏ hơn thì cho vào danh sách mới, cho đến khi 1 trong 2 m ng là rả ỗng

Cuối cùng, khi 1 trong 2 mảng là r ng thì ta l y ph n còn l i c a mỗ ấ ầ ạ ủ ảng cho vào m ng mả ới, ta được một mảng mới đã đượ ắc s p x p. ế

- Về code, sẽ chia ra làm 2 hàm là hàm Merge và hàm MergeSort: void Merge(int a[], int const low, int const mid, ins const high){

//tạo ra 2 bi n h ng a1, a2(vì biế ằ ến động khi đặt làm độ ớ l n c a m ng ủ ả sẽ cho ra lỗi)

auto const a1=mid-low+1;//biến a1 lưu độ ớ l n c a mủ ảng đầu auto const a2=high-mid;//biến a2 lưu độ ớ l n c a m ng th hai ủ ả ứ auto * lowArray = new int[a1],//t o bi n con tr m ng lowArray vào ạ ế ỏ ả mảng có độ lớn a1

auto * highArray = new int[a2];//tương tư như trên với độ lớn a2 //chia các mảng ra để ắ s p x p theo 2 mế ảng

for (auto i = 0; i < a1; i++) lowArray[i] = a[low + i]; for (auto j = 0; j < a2; j++)

</div><span class="text_page_counter">Trang 9</span><div class="page_container" data-page="9">

//sau khi đã hợp nhất xong, ta cần xóa các mảng tạm để giải phóng bộ nhớ vị các m ng t m là ki u dả ạ ể ữ liệu con trỏ để giải phóng b nh . ộ ớ

delete[] lowArray; delete[] highArray; }

void MergeSort(int a[], int const low, int const high){

if(low>=high) return;//điều kiện để thoát khỏi vòng lặp đệ quy auto mid=low+(high-low)/2;//lấy giá tr trung vị ị của mảng

</div><span class="text_page_counter">Trang 10</span><div class="page_container" data-page="10">

//chạy đệ quy cho mảng nhằm t o ra 2 m ng và cu i cùng là h p nh t 2 ạ ả ố ợ ấ mảng đó lại và tiếp tục chạy đệ quy cho đến khi xảy ra điều kiện trên.

MergeSort(a, low, mid); MergeSort(a, mid + 1, high); Merge(a, low, mid, high); }

- Về độ ph c t p: ứ ạ

Thời gian: vì thu t tốn ln chia m ng ra làm 2 ph n tậ ả ầ ừng đôi một nên thu t tốn có thậ ể được tính dựa trên phương trình sau:

T(n)=2T(n/2)+θ(n)

có thể được tính là O(n*log<small>2</small>(n)) và ln có độ ph c tứ ạp như vậy trong mọi trường h p nên Merge Sort rợ ất được ưa chuộng b i vì tính ở ổn định về thời gian c a thuủ ật tốn.

Khơng gian:O(1).

1.7. Thuật tốn Quick Sort

- Về ý tưởng: thuật toán Quick Sort cũng sử dụng về ý tưởng “Chia để ị” tương tự tr như Merge Sort. Thế nhưng thay vì như Merge Sort chia các mảng ra thành 2 mảng khác nhau r i tiồ ếp tục chia 2 cho đến khi đơn giản thì Quick Sort sẽ chia mảng ra thành các mảng dựa trên phần tử thường được gọi là pivot(ph n tầ ử chốt). Thường ph n tầ ử chốt được chọn sẽ là ph n tầ ử trung vị(phần tử nằm ở giữa mảng), lúc này thu t tốn sậ ẽ có độ ph c t p nhứ ạ ỏ hơn so với khi ta ch n pivot nọ ằm ở ph n tầ ử đầu ho c cu ặ ối.

- Về code:

</div><span class="text_page_counter">Trang 11</span><div class="page_container" data-page="11">

void QuickSort(int a[], int low, int high){

if(low>=high) return;//điều kiện để thoát khỏi đệ quy int l0=low;

int h0=high;

int pivot=a[low+(high-low)/2]//l y pivot nấ ằm ở giá trị trung vị c a mủ ảng while(l0<h0){

while(a[l0]<pivot) l0++;//tìm các giá trị được s p x p bên trái ắ ế ở while(a[h0]>pivot) h0--;//tương tự như ở bên phải

if(l0<h0){ //khi các giá trị chưa được sắp xếp ở 2 bên thì ta đổi ch ỗ

Thời gian: vì pivot của chúng ta khi cài đặt là trung vị c a mủ ảng, nên độ phức t p c a thu t toán luôn là ạ ủ ậ O(n*log<small>2</small>(n)), điều này khác v i vi c lớ ệ ấy pivot nằm ở giữa mảng vì lúc này khi ta chia m ng ra thì s có thả ẽ ể dính vào trường hợp pivot là giá trị l n nh t ho c nhớ ấ ặ ỏ nhất, độ phức tạp lúc này s là O(n ). Vì th nên pivot ẽ <small>2</small> ế thường được lấy ở ị v trí trung vị.

Khơng gian:O(1). 1.8. Thuật toán Radix Sort

- Ý tưởng: Thu t tốn Radix Sort hay cịn g i là Postmans Sort là m t trong nh ng ậ ọ ộ ữ thuật toán ph biổ ến dung để sắp xếp.Điểm khác bi t c a Radix Sort so v i các thu t toán ệ ủ ớ ậ sắp xếp khác chính là vi c thay vì so sánh các s vệ ố ới nhau để sắp xếp chúng theo các chiều tăng dần hoặc giảm d n, thì Radix Sort s d ng vi c phân chia các ch s thành t ng ch ầ ử ụ ệ ữ ố ừ ữ số khác nhau và chia chúng ra thành từng bucket theo hàng đơn vị, hàng chục, hàng trăm,… của chữ số và ti p tục như thế cho đếế n khi mảng đã đượ ắc s p x p. ế

- Về code:

</div><span class="text_page_counter">Trang 12</span><div class="page_container" data-page="12">

//hàm GetMax dùng để lấy giá trị l n nh t c a mớ ấ ủ ảng int GetMax(int a[], int n){

//hàm CountSort dùng để sắp xếp các giá trị theo số chữ số được chia theo exp void CountSort(int a[], int const n, int exp) {

auto *output=new int [n];//mảng output được tr vào bỏ ộ nhớ có n vùng nh ớ int count[10] = {0};//mảng count dùng để đựng s ố chữ số c a mảng khi ủ được chia ra

for (int i = 0; i < n; i++) {

count[(a[i] / exp) % 10]++;//duy t mệ ảng để chia mảng theo số chữ số theo exp

}

for (int i = 1; i < 10; i++)

count[i] += count[i - 1];//thay đổi count i để mảng count ch a các sứ ố chữ ố theo đúng thứ s tự

for (int i = n - 1; i >= 0; i--) {

output[count[(a[i] / exp) % 10] - 1] = a[i];//t o mạ ảng output b ng cách s p x p các mằ ắ ế ảng theo sự xuất hiện c a sủ ố chữ số c a mảng a ủ

count[(a[i] / exp) % 10]--;//tiếp t c trụ ừ đi các khoảng c a mủ ảng count cho đến khi đã sắp xếp h t theo s các ch s . ế ố ữ ố

}

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

a[i] = output[i];//Copy mảng a theo mảng output để ảng a bây gi m ờ đã đượ ắc s p x p theo ế

</div><span class="text_page_counter">Trang 13</span><div class="page_container" data-page="13">

Thời gian:Thu t toán Radix Sort ậ có độ ph c t p d trên s các ch s ứ ạ ữ ố ữ ố lớn nh t c a m ng O(d*(n+b)) v i d là sấ ủ ả ớ ố chữ số l n nhớ ất của mảng và b là độ lớn của mảng Count để chứa s các ch s . ố ữ ố

Khơng gian: vì cần thêm khơng gian để chứa các số chữ số nên độ phức tạp của thuật toán lúc này s là O(n+b). ẽ

</div><span class="text_page_counter">Trang 14</span><div class="page_container" data-page="14">

II. THỰC NGHI M CÁC THU T TOÁN ỆẬ

- Ở độ lớn t 25000 tr lên, v i các thuừ ở ớ ật toán có độ phức tạp là O(n*log<small>2</small>(n)) có thời gian ch y không quá khác bi t so v i các thuạ ệ ớ ật tốn có độ ph c t p O(nứ ạ <small>2</small>). Đặc biệt là thuật toán Bubble Sort, thế nhưng thuật toán Shell Sort l i không quá bi t so v i các thuạ ệ ớ ật tốn có độ phức t p nhạ ỏ hơn.

- Thuật toán Radix Sort theo tính tốn là thu t tốn có tậ ốc độ nhanh nh t tuy nhiên ấ cũng không tạo ra nhiều s khác bi t so với các thu t toán Heap Sort, Quick Sort và Merge ự ệ ậ Sort.

Giải thích:

</div><span class="text_page_counter">Trang 15</span><div class="page_container" data-page="15">

- Theo đánh giá về các thuật toán như ở trên, các thuật toán đều chạy tuân theo với độ ph c tứ ạp đã được tính tốn.

- Về sự khác biệt của Bubble Sort so v i Selection Sort và Insertion Sort chính là do ớ việc phải hốn đổi gi a các v trí trong mữ ị ảng c a Bubble Sort xủ ảy ra thường xuyên hơn so với 2 thu t toán trên nên c n nhiậ ầ ều thời gian hơn.

- Còn v Shell Sort tuy ề có độ ph c t p là O(n ) thứ ạ <small>2</small> ế nhưng độ phức tạp thực tế lại có thể nhỏ hơn do đặc thù của thuật tốn khiến cho thu t tốn có thậ ể có độ ph c t p nhứ ạ ỏ hơn nhờ vào độ chia.

2. Đối v i thuớ ật toán gần như đượ ắp xếp c s tăng dần

Hình 2: Th i gian các thu t tốn s p xờ ậ ắ ếp trong mảng gần như đượ ắc s p xếp tăng dần

Nhận xét:

- Các thu t toán O(n*log (n)) v n gi nguyên th i gian ch y c a mình v i m c x p ậ <small>2</small> ẫ ữ ờ ạ ủ ớ ứ ấ xỉ 0-200ms.

- Thuật toán ch y nhanh nh t là Insertion Sort v i tạ ấ ớ ốc độ ần như là 0ms. g - Thuật toán ch y ch m nh t v n là Bubble Sort. ạ ậ ấ ẫ

Giải thích:

</div>

×