Tổng quan về thuật toán sắp xếp

21 2.2K 16
Tổng quan về thuật toán sắp xếp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Tổng quan về thuật toán sắp xếp Trước tiên để hiểu rõ về thuật toán sắp xếp mình xin nói qua về nó trước Một trong những vấn đề nền tảng của khoa học máy tính là sắp xếp một tập các phần tử cho trước theo thứ tự nào đó. Có rất nhiều các giải pháp cho vấn đề này, được biết đến như là các thuật toán sắp xếp (sorting algorithms). Bên cạnh các thuật toán sắp xếp đơn giản và rất rỏ ràng, như là sắp xếp nổi bọt (bubble sort). Một số khác, như phương pháp sắp xếp nhanh (quick sort) thì lại rất phức tạp nhưng đổi lại có kết quả thực thi nhanh hơn một cách đáng kể. Dưới đây là liên kết tới các mô tả, phân tích, và mã nguồn cho 7 thuật toán sắp xếp quan trọng, phổ biến nhất hiện nay. Bubble sort Heap sort Insertion sort Merge sort Quick sort Selection sort Shell sort Những thuật toán sắp xếp quen thuộc này có thể được chia thành hai nhóm dựa theo độ phức tạp của chúng. Độ phức tạp của thuật toán (Algorithmic complexity) là một chủ đề khá rắc rối đòi hỏi sự tưởng tượng mà sẽ tốn nhiều thời gian để giải thích, nhưng ở đây có thể hiểu thông qua mối tương quan trực tiếp giữa độ phức tạp của thuật toán và hiệu quả của nó. Độ phức tạp của thuật toán thường được kí hiệu bởi một hàm O, trong đó O biểu diễn độ phức tạp của thuật toán đi kèm với một giá trị n biểu diễn kích thước của số lần chạy tối đa mà thuật toán đó dựa vào để xử lý trên dữ liệu. Ví dụ, O(n) có nghĩa là thuật toán có độ phức tạp tuyến tính. Cụ thể hơn , nó sẽ mất thời gian gấp 10 lần cho việc xử lý trên tập dữ liệu có 100 phần tử so với tập chỉ có 10 phần tử (10 * 10 = 100). Nếu độ phức tạp là O(n2) (quadratic complexity), thì nó sẽ phải tiêu tốn thời gian gấp 100 lần để xử lý trên tập 100 phần tử so với tập dữ liệu chỉ gồm 10 phần tử. Hai nhóm thuật toán sắp xếp được phân như sau: nhóm thứ nhất có độ phức tạp là O(n2) bao gồm bubble, insertion, selection, và shell sorts; Nhóm thứ hai có độ phức tạp là O(n log n) gồm heap, merge, và quick sorts. Bên cạnh độ phức tạp của thuật toán, tốc độ của các thuật toán sắp xếp có thể được so sánh dựa vào kinh nghiệm có được từ việc thử trên các tập dữ liệu. Vì tốc độ sắp xếp có thể thay đổi rất nhiều tùy theo đặc điểm của dữ liệu, nên để các kết quả thống kê chính xác dựa trên kinh nghiệm đòi hỏi việc chạy các thuật toán nhiều lần trên các dữ liệu khác nhau và tính trung bình. Thông thường tập dữ liệu kiểm tra được tạo ngẫu nhiên. Trong các biểu đồ thể hiện mức độ hiệu quả của thuật toán dưới đây, đường thấp nhất là "tốt nhất". Ghi nhớ rằng "tốt nhất" ở đây là một khái niệm không tuyệt đối vì nó còn tùy vào trường hợp sử dụng của bạn - nếu nhìn biểu đồ bạn sẽ thấy quick sort có lẽ là thuật toán nhanh nhất, nhưng nếu sử dụng nó để sắp xếp một tập 20 phần tử thì cũng giống như vác đại bác ra bắn ruồi Đồ thị trên đã thể hiện khá rỏ, Bubble sort là giải pháp cực kì không hiệu quả, và shell sort là sự cải tiến tạo ra cuộc bứt phá hết sức ngoạn mục. Chú ý rằng vào đường ngang đầu tiên của khu vực đồ thị thể hiện thời gian 100 giây- bạn sẽ thấy rằng không có thuật toán nào trong số này mà bạn muốn sử dụng cho tập dữ liệu lớn của một ứng dụng tương tác. Ngay cả khi dùng shell sort, người sử dụng cũng có nguy cơ ngồi bấm móng tay nếu bạn cố gắng sắp xếp nhiều hơn 10,000 phần tử. Nhìn từ phương diện tươi sáng hơn, tất cả những thuật toán thuộc nhóm O(n2) đều đơn giản một cách không ngờ (có thể shell sort là một ngoại lệ hơi phức tạp hơn). Với những chương trình dùng kiểm tra nhanh, những mẩu thử nghiệm gấp, hay các phần mềm dành cho người sử dụng nội bộ, chúng không phải là những lựa chọn tồi nếu bạn không đặt quá nặng về hiệu năng. Trong trường hợp tốc độ là ưu tiên hàng đầu, những giải thuật thuộc nhóm O(n log n) nên được sử dụng. Chú ý rằng thời gian trong đồ thị ngay trên đây được đo theo từng phần 10 của giây thay vì từng trăm giây như đồ thị của nhóm O(n2). Tuy nhiên mọi thứ thì không thật sự dễ dàng với các ứng dụng thực tiễn, bạn phải đứng trước sự lựa chọn cân bằng (trade-offs) giữa các yếu tố. Những thuật toán thuộc nhóm O(n log n) thì rất nhanh, nhưng tốc độ đó có được do phải trả giá cho sự phức tạp trong triển khai. Trong trường hợp đệ quy, các cấu trúc dữ liệu nâng cao, mảng đa chiều - việc sử dụng những thuật toán này sẽ tạo ra nhiều vấn đề phát sinh rất khó chịu. ( nhưng mà nên nhớ thuật có độ phức tạp O(n log n) nó khó nhưng mà nó nhanh) Post added at 01:36 PM Previous post was at 01:30 PM Bây giờ vào phần chính thôi Trước hết mình nói qua về thuật heap sort Heap sort là thuật toán chậm nhất trong số các thuật toán sắp xếp thuộc nhóm có độ phức tạp O(n log n), nhưng không giống như Merge và Quick sort nó không đòi hỏi sự đệ quy phức tạp hay nhiều mảng dữ liệu để xử lý. Điều này làm cho nó trở thành một lựa chọn hấp dẫn với tập dữ liệu rất lớn hàng triệu phần tử. Tuy nhiên sự lựa chọn thích hợp lúc nào cũng còn tùy thuộc vào kết cấu hạ tầng và mục tiêu của ứng dụng. Heap sort hoạt động cũng như sự gợi ý trong tên gọi - nó bắt đầu bằng việc xây dựng một heap out của tập dữ liệu, và sau đó loại phần tử lớn nhất và đặt nó vào vị trí cuối của mảng được sắp xếp. Sau việc xóa phần tử lớn nhất, nó tái cấu trúc heap và di chuyển phần tử lớn nhất kế tiếp còn lại và đặt nó vào vị trí mở kế cuối của mảng được sắp xếp. Thao tác này được lặp lại cho tới khi không còn phần tử bên trái trong heap và mảng được sắp xếp đã đầy. Cách triển khai căn bản đòi hỏi hai mảng dữ liệu - một giữ heap và một giữ những phần tử đã được sắp xếp. Việc thực hiện tiến trình sắp xếp chỉ trong một mảng duy nhất nhằm tiết kiệm không gian của mảng thứ hai là cần thiết, giải thuật sau đây dùng một ít kỉ xảo để chỉ sử dụng cùng một mảng cho lưu trử Heap và mảng đã được sắp xếp. Bất cứ khi nào một phần tử được xóa khỏi Heap, nó giải phóng một không gian lưu trử ở cuối mảng mà phần tử bị xóa có thể được đặt vào. 0 1 2 3 4 <SPAN style="FONT-FAMILY: arial">//doi cho </SPAN> <SPAN style="FONT-FAMILY: arial">void Swap(int &a,int &b) { int t; t=a; a=b; b=t; } //thu tuc hieu chinh heap void Shift(int a[],int r,int l) { int i,j,x; i=l; j=2*i; //phan tu lien doi x=a[i]; while(j<=r) { if (j<r) if(a[j]<a[j+1]) //xac dinh phan tu lien doi lon hon j++; if(a[j]<=x) break; //thoa quan he lien doi,dung else { 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 a[i]=a[j]; i=j; j=2*i; a[i]=x; } } } //hieu chinh day ban dau thanh heap void CreateHeap(int a[],int n) { int l; l=(n-1)/2; while(l>=0) { Shift(a,n-1,l); l ; } } //phuong phap cay void HeapSort(int a[],int n) { int r; CreateHeap(a,n); r=n-1; //vi tri phan tu cuoi mang while(r>0) 0 151 { Swap(a[0],a[r]); r ; Shift(a,r,0); } }</SPAN> Thuật toán sắp xếp nổi bọt (Bubble sort) Thuật toán sắp xếp nổi bọt là một trong các thuật toán phổ biến nhất với những lập trình viên mới bắt đầu sự nghiệp. Thuật toán này vận hành dựa trên việc so sánh liên tục các phần tử cạnh nhau của một mảng các số chưa được sắp xếp và cho phép phần tử nào nhẹ hơn sẽ được nổi lên trên (chuyển vị trí sang trái hoặc phải tùy theo việc muốn sắp xếp theo thứ tự tăng dần hay giảm dần). Bubble sort là thuật toán dễ triển khai nhưng cũng là một trong các thuật toán sắp xếp có hiệu suất kém nhất (độ phức tạp lên tới O(n2)). Để có thể xem minh họa về thuật toán này, bạn có thể truy cập trang Algolist (trang web chuyên minh họa về các thuật toán). Tuy vậy, có một cách hay hơn và dễ hiểu hơn để biết về thuật toán nổi tiếng (về sự đơn giản và chậm chạp) này là xem video dưới đây do các thành viên thuộc trường đại học Sapientia (Romania) trình diễn : Giải thuật Sắp xếp từ trên xuống Giả sử dãy cần sắp xếp có n phần tử. Khi tiến hành từ trên xuống, ta so sánh hai phần tử đầu, nếu phần tử đứng trước lớn hơn phần tử đứng sau thì đổi chỗ chúng cho nhau. Tiếp tục làm như vậy với cặp phần tử thứ hai và thứ ba và tiếp tục cho đến cuối tập hợp dữ liệu, nghĩa là so sánh (và đổi chỗ nếu cần) phần tử thứ n-1 với phần tử thứ n. Sau bước này phần tử cuối cùng chính là phần tử lớn nhất của dãy. Sau đó, quay lại so sánh (và đổi chố nếu cần) hai phần tử đầu cho đến khi gặp phần tử thứ n-2 Ghi chú: Nếu trong một lần duyệt, không phải đổi chỗ bất cứ cặp phần tử nào thì danh sách đã được sắp xếp xong. Sắp xếp từ dưới lên Sắp xếp từ dưới lên so sánh (và đổi chỗ nếu cần) bắt đầu từ việc so sánh cặp phần tử thứ n-1 và n. Tiếp theo là so sánh cặp phần tử thứ n-2 và n-1, cho đến khi so sánh và đổi chỗ cặp phần tử thứ nhất và thứ hai. Sau bước này phần tử nhỏ nhất đã được nổi lên vị trí trên cùng (nó giống như hình ảnh của các "bọt" khí nhẹ hơn được nổi lên trên). Tiếp theo tiến hành với các phần tử từ thứ 2 đến thứ n. Mô tả: void BubbleSort(int *c, int n) // Sap xep noi bot { for (int i = 0; i < n - 1; ++i) for (int j = n - 1; j > i; j) if (c[j] < c[j - 1]) { int t = c[j]; // t: bien tam c[j] = c[j - 1]; c[j - 1] = t; } } Thuật toán sắp xếp Nhanh (Quick-sort) Sắp xếp nhanh (Quicksort), còn được gọi là sắp xếp kiểu phân chia (part sort) là một thuật toán sắp xếp phát triển bởi C.A.R. Hoare, dựa trên phép phân chia danh sách được sắp thành hai danh sách con. Khác với sắp xếp trộn, chia danh sách cần sắp xếp a[1 n] thành hai danh sách con có kích thước tương đối bằng nhau nhờ chỉ số đứng giữa danh sách, sắp xếp nhanh chia nó thành hai danh sách bằng cách so sánh từng phần tử của danh sách với một phần tử được chọn được gọi là phần tử chốt. Những phần tử nhỏ hơn hoặc bằng phần tử chốt được đưa về phía trước và nằm trong danh sách con thứ nhất, các phần tử lớn hơn chốt được đưa về phía sau và thuộc danh sách đứng sau. Cứ tiếp tục chia như vậy tới khi các danh sách con đều có độ dài bằng 1. Phần tử chốt (pivot) Kỹ thuật chọn phần tử chốt ảnh hưởng khá nhiều đến khả năng rơi vào các 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ử chốt là trung vị của danh sách. 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ử chốt như sau: - Chọn phần tử đứng đầu hoặc đứng cuối làm phần tử chốt. - Chọn phần tử đứng giữa danh sách làm phần tử chốt. - Chọn phần tử trung vị trong 3 phần tử đứng đầu, đứng giữa và đứng cuối làm phần tử chốt. - Chọn phần tử ngẫu nhiên làm phần tử chốt. (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) Thuật giải Sau khi phần tử chốt được chọn giải thuật phân chia nên tiến hành như thế nào? - Một giải pháp đơn giản nhất cho vấn đề này là duyệt từ đầu đến cuối lần lượt so sánh các phần tử của danh sách với phần tử chốt. Theo cách này, ta phải tiến hành n phép so sánh, ngoài ra còn phải dành n đơn vị bộ nhớ để lưu giữ các giá trị trung gian. - Một giải pháp khác được đề nghị là duyệt theo hai đường. Một đường từ đầu danh sách, một đường từ cuối danh sách. Theo cách này, ta tìm phần tử đầu tiên tính từ trái lớn hơn phần tử chốt và phần tử đầu tiên phía phải nhỏ hơn hoặc bằng phần tử chốt rồi đổi chỗ cho nhau. Tiếp tục như vậy cho đến khi hai đường gặp nhau. - Để có thể gọi đệ quy ta xét bài toán phân chia một danh sách con của a: a[k1,k2] thành hai danh sách. 0 1 2 3 4 void QuickSort(int a[m], int l, int r) { int v = a[(l+r)/2]; //chọn phần tử ở giữa đoạn làm chốt int i = l; int j = r; int temp; do { while (a[i] < v) i++; //tìm phần tử phía đầu đoạn mà ≥ v while (a[j] > v) j ; //tìm phần tử phía cuối đoạn mà ≤ v //lúc này: a[i] ≥ v ≥ a[j] if (i<=j) { if (i<j) { temp = a[i]; a[i] = a[j]; 5 6 7 8 9 0 12 a[j] = temp; } //sau khi hoán đổi, ta có: a[i] ≤ v ≤ a[j] i++; j ; } } while (i<=j); //lúc này, a[l] a[j] a[i] a[r], nghĩa là: l ≤ j ≤ i ≤ r if (l<j) QuickSort(a,l,j); //nếu a[l] a[j] là 1 đoạn (nhiều hơn 1 phần tử) thì if (i<r) QuickSort(a,i,r); //nếu a[i] a[r] là 1 đoạn (nhiều hơn 1 phần tử) thì } Độ phức tạp -Về thời gian: O(nlog n) - Về dữ liệu: Khác nhau tùy vào cách hiện thực Thuật toán sắp xếp Chọn (Selection sort) Giải thuật Chọn phần tử nhỏ nhất trong n phần tử ban đầu, đưa phần tử này về vị trí đúng là đầu tiên của dãy hiện hành. Sau đó không quan tâm đến nó nữa, xem dãy hiện hành chỉ còn n-1 phần tử của dãy ban đầu, bắt đầu từ vị trí thứ 2. Lặp lại quá trình trên cho dãy hiện hành đến khi dãy hiện hành chỉ còn 1 phần tử. Dãy ban đầu có n phần tử, vậy tóm tắt ý tưởng thuật toán là thực hiện n-1 lượt việc đưa phần tử nhỏ nhất trong dãy hiện hành về vị trí đúng ở đầu dãy. Các bước tiến hành như sau: [...]... } Thuật toán sắp xếp trộn (Merge-sort) Trong khoa học máy tính, sắp xếp trộn (merge sort) là một thuật toán sắp xếp để sắp xếp các danh sách (hoặc bất kỳ cấu trúc dữ liệu nào có thể truy cập tuần tự, v.d luồng tập tin) theo một trật tự nào đó Thuật toán này là một ví dụ tương đối điển hình của lối thuật toán chia để trị Nó được xếp vào thể loại sắp xếp so sánh Giải thuật Các bước thực hiện thuật toán. .. } Thuật toán Shell-sort Được phát minh bởi Donald Shell vào năm 1959, Shell sort là thuật toán hiệu quả nhất trong nhóm các thuật toán sắp xếp có độ phức tạp O(n2) Đương nhiên, Shell sort cũng phức tạp nhất trong các thuật giải thuộc lớp này Shell sort là sự cải tiến của Insertion sort dựa vào hai nhận xét sau đây: Insertion sort sẽ rất hiệu quả nếu dữ liệu đầu vào hầu như đã được sắp xếp (đã được xếp. .. c[i] = c[m]; c[m] = t; 2 } 3 } Thuật toán sắp xếp Chèn (Insertion sort) Thuật toán sắp xếp chèn làm việc cũng giống như tên gọi - Nó thực hiện việc quét một tập dữ liệu, với mỗi phân tử, thủ tục kiểm tra và chèn phần tử đó vào vị trí thích hợp trong danh sách đích (chứa các phần tử đứng trước nó đã được sắp xếp) được tiến hành Phương pháp dễ dàng nhất để thực hiện thuật toán này là dùng hai vùng chứa... điểm Thuật toán trộn tự nhiên khác thuật toán trộn trực tiếp ở chỗ thay vì luôn cứng nhắc phân hoạch theo dãy con có chiều dài k, việc phân hoạch sẽ theo đơn vị là đường chạy ta chỉ cần biết số đường chạy của a sau lần phân hoạch cuối cùng là có thể biết thời điểm dừng của thuật toán vì dãy đã có thứ tự là dãy chi có một đường chạy Một nhược điểm lớn nữa của thuật toán trộn là khi cài đặt thuật toán. .. kém hiệu quả vì nó di chuyển các giá trị phần tử mỗ i lần chỉ một vị trí Shell sort là môt thuật toán sắp xếp với số gia giãm dần, thường được biết đến như là "comb sort" dành cho những khối chương trình hỗn độn chưa được làm sạch Thuật toán tạo ra nhiều luồng chạy duyệt qua danh sách dữ liệu, và mỗi lần sắp xếp một số trong những tập dữ liệu được định kích cở như nhau (tập phân đoạn được tách ra từ... chứa dữ liệu khác nhau - tập dữ liệu nguồn và tập dữ liệu mà các phần tử đã sắp xếp được chèn vào Tuy nhiên để tiết kiệm bộ nhớ, hầu hết các ứng dụng đều chỉ sử dụng một tập dữ liệu duy nhất Thuật toán được tiến hành bằng cách dịch chuyển phân tử hiện tại đang xét tuần tự qua những phân tử ở vùng dữ liệu phía trước đã được sắp xếp, phép hoán vị nó với phần tử liền kề được thực hiện một cách lặp lại cho... nữa của thuật toán trộn là khi cài đặt thuật toán đòi hỏi thêm không gian bộ nhớ để lưu các dãy phụ b, c Hạn chế này khó chấp nhận trong thực tế vì các dãy cần sắp xếp thường có kích thước lớn Vì vậy thuật toán trộn thường được dùng để sắp xếp các cấu trúc dữ liệu khác phù hợp hơn như danh sách liên kết hoặc file 1 #include 2 #include 3 #include 4 void nhap(int *a,int *n);... tiếp tục về sau; trong khi tập thứ ba sẽ chứa các phần tử ở vị trí 3, 6, 9, và tương tự kế đó Kích thước của tập dữ liệu được sử dụng trong mỗi lần duyệt dữ liệu có ảnh hưỡng lớn tới hiệu quả của việc sắp xếp Vài nhà nghiên cứu hàng đầu về khoa học máy tính, trong đó có cả Donald Knuth và Robert Sedgewick đã phát triển những phiên bản phức tạp hơn cho shell sort nhằm nâng cao hiệu quả tính toán bằng... Insertion sort Sau mỗi lần duyệt qua hết bộ dữ liệu thông qua các phân đoạn (có kích thước giãm dần >= 1) , kích thước của tập được sắp xếp trở nên lớn hơn, cho tới khi nó chứa toàn bộ danh sách dữ liệu (Chú ý rằng do kích thước của tập tăng lên, số lượng tập dữ liệu cần được sắp xếp sẽ giảm dần) Điều này làm cho Insertion sort đạt tới trường hợp tối ưu nhất, chạy mỗi vòng lặp với độ phức tạp tiến tới O(n) . như là các thuật toán sắp xếp (sorting algorithms). Bên cạnh các thuật toán sắp xếp đơn giản và rất rỏ ràng, như là sắp xếp nổi bọt (bubble sort). Một số khác, như phương pháp sắp xếp nhanh. }</SPAN> Thuật toán sắp xếp nổi bọt (Bubble sort) Thuật toán sắp xếp nổi bọt là một trong các thuật toán phổ biến nhất với những lập trình viên mới bắt đầu sự nghiệp. Thuật toán này vận. - 1]; c[j - 1] = t; } } Thuật toán sắp xếp Nhanh (Quick-sort) Sắp xếp nhanh (Quicksort), còn được gọi là sắp xếp kiểu phân chia (part sort) là một thuật toán sắp xếp phát triển bởi C.A.R.

Ngày đăng: 29/04/2014, 17:14

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan