Để tránh những hạn chế trên, ngôn ngữ C++ cung cấp cho ta một loại biến đặc biệt gọi là biến động với các đặc điểm sau: Chỉ phát sinh trong quá trình thực hiện chương trình chứ không
Trang 1CHƯƠNG II : MỘT SỐ THUẬT TOÁN TÌM KIẾM VÀ SẮP XẾP
CHƯƠNG III : DANH SÁCH LIÊN KẾT - NGĂN XẾP VÀ HÀNG ĐỢI
CHƯƠNG IV : CÂY
Trang 5andy = 25ted = &andy
andy =
&andy =ted =
*P
25
andy
Trang 6Một số hạn chế có thể gặp phải khi sử dụng các biến tĩnh:
Cấp phát ô nhớ dư, gây ra lãng phí ô nhớ
Cấp phát ô nhớ thiếu, chương trình thực thi bị lỗi
Để tránh những hạn chế trên, ngôn ngữ C++ cung cấp cho ta một loại biến đặc biệt gọi là biến động với các đặc điểm sau:
Chỉ phát sinh trong quá trình thực hiện chương trình chứ không phát sinh lúc bắt đầu chương trình
Khi chạy chương trình, kích thước của biến, vùng nhớ và địa chỉ vùng nhớ được cấp phát cho biến có thể thay đổi
Sau khi sử dụng xong có thể giải phóng để tiết kiệm chỗ trong bộ nhớ
Tuy nhiên các biến động không có địa chỉ nhất định nên ta không thể truy cập đến chúng được Vì thế, ngôn ngữ C+ + lại cung cấp cho ta một loại biến đặc biệt nữa để khắc phục tình trạng này, đó là biến con trỏ (pointer) với các đặc điểm:
Biến con trỏ không chứa dữ liệu mà chỉ chứa địa chỉ của dữ liệu hay chứa địa chỉ của ô nhớ chứa dữ liệu
Kích thước của biến con trỏ không phụ thuộc vào kiểu dữ liệu, luôn có kích thước cố định là 2 byte.
Trang 7 Toán tử lấy địa chỉ (&)
Trang 8Cú pháp: <Kiểu> * <Tên con trỏ>
Ý nghĩa: Khai báo một biến có tên là <Tên con trỏ> con trỏ dùng để chứa
địa chỉ của các biến có kiểu <Kiểu>
Trang 9char *ted = “hello”
5441
ted [4]
*(ted + 4)ted
2 Khai báo biến Con Trỏ
Trang 112 Các thao tác trên Con Trỏ
2.2 Nội dung của ô nhớ con trỏ chỉ tới (Toán tử *)
Trang 12Việc thực hiện các phép tính số học với con trỏ hơi khác so với các kiểu dữ liệu số nguyên khác, trước hết chỉ phép cộng và phép trừ được phép
dùng Nhưng kết quả cộng trừ phụ thuộc vào kích thức dữ liệu mà biến con trỏ trỏ tới
char chiếm 1byte, int chiếm 2 byte, long chiếm 4 byte
Trang 16<pointer > = new <kieu cau truc> delete < pointer >
Ví dụ: Giả sử ta có khai báo
Trang 171 Khái niệm
Cấu trúc danh sách liên kết là cấu trúc động, việc cấp phát nút và giải phóng nút trên danh sách xảy ra khi chương trình đang chạy Ta thường cấp phát nút cho danh sách liên kết bằng biến động
Các phần tử sẽ được cấp phát vùng nhớ trong quá trình thực thi chương trình, do đó chúng có thể nằm rải rác ở nhiều nơi khác nhau trong bộ nhớ (phân bố không liên tục)
Trang 19pFirst
1 Khái niệm
pFirst là con trỏ chỉ đến phần tử đầu tiên của danh sách.
Phần tử cuối của danh sách liên kết với vùng liên kết có nội dung NULL.
Mỗi nút của danh sách có trường info chứa nội dung của nút và trường
pNext là con trỏ chỉ đến nút kế tiếp trong danh sách.
pNextinfo
1FF60
1FF90 1FF30
Trang 20 Cấu trúc DSLK là cấu trúc động, các nút được cấp phát hoặc giải phóng khi chương trình đang chạy.
DSLK rất thích hợp khi thực hiện các phép toán trên danh sách thường bị biến động Trong trường hợp xoá hay thêm phần tử trong DSLK thì ta không dời các phần tử đi như trong mảng mà chỉ việc hiệu chỉnh lại trường pNext tại các nút đang thao tác Thời gian thực hiện các phép toán thêm vào và loại bỏ không phụ thuộc vào số phần tử của DSLK
Trang 21Hạn chế của Danh Sách Liên Kết :
Vì mỗi nút của DSLK phải chứa thêm trường pNext nên DSLK phải tốn thêm bộ nhớ
Tìm kiếm trên DSLK không nhanh vì ta chỉ được truy xuất tuần tự từ đầu danh sách
2 Các đặc tính
Trang 234 Các phép toán trên danh sách liên kết
4.1 Khởi tạo danh sách
Initialize: Khởi tạo một DSLK Ban đầu DSLK chưa có phần tử.
void Initialize ( node* & pFirst)
Trang 24InsertFirst( ): thêm một nút có nội dung x vào đầu DSLK.
void InsertFirst (node* &pFirst, int x)
Trang 25pFirst
x
4 Các phép toán trên danh sách liên kết
4.2 Thêm vào đầu danh sách
p x
p x
p
Trang 26 InsertAfter( ): thêm một nút có nội dung x vào sau nút có địa chỉ p trong DSLK First.
void InsertAfter ( node* &p , int x)
q->pNext = p->pNext;
p->pNext = q;
} }
4.3 Chèn 1 node mới sau node có địa chỉ p
Trang 27pFirst
4
4 Các phép toán trên danh sách liên kết
4.3 Chèn 1 node mới sau node có địa chỉ p
Trang 28 Empty( ): Kiểm tra danh sách rỗng.
int Empty ( node* pFirst)
Trang 29 DeleteFirst( ): Xoá phần tử đầu danh sách.
void DeleteFirst ( node* & pFirst)
4 Các phép toán trên danh sách liên kết
4.5 Xóa phần tử đầu danh sách
Trang 30x
Trang 31 DeleteAfter( ): Xoá phần tử đứng sau nút có địa chỉ p.
void DeleteAfter ( node* & p)
{
node * q;
if (p == NULL || p->pNext == NULL)
cout << “Cannot delete !\n”;
4 Các phép toán trên danh sách liên kết
4.6 Xóa phần tử đứng sau nút có địa chỉ là p
Trang 32x
q
Trang 33pFirst
x
4 Các phép toán trên danh sách liên kết
void Traverse ( node* pFirst)
Trang 34 DeleteAll( ): Xoá toàn bộ danh sách.
Có thể gán pFirst = NULL để xóa toàn bộ danh sách nhưng phần vùng nhớ đã
cấp cho các phần tử trong DS không được giải phóng.
Do đó chúng ta có thể dùng giải thuật sau
void DeleteAll ( node* & pFirst)
4.8 Xóa toàn bộ danh sách
Trang 35pFirst
4 Các phép toán trên danh sách liên kết
4.8 Xóa toàn bộ danh sách
x
x p
x
p
p
Trang 36 Search( ): Tìm nút đầu tiên trong DS có info bằng với x Do đây là DSLK nên ta phải tìm từ đầu DS Nếu tìm thấy nút có (info == x) thì trả về địa chỉ của
nút, nếu không, trả về NULL.
node* Search ( node* pFirst, int x)
Trang 37 SelectionSortList ( ): sắp xếp DSLK theo thứ tự info tăng dần.
Thuật toán:
So sánh tất cả các phần tử của DS để chọn ra một phần tử nhỏ nhất đưa về đầu DS;
Sau đó, tiếp tục chọn phần tử nhỏ nhất trong các phần tử còn lại để đưa về phần tử thứ hai trong DS.
Quá trình lặp lại cho đến khi chọn được phần tử nhỏ nhất thứ (n-1)
4 Các phép toán trên danh sách liên kết
4.10 Sắp xếp trong danh sách
Trang 38void SelectionSortList ( node* & pFirst)
Trang 394 Các phép toán trên danh sách liên kết