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

Download Report

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

•Xin vui lòng yên lặng!
Lê Mậu Long
1
Danh sách liên kết
(Linked List)
Lê Mậu Long
2
Giới thiệu
• Định nghĩa: Là danh sách bao gồm các
phần tử kết nối với nhau bằng 1 hay nhiều
mối liên kết
Lê Mậu Long
3
Các thao tác trên danh sách
•
•
•
•
•
•
•
•
•
•
Khởi tạo danh sách rỗng
Kiểm tra danh sách rỗng?
Chèn phần tử vào danh sách
Xóa phần tử trong danh sách
Tìm kiếm phần tử trong danh sách
Khởi đầu từ đầu danh sách
Lấy dữ liệu 1 phần tư
Chuyển sang phần tử kế tiếp
Hết danh sách
….
Lê Mậu Long
4
Danh sách liên kết đơn
• Là danh sách mà mỗi phần tử có 1 mối liên
kết để kết nối với phần tử kế tiếp
head
pos
rear
• Cài đặt: dựa trên con trỏ, bao gồm:
• 3 con trỏ: head (đầu ds), pos (phần tử hiện hành),
và rear (cuối ds)
• biến count: số phần tử của danh sách
Lê Mậu Long
5
Mô tả kiểu dữ liệu
typedef struct nodet {
elem data;
struct nodet *next;
} node;
typedef node *nodeptr;
Lê Mậu Long
data
next
6
head
pos
rear
typedef struct {
nodeptr head, pos, rear;
int count;
} list;
Lê Mậu Long
7
Khởi tạo danh sách rỗng
Gán
– head, pos và rear = NULL
– count = 0
// Hàm khởi tạo
void createlist(list &l)
{
l.head = l.pos = l.rear = NULL;
l.count = 0;
}
Lê Mậu Long
8
Kiểm tra danh sách rỗng?
• Kiểm tra số phần tử = 0
int emptylist(list l)
{
return l.count == 0;
}
Lê Mậu Long
9
Chèn phần tử x vào danh sách – cục bộ
(local)
• Cấp phát bộ nhớ cho newp và gán dữ liệu
• Chèn ở đầu ds (p==NULL)
head
pos
rear
newp
newp->next = head;
head = newp;
Lê Mậu Long
10
• Chèn sau phần tử p (p!=NULL)
head
pos
p
rear
newp
newp->next = p->next;
p->next = newp;
Lê Mậu Long
11
• Trường hợp chèn cuối (newpnext == NULL)
head
pos
p
rear
newp
• Thay đổi rear
rear = newp;
Lê Mậu Long
12
void
{
insertlist(list &l, elem &x, nodeptr p)
nodeptr newp = new node;
memcpy(&newp->data, &x, sizeof(elem));
if (p==NULL)
{
newp->next = l.head;
l.head = newp;
}
else
{
newp->next = p->next;
p->next = newp;
}
if (newp->next == NULL)
l.rear = newp;
l.count++;
}
Lê Mậu Long
13
Các hàm chèn phần tử – toàn cục (global)
• Chèn đầu danh sách
void inserthead(list &l, elem &x)
{
insertlist(l, x, NULL);
}
• Chèn vị trí hiện hành
void insertpos(list &l, elem &x)
{
insertlist(l, x, l.pos);
}
• Chèn cuối danh sách
void insertrear(list &l, elem &x)
{
insertlist(l, x, l.rear);
Lê Mậu Long
}
14
Xoá phần tử trong danh sách – cục bộ
• Xoá phần tử đầu danh sách (p=NULL)
t
p
rear
head
t = head;
head = t->next;
• Xoá phần tử sau p (p!=NULL)
head
p
t
t = p->next;
p->next
Lê Mậu
= t->next;
Long
rear
15
void deletelist(list &l, nodeptr p)
{
nodeptr t;
if (p==NULL)
{
t = l.head;
l.head = t->next;
}
else
{
t = p->next;
p->next = t->next;
}
if (t->next == NULL)
l.rear = p;
delete t;
l.count--;
Lê Mậu Long
}
16
Các hàm xóa phần tử – toàn cục
• Xóa phần tử đầu danh sách
void deletehead(list &l)
{
deletelist(l, NULL);
}
• Xóa phần tử tại vị trí hiện hành
void deletepos(list &l)
{
deletelist(l, l.pos);
}
Lê Mậu Long
17
Tìm kiếm trên DS
X=5
head
1
pos
8
2
c
5
rear
4
3
1.
Khởi đầu từ đầu danh sách (pos = NULL; c = head)
2.
Khi chưa hết ds và chưa tìm thấy
Chuyển sang phần tử kế tiếp (pos = c; c = cnext)
Lê Mậu Long
18
Tìm kiếm trên DS – bản nháp
int searchlist(list &l, elem x)
{
nodeptr c = l.head;
l.pos = NULL;
while (c!=NULL && x!=c->data)
{
l.pos = c;
c = c->next;
}
return c!=NULL;
}
Lê Mậu Long
19
Tìm kiếm trên DS – viết lại
int searchlist(list &l, elem x, int (*comp)(elem, elem))
{
nodeptr c = l.head;
l.pos = NULL;
while (c!=NULL && comp(x, c->data)!=0)
{
l.pos = c;
c = c->next;
}
return c!=NULL;
}
Lê Mậu Long
20
Tìm kiếm trên DS có thứ tự
int searchorderlist(list &l, elem x, int (*comp)(elem, elem))
{
nodeptr c = l.head;
l.pos = NULL;
while (c!=NULL && comp(x, c->data)>0)
{
l.pos = c;
c = c->next;
}
if (c!=NULL && comp(x, c->data)<0)
return 0;
return c!=NULL;
}
Lê Mậu Long
21
Ví dụ
int searchorderlist(list &l, elem x, int
(*comp)(elem, elem))
{
nodeptr c = l.head;
l.pos = NULL;
while (c!=NULL && comp(x, c->data)>0)
{
l.pos = c;
c = c->next;
}
if (c!=NULL && comp(x, c->data)<0)
return 0;
return c!=NULL;
}
// Hàm so sánh trên khoá
int ss(int x, int y)
{
return x-y;
}
//Đoạn chương trinh nhập dãy
createlist(l);
cout<<"\nNhap day:";
do {
cin>>x;
if (x>0)
{
searchorderlist(l, x, ss);
insertpos(l, x);
}
} while (x>0);
Lê Mậu Long
22
• Khởi đầu từ đầu danh sách
void start(list &l)
{
l.pos = l.head;
}
• Chuyển sang phần tử kế tiếp
void skip(list &l)
{
if (l.pos == NULL)
l.pos = l.head;
else
l.pos = l.pos->next;
}
Lê Mậu Long
23
• Kiểm tra hết danh sách
int eol(list l)
{
return l.pos == NULL;
}
• Lấy dữ liệu 1 phần tử
void getdata(list l, elem &x)
{
memcpy(&x, &l.pos->data, sizeof(elem));
}
• Số phần tử trong danh sách
int nolist(list l)
{
return l.count; Lê Mậu Long
}
24
Ngăn xếp (Stack)
• Là cấu trúc bao gồm các phần tử được truy xuất theo
nguyên tắc “vào sau, ra trước” (Last In, First Out –
LIFO)
Ví dụ: Chồng đĩa, đổi số ra nhị phân
19
1
9
1
4
0
2
0
1
1
1
0
0
1
1 Lê Mậu Long
25
Các thao tác trên Stack
4 thao tác cơ bản trên stack
• Khởi tạo stack rỗng: CreateStack(s)
• Kiểm tra stack rỗng: EmptyStack(s)
• Đưa phần tủ vào Stack: Push(s, x)
• Lấy phần tủ ra khỏi Stack: Pop (s, x)
Lê Mậu Long
26
Cài đặt trên cơ sở mảng
• Sử dụng
– Mảng e[Max] phần tử kiểu elem
(elementtype)
– Chỉ số top để chỉ đỉnh stack (nơi
đưa vào, lấy ra)
• Tạo tập tin Stack.cpp:
typedef struct {
elem e[Max];
int top;
} stack;
Lê Mậu Long
Max-1
top
0
1
1
e
27
Các thao tác trên Stack
• Khởi tạo stack s rỗng
void createstack(stack &s)
{
s.top = -1;
}
• Kiểm tra stack s rỗng?
int emptystack(stack s)
{
return s.top==-1;
}
Lê Mậu Long
28
• Đưa phần tử x vào stack s
void push(stack &s, elem &x)
{
if (s.top==Max-1) exit(0);
memcpy(&s.e[++s.top], &x, sizeof(elem));
}
• Lấy phần tử x ra khỏi stack s
void pop(stack &s, elem &x)
{
if (s.top==-1) exit(0);
memcpy(&x, &s.e[s.top--], sizeof(elem));
}
Lê Mậu Long
29
Ví dụ: Đổi số ra nhị phân
#define Max 32
#include <iostream.h>
typedef int elem;
#include "stack.cpp"
void main()
{
int i, n;
stack s;
cout<<"\nNhap so can doi:";
cin>>n;
createstack(s);
while (n>0)
}
{
i = n%2;
push(s, i);
n = n/2;
}
Lê Mậu Long
cout<<"Ket qua dang nhi phan :";
while (!emptystack(s))
{
pop(s, i);
cout<<i;
}
30
Đệ qui và tổ chức đệ qui
Lê Mậu Long
31
Định nghĩa
• Một định nghĩa được gọi là đệ qui nếu nó
được định nghĩa trên chính nó một cách
trực tiếp hay gián tiếp
• Đệ qui luôn gồm 2 phần
– Phần dừng
– Phần đệ qui
Lê Mậu Long
32
Ví dụ
Lê Mậu Long
33
Giai thừa
long gt(int n)
{
if (n==0) return 1;
return n*gt(n-1);
}
void main()
{
cout<<gt(5);
}
Lê Mậu Long
34
Khử bỏ đệ qui
• Sử dụng Stack
• Đệ qui được thay bằng:
– Hàm đệ qui: Vòng lặp
– Lời gọi đệ qui: Push các giá trị cục bộ
– Thực hiện đệ qui: Pop các giá trị cục bộ
• Lưu ý: Stack là cấu trúc LIFO
Lê Mậu Long
35
Bài toán Tháp Hanoi
#include <iostream.h>
void chuyen(int n, char A, char B, char C)
{
if (n==1)
cout<<"\nChuyen "<<A<<" qua "<<C;
else
{
chuyen(n-1, A, C, B);
chuyen(1, A, ' ', C);
chuyen(n-1, B, A, C);
}
}
void main()
{
cout<<"\n============ De qui ===============";
chuyen(3, 'a', 'b', 'c');
Lê Mậu Long
}
36
Bài toán Tháp Hanoi không đệ qui
1. Khởi tạo stack s rỗng
2. Push 1 bộ (3, ‘A’, ‘B’, ‘C’) vào s
3. Lặp
– Pop ra khỏi s 1 bộ (n, A, B, C);
– Nếu n=1 thì
•
cout<<"\nChuyen "<<A<<" qua "<<C;
– Ngược lại
•
•
•
Push 1 bộ (n-1, B, A, C) vào s
Push 1 bộ (1, A, ‘ ‘, C) vào s
Push 1 bộ (n-1, A, C, B) vào s
4. Cho đến khi s rỗngLê Mậu Long
37
Cài đặt trên cơ sở con trỏ
• Cài đặt tương tự như danh sách liên kết đơn với 2
thao tác
– Chèn đầu danh sách
– Lấy ra ở đầu danh sách
push
s
pop
Lê Mậu Long
38
• Tạo tập tin Stack.cpp:
typedef struct nodet {
elem data;
struct nodet *next;
} node;
typedef node *stack;
void createstack(stack &s)
{
s = NULL;
}
Lê Mậu Long
39
int emptystack(stack s)
{
return s==NULL;
}
s
newp
void push(stack &s, elem &x)
{
stack newp = new node;
memcpy(&newp->data, &x, sizeof(elem));
newp->next = s;
s = newp;
}
Lê Mậu Long
40
t
s
void pop(stack &s, elem &x)
{
stack t = s;
if (s==NULL) exit(0);
memcpy(&x, &s->data, sizeof(elem));
s = s->next;
delete t;
}
Chạy lại chương trình đổi nhị phân, nhận xét?
Lê Mậu Long
41
Hàng đợi (Queue)
• Là cấu trúc bao gồm các phần tử được truy xuất theo
nguyên tắc “vào trước, ra trước” (First In, First Out
– FIFO)
Ví dụ: Các vùng đệm giao tiếp giữa máy tính và các
thiết bị
Lê Mậu Long
42
Các thao tác trên Queue
4 thao tác cơ bản trên queue
• Khởi tạo queue rỗng: CreateQueue(q)
• Kiểm tra queue rỗng: EmptyQueue(q)
• Đưa phần tủ vào queue : AddQueue(q, x)
• Lấy phần tủ ra khỏi queue : RemoveQueue(q, x)
Lê Mậu Long
43
Cài đặt trên cơ sở mảng
• Sử dụng
– Mảng e[Max] phần tử kiểu elem (elementtype)
– Chỉ số
– front để chỉ đầu hàng đợi (nơi lấy ra)
– rear để chỉ cuối hàng đợi (nơi đưa vào)
Hai chỉ số front và rear tăng xoay vòng
• Tạo tập tin Queue.cpp:
typedef struct {
elem e[Max];
int front, rear;
Lê Mậu Long
} queue;
44
Các thao tác trên Queue
• Khởi tạo queue q rỗng
void createqueue(queue &q)
{
q.front = q.rear = 0;
}
• Kiểm tra queue q rỗng?
int emptyqueue(queue q)
{
return q.front == q.rear;
}
Lê Mậu Long
45
• Đưa phần tử x vào queue q
void addqueue(queue &q, elem &x)
{
int nr = (q.rear + 1) % Max;
if (nr == q.front) exit(0);
memcpy(&q.e[q.rear], &x, sizeof(elem));
q.rear = nr;
}
• Lấy phần tử x ra khỏi queue q
void removequeue(queue &q, elem &x)
{
if (q.front == q.rear) exit(0);
memcpy(&x, &q.e[q.front], sizeof(elem));
q.front = (q.front + 1) % Max;
Lê Mậu Long
}
46
Ví dụ: Radix sort
• Sắp xếp dãy số nguyên dương tăng dần
71, 32, 53, 70, 50 , 63, 84, 15, 26, 19, 8, 37, 17, 46, 98, 21
0
1
50
21
70
71
2
3
4
5
63
32
53
17
26
37
15
21
32
84
15
6
7
8
46
17
98
26
37
8
19
84
98
9
19
8
53
46
50
71
63
70
Lê Mậu
Long50, 53, 63, 70, 71, 84, 9847
8, 15, 17, 19, 21, 26, 32, 37,
46,
Cài đặt trên cơ sở con trỏ
• Cài đặt tương tự như danh sách liên kết đơn với 2
thao tác
– Chèn cuối danh sách
– Lấy ra ở đầu danh sách
rear
front
remove
add
Lê Mậu Long
48
• Tạo tập tin Queue.cpp:
typedef struct nodet {
elem data;
struct nodet *next;
} node;
typedef node *nodeptr;
typedef struct {
nodeptr front, rear;
} queue;
void createqueue(queue &q)
{
q.front = NULL;
}
Lê Mậu Long
49
int emptyqueue(queue q)
{
return q.front == NULL;
}
rear
front
void addqueue(queue &q, elem &x)
{
nodeptr newp = new node;
memcpy(&newp->data, &x, sizeof(elem));
newp->next = NULL;
if (q.front == NULL)
q.front = newp;
else
q.rear->next = newp;
q.rear = newp;
}
Lê Mậu Long
newp
50
t
rear
front
void removequeue(queue &q, elem &x)
{
nodeptr t = q.front;
if (q.front == NULL) exit(0);
q.front = t->next;
memcpy(&x, &t->data, sizeof(elem));
delete t;
}
Lê Mậu Long
51
• Viết hàm tách danh sách liên kết đơn
chứa các số nguyên thành 2 danh sách
chẵn/lẻ
Lê Mậu Long
52