Slide bài giảng cấu trúc dữ liệu
Trang 1ðại Học Sư Phạm Tp Hồ Chí Minh
Chương 2: Tìm kiếm & Sắp xếp
Trang 2Tìm kiếm & Sắp xếp
Mục tiêu:
• Giới thiệu một số thuật toán tìm kiếm và sắp xếp nội.
• Phân tích, đánh giá độ phức tạp của các giải thuật tìm
kiếm, sắp xếp.
Nội dung:
• Nhu cầu tìm kiếm và sắp xếp dữ liệu trong một hệ
thống thông tin.
• Các giải thuật tìm kiếm nội.
• Các giải thuật sắp xếp nội.
Các giải thuật
tìm kiếm nội
• Tìm tuần tự
• Tìm nhị phân
Tìm kiếm
Trang 3Các giải thuật tìm kiếm nội
Bài toán: Tìm vị trí xuất hiện của phần tử có giá trị x trên danh sách đặc a
•Tập dữ liệu được lưu trữ là dãy số a1, a2, ,aN
Trang 5TTTTììììm kie m kie m kiếááám tua m tua m tuầàààn t n t n tựựựự
{
for (int i=0; (i<N)&&(a[i]!=x ); i++);
if (i<N)
return i; // a[i] là phần tử có khoá x
return -1; // tìm hết mảng nhưng không có x
Trang 6Tìm kiếm tuần tự
int LinearSearch ( int a[], int N, int x)
{
// mảng gồm N phần tử từ a[0] a[N-1]
if (i<N)
return i; // a[i] là phần tử có khoá x
return -1; // tìm hết mảng nhưng không có x
}
Tìm kiếm tuần tự
• Đánh giá giải thuật:
• Vậy giải thuật tìm tuần tự có độ phức tạp
tính toán cấp n: T(n) = O(n)
Trang 7Tìm kiếm tuần tự
Nhận xét:
– Giải thuật tìm tuyến tính không phụ thuộc vào
thứ tự của các phần tử trong danh sách, do vậy đây là phương pháp tổng quát nhất để tìm kiếm trên một danh sách bất kỳ.
– Một thuật toán có thể được cài đặt theo nhiều
cách khác nhau, kỹ thuật cài đặt ảnh hưởng đến tốc độ thực hiện của thuật toán
Trang 8Tìm kiếm nhị phân
• Ý tưởng của giải thuật là tại mỗi bước tiến
hành so sánh x với phần tử nằm ở vị trí giữa
của dãy tìm kiếm hiện hành, dựa vào kết quả
so sánh này để quyết định giới hạn dãy tìm kiếm ở bước kế tiếp là nửa trên hay nửa dưới của dãy tìm kiếm hiện hành
Tìm kiếm nhị phân
Bước 1: left = VTĐ; right = VTC;
Bước 2: Trong khi left ≤≤≤≤ right lặp: //đoạn tìm kiếm chưa rỗng Bước 21: mid = (left+right)/2; // lấy mốc so sánh
Bước 22: Nếu a[mid] = x: //Tìm thấy
Dừng, vị trí xuất hiện: mid Bước 23: Nếu a[mid] > x: //tìm x trong dãy con a left a mid -1
Trang 10while (left <= right)
Trang 11Tìm kiếm nhị phân
• Đánh giá giải thuật:
• Giải thuật tìm nhị phân có độ phức tạp
tính toán cấp logn:
22
Tìm kiếm nhị phân
Nhận xét:
– Giải thuật tìm nhị phân dựa vào quan hệ giá trị
của các phần tử mảng để định hướng trong quá trình tìm kiếm, do vậy chỉ áp dụng được cho những dãy đã có thứ tự
– Giải thuật tìm nhị phân tiết kiệm thời gian hơn
rất nhiều so với giải thuật tìm tuần tự do
T nhị phân (n) = O(log 2 n) < T tuần tự (n) = O(n)
Trang 12Tìm kiếm nhị phân
Nhận xét:
– Khi muốn áp dụng giải thuật tìm nhị phân cần
phải xét đến thời gian sắp xếp dãy số để thỏa
điều kiện dãy số có thứ tự Thời gian này không nhỏ, và khi dãy số biến động cần phải tiến hành sắp xếp lại => khuyết điểm chính cho giải thuật tìm nhị phân
– Cần cân nhắc nhu cầu thực tế để chọn một trong
hai giải thuật tìm kiếm trên sao cho có lợi nhất
Định nghĩa bài toán sắp xếp
• Sắp xếp là quá trình xử lý một danh sách các
phần tử (hoặc các mẫu tin) để đặt chúng theo một thứ tự thỏa mãn một tiêu chuẩn nào đó dựa trên nội dung thông tin lưu giữ tại mỗi phần tử.
• Lưu ý : Thứ tự được đề cập ở đây là một thứ tự tổng quát
• Ví dụ : Hãy định nghĩa một thứ tự để dãy số sau là dãy tăng theo thứ tự này
Trang 13Khái niệm nghịch thế
• Khái niệm nghịch thế:
– Xét một mảng các số a 0 , a 1 , … a n
– Nếu có i<j và a i > a j , thì ta gọi đó là một nghịch thế.
• Mảng chưa sắp xếp sẽ có nghịch thế.
• Mảng đã có thứ tự sẽ không chứa nghịch thế
Phức tạp hơn Hiệu quả cao
Lớp thuật toán khác
Trang 14Selection sort – Ý tưởng
• Nhận xét: Mảng có thứ tự thì
a i = min(a i , a i+1 , …, a n-1 )
Ý tưởng: mô phỏng một trong những cách sắp
xếp tự nhiên nhất trong thực 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 dãy hiện
hành
– 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ử
Selection sort – Thuật toán
//input: dãy (a, N)
//output: dãy (a, N) đã được sắp xếp
• Bước 1 : i = Vị trí đầu;
• Bước 2 : Tìm phần tử a[min] nhỏ nhất trong dãy
• Bước 3 : Nếu min ≠ i: Hoán vị a[min] và a[i]
• Bước 4 : Nếu i chưa là Vị trí cuối
» i = Vị trí kế(i);
» Lặp lại Bước 2
Ngược lại: Dừng //N phần tử đã nằm đúng vị trí.
Trang 16Selection sort – Ví duï
Trang 18int min; // chỉ số phần tử nhỏ nhất trong dãy hiện hành
for ( int i=0; i<N-1 ; i++)
{
min = i;
for ( int j = i+1; j < N ; j++)
if (a[j] < a[min]) min = j; // ghi nhận vị trí phần tử nhỏ nhất
if (min != i)
Swap (a[min], a[i]);
}
}
Trang 19• Số lần hoán vị (một hoán vị bằng 3 phép
gán) phụ thuộc vào tình trạng ban đầu
của dãy số
Selection sort – Đánh giá giải thuật
38
Selection sort – Đánh giá giải thuật
• Ởû lượt thứ i, cần (N-i) lần so sánh để xác
định phần tử nhỏ nhất hiện hành
• Số lượng phép so sánh không phụ thuộc vào
tình trạng của dãy số ban đầu.
• Trong mọi trường hợp, số lần so sánh là:
2
1)n(ni)
(n
1 n 1 i
Trang 20Insertion Sort – Ý tưởng
• Nhận xét: mọi dãy a1 , a2 , , an luôn có i-1 phần tử đầu tiên a1 , a2 , ,ai-1 đã có thứ tự (2 ≤ i)
Ý tưởng chính: tìm cách chèn phần tử ai vào vị trí thích hợp của đoạn đã được sắp để có dãy mới a1 ,
a2 , ,ai trở nên có thứ tự
– Vị trí này chính là pos thỏa apos-1 ≤ ai < apos (1≤≤≤≤pos≤≤≤≤i). Chi tiết hơn:
– Dãy ban đầu a 1 , a 2 , , a n , xem như đã có đoạn gồm một phần tử a 1 đã được sắp
– Thêm a 2 vào đoạn a 1 sẽ có đoạn a 1 a 2 được sắp
– Thêm a 3 vào đoạn a 1 a 2 để có đoạn a 1 a 2 a 3 được sắp
– Tiếp tục cho đến khi thêm xong a N vào đoạn a 1 a 2 a N-1 sẽ có dãy a 1 a 2 a N được sắp
Insertion Sort – Thuật toán
//input: dãy (a, N)
//output: dãy (a, N) đã được sắp xếp
• Bước 1: i = 2; // giả sử có đoạn a[1] đã được sắp
• Bước 2: x = a[i]; Tìm vị trí pos thích hợp trong đoạn
a[1]
đến a[i] để chèn x vào
• Bước 3: Dời chỗ các phần tử từ a[pos] đến a[i-1] sang
phải 1 vị trí để dành chổ cho x
• Bước 4: a[pos] = x; // có đoạn a[1] a[i] đã được sắp
• Bước 5: i = i+1;
Nếu i ≤≤≤≤ n : Lặp lại Bước 2.
Ngược lại : Dừng
Trang 25Insertion Sort – Cài đặt
void InsertionSort ( int a[], int N )
{
int x;//lưu trữ a[i] tránh bị ghi đè khi dời chỗ các phần tử.
for ( int i=1 ; i<N ; i++) //đoạn a[0] đã sắp
Trang 26Insertion Sort – Nhận xét
• Khi tìm vị trí thích hợp để chèn a[i] vào đoạn
a[0] đến a[i-1], do đoạn đã được sắp có thể
sử dụng giải thuật tìm nhị phân để thực hiện việc tìm vị trí pos giải thuật sắp xếp chèn
nhị phân Binary Insertion Sort
– Lưu ý: Chèn nhị phân chỉ làm giảm số lần so sánh,
không làm giảm số lần dời chỗ.
• Ngoài ra, có thể cải tiến giải thuật chèn trực
tiếp với phần tử cầm canh để giảm điều kiện kiểm tra khi xác định vị trí pos.
Binary Insertion Sort – Cài đặt
void BInsertionSort ( int a[], int N )
Trang 27Insertion Sort – Đánh giá giải thuật
• Các phép so sánh xảy ra trong mỗi vòng lặp tìm vị trí thích hợp pos Mỗi lần xác định vị trí pos đang xét
không thích hợp dời chỗ phần tử a[pos-1] đến vị trí pos
• Giải thuật thực hiện tất cả N-1 vòng lặp tìm pos, do số lượng phép so sánh và dời chỗ này phụ thuộc vào tình trạng của dãy số ban đầu, nên chỉ có thể ước lược trong từng trường hợp như sau:
Phương pháp đổi chỗ trực tiếp
Interchange Sort
Trang 28Interchange Sort – Ý tưởng
• Nhận xét: Để sắp xếp một dãy số, ta có
thể xét các nghịch thế có trong dãy và làm triệt tiêu dần chúng đi
Ý tưởng chính:
– Xuất phát từ đầu dãy, tìm tất cả nghịch thế
chứa phần tử này, triệt tiêu chúng bằng cách đổi chỗ phần tử này với phần tử tương ứng trong cặp nghịch thế
– Lặp lại xử lý trên với các phần tử tiếp theo
trong dãy
Interchange Sort – Thuật toán
//input: dãy (a, N)
//output: dãy (a, N) đã được sắp xếp
• Bước 1 : i = 1; // bắt đầu từ đầu dãy
• Bước 2 : j = i+1; //tìm các cặp phần tử a[j] <
a[i], j>i
• Bước 3 : Trong khi j ≤≤≤≤ N thực hiện
• Nếu a[j]<a[i]: a[i]↔↔a[j];
• j = j+1;
• Bước 4 : i = i+1;
– Nếu i < n: Lặp lại Bước 2.
– Ngược lại: Dừng
Trang 31Interchange Sort - Cài đặt
Trang 32Interchange Sort
Đánh giá giải thuật
• Số lượng các phép so sánh xảy ra không phụ
thuộc vào tình trạng của dãy số ban đầu
• Số lượng phép hoán vị thực hiện tùy thuộc
vào kết quả so sánh
Phương pháp nổi bọt
Bubble sort
Trang 33Bubble sort – Ý tưởng
• Ý tưởng chính:
– Xuất phát từ cuối (đầu) dãy, đổi chỗ các
cặp phần tử kế cận để đưa phần tử nhỏ (lớn) hơn trong cặp phần tử đó về vị trí đúng đầu (cuối) dãy hiện hành, sau đó sẽ không xét đến nó ở bước tiếp theo,
– Ở lần xử lý thứ i có vị trí đầu dãy là i
– Lặp lại xử lý trên cho đến khi không còn
cặp phần tử nào để xét
66
Bubble sort – Thuật toán
//input: dãy (a, N)
//output: dãy (a, N) đã được sắp xếp
• Bước 1 : i = Vị trí đầu;
• Bước 2 : j = Vị trí cuối;//Duyệt từ cuối dãy ngược
về vị trí i
– Trong khi (j > i) thực hiện:
• Nếu a[j]<a[j-1]: a[j]↔↔a[j-1];//xét cặp phần tử kế cận
Trang 37Bubble sort - Cài đặt
void BubbleSort ( int a[], int N)
Trang 38Bubble sort - Đánh giá giải thuật
• Số lượng các phép so sánh xảy ra không phụ
thuộc vào tình trạng của dãy số ban đầu
• Số lượng phép hoán vị thực hiện tùy thuộc
vào kết quả so sánh
• Khuyết điểm:
– Không nhận diện được tình trạng dãy đã có
thứ tự hay có thứ tự từng phần
– Các phần tử nhỏ được đưa về vị trí đúng rất
nhanh, trong khi các phần tử lớn lại được
đưa về vị trí đúng rất chậm.
Bubble sort - Đánh giá giải thuật
Trang 39Bubble sort – Cải tiến
• Giải thuật ShakerSort:
– Dựa trên nguyên tắc đổi chỗ trực tiếp
– Tìm cách khắc phục các nhược điểm của
Giải thuật ShakerSort
//input: dãy (a, N)
//output: dãy (a, N) đã được sắp xếp
• Bước 1 :
– l = 1; r = n; //từ l đến r là đoạn cần được sắp xếp
– k = n; // ghi nhận lại vị trí k xảy ra hoán vị sau cùng
// để làm cơ sở thu hẹp đoạn l đến r
• Bước 2 :
– Bước 2a : j = r ; // đẩy phần tử nhỏ về đầu mảng
• Trong khi (j > l) :
– Nếu a[j]<a[j-1]: a[j] ↔↔a[j-1];
» k = j; //lưu lại nơi xảy ra hoán vị
– j = j-1;
• l = k; //loại bớt các phần tử đã có thứ tự ở đầu dãy
Trang 40– Nếu a[j]>a[j+1]: a[j] ↔↔a[j+1];
» k = j;//lưu lại nơi xảy ra hoán vị
– j = j+1;
• r = k; //loại bớt các phần tử đã có thứ tự ở cuối dãy
• Bước 3 : Nếu l < r: Lặp lại Bước 2
Sắp xếp cây - Heap sort
• Khi tìm phần tử nhỏ nhất ở bước i, phương
pháp sắp xếp chọn trực tiếp không tận dụng được các thông tin đã có được do các phép so sánh ở bước i-1
• Giải thuật Heap Sort khắc phục nhược điểm
này bằng cách chọn ra được một cấu trúc dữ liệu cho phép tích lũy các thông tin về sự so sánh giá trị các phần tử trong quá trình sắp xếp
Trang 41Sắp xếp cây - Heap sort
• Xét dãy số : 5 2 6 4 8 1
• Giả sử các phần tử của dãy được bố trí
theo quan hệ so sánh và tạo thành sơ
đồ dạng cây:
82
Sắp xếp cây - Heap sort
• Phần tử ở mức i chính là phần tử lớn trong cặp
phần tử tương ứng ở mức i+1
⇒ phần tử ở mức 0 (nút gốc của cây) luôn là phần tử lớn nhất của dãy
• Nếu loại bỏ phần tử gốc ra khỏi cây (nghĩa là đưa
phần tử lớn nhất về đúng vị trí), thì việc cập nhật cây chỉ xảy ra trên những nhánh liên quan đến phần tử mới loại bỏ, còn các nhánh khác được bảo toàn, nghĩa là bước kế tiếp có thể sử dụng lại các kết quả so sánh ở bước hiện tại.
Trang 42Sắp xếp cây - Heap sort
• Loại bỏ 8 ra khỏi cây và thế vào các chỗ
trống giá trị -∞ ∞ để tiện việc cập nhật lại cây :
Sắp xếp cây - Heap sort
• Toàn bộ nhánh trái của cây cũ được bảo
toàn
⇒ Bước kế tiếp để chọn được phần tử lớn
nhất hiện hành là 6, chỉ cần làm thêm
một phép so sánh 1 với 6.
Trang 43Sắp xếp cây - Heap sort
• Tiến hành nhiều lần việc loại bỏ phần tử gốc của
cây cho đến khi tất cả các phần tử của cây đều là
-∞, khi đó xếp các phần tử theo thứ tự loại bỏ trên cây sẽ có dãy đã sắp xếp
• Để cài đặt thuật toán hiệu quả, cần phải tổ chức
một cấu trúc lưu trữ dữ liệu có khả năng thể hiện được quan hệ của các phần tử trong cây với n ô nhớ thay vì 2n-1 như trong ví dụ
• Khái niệm heap và phương pháp sắp xếp Heapsort
do J.Williams đề xuất đã giải quyết được các khó khăn trên.
86
Sắp xếp cây - Heap sort
• Định nghĩa heap:
– Heap là một dãy các phần tử a left , a left+1 , ,
a right thoả các quan hệ:
• a i ≥ a 2i
• a i ≥ a 2i+1 với ∀∀i ∈∈ [left, right]
– Khi đó (a i , a 2i ), (a i ,a 2i+1 ) được gọi là các cặp phần tử liên đới.
– Heap được định nghĩa như trên được dùng
trong trường hợp sắp xếp tăng dần, khi sắp xếp giảm dần phải đổi chiều các quan hệ.
Trang 45Sắp xếp cây - Heap sort
• Một số tính chất của Heap:
– Tính chất 1: Nếu a left , a left+1 , …, a right là một heap thì khi cắt bỏ một số phần tử ở hai đầu của heap, dãy con còn lại vẫn là một heap.
– Tính chất 2: Nếu a 1 , a 2 , …, a n là một heap
thì phần tử a 1 (đầu heap) luôn là phần tử lớn nhất trong heap.
– Tính chất 3: Mọi dãy con a left , a left+1 , ,
a right thỏa: 2left > right đều là heap.
Trang 46Sắp xếp cây - Heap sort
• Sắp xếp dãy tăng dần qua 2 giai đoạn:
– Giai đoạn 1: Dựa vào tính chất 3 của heap
để hiệu chỉnh dãy ban đầu thành heap
– Giai đoạn 2: Dựa vào các tính chất 1 và 2
của heap để sắp xếp heap có được sau giai đoạn 1 thành dãy tăng dần
Heap sort – Giai đoạn 1
• Vấn đề: Đổi chổ một số phần tử trên
dãy a1, …, aN để dãy trở thành heap.
• Ý tưởng: theo tính chất 3, dãy con an/2+1 ,
an/2+2 an đương nhiên là heap Lần lượt thêm vào phía trước dãy các phần tử
an/2, an/2-1, …, a1; mỗi bước thêm vào cần hiệu chỉnh vị trí các phần tử theo mối quan hệ liên đới ta sẽ nhận được heap mong muốn.
Trang 47Heap sort – Giai đoạn 1
//input: dãy (a, N)
//output: dãy (a, N) là một heap
• Bước 1: left = N/2; //Thêm các phần tử
aleft, , a1 vào heap
• Bước 2: Trong khi left > 0
//Lưu ý: đoạn a left+1 , …, a N đã là heap
• Bước 21: Hiệu chỉnh đoạn a left , …, a N thành heap
• Bước 22: left = left - 1;
Trang 488
Trang 50Heap sort – Giai đoạn 2
• Vấn đề: Sắp xếp heap a 1 , …, a N thành dãy tăng dần
//Đặt: right = N dãy a 1 , …, a right là heap.
• Ý tưởng:
– Theo tính chất 2: a 1 sẽ là phần tử lớn nhất, vì vậy vị trí đúng của a 1 phải là right - cuối dãy.
Đổi chổ (a 1 , a right ) được thêm một phần tử ở đúng vị trí
Theo tính chất 3: dãy con a 2 , …, a right-1 vẫn là heap
Giảm right, thêm a 1 vào dãy và hiệu chỉnh lại
dãy a 1 , …, a right là heap.
Heap sort – Giai đoạn 2
//input: dãy (a, N) là heap
//output: dãy (a, N) sắp tăng dần
• Bước 1: right = N; //Bắt đầu thực hiện từ cuối
dãy
• Bước 2: Trong khi right > 1
//Đưa phần tử lớn nhất (a 1 )về vị trí right
• Bước 21: Hoánvị (a 1 , a right );
//Loại bỏ phần tử lớn nhất ra khỏi heap
• Bước 22: right := right -1;
• Bước 23: Hiệu chỉnh đoạn a 1 , a 2 , …, a right thành heap
//Hết lặp
Trang 51curr
Trang 52Shift (a, 1, right)
Trang 54Heap sort – Hiệu chỉnh heap
• Vấn đề: dãy con a left+1 , a left+2 , …, a right là heap,
cần hiệu chỉnh lại để a left , …, a right là heap
• Ý tưởng: do a left+1 , a left+2 , …, a right là heap nên tất cả các phần tử này đều đã thỏa điều kiện liên đới vấn đề trở thành: kiểm tra quan hệ liên
đới của a left và đổi chổ a left với liên đới thích hợp của nó nếu quan hệ liên đới bị vi phạm; sau khi hiệu chỉnh sự vi phạm điều kiện liên đới có thể lan truyền đến các vị trí mới hiệu chỉnh.
right
Trang 55Heap sort – Hiệu chỉnh heap
• Nhận xét: Quá trình hiệu chỉnh có nhiều
bước đổi chỗ trung gian không cần thiết
• Trong ví dụ: đổi chỗ (2, 15), sau đó đổi chổ
tiếp (2, 5): vị trí cuối cùng của 2 là 8, 2 ở vị trí 4 chỉ là tạm thời, không cần thiết
Có thể thực hiện việc dời chỗ hàng loạt,
sau đó đưa giá trị cần hiệu chỉnh vào đúng chỗ
110
Heap sort – Hiệu chỉnh heap
//input: dãy con a left+1 , a left+2 , …, a right là heap
//output: dãy con a left , a left+1 , …, a right là heap
• Bước 1:
• curr = left; x = a curr; //Bắt đầu kiểm tra từ vị trí left
• joint = 2*curr; //Liên đới thứ nhất của curr
• Bước 2: Trong khi (joint ≤ right) //Còn liên đới
để xét
• Bước 21: Nếu (joint < right) && (a joint+1 > a joint )
joint = joint + 1; //joint: vị trí liên đới lớn nhất
• Bước 22: Nếu a joint≤ x //Thỏa điều kiện liên đới
Chuyển đến Bước 3
• Bước 23: a curr = a joint ; curr = joint; joint = 2*curr;
//Hết lặp
• Bước 3: a curr = x;