Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 211 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
211
Dung lượng
1,44 MB
Nội dung
GIớI THIệU MƠN HọC Cấu Trúc Dữ Liệu Trong ngơn ngữ lập trình, liệu bao gồm hai kiểu : - Kiểu liệu đơn giản : char, int, long, float, enumeration, subrange - Kiểu liệu có cấu trúc : struct, array, file (kiểu liệu có kích thước khơng đổi) Giáo trình tập trung vào việc nghiên cứu kiểu liệu có cấu trúc có kích thước khơng đổi thay đổi ngơn ngữ lập trình, mơ tả thơng qua ngơn ngữ C Ngồi cịn giới thiệu giải thuật chung quanh cấu trúc liệu cách tổ chức, thực phép tốn tìm kiếm, thứ tự nội, thứ tự ngoại Điều kiện để tìm hiểu rõ ràng mơn học học viên biết khái niệm kỹ thuật lập trình ngơn ngữ C Trong phần mở đầu, giảng giới thiệu cách thức phân tích & thiết kế giải thuật trước tìm hiểu cấu trúc liệu cụ thể Vào cuối khóa học, sinh viên có thể: - Phân tích độ phức tạp chương trình có kích thước nhỏ trung bình - Nhận thức cần thiết việc thiết kế cấu trúc liệu - Làm quen với khái niệm stacks, queues, danh sách đặc, danh sách liên kết, nhị phân, nhị phân tìm kiếm, - Hiểu nguyên lý việc xây dựng chương trình máy tính - Có thể chọn lựa việc tổ chức liệu phù hợp giải thuật xử lý liệu có hiệu xây dựng chương trình Sinh viên cần lưu ý rằng, tùy vào công việc cụ thể mà ta nên chọn cấu trúc liệu thích hợp theo hướng tối ưu thời gian thực hay tối ưu nhớ Chương I PHÂN TíCH & THIếT Kế GIảI THUậT I mở đầu Hầu hết tốn có nhiều giải thuật khác để giải chúng Vậy làm chọn giải thuật tốt ? Việc chọn lựa phụ thuộc vào nhiều yếu tố : Độ phức tạp tính tốn giải thuật, chiếm dung lượng nhớ, tần suất sử dụng, tính đơn giản, tốc độ thực Thông thường mục tiêu chọn lựa : Giải thuật rõ ràng, dễ hiểu, dễ mã hóa hiệu chỉnh Giải thuật sử dụng có hiệu tài nguyên máy tính đặc biệt chạy nhanh tốt Do viết chương trình để chạy lần chạy mục tiêu quan trọng Ngược lại viết chương trình để chạy nhiều lần phí tổn chạy chương trình vượt q phí tổn lập chương trình, phải nhập nhiều số liệu Nói chung, người lập trình phải biết chọn lựa, viết, đánh giá giải thuật để có giải thuật tối ưu cho tốn II đánh giá thời gian chạy chương trình Thời gian chạy chưong trình phụ thuộc vào : Input cho chương trình Chất lượng mã sinh chương trình dịch Trạng thái tốc độ lệnh chạy máy Độ phức tạp thời gian giải thuật Điều chức nhập Kích thước input (ví dụ n) ta thường ký hiệu T(n) đại lượng thời gian cần thiết để giải tốn kích thước n Điều 2, thường đánh giá khó khăn phụ thuộc vào phần mềm chương trình dịch phần cứng máy Điều điều mà người lập trình cần khảo sát để làm tăng tốc độ chương trình III ký hiệu o(n) Ω (n) : Ta đánh giá tỷ lệ phát triển hàm T(n) qua ký hiệu O(n) Ta nói thời gian chạy T(n) chương trình O(n2) có nghĩa : ∃ c > n0 cho ∀ n ≥ n0 ta có T(n) ≤ c.n2 Ví dụ : Giả sử T(0) = 1, T(1) = 4, v v Tổng quát T(n) = (n +1)2 ta nói T(n) O(n2) đặt c1 = 4, n0 = 1, n ≥ ta có (n +1)2 ≤ 4n2 Nhưng khơng thể lấy n0 = T(0) = khơng nhỏ c.0 = 0,∀c; giả thiết n ≥ T(n) ≥ Ta nói T(n) O(f(n)) ∃ const c n0 cho T(n) ≤ c.f(n), ∀ n ≥ n0 Chương trình chạy với thời gian O(f(n)) ta nói phát triển tỷ lệ với f(n) Khi nói T(n) O(f(n)) f(n) chặn T(n) Để nói chặn T(n) ta dùng ký hiệu Ω Ta nói T(n) Ω(g(n)) ∃ const c, n0 cho T(n) ≥ c.g(n), ∀ n ≥ n0 Ví dụ : Để kiểm tra T(n) = n3 + 2n2 Ω(n3) ta đặt c = T(n) ≥ c.n3, ∀n = 0, 1, (no= 0) * Sự trái ngược tỷ lệ phát triển : Ta giả sử chương trình đánh giá cách so sánh hàm thời gian chúng với tỷ lệ không đáng kể Khi ta nói chương trình có thời gian chạy O(n2) Nếu chương trình chạy 100.n thời gian (mili giây) chương trình chạy 5.n thời gian, ta có tỷ số thời gian chương trình 5.n3/100.n2 = n/20, nghĩa n = 20 thời gian chạy chương trình nhau, n < 20 chương trình chạy nhanh chương trình Do n > 20 nên dùng chương trình Ví dụ : Có chương trình có độ phức tạp khác biểu diễn bảng Thời gian chạy T(n) 100.n 5.n2 n3/2 2n Kích thước tốn tối đa cho 103s 10 14 12 10 Kích thước tốn tối đa cho 104s 100 45 27 13 Tỷ lệ tăng kích thước 10.0 lần 3.2 lần 2.3 lần 1.3 lần Giả sử 103s chương trình giải tốn có kích thước tối đa cột Nếu có máy tốt tốc độ tăng lên 10 lần kích thước tối đa tương ứng chương trình trình bày cột Tỉ lệ hai cột 1,2 ghi cột Như đầu tư tốc độ 10 lần thu lợi có 30% kích thước tốn dùng chương trình có độ phức tạp O(2n) IV cách tính thời gian chạy chương trình : Qui tắc tổng: Giả sử T1(n) T2(n) thời gian chạy chương trình P1 P2 tương ứng đánh giá O(f(n)) O(g(n)) Khi T 1(n) + T2(n) O(max(f(n),g(n))) (chạy xong chương trình P1 chạy P2) Chứng minh: Theo định nghĩa O(f(n)) O(g(n)) ∃ c1, n1, c2, n2 cho T1(n) ≤ c1.f(n) ∀ n ≥ n1 ; T2(n) ≤ c2.g(n) ∀ n ≥ n2 Đặt n0 = max(n1, n2) Nếu n ≥ no T1(n) + T2(n) ≤ (c1 + c2).max(f(n),g(n)) Qui tắc tích: T1(n) T2(n) O(f(n).g(n)) Chứng minh : tương tự tổng Ví dụ : Có chương trình có thời gian chạy tương ứng O(n 2), O(n3), O(n.logn) Thế thời gian chạy chương trình đồng thời O(max(n 2, n3, nlogn)) O(n3) Nói chung thời gian chạy dãy cố định bước thời gian chạy lớn bước dãy Cũng có trường hợp có hay nhiều bước có thời gian chạy không tương xứng (không lớn m khơng nh ỏ h ơn) Khi qui tắc tính tổng phải tính trường hợp Ví dụ : f(n) = g(n) = { { n4 n chẵn n2 n2 n lẻ n chẵn n3 n lẽ Thời gian chạy O(max(f(n),g(n))) n4 n chẵn n3 n lẻ Nếu g(n) ≤ f(n), ∀ n ≥ no, no const O(f(n)+g(n)) O(f(n)) Ví dụ : O(n2 + n) = O(n2) Trước đưa qui tắc chung để phân tích thời gian chạy chương trình ta xét ví dụ đơn giản sau Ví dụ : Xét chương trình Bubble dùng dãy số nguyên theo chiều tăng Procedure Bubble (var A: array [1 n] of integer); Var i, j, temp : integer ; Begin For i := to n For j := n downto i If A[j-1] > A[j] then Begin temp := A[j-1] ; A[j-1] := A[j] ; A[j] := temp ; End ; End ; Phân tích : - N số phần tử - kích thước tốn Mỗi lệnh gán từ dòng - > dòng đơn vị thời gian, theo qui tắc tính tổng O(max(1,1,1) = O(1) - Vòng If For lồng nhau, ta phải xét từ Đối với điều kiện sau If phải kiểm tra O(1) thời gian Ta không thân lệnh If từ - có thực hay khơng Vì xét trường hợp xấu nên ta giả thuyết lệnh từ - có thực Vậy nhóm If từ lệnh -6 làm O(1) thời gian - Ta xét vịng lặp ngồi từ - Nguyên tắc chung vòng lặp: thời gian vòng lặp tổng thời gian lần lặp thân vịng lập O(1) cho lần lặp số tăng Số lần lặp từ - n - i +1 Vậy theo qui tắc tích : O((n - i +1), 1) O(n -i +1) - Ta xét vịng ngồi chứa lệnh chương trình Lệnh làm n-1 lần, tốn n-1 đơn vị thời gian Vậy tổng thời gian chạy chương trình bị chặn thời gian cố định : n ∑ (n − i + 1) = n * (n − 1) / tức O(n2) i =2 Tuy nhiên khơng có qui tắc đầy đủ để phân tích chương trình Nói chung thời gian chạy lệnh nhóm lệnh hàm kích thước input hay nhiều biến Nhưng có n - kích thước tốn thơng số cho phép thời gian chạy chương trình Qui tắc tính thời gian chạy a) Thời gian chạy lệnh gán, read, write có giả thiết O(1) b) Thời gian chạy dãy lệnh xác định theo qui tắc tổng; nghĩa thời gian chạy dãy thời gian lớn lệnh dãy lệnh c) Thời gian chạy lệnh If thời gian thực lệnh điều kiện cộng với thời gian kiểm tra điều kiện Thời gian thực lệnh If có cấu trúc If then eles thời gian kiểm tra điều kiện cộng với thời gian lớn lệnh rẽ nhánh true false d) Thời gian thực vòng lặp tổng thời gian thực thân vòng lặp thời gian kiểm tra kết thúc vòng lặp e) Gọi thủ tục:Nếu chương trình có thủ tục khơng có thủ tục đệ qui ta tính thời gian chạy lúc, thủ tục không gọi đến thủ tục khác Tất nhiên phải có thủ tục trường hợp này, khơng phải có thủ tục đệ qui Sau ta đánh giá thời gian chạy thủ tục có gọi, đến thủ tục không chứa lời gọi đánh giá Cứ ta lại đánh giá thời gian chạy thủ tục có lời gọi đến thủ tục đánh giá, nghĩa thủ tục đánh giá sau đánh giá hết thủ tục mà gọi Nếu có thủ tục đệ qui khơng thể tìm thứ tự tất thủ tục cho thủ tục gọi đến thủ tục đánh giá Khi ta phải lập liên hệ thủ tục đệ qui với hàm thời gian chưa biết T(n) n kích thước đối số thủ tục Lúc ta nhận truy hồi T(n), nghĩa phương trình diễn tả T(n) qua T(k) với giá trị k khác Ví dụ : Xét chương trình đệ qui tính n giai thừa (n!), n kích thước hàm nêu Function Fact (n:integer) : LongInt ; Begin If n d n ≤ T(n) = Giải phương trình : Giả sử n > 2, ta khai triển T(n-1) cơng thức : T(n) = 2.c + T(n-2) n > Sau ta lại thay T(n-2) = c + T(n-3) ta T(n) = 3.c + T(n-3) n > T(n) = i.c + T(n-i) n > i Cuối ta thay i = n - 1, ta T(n) = c(n-1) + T(1) = c(n-1) + d Kết luận T(n) O(n) V phân lớp thuật toán : Như ý trên, hầu hết thuật tốn có tham số N, Thơng thường số lượng phần tử liệu xử lý mà ảnh hưởng nhiều tới thời gian chạy Tham số N bậc đa thức, kích thước tập tin xếp hay tìm kiếm, số nút đồ thị Hầu hết tất thuật tốn giảng có thời gian chạy tiệm cận tới hàm sau : Hầu hết tất thị chương trình thực lần hay nhiều vài lần Nếu tất thị chương trình có tính chất nói thời gian chạy số Điều hiển nhiên mục tiêu phấn đấu để đạt việc thiết kế thuật toán logN Khi thời gian chạy chương trình logarit, tức thời gian chạy chương trình tiến chậm N lớn dần Thời gian chạy loại xuất chương trình mà giải tốn lớn cách chuyển thành tốn nhỏ hơn, cách cắt bỏ kích thước bớt số Với mục đích chúng ta, thời gian chạy có xem nhỏ số "lớn" Cơ số logarit làm thay đổi số khơng nhiều: Khi n 1000 logN số 10; 10 số ; N 1000000, logN nhân gấp đôi Bất N nhân gấp đôi, logN tăng lên thêm số, logN không nhân gấp đôi tới N tăng tới N2 N Khi thời gian chạy chương trình tuyến tính, nói chung trường hợp mà số lượng nhỏ xử lý làm cho phần tử liệu nhập Khi N 1.000.000 thời gian chạy cỡ Khi N nhân gấp đôi thời gian chạy nhân gấp đơi Đây tình tối ưu cho thuật tốn mà phải xử lý N liệu nhập (hay sản sinh N liệu xuất) NlogN Đây thời gian chạy tăng dần lên cho thuật toán mà giải tốn cách tách thành toán nhỏ hơn, giải chúng cách độc lập sau tổ hợp lời giải Bởi thiếu tính từ tốt (có lẽ "tuyến tính logarit" ?), nói thời gian chạy thuật toán "NlogN" Khi N 1000000, NlogN có lẽ khoảng triệu Khi N nhân gấp đôi, thời gian chạy bị nhân lên nhiều gấp đôi (nhưng không nhiều lắm) N2 Khi thời gian chạy thuật tốn bậc hai, trường hợp có ý nghĩa thực tế cho toán tương đối nhỏ Thời gian bình phương thường tăng lên thuật toán mà xử lý tất cặp phần tử liệu (có thể vịng lặp lồng nhau) Khi N 1000 thời gian chạy 1000000 Khi N nhân đơi thời gian chạy tăng lên gấp lần N3 Tương tự, thuật toán mà xử lý phần tử liệu (có lẽ vịng lặp lồng nhau) có thời gian chạy bậc có ý nghĩa thực tế toán nhỏ Khi N 100 thời gian chạy 1.000.000 Khi N nhân đơi thời gian chạy tăng lên gấp lần 2n Một số thuật tốn có thời gian chạy lũy thừa lại thích hợp số trường hợp thực tế, thuật tốn "sự ép buộc thơ bạo" để giải tốn Khi N 20 thời gian chạy xấp xỉ 1.000.000 Khi N gấp thời gian chạy nâng lên lũy thừa Thời gian chạy chương trình cụ thể số nhân với số hạng nói cộng thêm số hạng nhỏ Các giá trị số số hạng phụ thuộc vào kết phân tích chi tiết cài đặt Hệ số số liên quan tới số thị bên vòng lặp : tầng tùy ý thiết kế thuật tốn phải cẩn thận giới hạn số thị Với N lớn số đóng vai trị chủ chốt, với N nhỏ số hạng đóng góp vào so sánh thuật tốn khó khăn Ngồi hàm vừa nói cịn có số hàm khác, ví dụ thuật toán với N phần tử liệu nhập mà có thời gian chạy bậc theo N phân lớp thuật tốn N 3/2 Một số thuật tốn có giai đoạn phân tách thành toán có thời gian chạy xấp xỉ với Nlog2N VI công thức truy hồi sở : Phần lớn thuật toán dựa việc phân rã đệ qui toán lớn thành toán nhỏ hơn, dùng lời giải toán nhỏ để giải toán ban đầu Thời gian chạy thuật tốn xác định kích thước số lượng toán giá phải trả phân rã Trong phần ta quan sát phương pháp sở để phân tích thuật tốn trình bày vài công thức chuẩn thường áp dụng việc phân tích nhiều thuật tốn Tính chất tự nhiên chương trình đệ qui thời gian chạy cho liệu nhập có kích thước N phụ thuộc vào thời gian chạy cho liệu nhập có kích thước nhỏ : điều diễn dịch thành cơng thức tốn học gọi quan hệ truy hồi Các cơng thức mơ tả xác tính thuật tốn tương ứng, để có thời gian chạy phải giải toán truy hồi Bây ý vào công thức thuật tốn Cơng thức : Cơng thức thường dùng cho chương trình đệ qui mà có vịng lặp duyệt qua liệu nhập để bỏ bớt phần tử Cn = Cn-1 + n, với n >= C1 = Chứng minh : Cn khoảng n2/2 Để giải công thức truy hồi trên, áp dụng cơng thức sau : Cn = Cn-1 + n = Cn-2 + (n-1) + n = = C1 + + + (n-2) + (n-1) + n = + + + n = n(n+1)/2 Công thức : Cơng thức dùng cho chương trình đệ qui mà chia liệu nhập thành phần bước Cn = Cn/2 + 1, với n >= C1 = Chứng minh : Cn khoảng logn Phương trình vơ nghĩa n chẵn hay giả sử n/2 phép chia nguyên : giả sử n = m công thức luôn có nghĩa Chúng ta viết sau : C 2m = C 2m −1 + = C 2m − + = C 2m − + = = C 2m − m + m = m = log n Cơng thức xác cho n tổng quát phụ thuộc vào biểu diễn nhị phân n, nói chung Cn khoảng logn với n Công thức : Công thức dùng cho chương trình đệ qui mà chia đơi liệu nhập kiểm tra phần tử liệu nhập Cn = Cn/2 + n, với n >= C1 = Chứng minh : Cn khoảng 2n Tương tự trên, cơng thức tổng n + n/2 + n/4 + (dĩ nhiên điều xác n lũy thừa 2) 10 0 0 0 0 0 0 1 1 1 P4 = 0 0 0 0 0 0 0 1 1 1 1 P5 = 0 0 0 0 0 0 0 1 1 1 1 P3 = Bao đóng truyền ứng đồ thị G : 0 0 0 0 0 1 0 * Chương trình: #include #include const MAX = 4; int G[MAX][MAX]= { {0,0,1,0}, {0,0,1,0}, {0,0,1,1}, {0,1,0,0} }; void Xuat(int P[][MAX]) { int i,j; for( i=0; i