1. Trang chủ
  2. » Công Nghệ Thông Tin

thuật toán trên cấu trúc cây

32 275 0
Tài liệu đã được kiểm tra trùng lặ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

Thông tin cơ bản

Định dạng
Số trang 32
Dung lượng 478,71 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

2003 - 2005 5- xấu nhất đây là trường hợp đảm bảo thuật toán không vượt qua ngưỡng này - trung bình mang tính ước lượng Việc phân tích phụ thuộc vào các thông tin chi tiết trên - chi phí

Trang 1

C programming 2003 - 2005 1 C programming 2003 - 2005 2

THUẬT TOÁN THUẬT TOÁN TRÊN CẤU TRÚC CÂY

Trang 2

C programming 2003 - 2005 3

Giới thiệu

Thuật toán - Thuật giải (Algorithm) là phương pháp để giải một bài

toán

Cấu trúc dữ liệu (Data structure): cách lưu trữ thông tin

Các thuật toán hiệu quả dùng những cấu trúc dữ liệu được tổ chức

Sử dụng máy tính trong công việc, con người luôn mong muốn

- máy tính chạy càng ngày càng nhanh hơn

- xử lý được nhiều dữ liệu hơn

- có thể giải quyết được những vấn đề tưởng chừng như không

thể giải quyết được

Công nghệ máy tính chỉ nâng cao được các thứ theo một hệ số cố

định

Hãy suy nghĩ

Một thuật toán được thiết kế cẩn thận có thể thực hiện được mức

độ cải tiến lớn hơn nhiều:

Nghiên cứu thuật toán là vấn đề luôn tồn tại từ xưa đến nay

Phân tích thuật toán

Người ta so sánh các thuật toán dựa trên các phép ước lượng chi phí (thời gian chạy của thuật toán, lượng bộ nhớ mà thuật toán phải dùng) cho một thuật toán khi áp dụng thuật toán vào một bài toán cụ thể

Luôn phải tinh chỉnh và kiểm tra các ước lượng để có được thuật toán tốt nhất

Đối với một thuật toán, chúng ta thường quan tâm đến các yếu tố sau

Kích thước, dung lượng của dữ liệu đầu vào: N Thời gian thực hiện Thường tỉ lệ với:

1 log N

N

N log N

N2

2N

Đôi khi chúng ta cũng gặp các tỉ lệ khác như:

log log N (log(log(N)) log* N số các log cho đến khi đạt đến 1

Thường thì người ta sẽ viết ra các công thức ước lượng thời gian chạy trong các trường hợp:

Trang 3

C programming 2003 - 2005 5

- xấu nhất (đây là trường hợp đảm bảo thuật toán không vượt

qua ngưỡng này)

- trung bình (mang tính ước lượng)

Việc phân tích phụ thuộc vào các thông tin chi tiết trên

- chi phí của các thao tác cơ sở trong quá trình xử lý

- thuộc tính của dữ liệu đầu vào

Để phân tích độ phức tạp của thuật toán, người ta sử dụng ký pháp

chữ O lớn (big O-notation)

T(N) được gọi là có mức độ phức tạp O(F(N)) nếu tồn tại một giá trị

c (hằng) và n0 , sao cho với mọi N > n0 ta có:

T(N) ≤ c * F(N)

T(N) là độ phức tạp chính xác của một thuật toán được đề nghị để

giải bài toán có kích thước N F(N) là giới hạn trên, nghĩa là các

thời gian/không gian hay nói chung là các chi phí cho bài toán có

kích thước N không vượt qua mức F(N)

Trên thực tế, bao giờ người ta cũng muốn tính được giá trị F(N)

nhỏ nhất – chi phí nhỏ nhất phải có

Ví dụ: T(N) = 3 * N2 + 5

Dễ thấy rằng, với c = 4 và n0 = 2, ta có:

3 * N2 + 5 ≤ 4 * N2 nghĩa là T(N) là O(N2)

C programming 2003 - 2005 6

Biểu Diễn Xếp Chồng (Stack) Bằng Cấu Trúc DSLK

Các thao tác trên Stack đã được giới thiệu trong phần trước

Chúng ta sẽ cài đặt Stack bằng DSLK

typedef struct intListNode * IntListPtr;

typedef struct intListNode { int data;

return (ps->top == NULL);

} int PushStack (IntStack * ps, int num) {

Con trỏ đến nút đầu tiên trong danh sách các phần tử của stack

Số lượng phần tử trong danh sách (# số phần tử của Stack)

Trang 4

int TopStack (IntStack * ps)

{ /* assume that stack is not empty */

return ps->top->data;

}

/* cach 2: cai dat khac cua PopStack, vua day phan

tu o dinh ra khoi stack vua lay gia tri cua

PopStack(&s); // su dung PopStack cach 1 }

}

Trang 5

C programming 2003 - 2005 9

Hàng Đợi Bằng Cấu Trúc DSLK

Tương tự như cách biểu diễn Xếp chồng bằng cấu trúc DSLK, hãy

biểu diễn Hàng Đợi cũng bằng cấu trúc DSLK

Danh Sách Liên Kết Kép (Doubly-Linked List)

Danh Sách Liên Kết Hai Đầu (Double Ended Queue)

Danh Sách Liên Kết Tổng Quát

Trong danh sách thường, mỗi phần tử mang dữ liệu riêng

(1,2,3,4) Trong danh sách tổng quát, mỗi phần tử có thể là một danh sách (1,2,(3,4),5)

struct intGenListNode {

struct intGenListNode * subList;

struct intGenListNode * next;

};

Với cấu trúc danh sách tổng quát, chúng ta có thể sử dụng đệ quy

để duyệt và hiển thị nội dung toàn bộ danh sách

void DisplayIntgenList (struct intGenListNode * glptr) {

while (glptr != NULL) {

if (glptr->subList == NULL)

printf ("%d\n", glptr->data);

} else

DisplayIntGenList (glptr->subList);

} glptr = glptr->next;

} }

Head Size

1 null 2 null ? 5 null null

3 null

4 null null

Trang 6

Đây là những thuật toán sắp xếp đơn giản, dễ cài đặt

Chạy rất nhanh với những tập tin dữ liệu kích thước nhỏ

Trong một số trường hợp đặc biệt, các thuật toán này chạy rất hiệu

quả

Khái niệm và điều kiện:

- Các tập tin (Files) chứa các bản ghi (Records) phân biệt với

nhau bởi khóa (Keys)

- Nội dung của tập tin chứa được trong bộ nhớ

typedef int itemType

#define less (A, B) ( A < B )

#define exch (A, B) {itemType t = A; A = B; B = t; }

C programming 2003 - 2005 12

Trên đây chúng ta dùng cách khai báo macro, không phải là định nghĩa hàm con (subroutine)

- Macro: đơn giản, chi phí thấp

- Hàm con: tổng quát hơn, nhưng tốn kém hơn

Trang 8

Thuật toán nổi bọt được cải tiến để chạy nhanh hơn:

- thêm kiểm tra điều kiện dừng nếu không có hoán vị

- nổi bọt hai chiều

C programming 2003 - 2005 16

Tính chất của các giải thuật sắp xếp cơ bản

Thời gian chạy là bậc 2

Với các tập tin có các bản ghi gần như đã theo thứ tự bubble sort và insertion sort có thể đạt mức tuyến tính (trong trường hợp này thuật toán sắp xếp nhanh quicksort lại

có mức độ phức tạp bình phương)

Trang 9

C programming 2003 - 2005 17

Sắp xếp con trỏ

Khi sắp xếp các bản ghi lớn, có nhiều trường, nên thực hiện cách

hoán vị các tham chiếu đến các bản ghi thay vì phải hoán vị toàn

bộ nội dung bản ghi

typedef int itemType

#define less(A, B) (data[A].key < data[B].key)

#define exch(A, B) {itemType t = A; A = B; B = t;}

Trong trường hợp dùng con trỏ đến các bản ghi

typedef dataType* itemType

#define less(A, B) (*A.key < *B.key)

#define exch(A, B) {itemType t = A; A = B; B = t;}

C programming 2003 - 2005 18

Sắp xếp với các bản ghi có hai khóa

Sắp xếp theo khóa thứ nhất, sau đó sắp tiếp theo khóa thứ 2

Battle 4 Chen 2 Chen 2 Furia 3 Fox 1 Kanaga 3 Furia 3 Andrews 3 Gazsi 4 Rohde 3

Trang 10

}

Trang 11

Cài đặt thuật toán

void shellshort(itemType a[], int l, int r) {

int h = incs[k];

for(i = l+h; i <= r; i++) {

Thuật toán Shellsort có tốc độ sắp xếp nhanh, cài đặt đơn giản; rất thích hợp với các tập tin nhỏ và vừa, với các tập tin lớn, thuật toán Shellsort vẫn có hiệu suất thực hiện rất cao

Tỉ lệ tăng nào là thích hợp?

• có nhiều tỉ lệ đã được chứng minh hiệu quả

• dễ chọn nhất là dùng: 1, 4, 13, 40, 121, 363, 1090,

Trang 12

C programming 2003 - 2005 23

Thuật toán CombSoft

Giả sử chúng ta sắp xếp tăng dần một danh sách bằng thuật toán

nổi bọt

Thuật toán sắp xếp nổi bọt có một nhược điểm là nếu phần tử

tương đối nhỏ nằm ở gần cuối danh sách thì sẽ di chuyển rất chậm

về phía đầu (có thể gọi đây là con rùa) Còn phần tử có trị khóa

lớn, nằm gần đầu danh sách thì lại di chuyển rất nhanh về phía vị

trí của nó (hãy cứ gọi phần tử loại thế này là con thỏ)

Một cải tiến nhỏ, trong đó khoảng cách giữa các phần tử cần so

sánh lớn hơn 1 sẽ cho phép biến các “con rùa” thành “con thỏ”

Sắp xếp nổi bọt

C programming 2003 - 2005 24

Cài đặt thuật toán

#define SHRINKFACTOR 1.3 comb_sort(itemType a[], int size) {

int switches, i, j, top, gap;

top = size - gap;

for(i=0; i<top; ++i) {

}

Đây không phải là thuật toán Shellsort

(tham khảo Stephen Lacey, Richard Box Byte 4,1991)

Trang 13

C programming 2003 - 2005 25

Quicksort

Để sắp xếp một mảng, đầu tiên chia mảng thành 3 phần:

Các phần tử a[i] có giá trị bằng nhau không thay đổi vị trí

Các phần tử không lớn hơn a[i] nằm ở bên trái phần tử thứ i

Các phần tử không nhỏ hơn a[i] nằm ở bên phải phần tử thứ i

Sau đó, thực hiện việc sắp xếp các phần bên trái và bên phải Việc

sắp xếp này thực hiện đệ quy

A S O R T I N G E X A M P L E

A A E E T I N G O X S M P L R

A A E

A A

A

L I N G O P M R X T S L I G M O P N

G I L

I L

I

N P O

O P

P

S T X T X T A A E E G I L M N O P R S T X C programming 2003 - 2005 26 Phân vùng (partitioning) Để phân vùng một mảng trước khi thực hiện sắp xếp, đầu tiên, chúng ta chọn ra một phần tử làm mốc a[i0] • quét mảng từ bên phải sang để tìm phần tử nhỏ hơn a[i0], • quét mảng từ bên trái sang để tìm phần tử lớn hơn a[i0] • hoán vị hai phần tử tìm được • lặp lại 3 bước trên cho đến khi các vị trí dò tìm vượt qua mốc A S O R T I N G E X A M P L E

A S

A M P L A A S M P L E

O

E X

A A E O X S M P L E

R

E R T I N G

A A E E T I N G O X S M P L R

Cài đặt thuật toán phân vùng

v: phần tử mốc i: vị trí dò từ trái sang phải j: vị trí dò từ phải sang trái

int partition(Item a[], int l, int r) {

int i, j;

v = a[r]; i = l-1; j = r;

for( ; ; )

Trang 14

Sẽ có quá nhiều lần gọi đệ qui

thời gian chạy phụ thuộc vào đầu vào

Trong trường hợp xấu nhất

- thời gian tăng theo tỉ lệ bình phương

- bộ nhớ tăng tuyến tính

C programming 2003 - 2005 28

Thuật Toán Quicksort Không Đệ Qui

Chúng ta có thể dùng stack để khử tính đệ quy trong cài đặt thuật toán Quicksort ở trên

#define push2(A, B) push(A); push(B);

void quicksort(Item a[], int l, int r) {

Phân tích thuật toán

Tổng thời gian chạy là tổng của chi_phí * tần_suất

của tất cả các phép toán cơ bản

chi_phí (cost) phụ thuộc vào kiểu máy tính tần_suất (frequency) phụ thuộc vào thuật toán và loại dữ liệu đầu vào

Trang 15

• các tập con cũng có thứ tự ngẫu nhiên

Số lần so sánh trung bình được xác định theo công thức:

Bài toán: tìm x trong một mảng

Điều kiện: mảng đã được sắp thứ tự tăng (giảm) dần Các hàm tìm kiếm sẽ trả về -1 nếu x không xuất hiện trong dãy, ngược lại, các hàm tìm kiếm sẽ trả về chỉ số của x trong dãy

Tìm kiếm theo phương pháp lặp

Do dãy số đã có thứ tự tăng dần, nên nếu tìm được phần tử đầu tiên có giá trị lớn hơn x thì có thể kết luận dãy số không chứa phần

tử x Vì vậy, chúng ta có thể rút ngắn thời gian tìm kiếm

Cài đặt thuật toán

int search(int a[], int n, int x) {

int x, pos, size;

else

printf(“%d is not in array\n”, x);

}

Trang 16

C programming 2003 - 2005 31

Tìm Kiếm Nhị Phân

1 Phạm vi tìm kiếm ban đầu là toàn bộ danh sách

2 Lấy phần tử chính giữa của phạm vi tìm kiếm (a[j]) rồi so sánh

với x

• Nếu a[j] = x trả về chỉ số j STOP

• Nếu a[j] > x Phạm vi tìm kiếm mới là các phần tử có chỉ

số nhỏ hơn j

• Nếu a[j] < x Phạm vi tìm kiếm mới là các phần tử có chỉ

số lớn hơn j

3 Nếu còn tồn tại phạm vi tìm kiếm thì lặp lại bước 2,

ngược lại, không tìm thấy x STOP

Cài đặt thuật toán

int Binary_Search(int a[], int n, int x)

{

// gia su ban dau chua tim duoc

unsigned char found=FALSE;

// Pham vi tìm kiem ban dau tu k=0 den m = n-1

Tìm Kiếm Nhị Phân bằng Đệ Qui

1 Phạm vi tìm kiếm ban đầu là toàn bộ danh sách k=0 đến m=n-1

2 Lấy phần tử chính giữa của phạm vi tìm kiếm (a[j]) rồi so sánh với x

• Nếu a[j] = x trả về chỉ số j STOP

• Nếu a[j] > x Phạm vi tìm kiếm mới là các phần tử có chỉ

số nhỏ hơn j Gọi đệ quy hàm tìm kiếm với phạm vi mới

là (k, j-1)

• Nếu a[j] < x Phạm vi tìm kiếm mới là các phần tử có chỉ

số lớn hơn j Gọi đệ quy hàm tìm kiếm với phạm vi mới là (j+1, m)

3 Điều kiện dừng: x=a[j] hoặc k > m

Cài đặt thuật toán

int Binary_Search2(int a[], int k,int m, int x) {

int j=(k+m) /2;

if (k>m) return -1 ; else if (a[j]==x) return j ; else

Binary_Search2(a, (a[j]<x ? j+1:k),

}

Trang 17

C programming 2003 - 2005 33

Các Thuật Toán Trên Cấu Trúc Cây

Cây là một cấu trúc dữ liệu rất thông dụng và quan trọng trong

nhiều phạm vi khác nhau của kỹ thuật máy tính

Ví dụ: Tổ chức các quan hệ họ hàng trong một gia phả, mục lục

của một cuốn sách, xây dựng cấu trúc cú pháp trong các trình biên

dịch

Khái niệm

Cây là tập hợp các phần tử gọi là nút, (một nút có thể có kiểu bất

kỳ) và tập các cạnh có định hướng kết nối các cặp nút trong cây

Nút gốc (Root): là nút ở “trên cùng” trong một cây Trên nút gốc

không có nút nào nữa

Nút con (child): nút kế tiếp (phía dưới) của một nút trong cây Một

nút có thể có nhiều nút con, các nút con này được nhìn theo

thứ tự từ trái sang phải Nút con tận cùng bên trái là nút đầu

tiên và nút con tận cùng bên phải là nút con cuối cùng

Nút cha (parent): nút liền kề (phía trên) của một nút trong cây Một

nút chỉ có duy nhất một nút cha

Các nút anh em (siblings): các nút con của cùng một nút

Các cạnh/nhánh (edge/branch): đường nối từ nút cha đến các nút

con của nó

Nút tổ tiên (Ancestors): Các nút tổ tiên của một nút bao gồm nút

cha của nút, nút cha của nút cha, v.v đến trên cùng là nút

gốc

Nút hậu duệ (Descendant): Các nút hậu duệ của một nút bao gồm

các nút con của nút, các nút con của nút con, v.v đến các

nút lá của các nhánh thuộc nút

Đường đi (Path): là chuỗi các cạnh nối từ một nút đến một trong

số các nút hậu duệ của nó

Chiều dài đường đi (Path length): số cạnh trong đường đi

Nút lá (Leaf): Nút không có nút con

Nút trung gian (Interior node): nút có ít nhất một nút con

C programming 2003 - 2005 34

Độ sâu hay mức của một nút (Depth/level): được tính bằng

chiều dài đường đi từ nút gốc đến nút đang xét

Chiều cao của cây (height): chiều dài đường đi dài nhất trong

cây

Cây con (subtree): cây bao gồm nút và tất cả các nút hậu duệ của

nó Nút gốc và toàn bộ cây không được xem là cây con

Trang 18

C programming 2003 - 2005 35

Cây Nhị Phân

Trong cây nhị phân, mỗi nút có tối đa hai nút con: nút con nhánh

trái và nút con nhánh phải

Khi một nút chỉ có một nút con, cần phải phân biệt là nút con bên

nhánh trái, hay nút con bên nhánh phải, chứ không chỉ đơn thuần

Một cây nhị phân được gọi là cây nhị phân đúng nếu nút gốc và tất

cả các nút trung gian đều có 2 nút con

Ghi chú: nếu cây nhị phân đúng có n nút lá thì cây này sẽ có tất cả 2n-1 nút

Cây nhị phân đầy

Một cây nhị phân được gọi là đầy với chiều cao d thì

- nó phải là cây nhị phân đúng

- tất cả các nút lá đều có mức d Ghi chú: cây nhị phân đầy là cây nhị phân có số nút tối đa ở mỗi mức

Trang 19

C programming 2003 - 2005 37

Cây nhị phân tìm kiếm (Binary search tree)

Một cây nhị phân được gọi là cây nhị phân tìm kiếm nếu và chỉ nếu

đối với mọi nút của cây thì khóa của một nút bất kỳ phải lớn hơn

khóa của tất cả các nút trong cây con bên trái của nó và phải nhỏ

hơn khóa của tất cả các nút trong cây con bên phải của nó

Cây nhị phân cân bằng (AVL tree):

Một cây nhị phân được gọi là cây nhị phân cân bằng nếu và chỉ

nếu đối với mọi nút của cây thì chiều cao của cây con bên trái và

chiều cao của cây con bên phải hơn kém nhau nhiều nhất là 1

(Theo Adelson Velski và Landis)

C programming 2003 - 2005 38

Cây nhị phân cân bằng hoàn toàn

Một cây nhị phân được gọi là cân bằng hoàn toàn nếu và chỉ nếu đối với mọi nút của cây thì số nút của cây con bên trái và số nút của cây con bên phải hon kém nhau nhiều nhất là 1

Trang 20

C programming 2003 - 2005 39

Các phép duyệt cây nhị phân (Traverse)

Là quá trình đi qua các nút đúng một lần

Preorder (NLR)

Duyệt qua nút gốc trước, sau đó qua cây con bên trái, dùng

preorder cho cây con bên trái Cuối cùng qua cây con bên phải và

dùng preorder cho cây con bên phải

If the node is NULL

Return

Else

Visit the item in the node to do something

Traverse (Preorder) the node’s left subtree

Traverse (Preorder) the node’s right subtree

If the node is NULL Return

Else Traverse the node’s left subtree (LNR) Visit the item in the node to do something Traverse the node’s right subtree (LNR)

Ví dụ trong hình: 2Æ1Æ6Æ4Æ7Æ3Æ8Æ5Æ9

Postorder (LRN)

Qua cây con bên trái duyệt trước (theo thứ tự LRN) Sau đó duyệt qua cây con bên phải (theo thứ tự LRN) Cuối cùng duyệt qua nút gốc

If the node is NULL Return

Else Traverse the node’s left subtree (LRN) Traverse the node’s right subtree (LRN) Visit the item in the node to do something

Ví dụ trong hình: 2Æ6Æ7Æ4Æ8Æ9Æ5Æ3Æ1

Ngày đăng: 04/07/2014, 01:16

HÌNH ẢNH LIÊN QUAN

Bảng phân biệt các trường hợp cây bị mất cân bằng khi thêm nút - thuật toán trên cấu trúc cây
Bảng ph ân biệt các trường hợp cây bị mất cân bằng khi thêm nút (Trang 29)

TỪ KHÓA LIÊN QUAN

w