1. Trang chủ
  2. » Giáo Dục - Đào Tạo

Truy hồi thông tin và tìm kiếm web

346 111 0

Đ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 346
Dung lượng 19,1 MB

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

Nội dung

Phân tích thuật toán• Nhằm xác định thời gian chạy độ phức tạp của thuật toán dưới dạng một hàm f của kích thước đầu vào n − VD: Thời gian tìm tuần tự một phần tử x trong một dãy n phầ

Trang 1

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

Nguyễn Mạnh Hiển

hiennm@tlu.edu.vn

Trang 3

1 Phân tích thuật toán là gì?

Trang 4

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

• Nhằm xác định thời gian chạy (độ phức tạp) của thuật

toán dưới dạng một hàm f của kích thước đầu vào n

− VD: Thời gian tìm tuần tự một phần tử x trong một dãy

n phần tử là f(n) = n

• Đơn vị thời gian:

− Không phải là giờ, phút, giây

− Mà là thao tác cơ bản, VD: cộng, nhân, so sánh

− Mỗi thao tác cơ bản có thời gian chạy là hằng (một

lượng thời gian nhỏ không phụ thuộc vào kích thước đầu vào n)

Trang 5

Đếm số thao tác cơ bản

• Nhận diện các thao tác cơ bản trong thuật toán

• Xác định thao tác cơ bản T chiếm nhiều thời gian chạy nhất so với các thao tác cơ bản còn lại

− Thao tác T này thường xuất hiện trong các vòng lặp

• Đếm số lần thực hiện thao tác T, sẽ thu được hàm thời gian chạy f(n)

Trang 6

bool isSorted(T *a, int n) {

bool sorted = true;

for (int i=0; i<n-1; i++)

if (a[i] > a[i+1]) sorted = false;

return sorted;

}

Số lần in ra màn hình = n

Số phép so sánh = n – 1

Có thể cải tiến thuật toán bên trên?

Ví dụ 2: Nhân ma trận tam giác

dưới với véctơ (mã giả)

Trang 7

2 Các ký hiệu tiệm cận

Trang 8

• Có 3 cách phân tích tiệm cận tương ứng với ba ký

hiệu tiệm cận sau đây:

− Ô-mê-ga lớn:   tìm cận dưới của f(n)

− Tê-ta lớn:   tìm cận chặt của f(n)

Trang 9

f(n) bị chặn trên bởi g(n)theo nghĩa tiệm cận

Trang 10

f(n) bị chặn dưới bởi g(n)theo nghĩa tiệm cận

Trang 14

Một số quy tắc

• Nếu f(n) = a0 + a1n + … + aknk (ak > 0)

 f(n) = O(nk)

• logkn = O(n) với k là một hằng số

(hàm lôgarit tăng chậm hơn hàm tuyến tính)

Chú ý: Trong môn học này, khi viết hàm lôgarit mà không chỉ rõ cơ số, ta ngầm hiểu cơ số đó là 2

Trang 15

3 Tốc độ tăng của các hàm

Trang 16

Tốc độ tăng của một số hàm cơ bản

Hàm Tên

log n Lôgarit log2 n Lôgarit bình phương

Trang 17

Hàm nào tăng chậm hơn?

• f(n) = n log n và g(n) = n1,5

• Lời giải:

− Chú ý rằng g(n) = n1,5 = n * n0,5

− Vì vậy, chỉ cần so sánh log n và n0,5

− Tương đương với so sánh log2 n và n

− Tham khảo bảng trong slide trước: log2n tăng chậm hơn n

− Suy ra f(n) tăng chậm hơn g(n)

Trang 19

4 Các ví dụ phân tích thuật toán

Trang 20

• Cả 4 thao tác đó được lặp lại n lần

• Thời gian chạy: t(n) = 4n = O(n)

Chú ý: Ở đây, ta bỏ qua 3 thao tác cơ bản điều khiển quá trình lặp ở dòng 1 Kết quả phân tích thuật toán sẽ không thay đổi nếu tính thêm cả 3 thao tác đó.

Trang 22

• Chỉ cần cộng thời gian chạy của các vòng lặp

• Thời gian chạy tổng thể: t(n) = 3n + 5n = 8n = O(n)

Trang 23

• Phân tích các vòng lặp từ trong ra ngoài:

− Vòng lặp bên trong thực hiện 3n thao tác cơ bản

− Mỗi bước lặp của vòng lặp bên ngoài thực hiện 2 + 3n thao tác cơ bản

• Thời gian chạy tổng thể: t(n) = (2 + 3n)n = 3n2 + 2n = O(n2)

Trang 24

− Phép gán ở dòng 2 được thực hiện 0 hoặc 1 lần

− Phép gán ở dòng 5 được thực hiện 0 hoặc n lần

• Trong trường hợp tồi nhất (điều kiện x > 0 sai), phép gán ở dòng 5 chạy n lần

• Thời gian chạy: t(n) = 1 + n = O(n)

Trang 26

= 3k + t(n – k)

• Chọn k = n – 1, khi đó:

t(n) = 3(n – 1) + t(1) = 3n – 2 = O(n)

Trang 28

Tìm kiếm nhị phân

• Cho mảng a đã sắp xếp tăng dần

• Tìm x trong mảng a:

− So sánh x với phần tử ở chính giữa mảng a[mid]

− Nếu x < a[mid], tìm x ở nửa bên trái của mảng

− Nếu x > a[mid], tìm x ở nửa bên phải của mảng

− Nếu x = a[mid], báo cáo vị trí tìm được x là mid

− Nếu không còn phần tử nào để xét, báo cáo không tìm được x

Trang 30

Tìm kiếm nhị phân – thuật toán

mid  (left + right) / 2

else if (x > a[mid]) left  mid + 1

}

return –1 }

Trang 31

Tìm kiếm nhị phân – phân tích

• Nếu n = 1, chỉ mất một phép so sánh x với phần tử duy nhất của

mảng

• Nếu n > 1, mất một phép so sánh x với phần tử chính giữa mảng, sau

đó là mất thời gian tìm x trong một nửa (trái hoặc phải) của mảng

• Suy ra thời gian chạy của thuật toán:

t(n) = 1 + t(n/2) (với n > 1)

= 1 + 1 + t(n/4)

= 1 + 1 + 1 + t(n/8)

= k + t(n/2 k )

• Chọn k = log n, khi đó:

t(n) = log n + t(1) = log n + 1 = O(log n)

Trang 32

Nguyễn Mạnh Hiển

hiennm@tlu.edu.vn

Trang 34

1 Cấu trúc dữ liệu là gì?

Trang 35

Cấu trúc dữ liệu

• Là cách tổ chức dữ liệu trong máy tính sao cho các thao tác xử lý dữ liệu (như tìm, chèn, xóa) trở nên hiệu quả hơn

Trang 36

Cài đặt cấu trúc dữ liệu

Mỗi cấu trúc dữ liệu được cài đặt bằng một lớp C++:

template <typename T>

class Tên-Cấu-Trúc-Dữ-Liệu {

public:

hàm tạo (constructor) hàm hủy (destructor) các thao tác xử lý private:

các trường dữ liệu các thao tác trợ giúp };

(T là kiểu dữ liệu của các phần tử trong cấu trúc dữ liệu)

Trang 37

2 Vector

Trang 38

• Lưu trữ một dãy phần tử có kích thước thay đổi được (trong khi kích thước của mảng cố định sau khi khai báo)

• Các thao tác chính:

− Chèn và xóa phần tử ở cuối vector

− Chèn và xóa phần tử ở giữa vector

− Lấy kích thước vector

− Truy nhập phần tử dùng chỉ số

Trang 39

các thao tác khác private:

int size; // kích thước vector (số phần tử) int capacity; // dung lượng vector (sức chứa)

T * array; // con trỏ tới mảng chứa các phần tử các thao tác trợ giúp

};

3 8 array

size 2 capacity 4

Trang 40

Hàm tạo và hàm hủy

// initCapacity là dung lượng ban đầu của// vector, có giá trị ngầm định bằng 16Vector(int initCapacity = 16) {

Trang 41

Toán tử gán

// rhs (right-hand side) là vector ở vế phải của phép gán // this là con trỏ tới vector hiện hành, tức là vế trái Vector & operator=(Vector & rhs) {

if (this != &rhs) { // ngăn cản tự sao chép

delete[] array; // xóa mảng hiện tại size = rhs.size; // đặt kích thước mới capacity = rhs.capacity; // đặt dung lượng mới array = new T[capacity]; // tạo mảng mới

// Sao chép các phần tử từ vế phải sang vế trái for (int i = 0; i < size; i++)

=

Trang 42

Kích thước vector và truy nhập phần tử

// Trả về kích thước vector

int getSize() {

return size;

}

// Nếu vector rỗng, trả về true;

// ngược lại trả về false

bool isEmpty() {

return (size == 0);

}

// index là chỉ số của phần tử cần truy nhập

T & operator[](int index) {

return array[index];

}

Trang 43

3 Chèn phần tử

Trang 44

Tăng dung lượng vector

// Đây là thao tác trợ giúp cho các thao tác chèn.

// newCapacity là dung lượng mới (phải lớn hơn kích thước) void expand(int newCapacity) {

if (newCapacity <= size)

return;

T * old = array; // old trỏ tới mảng cũ

array = new T[newCapacity]; // array trỏ tới mảng mới for (int i = 0; i < size; i++)

array[i] = old[i]; // sao chép cũ sang mới delete[] old; // xóa mảng cũ

capacity = newCapacity; // đặt dung lượng mới

}

Trang 45

Chèn phần tử vào cuối vector

// newElement là phần tử mới cần chèn vào cuối vector

void pushBack(T newElement) {

// Gấp đôi dung lượng nếu vector đã đầy

Trang 46

Chèn phần tử vào giữa vector

// pos (position) là vị trí chèn.

// newElement là phần tử mới cần chèn.

void insert(int pos, T newElement) {

// Gấp đôi dung lượng nếu vector đã đầy

if (size == capacity)

expand(2 * size);

// Dịch các phần tử ở pos và sau pos sang phải 1 vị trí

for (int i = size; i > pos; i )

size

pos = 1

phải dịch 8, 9,

2, 5 sang phải

Trang 47

4 Xóa phần tử

Trang 49

Xóa phần tử ở giữa vector

// pos (position) là vị trí của phần tử cần xóa

void erase(int pos) {

// Dịch các phần tử sau pos sang trái 1 vị trí

for (int i = pos; i < size - 1; i++)

size

pos = 1

phải dịch 9, 2, 5 sang trái

Trang 50

5 Thời gian chạy

Trang 51

Phân tích thời gian chạy

• Hàm tạo, hàm hủy: O(1)

• Toán tử gán: O(n) – vì phải sao chép các phần tử

• getSize, isEmpty, operator[]: O(1)

• expand: O(n) – vì phải sao chép các phần tử

Trang 52

Danh sách liên kết (Linked Lists)

Nguyễn Mạnh Hiển

hiennm@tlu.edu.vn

Trang 53

Nội dung

1 Danh sách liên kết

2 Danh sách liên kết đơn

3 Danh sách liên kết đôi

4 Danh sách liên kết vòng tròn

Trang 54

1 Danh sách liên kết

Trang 55

− một hoặc nhiều liên kết tới các nút lân cận

• Các nút nằm rải rác trong bộ nhớ máy tính (trong khi các phần tử của mảng và vector nằm liên tục)

Trang 56

Các kiểu danh sách liên kết

Danh sách liên kết đơn

Danh sách liên kết đôi

Danh sách liên kết vòng tròn

Trang 57

2 Danh sách liên kết đơn

Trang 58

Danh sách liên kết đơn

• Có một liên kết duy nhất giữa hai nút liên tiếp

• Các thao tác chính:

− Chèn phần tử mới vào đầu danh sách

− Xóa phần tử đầu danh sách

− Lấy phần tử đầu danh sách

Trang 59

Cài đặt danh sách liên kết đơn

template <typename T>

class SingleList {

public:

hàm tạo, hàm hủy chèn/xóa ở đầu danh sách lấy phần tử đầu danh sách

private:

struct Node { }; // kiểu dữ liệu của các nút Node * head; // con trỏ tới nút đầu danh sách };

Trang 60

Kiểu dữ liệu của các nút

Trang 61

Hàm tạo và hàm hủy

SingleList() {

head = NULL;

}

// Hàm empty kiểm tra trạng thái rỗng

// Hàm popFront xóa phần tử đầu danh sách// (tham khảo các slide sau cho hai hàm đó)

~SingleList() {

while (!empty())

popFront();

}

Trang 63

Chèn vào đầu danh sách

Trang 64

Chèn vào đầu danh sách (tiếp)

// e (element) là phần tử cần chèn

void pushFront(T e) {

// v là nút mới, trong đó v.next = head có// nghĩa là v trỏ tới nút đầu danh sách.Node * v = new Node(e, head);

// Nút đầu danh sách bây giờ là v, vì vậy// phải cập nhật con trỏ head

head = v;

}

Trang 65

Xóa phần tử đầu danh sách

Trang 66

Xóa phần tử đầu danh sách (tiếp)

void popFront() {

// Giữ lại nút đầu danh sách

Node * old = head;

// Nhảy sang nút kế tiếp

head = head->next;

// Xóa nút đầu danh sách cũ

delete old;

}

Trang 67

Phân tích thời gian chạy

• Hàm tạo: O(1)

• Hàm hủy: O(n) – vì phải xóa n phần tử

• Kiểm tra rỗng: O(1)

• Lấy phần tử đầu danh sách: O(1)

• Chèn/xóa ở đầu danh sách: O(1)

Vì sao không nên chèn/xóa ở cuối danh sách liên kết đơn?

Trang 68

3 Danh sách liên kết đôi

Trang 69

Danh sách liên kết đôi

• Mỗi nút chứa hai liên kết:

− Liên kết tới nút tiếp theo

− Liên kết về nút phía trước

• Các thao tác chính:

− Chèn/xóa ở đầu, cuối hoặc vị trí hiện hành

− Lấy phần tử ở đầu, cuối hoặc vị trí hiện hành

− Duyệt danh sách tiến hoặc lùi

• Chú ý: header và trailer là những nút giả (không chứa phần tử), được dùng để thuận tiện cho việc lập trình

Trang 70

Cài đặt danh sách liên kết đôi

template <typename T> // T là kiểu phần tử class DoubleList {

public:

hàm tạo, hàm hủy, kiểm tra rỗng các thao tác chèn/xóa

các thao tác lấy phần tử các thao tác duyệt danh sách private:

struct DNode { }; // kiểu của các nút DNode * header; // đầu danh sách DNode * trailer; // cuối danh sách DNode * currentPos; // vị trí hiện hành };

Trang 71

Kiểu dữ liệu của các nút

struct DNode {

T elem; // phần tử

DNode * next; // liên kết về phía sauDNode * prev; // liên kết về phía trước};

Trang 72

Khai báo các thao tác

DoubleList(); // hàm tạo

~DoubleList(); // hàm hủy

bool empty(); // kiểm tra rỗng

T front(); // lấy phần tử đầu danh sách

T back(); // lấy phần tử cuối danh sách

T current(); // lấy phần tử hiện hành

bool moveNext(); // chuyển sang nút tiếp theo bool movePrevious(); // chuyển về nút phía trước void moveFront(); // chuyển về đầu danh sách

void moveBack(); // chuyển về cuối danh sách

Trang 73

Khai báo các thao tác (tiếp)

void pushFront(T e); // chèn vào đầu danh sáchvoid pushBack(T e); // chèn vào cuối danh sáchvoid popFront(); // xóa ở đầu danh sách

void popBack(); // xóa ở cuối danh sách

// Chèn vào trước vị trí hiện hành

void insert(T e);

// Xóa ở vị trí hiện hành

void remove();

Trang 74

Chèn vào trước vị trí hiện hành

Chèn vào trước nút này (nút v)

Đây là nút cần chèn (nút u)

Trang 75

Chèn vào trước vị trí hiện hành (tiếp)

// Chèn phần tử e vào trước vị trí hiện hành

void insert(T e) {

DNode * v = currentPos; // Nút hiện hành v

DNode * u = new DNode; // Nút mới u

u->elem = e; // Nút mới u chứa e,

u->next = v; // liên kết với nút sau và u->prev = v->prev; // liên kết với nút trước v->prev->next = u; // Nút trước liên kết với u v->prev = u; // Nút sau liên kết với u

}

Trang 76

Xóa ở vị trí hiện hành

Nút cần xóa (nút v)

Nút sau (nút w)Nút trước

(nút u)

Trang 77

Xóa ở vị trí hiện hành (tiếp)

// Xóa nút v nằm ở vị trí hiện hành

void remove() {

DNode * v = currentPos; // Nút hiện hành v

DNode * u = v->prev; // Nút trước nút v

DNode * w = v->next; // Nút sau nút v

u->next = w; // Nút trước trỏ tới nút sau w->prev = u; // Nút sau trỏ về nút trước delete v; // Xóa nút hiện hành cũ

currentPos = w; // Vị trí hiện hành mới

}

Trang 78

4 Danh sách liên kết vòng tròn

Trang 79

Danh sách liên kết vòng tròn

• Cấu trúc tương tự như danh sách liên kết đơn

• Nhưng có thêm con trỏ đặc biệt cursor trỏ đến cuối danh sách (back),

và liên kết next của nút cuối trỏ vòng về đầu danh sách (front)

• Các thao tác chính:

− Chèn và xóa ở sau cursor

− Lấy phần tử ở đầu và cuối danh sách

− Dịch chuyển cursor sang vị trí tiếp theo

Trang 80

Cài đặt danh sách liên kết vòng tròn

template <typename T> // T là kiểu phần tử

struct CNode { }; // kiểu của các nút CNode * cursor; // con trỏ đặc biệt };

Trang 81

Kiểu dữ liệu của các nút

struct CNode {

T elem; // phần tử

CNode * next; // liên kết về phía sau};

Trang 82

Khai báo các thao tác

CircleList(); // hàm tạo

~CircleList(); // hàm hủy

bool empty(); // kiểm tra rỗng

T front(); // lấy phần tử đầu danh sách

T back(); // lấy phần tử cuối danh sáchvoid moveNext(); // dịch chuyển cursor

void insert(T e); // chèn vào sau cursor

void remove(); // xóa nút sau cursor

Trang 83

Chèn vào sau cursor

// Chèn phần tử e vào sau cursor

void insert(T e) {

CNode * v = new CNode; // tạo nút mới v

v->elem = e; // nút mới chứa e

if (cursor == NULL) { // nếu danh sách rỗng

v->next = v; // nút v trỏ tới chính nó cursor = v; // cursor trỏ tới nút v }

else { // nếu danh sách không rỗng

v->next = cursor->next;

cursor->next = v;

}

}

Trang 84

Xóa nút sau cursor

void remove() {

CNode * old = cursor->next; // nút cần xóa

if (old == cursor) // nếu danh sách chỉ có một nút

cursor = NULL; // cursor thành NULL sau khi xóa else // nếu danh sách có nhiều nút

cursor->next = old->next;

delete old;

}

Trang 85

Ngăn xếp và Hàng đợi (Stacks and Queues)

Nguyễn Mạnh Hiển

hiennm@tlu.edu.vn

Trang 86

Nội dung

1 Ngăn xếp

2 Hàng đợi

Trang 87

1 Ngăn xếp

Trang 88

Ngăn xếp

• Một danh sách theo kiểu vào sau ra trước

LIFO (Last In First Out)

• Ba thao tác cơ bản (xảy ra ở đỉnh ngăn xếp):

Trang 89

Cài đặt ngăn xếp – cách 1

• Cài đặt bằng danh sách liên kết đơn:

• Các thao tác:

− push : gọi thao tác pushFront của DSLK đơn

− top : gọi thao tác front của DSLK đơn

head

Trang 90

Cài đặt ngăn xếp – cách 2

• Cài đặt bằng mảng:

• push(e) : topOfStack++, theArray[topOfStack] = e

• pop :

topOfStack • top : return theArray[topOfStack]

• Chú ý: Khi ngăn xếp rỗng thì topOfStack = -1

theArray

topOfStack = 3

0 1 2 3 4 5 6 7 8

Trang 91

Một số ứng dụng của ngăn xếp

• Cân bằng thẻ (tag) trong một trang HTML

• Định giá biểu thức hậu tố

Trang 100

Định giá biểu thức hậu tố

• Giả sử phải định giá biểu thức trung tố sau:

4,99 ∗ 1,06 + 5,99 + 6,99 ∗ 1,06

− Máy tính khoa học sẽ cho kết quả 18,69  đúng

− Máy tính giản đơn (tính tuần tự từ trái sang phải) sẽ cho kết quả 19,37  sai !

• Nếu tổ chức biểu thức dưới dạng hậu tố (toán tử viết sau các toán hạng của nó) rồi ứng dụng ngăn xếp, tính tuần tự từ trái sang phải sẽ cho kết quả đúng (tức là không cần quan tâm độ

ưu tiên của các toán tử)

4,99 1,06 ∗ 5,99 + 6,99 1,06 ∗ +

Trang 102

Ví dụ

• Định giá biểu thức 6 5 2 3 + 8 ∗ + 3 + ∗

• Đặt bốn toán hạng đầu tiên vào ngăn xếp

Trang 103

6 5 2 3 + 8 ∗ + 3 + ∗

• Đọc “+”, lấy 3 và 2 ra, cộng lại được 5, đặt 5 vào ngăn xếp

Trang 104

6 5 2 3 + 8 ∗ + 3 + ∗

• Đặt 8 vào ngăn xếp

Trang 105

6 5 2 3 + 8 + 3 + ∗

• Đọc “∗”, lấy 8 và 5 ra, nhân vào được 40, đặt

40 vào ngăn xếp

Trang 106

6 5 2 3 + 8 ∗ + 3 + ∗

• Đọc “+”, lấy 40 và 5 ra, cộng lại được 45, đặt 45 vào ngăn xếp

Trang 107

6 5 2 3 + 8 ∗ + 3 + ∗

• Đặt 3 vào ngăn xếp

Trang 108

6 5 2 3 + 8 ∗ + 3 +

• Đọc “+”, lấy 3 và 45 ra, cộng lại được 48, đặt 48 vào ngăn xếp

Ngày đăng: 23/12/2020, 23:51

TỪ KHÓA LIÊN QUAN

TRÍCH ĐOẠN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w