1. Trang chủ
  2. » Giáo án - Bài giảng

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

18 0 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

Tiêu đề Danh sách liên kết đơn
Chuyên ngành Data Structures
Thể loại Note
Định dạng
Số trang 18
Dung lượng 427,93 KB

Nội dung

Ví dụ: int *a = new int[n], HocSinh *a = new HocSinh[n] => Ưu điểm: Không phải tốn bộ nhớ, cần xài bao nhiêu thì cấp bấy nhiêu Đã gọi là mảng thì phải tuân thủ theo nguyên tắc sau: Các p

Trang 1

1

DANH SÁCH LIÊN KẾT ĐƠN

Mảng tĩnh

Ví dụ: int a[100], HocSinh a[100]

=> Nhược điểm: Kích thước bộ nhớ lúc nào cũng phải là 100, dù là xài không hết

=> tốn bộ nhớ

Mảng động

Ví dụ: int *a = new int[n], HocSinh *a = new HocSinh[n]

=> Ưu điểm: Không phải tốn bộ nhớ, cần xài bao nhiêu thì cấp bấy nhiêu

Đã gọi là mảng thì phải tuân thủ theo nguyên tắc sau: Các phần tử

trong bộ nhớ phải nằm liên tục nhau

=> Giả sử cần cấp phát 1 mảng 5 phần tử, trong bộ nhớ lúc này cũng còn trống đúng 5 ô nhưng vấn đề 5 ô không nằm liên tục nhau => giải pháp dùng mảng đã THẤT BẠI

Lúc này họ sẽ dùng giải pháp khác đó là DANH SÁCH LIÊN KẾT

=> Sẽ không cần các ô nhớ phải nằm cạnh nhau, ô này nằm chỗ này, ô khác nằm chỗ khác cũng đc, miễn có đủ 5 ô là đc

Danh sách liên kết có 4 dạng sau

- 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 đơn

- Danh sách liên kết vòng kép

So sánh ưu và nhược điểm của mảng so với danh sách liên kết đơn

* Mảng *

- Nhược điểm:

✓ Các phần tử phải liên tục nhau, cho nên giả sử cần 5 ô nhưng còn trúng đúng 5 ô nhưng 5 ô đó không liên tục nhau => ko dùng mảng

đc

✓ Luôn phải yêu cầu cấp trước 1 số lượng nào đó (mảng tĩnh là 100, mảng động là n)

✓ Thao tác thêm/Xóa phải lùi lại hay tiến lên rất nhiều phần tử => chương trình chạy chậm

- Ưu điểm:

Trang 2

2

✓ Tiết kiệm bộ nhớ hơn (chỉ đối với mảng động)

✓ Truy xuất tới 1 phần tử sẽ nhanh hơn (nhờ vào toán tử lấy chỉ số) vd: a[3];

* Danh sách liên kết *

- Ưu điểm:

✓ Các phần tử không cần liên tục, khắc phục tình trạng phân mảnh

bộ nhớ

✓ Không cần cấp phát trước số lượng như bên mảng, dữ liệu luôn được ở trạng thái động (khi nào cần thì cứ thêm vào như bình thường, không cần phải cấp lại bộ nhớ như bên mảng)

✓ Các thao tác thêm/xóa không cần phải lùi lại hay tiến lên các phần tử từ vị trí đó trở xuống như bên mảng mà ta chỉ cần bẻ lại mối liên kết giữa 2 Node là được => chạy nhanh hơn bên mảng trong trường hợp này

- Nhược điểm:

✓ 1 Node trong danh sách liên kết sẽ lưu 2 thông tin đi kèm đó là Data (dữ liệu chứa) và 1 con trỏ pNext để tạo mối liên kết giữa Node này với Node kia

=> đòi hỏi bộ nhớ phải tốn nhiều hơn mảng

✓ Muốn truy xuất tới 1 Node cụ thể (ví dụ Node nằm ở đoạn giữa danh sách) thì ta phải đi từ đầu cho đến đó hoặc đi từ cuối cho đến đó

✓ Nếu như danh sách liên kết mà vô tình tại 1 Node nào đó ta bị hư

là hư hết cả danh sách bởi vì nó truyền dữ liệu liên tục cho nhau

Trang 3

3

1 ĐỊNH NGHĨA danh sách liên kết đơn

2 Hàm KHỞI TẠO danh sách rỗng

3 Hàm kiểm tra có phải danh sách RỖNG

4 Tạo một NÚT có thành phần dữ liệu là x

5 Chèn một phần tử vào danh sách

• Chèn phần tử vào ĐẦU danh sách

• Chèn phần tử vào CUỐI danh sách

• Chèn phần tử vào SAU phần tử q cho trước trong danh sách

• Chèn phần tử vào TRƯỚC phần tử q cho trước trong danh sách

6 Hủy một phần tử trong danh sách

• Hủy phần tử ĐẦU danh sách

• Hủy phần tử CUỐI danh sách

• Hủy một phần tử có KHÓA x

• Hủy phần tử vào SAU phần tử q cho trước trong danh sách

• Hủy phần tử vào TRƯỚC phần tử q cho trước trong danh sách

7 Duyệt danh sách

8 Tìm một phần tử trong danh sách theo một điều kiện cho trước

9 Sắp xếp danh sách

❖ Thay đổi thành phần dữ liệu trong các phần tử để tạo danh sách có thứ tự

Uư điểm: Cài đặt đơn giản,tương tự như mảng

Nhược điểm: Thêm vùng nhớ để hoán vị Nên phù hợp với kích thước dữ liệu nhỏ

Ví dụ: SelectionSort

❖ Thay đổi thành phần liên kết các phần tử

Uư điểm: Không phụ thuộc vào kích thước bản chất dữ liệu, thao tác sắp xếp nhanh hơn

Nhược điểm: Cài đặt phức tạp

Ví dụ: QuickSort, MergeSort, RadiSort 10.Hủy danh sách liên kết

Trang 4

4

1 ĐỊNH NGHĨA danh sách liên kết đơn

NULL NODE(nút)

LIST(danh sách)

❖ Danh sách liên kết đơn là: Trong danh sách mà mỗi phần

tử liên kết với phần tử đứng liền sau trong danh sách

❖ Mỗi phần tử trong danh sách liên kết đơn là một cấu trúc

có hai thành phần

chỉ

• Thành phần dữ liệu:Lưu trữ thông tin về bản thân phần tử

• Thành phần liên kết:Lưu địa chỉ phần tử đứng sau trong danh sách hoặc bằng NULL nếu là phần tử cuối danh sách

Ví dụ 1: Khai báo cấu trúc danh sach liên kết đơn PHANSO

BG:

struct PhanSo {

int Tu;

int Mau;

};

typedef struct PhanSo PS;

struct Node {

Trang 5

5

PS Info; //PS là kiểu Data:int, Diem

struct Node*pNext;

};

typedef struct Node NODE;

struct List {

NODE*pHead; //Lưu địa chỉ phần tử đầu tiên NODE*pTail; //Lưu địa chỉ phần tử cuối };

typedef struct List LIST;

Ví dụ 2: Khai báo cấu trúc danh sach liên kết đơn HOCSINH

BG:

struct SinhVien //Phần Data cảu Info {

char Ten[30];

int MSSV;

};

typedef struct SinhVien SV;

struct Node {

SV Info;

struct Node*pNext;

};

typedef struct Node NODE;

struct List {

NODE*pHead; //Lưu địa chỉ phần tử đầu tiên NODE*pTail; //Lưu địa chỉ phần tử cuối };

typedef struct List LIST;

2 Hàm KHỞI TẠO danh sách rỗng

Thao tác thực hiện: Thao tác này sẽ khởi tạo một danh sách đơn rỗng ban đầu Địa chỉ nút đầu với nút cuối đều là NULL void CreateList(LIST &l)

Trang 6

6

{

l.pHead = NULL;

l.pTail = NULL;

}

3 Hàm kiểm tra có phải danh sách RỖNG

int IsEmpty(LIST l) {

if (l.pHead == 0)

return 1; //danh sách rỗng return 0; //danh sách không rỗng }

4 Tạo một NÚT có thành phần dữ liệu là x

Thao tác thực hiện: Thao tác này sẽ tạo một phần tử mới và gán thành phần dữ liệu là x, chuẩn bị cho thao tác thêm phần tử vào danh sách Hàm trả về địa chỉ phần tử mới

NODE *CreateNode(KDL x) {

NODE *p = new NODE; //Cấp phát bộ nhớ

if (p == NULL) return NULL; //Hoặc exit(1); danh sách rỗng p->Info = x;//gán dữa liệu cho nút

p->pNext = NULL;

return p;

}

5 Chèn một phần tử vào danh sách

• Chèn phần tử vào ĐẦU danh sách

Trang 7

7

6 nf

x

.

pHead

p=pHead

p->pNext=pHead

• Thuật toán

Nếu List rỗng thì + pHead = p;

+ pTail = pHead;

Ngược lại + p->pNext = pHead;

+ pHead = p

• Hàm chèn một phần tử vào ĐẦU danh sách AddHead

void AddHead(LIST &l, NODE* p) {

if (l.pHead == NULL) {

l.pHead = p;

l.pTail = l.pHead;

} else {

p->pNext = l.pHead;

l.pHead = p;

} }

• Chèn phần tử vào CUỐI danh sách

Trang 8

8

x

pHead

pTail->pNext=p

pTail

pTail=p

• Thuật toán Nếu danh sách rỗng thì + pHead = p;

+pTail = pHead;

Ngược lại + pTail->pNext = p;

+pTail = p

• Hàm chèn một phần tử vào CUỐI danh sách AddTail

void AddTail(LIST &l, Node *p) {

if (l.pHead == NULL) {

l.pHead = p;

l.pTail = l.pHead;

} else {

l.pTail->pNext = p;

l.pTail = p;

} }

• Chèn phần tử vào SAU phần tử q cho trước trong danh sách

Trang 9

9

x

pTail

q pHead

• Thuật toán Nếu (q!= NULL) thì B1: p->pNext = q->pNext B2:

+ q->pNext = p + nếu q = pTail thì pTail = p

• Hàm chèn một phần tử vào SAU nút q danh sách InsertAfterQ

void InsertAfterQ(LIST &l, NODE *p, NODE *q) {

if (q != NULL) {

p->pNext = q->pNext;

q->pNext = p;

if (l.pTail == q) l.pTail = p;

} else AddHead(l, p);// thêm q vào đầu list }

• Chèn phần tử vào TRƯỚC phần tử q cho trước trong danh sách

6 Hủy một phần tử trong danh sách

• Hủy phần tử ĐẦU danh sách

Trang 10

10

6 nf

pHead=pHead->pNext

p=pHead

• Thuật toán

Nếu (pHead != NULL) thì B1 : p = pHead

B2 :pHead = pHead->pNext delete p;

B3 : Nếu pHead == NULL thì

pTail = NULL

• Hàm hủy phần tử ĐẦU danh sách

int RemoveHead(List &l, int &x) {

NODE *p;

if (l.pHead != NULL) {

p = l.pHead;

x = p->Info; //lưu Data của nút cần hủy l.pHead = l.pHead->pNext;

delete p;

if (l.pHead == NULL)

l.pTail = NULL;

return 1;

} return 0;

• Hủy phần tử CUỐI danh sách

Trang 11

11

• Hủy một phần tử có KHÓA x

• Thuật toán

*Bước 1: Tìm phần tử p có khoá bằng x, và q đứng trước

p

*Bước 2 : Nếu(p != NULL) thì //tìm thấy phần tử có khoá bằng x

Hủy p ra khỏi List bằng cách hủy phần tử đứng sau

q

Ngược lại Báo không tìm thấy phần tử có khoá

• Hàm hủy một phần tử có KHÓA x

int RemoveX(List &l, int x) {

Node *p, *q = NULL; p = l.Head;

while ((p != NULL) && (p->Info != x)) //tìm {

q = p;

p = p->Next;

}

if (p == NULL) //không tìm thấy phần tử có khoá bằng x

return 0;

if (q != NULL)//tìm thấy phần tử có khoá bằng x

DeleteAfterQ(l, q, x);

else //phần tử cần xoá nằm đầu List

RemoveHead(l, x);

return 1;

}

• Hủy phần tử vào SAU phần tử q cho trước trong danh sách

Trang 12

12

6 nf

pTail

pHead

7 6f

p=q->pNext

q=pNext=p->pNext

• Thuật toán

Nếu (q != NULL) thì //q tồn tại trong List B1 : p = q->pNext;// p là phần tử cần hủy B2 :

+ Nếu(p != NULL) thì // q không phải là phần tử cuối q->pNext = p->pNext;// tách p ra khỏi xâu

+ Nếu(p == pTail) // nút cần hủy là nút cuối

pTail = q;

delete p;// hủy p

• Hàm hủy phần tử vào SAU phần tử q cho trước trong danh sách

int RemoveAfterQ(List &l, Node *q, int &x) {

Node *p;

if (q != NULL) {

p = q->pNext; //p là nút cần xoá

if (p != NULL) // q không phài là nút cuối {

if (p == l.pTail) //nút cần xoá là nút cuối cùng

l.pTail = q;// cập nhật lạ pTail q->pNext = p->pNext; x = p->Info;

delete p;

} return 1;

} else

return 0;

}

Trang 13

13

• Hủy phần tử vào TRƯỚC phần tử q cho trước trong danh sách

7 Duyệt danh sách

• Duyệt danh sách là thao tác thường được thực hiện khi có nhu cầu cần xử lý các phần tử trong danh sách như : + Đếm các phần tử trong danh sách

+ Tìm tất cả các phần tử trong danh sách thảo điều kiện

+ Hủy toàn bộ danh sách

• Thuật toán

• *Bước 1:

p = pHead;// p lưu địa chỉ của phần tử đầu trong List

• *Bước 2:

Trong khi (danh sách chưa hết) thực hiện + xử lý phần tử p

+ p = p->pNext;// qua phần tử kế

• Ví dụ

//Cài đặt xuất các phần tử trong dslk void Output(LIST l)

{

NODE *p = l.pHead;

while (p != NULL) {

cout << p->Info;

p = p->pNext;

cout << " "; //Cac cac phan tu }

}

//Nhập danh sách liên kết đơn void Input(LIST&l)

{

int n;

cout << "Nhap vao n = ";

cin >> n;

Trang 14

14

CreateList(l);

for (int i = 1; i <= n; i++) {

int x;

cout << "Nhap so nguyen " << " ";

cin >> x;

NODE *p = CreateNode(x);

if (p != NULL)

AddHead(l, p);

} }

8 Tìm một phần tử trong danh sách theo một điều kiện cho trước

• Thuật toán

Tìm tuần tự (hàm trả về), các *Bước của thuật toán tìm nút có Info bằng x trong list đơn

**Bước 1: p = pHead; // địa chỉ của phần tử đầu trong list đơn

**Bước 2:

Trong khi p!= NULL và p->Info != x

p = p->pNext; // xét phần tử kế

**Bước 3:

+Nếu p != NULL thì p lưu địa chỉ của nút có Info = x

+ Ngược lại : Không có phần tử cần tìm

• Hàm tìm

Node *Search(LIST l, Data x) {

Node *p;

p = l.pHead;

while ((p != NULL) && (p->Info != x))

p = p->pNext;

return p;

}

Node * Search (LIST l, int x) {

Trang 15

15

Node * p ;

p = l pHead ;

while ( p != NULL ) {

if ( p -> Info == x)

return p ;

p = p -> pNext ; }

return NULL ; }

9 Sắp xếp danh sách

• void SelectionSort(LIST &l) {

Node *p, *q, *min;

p = l.pHead;

while (p != l.pTail) {

min = p;

q = p->pNext;

while (q != NULL) {

if (q->Info<p->Info)

min = q;

q = q->pNext;

} Swap(min->Info, p->Info);

p = p->pNext;

} }

• Thuật toán sắp xếp Quick Sort

*Bước 1:

Chọn X là phần tử đầu xâu L làm phần tử cầm canh Loại

X ra khỏi L

*Bước 2 :

Trang 16

16

Tách xâu L ra làm 2 xâu L1(gồm các phần tử nhỏ hơn hoặc bằng x) và L2(gồm các phần tử lớn hơn X)

*Bước 3 : Nếu(L1 != NULL) thì QuickSort(L1)

*Bước 4 : Nếu(L2 != NULL) thì QuickSort(L2)

*Bước 5 : Nối L1, X, L2 lại theo thứ tự ta có xâu L đã được sắp xếp

• void QuickSort(List &l) {

NODE *p;

NODE *X; //X lưu địa chỉ của phần tử cầm canh List L1, L2;

if (l.pHead == l.pTail)

return; //đã có thứ tự DS rỗng hoặc có một phần

tử

else {

CreateList(L1); //Hàm tạo ds L1 rỗng CreateList(L2); //Hàm tạo ds L2 rỗng

X = l.pHead;

l.pHead = X->pNext;

while (l.pHead != NULL) //tách L = L1 + L2 {

p = l.pHead;

l.pHead = p->pNext;

p->pNext = NULL;

if (p->Info <= X->Info)

AddHead(L1, p); //Hàm thêm pt p vào L1 else

AddHead(L2, p); //Hàm thêm pt p vào L2 }

QuickSort(L1); //Gọi đệ quy sắp xếp L1 QuickSort(L2); //Gọi đệ quy sắp xếp L2

if (L1.pHead != NULL) //nối L1, L2 va X vào danh sách

{

l.pHead = L1.pHead;

L1.pTail->pNext = X; //nối X vào }

else

l.pHead = X;

X->pNext = L2.pHead;

if (L2.pHead != NULL) //L2 có trên một phần tử

l.pTail = L2.pTail;

else //l2 không có phần tử nào

Trang 17

17

l.pTail = X;

} }

• Thuật tóan sắp xếp Merge Sort

* *Bước 1: Phân phối luân phiên từng đường chạy của xâu L vào 2 xâu con L1 và L2

* *Bước 2 : Nếu L1 != NULL thì Merge Sort(L1)

* *Bước 3 : Nếu L2 != NULL thì Merge Sort(L2)

* *Bước 4 : Trộn L1 và L2 đã sắp xếp lại ta có xâu L

đã được sắp xếp Không tốn thêm không gian lưu trữ cho các dãy phụ

10.Hủy danh sách liên kết đơn

• Thuật toán

*Bước 1:

Trong khi(danh sách chưa hết) thực hiện

• B11 :

p = pHead;

pHead = pHead->pNext; // cập nhật pHead

• B12 : Hủy p

*Bước 2 : pTail = NULL; // bảo toàn tính nhất quán khi xâu rỗng

• Hàm hủy danh sách liên kết đơn

void RemoveList(List &l) {

NODE *p;

while (l.pHead != NULL)//còn phần tử trong List {

p = l.pHead;

l.pHead = p->pNext;

delete p;

} }

Trang 18

18

Mảng môt chiều Danh sách liên kết đơn

For(int i=0;i<n;i++) For(NODE

*p=l.pHead;p!=NULL;p=p->pNext) For(int i=0;i<n-1;i++) For(NODE

*p=l.pHead;p!=l.pTail;p=p->pNext) Int i=0

While (i<n)

{

i++;

}

NODE *p=l.pHead;

While (p!=NULL) {

……

p=p->pNext;

}

Ngày đăng: 03/11/2024, 15:47

w