Tài liệu Sắp xếp theo kiểu : Quick sort pdf

9 880 8
Tài liệu Sắp xếp theo kiểu : Quick sort pdf

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

Thông tin tài liệu

Thuật toán dựa trên kỹ thuật chia để trị,được đề xuất bởi C.A.R Hoare Ý tưởng như sau:Sắp xếp dãy khóa k[1 n] thì có thể coi là sắp xếp đoạn từ chỉ số 1 tới chỉ số n trong dãy khóa đó. Nếu đoạn đó có ít hơn 2 khóa thì không cần làm gì cả,nếu đoạn đó có ít nhất 2 khóa,ta chọn một khóa ngẫu nhiên nào đó làm chốt(pivot).Mọi khóa nhỏ hơn khóa chốt được xếp vào vị trí đứng trước chốt,mọi khóa lớn hơn chốt được xếp vào vị trí sau chốt.Sau phép hoán chuyển như vậy thì đoạn đang xét được chia làm 2 đoạn khác rỗng mà mọi khóa trong đoạn đầu đều <=chốt và mọi khóa trong đoạn sau đều >=chốt.Vấn đề trở thành sắp xếp 2 đoạn mới được tạo ra (độ dài ngắn hơn độ dài đoạn ban đầu ) bằng phương pháp tương tự (gọi đệ quy) Độ phức tạp là O(n*lgn) #include <stdio.h> #include <stdlib.h> #define MAX 10000 char *input="day_so.inp"; float a[MAX]; int N; void quick_sort(int l,int r) { float tg; float pivot; int i,j; if (l<r) //truong hop suy bien { i=l;j=r; pivot=a[random(r-l+1)+l]; //Các bạn đặc biệt chú ý chỗ này do { while (a[i]<pivot) i++; while (a[j]>pivot) j--; if (i<=j) { tg=a[i]; a[i]=a[j]; a[j]=tg; i++;j--; } } while (i<=j); quick_sort(l,j); quick_sort(i,r); } } void main() { FILE *f; int i; /* Doc du lieu vao tu FILE */ f=fopen(input,"r"); fscanf(f,"%d",&N); for (i=0;i<N;i++) fscanf(f,"%f",&a[i]); fclose(f); quick_sort(0,N-1); /* in ra ket qua */ printf("\n Mang sau khi da sap xep la:\n"); for (i=0;i<=N-1;i++) printf("%f\n",a[i]); getch(); } Các bạn lưu ý trong đoạn code của mình dùng "pivot=a[random(r-l+1)+l]",cái này rất nhiều sách không đề cập tới,thông thường người ta chọn chốt là phần tử đầu dãy a[l] hoặc cuối dãy a[r] nhưng với bộ dữ liệu đặc biệt sẽ làm tăng thời gian thực hiện chương trình,do đó mình chọn phần tử bất kì làm chốt. Nếu có bug các bạn post lên để mình fix nha. Phép chọn pivot cần có độ phức tạp O(1). Pivot tối ưu là median của mảng, tức là một giá trị sao cho một nửa số phần tử của mảng nhỏ hơn nó và nửa còn lại lớn hơn hoặc bằng nó. Nhưng bởi vì không có cách nào O(1) để tính được median, người ta chỉ có thể chọn pivot một cách hú họa và bất kể cách đó hú họa thế nào thì độ phức tạp của Quicksort trong trường hợp xấu nhất vẫn là O(n^2), không thể giảm được nữa. Do đó để giảm thời gian tính toán, nên chọn pivot theo những công thức rất đơn giản (chẳng hạn như lấy phần tử đầu tiên của mảng, lấy trung bình cộng của phần tử đầu tiên và cuối cùng trong mảng, hoặc lấy trung bình cộng của 4 phần tử trong mảng), không nên dùng công thức phức tạp nhất là công thức chứa lời gọi hàm (random, .) thì càng không nên. Quick sort là phương pháp đổi chỗ từng phần (partition exchange), đây là phương pháp rất hiệu quả, rất thông dụng Nội dung của phương pháp này như sau: Chọn một nút bất kỳ trong danh sách(Giả sử là nút đầu tiên) gọi là nút làm trục (pivot node), xác định vị trí hợp lệ của nút này trong danh sách (gọi là vị trí pivot). Tiếp theo chúng ta phân hoạch các nút còn lại trong danh sách cần sắp xếp sao cho từ vị trí 0 đến vị trí pivot-1 đều có nội dung nhỏ hơn hoặc bằng nút làm trục, các nút từ vị trí pivot+1 đến n-1 đều có nội dung lớn hơn nút làm trục. Quá trình lại tiếp tục như thế với hai danh sahs con từ trị trí 0 đến vị trí pivot-1 và từ vị trí pivot+1 đến vị trí n-1, . Sau cùng chúng ta sẽ được danh sách có thứ tự. Mô tả giải thuật Quick sort Code: Giai thuat: QuickSort(nodes[], low, up) Mo Ta; Giair thuat QuickSort, dung phuong phap de qui sawp xep va cac nut trong danh sach giua hai vi tri low va up Du Lieu nhap: - Danh sach cac nut chua sap xep (giua hai vi tri low va up) - low va up Du Lieu Xuat: Danh sach cac nut (giua hai vi tri low va up) da duoc sap xep Hanh dong if(low >= up) // dieu kien dung ket thuc giai thuat if(low < up) - Phan hoach: partition(nodes[], low, up, pivot) + partition phan danh sach thanh ba phan: * nut lam truc: nodes[low] tro thanh nodes[pivot] * danh sach con 1: nodes[i] <= nodes[pivot] (voi i < pivot) * danh sach con 2: nodes[i] > nodes[pivot] (voi i > pivot) - Goi de qui: QuickSort(nodes[], low, pivot-1) - Goi de qui: QuickSort(nodes[], pivot+1, up) Ket Thuc Giải thuật Partion vấn đề tiếp theo là giải thuật partition giúp phân danh sách làm ba phần: • Nút đầu (nút làm trục) đặt ở vị trí pivot • Danh sách con 1 từ vị trí low đến pivot-1 có nội dung nhỏ hơn hay bằng nút làm trục. • Danh sách con 2 từ vị trí low đến pivot+1 có nội dung lớn hơn hay bằng nút làm trục. Người ta xử lý giải thuật partition theo mô tả như sau: 1. Chọn nodes[low] (nút đầu tiền) là nút làm trục. 2. Quét danh sách theo hai hướng để đổi chỗ các cặp nút "sai" vị trí, nơi gặp nhau của hai hướng quét chính là vị trí pivot: Quét từ low lên: con trỏ l xuất phát từ vị trí low và tăng dần lên, dừng lại khi gặp nút có nội dung lớn hơn nút làm trục, ghi nhận vị trí l lúc này. • Quét từ up xuống: con trỏ u xuất phát từ vị trí up và giảm dần xuống, dừng lại khi gặp nút có nội dung nhỏ hơn hay bằng nút làm trục, ghi nhận vị trí u lúc này. • Đổi chỗ hai nút tại vi trí l và u. • Cứ tiếp tục quét theo hai hướng và đổi chỗ các cặp nút "sai" vị trí, quá trình này dừng lại khi l = u (hai hướng quét gặp nhau), nợi gặp nhau chính là vị trí pivot (pivot = u = l). 3. Đổi chỗ hai nút tại vị trí low (nút làm trục) và nút tại vị trí pivot. Sau đây là giải thuật partition Code: Giai Thuat: partition(nodes[], low, up, pivot) Mo Ta: Giai thuat partition, phan danh sach thanh 3 phan . Du Lieu Nhap: Danh sach cac nut trong khoang vi tri tu low den up. Du Lieu Xuat: Dua nut lam truc ve vi tri pivot, doi cho cac nut trong danh sach sao cho cac nut co noi dung nho hon hay bang nut lam truc duoc bo tri truoc nut lam truc, cac nut co noi dung lon hon nut lam truc duoc bo tri sau nut lam truc. Hanh Dong 1. Chon nodes[low] la nut lam truc Gan: l = low; u = up; 2. while(l < u) // vong lap xu ly hai huong quet { Quet huong tu low len, dung lai khi gap nut lon hon nut lam truc, ghi nhan vi tri l luc nay Quet huong tu up xuong, dung lai khi gap nut nho hon hay bang nut lam truc, ghi nhan vi tri u luc nay Doi cho hai nut tai hai vi tri l va u } 3. Doi cho hai nut tai hai vi tri low va u (luc nay pivot = u) Ket Thuc Cài đặt giải thuật QuickSort Sau đây là hàm QuickSort() dùng phương pháp đệ qui, hàm này có gọi hàm partition() để phân hoạch danh sách con thành 3 phần. Hàm QuickSort() void QuickSort(int nodes[], int low, int up) { int pivot; if(low >= up) // dieu kien dung return; if(low < up) { partition(nodes, low, up, &pivot); QuickSort(nodes, low, pivot - 1); QuickSort(nodes, pivot + 1, up); } } Hàm partition(): void partition(int nodes [], int low, int up, int *pivot) { int nuttruc, l, u, temp; nuttruc = nodes[low]; // Chon nut dau lam nut truoc l = low; u = up; while(l < u) { while(nodes[l] <= nuttruc && l < up) l++; while(nodes[u] > nuttruc) u--; if(l < u) { // doi cho cap nut sai vi tri temp = nodes[l]; nodes[l] = nodes[u]; nodes[u] = temp; } } // doi cho hia nut tai low va u (luc nay u la vi tri nut truc) nodes[low] = nodes[u]; nodes[u] = nuttruc; *pivot = u; } Nhận xét, so sánh • Quick Sort phức tạp hơn Bubble Sort nhưng hiệu quả hơn. • Quick Sort thích hợp cho danh sách ban đầu chưa có thứ tự. • Quick Sort kém hiệu quả khi danh sách ban đầu gần có thứ tự. Đặc biệt với danh sách dã có thứ tự (lớn dần hoặc nhở dần) lại là trường hợp xấu nhất của giải thuật Quick Sort. Chương trình minh họa sắp xếp một mảng kiểu int #include <iostream.h> #include <conio.h> void partition(int nodes [], int low, int up, int *pivot); void QuickSort(int nodes[], int low, int up) { int pivot; if(low >= up) // dieu kien dung return; if(low < up) { partition(nodes, low, up, &pivot); QuickSort(nodes, low, pivot - 1); QuickSort(nodes, pivot + 1, up); } } void partition(int nodes [], int low, int up, int *pivot) { int nuttruc, l, u, temp; nuttruc = nodes[low]; // Chon nut dau lam nut truoc l = low; u = up; while(l < u) { while(nodes[l] <= nuttruc && l < up) l++; while(nodes[u] > nuttruc) u--; if(l < u) { // doi cho cap nut sai vi tri temp = nodes[l]; nodes[l] = nodes[u]; nodes[u] = temp; } } // doi cho hia nut tai low va u (luc nay u la vi tri nut truc) nodes[low] = nodes[u]; nodes[u] = nuttruc; *pivot = u; } void main() { int mang[7]; for(int i = 0;i < 7;i++) { cout<<"Nhap vao phan tu thu "<<(i+1)<<": "; cin>>mang[i]; } cout<<endl; for(int i = 0;i < 7;i++) { cout<<mang[i]<<" "; } QuickSort(mang, 0, 7); cout<<endl<<endl<<"danh sach sau khi da sap xep: "; for(int i = 0;i < 7;i++) { cout<<mang[i]<<" "; } getch(); } Ý tưởng: Gần giống như merge sort , ta sẽ chia đôi mảng cần xếp rồi sắp xếp các mảng con , sau đó sẽ ghép mảng con đã sắp xếp thành mảng ban đầu nhưng đã sắp xếp Điểm khác nhau: Là chổ ta chia đôi mảng con theo nguyên tắc riêng : Gọi điểm mà tại đó ta chia đôi mảng ban đầu có trị là pivot (không nhất thiết là nằm đúng vị trí chính giữa . Nhưng giải thuật sẽ chạy tốt nếu nó nằm gần điểm chính giữa) và ta sẽ không cần merge 2 dãy con (do đó nhìn chung sẽ chạy nhanh hơn ) , thỏa điều kiện là tất cả những phần tử bên trái pivot đều nhỏ hơn pivot và nằm bên phải pivot thì lớn hơn pivot VD : 2 4 6 7 18 14 13 Ta chọn phần tử pivot = 7 ( do bên trái của nó nhỏ hơn pivot = 7 , bên phải lớn hơn pivot = 7 ) (Thực ra ta phải tìm pivot , do ở đây làm bằng tay nên dễ thấy ) Chia đôi : A = { 2 4 6 } B = { 18 14 13 } + Sắp A + Trong dãy A ta cũng chọn phần tử pivot là 4 + Được 2 dãy con là A11 = {2 } A12 = {6} + Sắp A11 ( sắp sẵn rồi ) + Sắp A12 ( sắp sẵn rồi ) + Tạo lại mảng A = { A11 được sắp , pivot , A12 được sắp } = {2 4 6 } +Sắp B . Trong B ta không thấy được pivot do chọn cái nào ta cũng không thấy thỏa . Nhưng mục tiêu của quick sort là làm sao ta được dãy <C= dãy có trị nhỏ hơn pivot > pivot < D= dãy có trị lớn hơn pivot > Sắp 2 dãy con C , D rồi ghép lại ta được dãy sắp thứ tự . Vì vậy áp dụng một giải thuật tìm pivot trong B( sẽ trình bày sau ) ta sẽ được C = { 13} D = {18} và pivot là 14 . + Sắp dãy con C , D ( do chỉ có một phần tử nên không sắp , hoặc tìm pivot) + Ghép lại tạo thành mảng B được sắp B = { 13 14 18 } +Ghép A và B để tạo thành mảng được sắp xếp ban đầu Code: void Sortable_List<Record>::quick_sort() { recursive_quick_sort(0 , count -1 ) ; } void Sortable_List<Record>::recursive_quick_sort(int low , int high ) { int pivot_position ; if (low < high ) { pivot_position = partition (low , high ) ; recursive_quick_sort(low , high ) ; recursive_quick_sort(pivot_position +1 , high ) ; } } int Sortable_List<Record>::partition(int low , int high ) { Record pivot ; int i , last_small; swap ( low , (low +high) /2 ; pivot = entry[low] ; last_small = low ; for ( i = low +1 ; i <= high ; i ++ ) { if(entry[i] < pivot ) { last_small = last_small +1 ; swap ( last_small , i ) ; } } swap (low , last_small ); return last_small ; } . Sortable_List<Record> ;:: quick_ sort( ) { recursive _quick_ sort( 0 , count -1 ) ; } void Sortable_List<Record> ;:: recursive _quick_ sort( int low , int high. cần xếp rồi sắp xếp các mảng con , sau đó sẽ ghép mảng con đã sắp xếp thành mảng ban đầu nhưng đã sắp xếp Điểm khác nhau: Là chổ ta chia đôi mảng con theo

Ngày đăng: 12/12/2013, 20:15

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

Tài liệu liên quan