Đến một bước nào đó, khi khôngcó phép đổi chỗ nào xảy ra thì toàn bộ dãy đã được sắp.Như vậy, sau lần duyệt đầu tiên, phần tử nhỏ nhất của dãy sẽ lần lượt được đổi chỗ cho cácphần tử lớn
Trang 1BỘ GIÁO DỤC VÀ ĐÀO TẠO TRƯỜNG ĐẠI HỌC CÔNG NGHỆ ĐÔNG Á
Trang 2BỘ GIÁO DỤC VÀ ĐÀO TẠO TRƯỜNG ĐẠI HỌC CÔNG NGHỆ ĐÔNG Á
Trang 3Tên sinh viên Mã sinh viên Phân Công
Heap Sort Định giá biểu thức số học
Merge Sort Chuyển từ 10 -> 2
Quick Sort Cây nhị phân
Insertion Sort Stooge Sort
Bubble Sort Tìm kiếm tuyến tính
Select Sort Tìm kiếm Nhị Phân
Trang 4MỤC LỤC
Nô 6i dung
Trang 5NỘI DUNG:
I.Các thuật toán sắp xếp
1 Bubble Sort – Nổi Bọt :
Giải thuật sắp xếp nổi bọt được thực hiện theo nguyên tắc: Duyệt nhiều lần từ cuối lên đầudãy, tiến hành đổi chỗ 2 phần tử liên tiếp nếu chúng ngược thứ tự Đến một bước nào đó, khi không
có phép đổi chỗ nào xảy ra thì toàn bộ dãy đã được sắp
Như vậy, sau lần duyệt đầu tiên, phần tử nhỏ nhất của dãy sẽ lần lượt được đổi chỗ cho cácphần tử lớn hơn và “nổi” lên đầu dãy Lần duyệt thứ 2, phần tử nhỏ thứ 2 sẽ nổi lên vị trí thứ nhì dãy.v.v
● Ý tưởng:
▪ Xuất phát từ cuối dãy, đổi chỗ các cặp phần tử kế cận để đưa phần tử nhỏ hơn trong cặp phần tử đó về vị trí đúng đầu dãy hiện hành, sau đó sẽ không xét đến nó ở bước tiếp theo, do vậy ở lần xử lý thứ i sẽ 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
● Các Bước Của Thuật Toán
❖ Bước 1 : i = 0; // lần xử lý đầu tiên
❖ Bước 2 : j = N-1;//Duyệt từ cuối dãy ngược về vị trí i
Trong khi (j > i) thực hiện:
Ngược lại : Lặp lại Bước 2
● Cài Đặt Thuật Toán Nổi Bọt :
Trang 6# hai số sẽ đổi chỗ cho nhau
arr[j], arr[j + ] = arr[j + 1 1] arr[j],
arr = [4, , , , , , , , 9 14 6 17 8 5 2 11 3 27 12, , , ]
bubbleSort(arr)
print("Sorted array is:")
for in i range len( (arr)):
Trang 9● Độ Phức Tạp Của Thuật Toán Nổi Bọt
Thuật toán sắp xếp nhanh QuickSort:
Sắp xếp nhanh (quicksort) là là thuật toán sắp xếp dựa trên kỹ thuật chia để trị, cụ thể ý tưởng là: chọn một điểm làm chốt (gọi là pivot), sắp xếp mọi phần tử bên trái chốt đều nhỏ hơn chốt và mọi phần tử bên phải đều lớn hơn chốt, sau khi xong ta được 2 dãy con bên trái và bên phải, áp dụng tương tự cách sắp xếp này cho 2 dãy con vừa tìm được cho đến khi dãy con chỉ còn 1 phần tử
Phần tử được chọn làm chốt rất quan trọng, nó quyết định thời gian thực thi của thuật toán Phần tử được chọn làm chốt tối ưu nhất là phần tử trung vị, phần tử này làm cho số phần tử nhỏ hơn trong dãy bằng hoặc xấp xỉ số phần tử lớn hơn trong dãy Tuy nhiên, việc tìm phần tử nàyrất tốn kém, phải có thuật toán tìm riêng, từ đó làm giảm hiệu suất của thuật toán tìm kiếm nhanh,
do đó, để đơn giản, người ta thường sử dụng phần tử chính giữa làm chốt
Thuật toán phân chia và trị này là thuật toán sắp xếp được sử dụng thường xuyên nhất được đề cập trong bài viết này Khi được định cấu hình chính xác, nó cực kỳ hiệu quả và không yêu cầu sửdụng merge sort Chúng tôi phân vùng danh sách xung quanh một phần tử trục, sắp xếp các giá trịxung quanh trục
Chọn pivot là số cuối cùng có giá trị 32
So sánh số bên trái đầu tiên là 50 > 23(số bên phải) và 50 > 32 (pivot) => Đổi vị trí 50 với 23
So sánh tiếp tục 50 > 9 (số bên phải) và 50 > 32 (pivot) => Đổi vị trí 50 với 9
So sánh tiếp tục 50 > 18 (số bên phải) và 50 > 32 (pivot) => Đổi vị trí 50 với 18
So sánh tiếp tục 50 < 61 (số bên phải) và 50 > 32 (pivot) => Giữ nguyên vị trí 50
So sánh tiếp tục 50 < 32 (số bên phải) và 50 > 32 (pivot) => Đổi vị trí 50 với 32
Trang 10Vậy sau bước này chúng ta có 2 mảng lớn hơn 32 và nhỏ hơn 32 Tiếp tục làm lại như vậy với 2 mảnh
Trong bài viết này, mình cũng sẽ sử dụng phần tử chính giữa làm chốt, thuật toán cài đặt trong PyThon như sau
Code :
defpartition(arr low high, , ):
= ( i low-1 )
pivot arr = [high]
forjinrange(low high , ):
ifarr[] <= pivot:
= i i+
arr[i arr], [] = arr[j arr], []
arr[+ ],1arr[high] = arr[high arr], [+]
return ( i+ )
defquickSort(arr low high, , ):
iflow high < :
= pi partition(arr low high, , )
quickSort(arr low pi, , -1)
quickSort(arr pi, + , high)
arr = [int( ) xfor xininput("Nhập Số ").split(",")]
Trang 114 1 2 5 , 26, 14, 3,7, 12 26>= 7 >= 3; Swap (26,7)
5 1 2 , , 3 5 7, , , 14 26, 12 26>= 14 >= 12; Swap (7,3)
3 Merge Sort - Sắp Xếp Hợp Nhất :
❖ Giải thuật Merge sort sắp xếp dãy a , a , , a1 2 n dựa trên nhận xét sau:
▪ Mỗi dãy a1, a , , a2 n bất kỳ là một tập hợp các dãy con liên tiếp mà mỗi dãy con đều đã có thứ tự
• Ví dụ: dãy 2 ,5 ,4, 3, 9, 8, 6, 17 ,14, 11có thể coi như gồm 6 dãy con không giảm (2); (5, 4); (3); (9, 8); (6, 17);(14,11)
▪ Dãy đã có thứ tự coi như có 1 dãy con.
❖ Hướng tiếp cận: tìm cách làm giảm số dãy con không giảm của dãy ban đầu.
MergeSort arr mid+ ( , 1,R)
Merge arr mid ( , , L, R)
} }
❖ Các dãy con tăng dần sẽ được tách ra 2 dãy phụ theo nguyên tắc phân phối đều luân phiên
❖ Trộn từng cặp dãy con của hai dãy phụ thành một dãy con của dãy ban đầu � dãy mới có số lượng dãy con giảm đi so với dãy ban đầu
❖ Bước 1 : k = 1; // dãy con có 1 phần tử là dãy không giảm
❖ Bước 2 : Lặp trong khi (k < N) // dãy còn hơn 1 dãy con
Trang 12▪ Bước 2.1: Phân phối đều luân phiên dãy a , a , …, a thành 2 dãy 1 2 n
b, c theo từng nhóm k phần tử liên tiếp nhau.
# Sao chép dữ liệu vào mảng tạm thời L [] và R []
while i < len(L) and j < len(R):
for in range leni ( (arr)):
print(arr[i], end=" ")
Trang 13- Độ phức tạp của thuật toán:
Độ phức tạp về thời gian: Sắp xếp các mảng trên các máy khác nhau Merge Sort là một
thuật toán đệ quy và độ phức tạp thời gian có thể được biểu thị như sau
T (n) = 2T (n / 2) + θ (n)
- Sự lặp lại trên có thể được giải quyết bằng cách sử dụng phương pháp tree lặp lại hoặc phương pháp Master Nó nằm trong trường hợp II của Phương pháp Master và nghiệm của sự tái diễn là θ (nLogn) Độ phức tạp thời gian của Sắp xếp hợp nhất(Merge Sort) là θ(nLogn) trong cả 3 trường hợp (xấu nhất, trung bình và tốt nhất) vì sắp xếp hợp nhất luôn chia mảng thành hai nửa và mất thời gian tuyến tính để hợp nhất hai nửa
Trang 14Giải thuật này coi như dãy được chia làm 2 phần Phần đầu là các phần tử đã được sắp Từ phần tử tiếp theo, chèn nó vào vị trí thích hợp tại nửa đã sắp sao cho nó vẫn được sắp
● Giả sử có một dãy a , a , ,a trong đó i phần tử đầu tiên a , a , ,a đã 0 1 n-1 0 1 i-1
có thứ tự
● Tìm cách chèn phần tử a vào vị trí thích hợp của đoạn đã được sắp để có i
dãy mới a , a , ,a trở nên có thứ tự Vị trí này chính là vị trí giữa hai 0 1 i
phần tử a và a thỏa a < a < a (1≤k≤i) k-1 k k-1 i k
Các Bước Của Thuật Toán
1 Bước 1 : i = 1; //giả sử có đoạn a[1] đã được sắp
2 Bước 2 : x = a[i]; Tìm vị trí pos thích hợp trong đoạn a[1] đến a[i-1]
# Di chuyển các phần tử của arr [0 i-1], đó là
# lớn hơn khóa, đến một vị trí phía trước
# vị trí hiện tại của họ
Trang 1611 12 (a[10];a[11])
5 Select Sort – Sắp Xếp Chọn:
▪ Chọn phần tử nhỏ nhất trong n phần tử trong dãy hiện hành ban đầu
▪ Đưa phần tử này về vị trí đầ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 hiện hành ban đầu
▪ Bước 2 : Tìm phần tử a[min] nhỏ nhất trong dãy hiện hành từ a[i] đến a[n]
▪ Bước 3 : Đổi chỗ a[min] và a[i]
for in i range len( (A)):
# Tìm phần tử tối thiểu còn lại
# Mã trình điều khiển để kiểm tra ở trên
print ("Sorted array")
Trang 188 9 11 2 3 4 5 6 8 9 11 12 17 27 14 a[8], a[11]
● Sắp xếp Stooge là một thuật toán sắp xếp đệ quy Nó được định nghĩa như bên dưới (để sắp xếp thứ tự tăng dần)
Bước 1: Nếu giá trị tại chỉ mục 0 lớn hơn
giá trị ở chỉ mục cuối cùng, hoán đổi chúng
Bước 2: Đệ quy,
a) Stooge sắp xếp 2/3 ban đầu của mảng
b) Stooge sắp xếp 2/3 cuối cùng của mảng
c) Stooge sắp xếp lại 2/3 ban đầu để xác nhận
●LƯU Ý: Luôn lấy điểm kết thúc của ((2/3) * N) để chọn phần tử
Minh họa thuật toán :
Trang 19l >= h:if
return
# Nếu phần tử đầu tiên nhỏ hơn
# cuối cùng, hãy hoán đổi chúng
Trang 207 Heap Sort – Sắp Xếp Đống:
Heap sort là một kỹ thuật sắp xếp dựa trên so sánh dựa trên cấu trúc dữ liệu Binary Heap Nó tương tự như sắp xếp lựa chọn trong đó đầu tiên chúng ta tìm phần tử tối thiểu và đặt phần tử tối thiểu ở đầu Chúng tôi lặp lại quá trình tương tự cho các phần tử còn lại
Tại sao biểu diễn dựa trên mảng cho Binary Heap?
Vì một đống nhị phân là một cây nhị phân hoàn chỉnh, nó có thể dễ dàng được biểu diễn dưới dạng một mảng và biểu diễn dựa trên mảng là không gian hiệu quả Nếu nút cha được lưu trữ ở chỉ mục I, nút con bên trái có thể được tính bằng 2 * I + 1 và nút con bên phải bằng 2 * I + 2 (giả
sử việc lập chỉ mục bắt đầu bằng 0)
Thuật toán sắp xếp đống để sắp xếp theo thứ tự tăng dần:
1 Xây dựng một đống tối đa từ dữ liệu đầu vào
2 Tại thời điểm này, mục lớn nhất được lưu trữ ở gốc của đống Thay thế nó bằng mục cuối cùng
của đống, sau đó giảm kích thước của đống xuống 1 Cuối cùng, chất đống gốc của cây
3 Lặp lại bước 2 khi kích thước của đống lớn hơn 1.
Các số trong ngoặc đại diện cho các chỉ số trong mảng
biểu diễn dữ liệu
Áp dụng quy trình heapify cho chỉ mục 1:
Trang 21/ \
4 (3) 1 (4)
Thủ tục heapify tự gọi đệ quy để xây dựng heap
theo cách từ trên xuống
Code:
# Để xếp đống cây con bắt nguồn từ chỉ mục i
# n là kích thước của đống
def heapify(arr n i):, ,
largest = i # Initialize largest as root
Trang 22● Kết Quả:
II.Các thuật toán Tìm Kiếm :
Binany Search (Tìm kiếm nhị phân) là một giải thuật tìm kiếm nhanh với độ phức tạp thời gian chạy
là Ο(log n) Giải thuật tìm kiếm nhị phân làm việc dựa trên nguyên tắc chia để trị (Divide and Conquer) Để giải thuật này có thể làm việc một cách chính xác thì tập dữ liệu nên ở trong dạng đã được sắp xếp
- Binary Search tìm kiếm một phần tử cụ thể bằng cách so sánh phần tử tại vị trí giữa nhất của tập dữ liệu Nếu tìm thấy kết nối thì chỉ mục của phần tử được trả về Nếu phần tử cần tìm là lớn hơn giá trị phần tử giữa thì phần tử cần tìm được tìm trong mảng con nằm ở bên phải phần
tử giữa; nếu không thì sẽ tìm ở trong mảng con nằm ở bên trái phần tử giữa Tiến trình sẽ tiếp tục như vậy trên mảng con cho tới khi tìm hết mọi phần tử trên mảng con này
Cách Binary Search làm việc
- Để Binary Search làm việc thì mảng phải cần được sắp xếp Để tiện cho việc theo dõi, mình
sẽ cung cấp thêm các hình minh họa tương ứng với mỗi bước
Giả sử chúng ta cần tìm vị trí của giá trị 31 trong một mảng bao gồm các giá trị như hình dưới đây bởi sử dụng Binary Search:
Đầu tiên, chúng ta chia mảng thành hai nửa theo phép toán sau:
Với ví dụ trên là 0 + (9 – 0)/ 2 = 4 (giá trị là 4.5) Do đó 4 là chỉ mục giữa của mảng.
Trang 23Bây giờ chúng ta so sánh giá trị phần tử giữa với phần tử cần tìm Giá trị phần tử giữa là 27 và phần tử cần tìm là 31, do đó là không kết nối Bởi vì giá trị cần tìm là lớn hơn nên phần tử cần tìm sẽ nằm ở mảng con bên phải phần tử giữa.
Chúng ta thay đổi giá trị ban-đầu thành chỉ-mục-giữa + 1 và lại tiếp tục tìm kiếm giá trị chỉ-mục-giữa.
[ban-đầu = chỉ-mục-giữa + 1]
[chỉ-mục-giữa = ban-đầu + (cuối + ban-đầu)/ 2]
Bây giờ chỉ mục giữa của chúng ta là 7 Chúng ta so sánh giá trị tại chỉ mục này với giá trị cần tìm.
Giá trị tại chỉ mục 7 là không kết nối, và ngoài ra giá trị cần tìm là nhỏ hơn giá trị tại chỉ mục 7 do đó chúng ta cần tìm trong mảng con bên trái của chỉ mục giữa này.
Tiếp tục tìm chỉ-mục-giữa lần nữa Lần này nó có giá trị là 5.
So sánh giá trị tại chỉ mục 5 với giá trị cần tìm và thấy rằng nó kết nối.
Trang 24Do đó chúng ta kết luận rằng giá trị cần tìm 31 được lưu giữ tại vị trí chỉ mục 5
Binary Search chia đôi lượng phần tử cần tìm và do đó giảm số lượng phép so sánh cần thực hiện nên giải thuật tìm kiếm này được thực hiện khá nhanh.
2 Tìm kiếm tuyến tình - Linear Search
Mỗi phần tử đều được kiểm tra và nếu tìm thấy bất kỳ kết nối nào thì phần tử cụ thể đó được trả về; nếu không tìm thấy thì quá trình tìm kiếm tiếp tục diễn ra cho tới khi tìm kiếm hết dữ liệu
Một cách tiếp cận đơn giản là thực hiện tìm kiếm tuyến tính , tức là
Bắt đầu từ phần tử ngoài cùng bên trái của arr [] và lần lượt so sánh x với từng phần tử của arr []
Nếu x khớp với một phần tử, trả về chỉ mục
Trang 25Nếu x không khớp với bất kỳ phần tử nào, trả về -1.
Bước 1 : Tạo 1 mảng stack
Bước 2 : Đưa phần dư của số nguyên chia cho 2 vào mảng stack Bước 3 : Tạo 1 biến b
Bước 4 : Đọc giá trị stack lấy giá trị từ cuối chuỗi lên đầu
Minh họa các bước:
Trang 261 Giá trị của tất cả các Node ở cây con bên trái phải <= giá trị của Node gốc.
2 Giá trị của tất cả các Node ở cây con bên phải phải > giá trị của Node gốc
3 Tất cả các cây con(bao gồm bên trái và phải) cũng đều phải đảm bảo 2 tính chất trênCây tìm kiếm nhị phân là một cấu trúc dữ liệu hiệu quả cho phép chúng ta xây dựng nên một danh sách mà dữ liệu trên đó được sắp xếp:
● Nó được gọi là cây nhị phân vì mỗi Node của cây chỉ có tối đa hai con
● Nó được gọi là cây tìm kiếm nhị phân vì nó có thể được sử dụng để tìm kiếm sự hiện diện của một phần tử trong thời gian O(log (n))
● Code:
Trang 27class BinaryTree:
def init (self):
self. left = None
self. right = None
self. value = None
def add_value(self, value):
if self. value is None:
self. value = value
elif value > self. value:
if self. right is None:
self. right = BinaryTree()
self. right.add_value(value)
elif value < self. value:
if self. left is None:
self. left = BinaryTree()
self. left.add_value(value)
def pre_order_traverse(self, visitor_func=None):
# Thực hiện đơn đặt hàng trước của cây
# Quay lại danh sách các phần tử
if self. value is not None:
# Truy cập vào nút gốc trước tiên
if visitor_func:
visitor_func(self. value)
# Sau đó truy cập nút bên trái nếu có
if self. left is not None:
self. left.pre_order_traverse(visitor_func)
# Sau đó truy cập vào nút bên phải nếu có
if self. right is not None:
self. right.pre_order_traverse(visitor_func)
def in_order_traverse(self, visitor_func=None):
# Thực hiện truyền đi theo thứ tự của cây
# Quay lại danh sách các phần tử
if self. value is not None:
# Truy cập nút bên trái trước nếu có
if self. left is not None:
self. left.in_order_traverse(visitor_func)
# Sau đó, hãy truy cập vào nút gốc
if visitor_func:
visitor_func(self. value)
# Sau đó truy cập vào nút bên phải nếu có
if self. right is not None:
self. right.in_order_traverse(visitor_func)
def post_order_traverse(self, visitor_func=None):
# Thực hiện truyền qua thứ tự sau của cây
# Quay lại danh sách các phần tử
if self. value is not None:
# Truy cập nút bên trái trước nếu có
Trang 28self. left.post_order_traverse(visitor_func)
# Sau đó truy cập vào nút bên phải nếu có
if self. right is not None:
Trang 29II Duyệt trung thứ tự trong cây nhị phân
- Trong cách duyệt này, cây con bên trái được truy cập đầu tiên, sau đó là nút gốc vàsau đó là cây con bên phải Bạn nên luôn luôn ghi nhớ rằng mỗi nút đều có thể biểudiễn một cây con
- Nếu một cây nhị phân được duyệt trung thứ tự, kết quả tạo ra sẽ là các giá trị khóađược sắp xếp theo thứ tự tăng dần
- Ở hình ví dụ minh họa trên, là nút gốc Với phương thức duyệt trung thứ tự, chúngA
ta bắt đầu từ nút gốc , di chuyển tới cây con bên trái của nút gốc Tại đây, cũngA B B
được duyệt theo cách thức duyệt trung thứ tự Và tiến trình tiếp tục cho đến khi tất cảcác nút đã được truy cập Kết quả của cách thức duyệt trung thứ tự cho cây trên sẽ là:
D → B → E → A → F → C → G
- Trong cách thức duyệt hậu thứ tự trong cây nhị phân, nút gốc của cây sẽ được truycập cuối cùng, do đó bạn cần chú ý Đầu tiên, chúng ta duyệt cây con bên trái, sau đó
sẽ duyệt cây con bên phải và cuối cùng là duyệt nút gốc