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

Ebook cấu trúc dữ liệu và giải thuật phần 2 ths an văn minh, ths trần hùng cường

95 425 11
Tài liệu được quét OCR, nội dung có thể không chính xác

Đ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 95
Dung lượng 2,29 MB

Nội dung

Trang 1

Chuong 4 ~ DANH SACH TUYEN TINH Trong chương này chỳng ta sẽ nghiờn cứu danh sỏch tuyến tớnh, một trong cỏc mụ hỡnh đữ liệu quan trọng nhất, được sử dụng thường xuyờn trong việc cài đặt cỏc bài toỏn ứng dụng Cỏc phương phỏp cài

đặt danh sỏch khỏc nhau sẽ được xem xột Hai kiểu dữ liệu trừu tượng đặc biệt quan trọng là ngăn xếp (Stack) và hàng đợi (Queue) sẽ được

nghiờn cứu Chương này cũng sẽ trỡnh bày một số ứng dụng phổ biến

của danh sỏch

I KHÁI NIỆM DANH SÁCH TUYẾN TÍNH

t.1, Khỏi niệm danh sỏch

VỀ mặt toỏn học, danh sỏch là một dóy hữu hạn cỏc phần tử thuộc cựng một lớp đối tượng nào đú Chăng hạn danh sỏch sinh viờn

của một lớp, danh sỏch cỏc số nguyờn, danh sỏch cỏc bỏo xuất bản

hàng ngày ở thỳ đụ, v.v

Giỏ sử L là một danh sỏch cú n phõn tử (n >= 0)

L =(al, a2, , an)

Ta gọi n là độ dài của đanh sỏch Nộu n >= | thi al được gọi là phần tử đầu tiờn, an được gọi là phần tử cuối cựng của danh sỏch L Nếu n = 0 thỡ danh sỏch L được gọi là danh sỏch rỗng

Trang 2

84 Cấu trỳc đữ liệu và giải thuật

1.2 Cỏc phộp toỏn trờn danh sỏch

Khi mụ tả một mụ hỡnh dữ liệu, chung ta cần xỏc định cỏc phộp toỏn cú thể thực hiện trờn mụ hỡnh toỏn học được dựng làm cở sở cho mụ hỡnh đữ liệu Cú rất nhiều phộp toỏn trờn danh sỏch Trong cỏc ứng dụng, thụng thường chỳng ta chỉ sử dụng một nhúm cỏc phộp toỏn nào

đú Sau đõy là một số phộp toỏn cơ bản trờn danh sỏch tuyến tớnh

Giả sử L là một đanh sỏch, cỏc phần tử của nú cú kiờu #m, k là

vị trớ của một phõn tử trong đanh sỏch Cỏc phộp toỏn sẽ được mụ tả bởi cỏc hàm sau đõy:

1 Khởi tạo danh sỏch rỗng

void Initialize(List *L);

2 Xỏc định độ đài của danh sỏch

int Length(List *L);

3 Loại phõn tử ở vị trớ thứ k của danh sỏch

void Delete(int k, List *L):

4, Xen phan tie X vao danh sach sau vi tri thir k void Insert_After(Itemt X, int k, List *L); 5 Xen phan tir X vao danh sach tritộc vi tri thie k

void Insert_Before(Item X int k List *1.);

6 Tỡm phõn tử X trong danh danh sỏch

int Search(UItem X Lisf *L);

Hàm Search trả về ] nếu X cú trong L ngược lại trả về 0

7 Kiểm tra xem danh sỏch cú rụng khụng?

int Empty(List *L); //Ham Empty tra về è nếu L rỗng ngược

Trang 3

(hương 4: Danh sỏch tuyộn tinh 85

8 Kiểm tra xem danh sỏch cú đõy khụng?

int Full(List *L); //Ham Full tra về I nộu L day, ngược lại trả về 0

9 Duyệt danh sỏch

Trong nhiều ứng dụng chỳng ta phải đi qua danh sỏch, từ đầu

đến cuối danh sỏch và thực hiện một nhúm cỏc thao tỏc nào đú đối với mỗi phần tử của đanh sỏch

void Traverse(List *1.); 10, Cac phộp toan khac

Cũn cú thể kể ra nhiều phộp toỏn khỏc Chăng hạn truy nhập đến phõn tử thứ Ă của danh sỏch (để tham khảo hoặc thay thể), kết hợp hai

danh sỏch thành một danh sỏch, tỏch một danh sỏch thành nhiều danh sỏch v.V,

Vớ dụ: Giả sử cú danh sỏch L = (3, 2, 1, 5) Khi đú, thực hiện

Delete(3, L) ta được danh sỏch (3, 2, $5) Kết quả của Insert_Before(1,

6, L) ta được danh sỏch (6, 3, 2, 1, 5)

Sau đõy ta sẽ xột một số loại danh sỏch và ứng dụng của chỳng

2 LƯU TRỮ KẺ TIẾP CỦA DANH SÁCH TUYẾN TÍNH

Ta biết rằng đanh sỏch tuyến tớnh là một đanh sỏch hoặc rỗng hoặc cú dạng L = (al, a2 , an) Trong danh sỏch tuyến tớnh luụn tụn tại một phần tử đầu là a! và một phần tử cuối là an (n > 1)

Dờ lưu trữ danh sỏch tuyến tỉnh trong bộ nhớ mỏy tớnh, một phương phỏp rất tự nhiờn là sử dụng mảng một chiều, trong đú mỗi

thành phần của mỏng lưu trữ một phần tử tương ứng của danh sỏch, cỏc

nhõn tử kế nhau của danh sỏch được lưu trữ trong cỏc thành phần kế

°hau của mảng Lưu trữ danh sỏch theo cỏch này gọi là lưu trữ kế tiếp

Tuy nhiờn, việc sử đụng mảng một chiều cũng cú những ưu

Trang 4

86 Cấu trỳc dữ liệu và giải thuật

+ Vỡ mảng được lưu trữ kế tiếp nờn việc truy nhập vào một thành phần nào đú được thực hiện trực tiếp dựa vào địa chớ tớnh được

(chỉ số), nờn tốc độ nhanh và đồng đều đối với mọi phần tử

+ Khi khai bỏo một mảng ta phải xỏc định số lượng phần tử của mảng, điều này sẽ tuỳ thuộc vào số lượng phản tử của danh sỏch mà mảng sẽ lưu trữ, nhưng điều này rất khú thực hiện vi số lượng phản tư

của đanh sỏch luụn luụn biến động Do đú, cú thể dẫn đến lăng phớ bộ

nhớ (cú những phần tử mảng khụng được sử dụng) hoặc thiếu bộ nhớ

(do tất cả cỏc phần tử mỏng đó được sử dụng trong khi ta cần thờm vào danh sỏch một số phần tử nào đú)

Sau đõy ta trỡnh bày cỏch cài đặt danh sỏch tuyờn tớnh bởi mảng một chiều:

Giỏ sử độ dài tối đa của danh sỏch là một số nguyờn đương N

nảo đú, cỏc phần tử trong danh sỏch cú kiểu dữ liệu là em em cú thể là cỏc kiờu đữ liệu đơn (số nguyờn, số thực, ký tự), hoặc cỏc kiểu dữ liệu cú cấu trỳc (chuỗi, cấu trỳc) Danh sỏch được biểu diễn bởi

một cầu trỳc gồm hai thành phần đữ liệu

+ Thành phan thứ nhất là mang cỏc Iứer, phần tử thứ 1 của danh

sỏch được lưu trữ bởi phần tử thứ Ă của mảng

+ Thành phần thứ hai ghi chỉ số của phõn tử mảng lưu trữ phần tử cuối cũng của danh sỏch

Trang 5

Chương 4: Danh sỏch tuyến tớnh Đ7 1 phần tử thứ nhất | 2 phần tứ thứ hai Danh sỏch count phần tử cưối cựng | Chưa cú Maxlength |

Hỡnh 4.1: Mỏng biểu diễn danh sỏch

Trong cỏch cài đặt danh sỏch bởi mảng, cỏc phộp toỏn trờn danh sỏch được thực hiện rất dễ dang Đề khởi tạo danh sỏch rỗng chỉ cần một lệnh gỏn: L.count = 0; Độ đài thực của đanh sỏch là L.count, danh sỏch đầy nếu L.count=Maxlength Vi du: Khai bao danh sỏch lưu trữ thụng tin về sinh viờn #define Maxiength 100 struct student item ở đõy là student {

char std_no[10]; Ma sinh viộn

char std_name[30]; /Họ tờn

int age; HTudi

Trang 6

88 Cấu trỳc đữ liệu và giải thuật

Dưới đõy ta cài đặt hai phộp toỏn trờn danh sỏch: phộp toỏn bổ sung một phần tử mới vào danh sỏch và phộp toỏn loại bỏ một phần tử khúi danh sỏch

l Loại bo một phan tir o vi tri k trong danh sỏch

int DefeteL(int k, struct List *L) { inti: if (k>=1 && k <= L->count) { i=k;: while (i < L->count) { L->E[i] = L->Efi+1] ; i=it+1; } L->count = L->count - 1; return 1; } else retum 0: }

Hàm DeleteL thực hiện phộp loại một phần tử ở vị trớ k trong

danh sỏch Phộp toỏn được thực hiện khi danh sỏch khụng rỗng và k

chỉ vào một phần tử trong danh sỏch Giỏ trị trả về của hàm cho biết phộp toỏn cú được thực hiện thành cụng hay khụng (trả về 1 nếu thành cụng, trả về 0 nếu khụng thành cụng) Khi loại bỏ, ta phải dồn cỏc phần tử ở cỏc vị tri k+1, k+2, , L.count lờn trờn một vị trớ và giảm số lượng phần tử của danh sỏch đi một đơn vị (L.count = L.count — 1)

2 Bồ sung một phản từ vào trước phõn tử ở vị trớ k trong danh sỏch (dữ liệu của phõn tử này được lưu trong biến Xỡ)

int InsertL(int k, Item X, struct List *L)

{

Trang 7

Chương 4: Danh sỏch tuyến tinh 89 if (L->count < Maxlength && k <= L->caunt && k>=1) { i= L->count + 1; while (i > k) { L->E€[i] = L->E[i-1] : i=t- 14: } L->count = L->count + 1; L->E[k] = X: return 1; } else return 0; }

Hàm Insertl thực hiện phộp bỏ sung một phần tử vào trước phần

tử ở vị trớ k trong danh sỏch Phộp toỏn được thực hiện khi danh sỏch chưa đõy và k chỉ vào một phần tử trong danh sỏch Giỏ trị trả về của

hảm cho biết phộp toỏn cú được thực hiện thành cụng hay khụng (trả về 1; thành cụng, trả vẻ 0: khụng thành cụng) Khi bụ sung, ta phải dón cỏc phần tử ở cỏc vị trớ L.count, k+1, k xuống đưới một vị trớ và

tăng số lượng phần tử của danh sỏch lờn một đơn vị (L.count = L.count + ])

* Nhận với ve phương phỏp cài đặt danh sỏch bởi mảng: Việc cài đặt danh sỏch bởi mảng cú một số ưu điểm và nhược điểm sau:

Ưu điểm: Do tớnh chất của mảng, nờn việc cài đặt đanh sỏch bởi mảng cho phộp ta truy nhập trực tiếp vào bất kỳ phần tử nào trong danh sỏch nờn tốc độ truy nhập nhanh và đồng đều đổi với mọi phan tử Cỏc phộp toỏn cũng đờu được thực hiện một cỏch dễ dàng

Nhược điểm: Khi thực hiện cỏc phộp toỏn bố sung một phần tử

Trang 8

90 Cau tric dit liộu va giai thuat

nao đú, ta phải đầy tất cả cỏc phần từ sau k xuống dưới hoặc lờn trờn một vị trớ, nờn tốn nhiều thời gian Tuy nhiờn, nhược điểm chủ yếu của phương phỏp cài đặt này là khụng gian nhớ cụ định dành để lưu trữ cỏc phần tử của danh sỏch Khụng gian nhớ này bị quy định bởi kớch thước của mảng (kớch thước của mỏng được xỏc định khi khai bỏo và

nú khụng thờ thay đổi trong khi thực hiện chương trỡnh) Do đú cú thể

dẫn đến trường hợp lóng phớ bộ nhớ (do khai bỏo kớch thước mảng quả lớn so với số lượng cỏc phần tử của danh sỏch) hoặc thiếu bộ nhớ (mảng đó đầy trong khi ta muốn bụ sung thờm một số phần tử nảo đú vào danh sỏch)

Đề khắc phục cỏc nhược điểm trờn đõy người ta sử đụng một

phương phỏp khỏc để cài đặt danh sỏch tuyến tớnh đú là danh sỏch

múc nồi

3 DANH SÁCH MểC NểI

Như đó nờu ở phần trờn, lưu trữ kế tiếp đối với danh sỏch tuyến

tớnh đó bộc lộ rừ nhược điểm trong trường hợp thực hiện thường xuyờn cỏc phộp bụ sung hoặc loại bỏ phõn tử trường hợp xử lý đồng thời nhiều danh sỏch, v.v

Việc sử dụng con trỏ hoặc mỗi nối để tổ chức danh sỏch tuyến

tớnh, mà ta gọi là danh sỏch múc nối (hay cũn gọi là danh sỏch liờn kết),

chớnh là một giải phỏp nhằm khắc phục nhược điểm trờn Tuy nhiờn,

trước khi tỡm hiểu về danh sỏch múc nổi ta nhắc lại một số khỏi niệm

về con trỏ, phương tiện được sử dụng để cài đặt danh sỏch múc nối

3.1 Kiểu con trồ và cỏc khỏi niệm liờn quan

Tất cả cỏc biến cú kiờu đữ liệu mà ta đó nghiờn cứu như số ký

tự, mảng, cấu trỳc được goi la biộn tinh vi chỳng được xỏc định một cỏch rừ ràng khi khai bỏo, sau đú chỳng được dựng thụng qua tờn

Thời gian tồn tại của biến tĩnh cũng là thời gian tồn tại của khối chương trỡnh cú chứa khai bỏo biến này Chăng hạn, cỏc biến tĩnh

Trang 9

Chương 4: Danh sỏch tuyến tớnh 9]

chương trỡnh được thực hiện cho đến khi kết thỳc chương trỡnh, cũn

cỏc biến tĩnh được khai bỏo trong một hàm (biến cục bộ) sẽ tổn tại từ khi hàm được triệu gọi cho đến khi kết thỳc

Ngoài cỏc biến tĩnh được xỏc định trước, người ta cũn cú thờ tạo

ra cỏc biến trong lỳc chạy chương trỡnh, tuỷ theo nhu cầu Việc tạo ra

cỏc biến theo kiểu nảy được gọi là cấp phỏt bộ nhớ động cỏc biến

được tạo ra được gọi là biến động

Cỏc biến động khũng cú tờn Trong C/C++, dộ tao ra biển động người ta sử dụng một kiểu biến đặc biệt; gọi là con trũ và cỏc hàm/toỏn tử cấp phỏt bộ nhớ động (malloc(), ealloc(, reallocÚ trong thư viện malloc.h, toỏn tứ new) thụng qua con trỏ Khi khụng sử dụng biến động nữa, người ta cú thể xoỏ nú khỏi bộ nhớ, việc này gọi là thu

hồi bộ nhớ động Để thu hồi bộ nhớ dành cho biến động, người ta

dựng hàm ỉ#ee(J/toỏn từ đelete và thụng qua con trỏ đó sử dụng để tạo

ra biến động

So với biến tĩnh việc sử dụng biến động cú ưu điểm là tiết kiệm được bộ nhớ Bởi vỡ khi cần dựng biến động thỡ người ta sẽ tạo ra nú

và khi khụng cần nữa người ta lại cú thể xoỏ nú khụi bộ nhớ Cũn đối với cỏc biến tĩnh, chỳng được xỏc định và cấp phỏt bộ nhớ khi biẻn dịch, chỳng sẽ chiếm giữ bộ nhớ trong suốt thời gian chương trỡnh làm

việc Chằng hạn, nếu cần sử dụng một mảng ta phải khai bỏo ngay ở

phần đầu chương trỡnh, ngay lỳc này ta đó phải xỏc định kớch thước của mảng và thường khai bỏo dụi ra, gõy lóng phớ bộ nhớ

3.1.1, Con trỏ

Con trỏ là một biến dựng để chứa địa chỗ nhớ chỉ của một biến

khỏc

Cỏch khai bỏo con trỏ

<Kiộu dit ligu> <*tộn con trộ>; Vidu:

Trang 10

92 Cầu trỳc dữ liệu và giải thuật

3.1.2 Cac phộp toỏn con trỏ

Gia su cộ khai bao inf *p, *q, x;

Khi đú ta cú thể thực hiện cỏc phộp toỏn

+ Gan dia chi cho con tro

Vi du: p = &x; //Gỏn địa chỉ của biến x cho p, hay p trỏ vào x

+ Phộp gan hai con trộ cing kiộu Vi du: q = p: //q va p cing tro Vào X

+ Phộp so sỏnh hai con trỏ cựng kiểu gồm: so sỏnh == (băng

nhau) và phộp sỏnh != (khỏc nhau)

Phộp toỏn một ngụi * được sử dụng với con trỏ đề trả về giỏ trị của chỗ nhớ đo con trỏ trỏ đến hoặc làm thay dỗi gia tr] cua chỗ nhớ đú Cỏch việt <*ftờn_con_ trỏ> Vĩ dụ: Pp X inf*p, X, y; Ứ73——— 100 x= 100;

p= &x; Ip tra vao x

y="“p; //khi đú ta cú giỏ trị của y = 100

*p = 500; //Khi đú ta cũng cú x = 500 3.1.3 Giỏ ri NULL

NULL là một giỏ trị con trỏ đặc biệt đảnh cho cỏc biến con trỏ, nú được dựng để bỏo rằng con trỏ khụng lưu địa chỉ của biến nào Giỏ

trị NULL cú thờ được đem gỏn cho bất kỳ biển con tro nao Duong nhiờn khi đú việc thõm nhập vào biến động thụng qua con trộ cộ gia tri NULL la vộ nghia

3.1.4 Con tro cau tric

Con trỏ chứa địa chỉ của một biển cấu trỳc được gọi là con trỏ

cầu trỳc, khi đú ta cú thộ thao tỏc với cầu trỳc thụng qua con trỏ Việc

truy nhập vào cỏc thành phần của cấu trỳc bằng con trỏ được viết theo cỏch sau:

Trang 11

Chương 4: Danh sỏch tuyến tớnh 93 Vớ dụ: struct Hoc_sinh { char Hio ten[25]; int tuoi; float diem; k struct Hoc_sinh “p, h: p = &h;

Khi đú việc truy xuất vào cỏc thành phần của cấu trỳc h thụng

qua con trú p được viết như sau:

strcpy(p->Ho_ten, “Nguyen Trong Huan’); p->diem = 7.4

cin>>p->tuoi;

3.1.6 Cap phat va thu hội bộ nhớ động

Trong ngụn ngữ lập trỡnh C cú thờ sử dụng cỏc hàm cấp phỏt bộ

nhớ động gồm: malloe(), calloc() cỏc ham này được định nghĩa trong, thư viện malloc.h

+ Hàm malloc() cấp phỏt cho con trú một vựng nhớ liờn tiếp

kớch thước vựng nhớ được chớ ra bởi tham số size Cỳ phỏp: void *malloc(size_t size)

Trong đú size là kớch thước vựng nhớ được cấp phỏt cho con trỏ được tớnh băng byte

Vĩ dụ:

int *p;

p=(int*) malloc (sizeof(inh),

Cõu lệnh trờn cấp phỏt cho con trỏ p một chỗ nhớ cú kớch thước băng một dữ liệu kiờu fart

Trang 12

94 Cấu trỳc dữ liệu và giải thuột + Ham calloc() cấp phỏt một vựng nhớ gồm n chỗ nhớ Cui phap: void *calloc(int n, size_t size) Vĩ dụ: int *p; p=(int*)calloc(1, sizeof(int); Đưới đõy ta xột một chương trỡnh được cài đặt với con trỏ cõu trỳc: #include<stdio.h> #include<conio.h> #include<malloc.h> struct Hoc sinh { char ht{25); int tuoi; char qa{[40]; } void Nhap_du_lieu(struct Hoc_sinh “p) {

cout<< “\tHo ten: ” ; fflush(stdin); gets(p->ht): cout<< “\tTuoi: *; cin>>p->tuai:

cout<< “\tQue quan: ”; fflush(stdin) ; gets(p->qq);

}

void Hien_thi(struct Hoc_sinh p)

{ cout<< “\tHo ten: ”<<p->ht<<endl;

cout<< "fTuoi: "<<p->tuoi<<endl;

Trang 13

Chuong 4: Danh sach tuyộn tinh 95 cout<< “Nhap thong tin ve hoc sinh’<<endi; Nhap_ du_lieu(p); cout<< “Thong tin hoc sinh vua nhap"<<endi; Hien_thi(*p); free(p); getch() }

Qua vi du trộn ta thay răng để đưa dữ liệu vào bộ nhớ thụng qua biến con trỏ ta cần phải cấp phỏt bộ nhớ cho nú và sau khi khụng sử

dụng nữa ta cú thờ xoỏ nú khỏi bộ nhớ Tuy nhiờn nếu chỉ lưu trữ đữ

liệu đơn giản như vậy thỡ ta khụng cần đến biến con trỏ, nú được sử dụng trong một ứng dụng quan trọng hơn đú là việc cài đặt danh sỏch

liờn kết Sau đõy ta xột tới một số đạng danh sỏch múc ni 3.2 Danh sỏch múc nụi đơn

3.2.1 Nguyờn tặc

Trong cỏch cải đặt này, danh sỏch múc nỗi được tạo nờn từ cỏc phần tử nhỏ mả ta gọi là nỳt (Node) Cỏc nỳt này cú thể năm bat ky

đõu trong bộ nhớ mỏy tớnh Mỗi nỳt là một cấu trỳc gồm hai thành

phõn, #yor chứa thụng tin của phần tử trong danh sỏch, mex/ là một

con trỏ, nú trỏ vào nỳt đứng sau Qui cỏch của mỗi nỳt cú thể hỡnh

dung như sau:

| infor | next |

Riờng nỳt cuối cựng thỡ khụng cú nỳt đứng sau nú nờn thành

phan next cua nỳt này cú giỏ trị NULL đề bỏo kờt thỳc danh sỏch

Đờ cú thờ truy nhập vào mọi nỳt trong danh sỏch, ta phải truy nhập

từ nỳt đầu tiờn, nghĩa là cần cú một con trỏ L, trỏ tới nỳt đầu tiờn này Nếu dựng mũi tờn đẻ chỉ mỗi nỗi ta sẽ cú hỡnh ảnh của một danh

Trang 14

96 Cấu trỳc dữ liệu và giai thuật ty a1 a2 a3 a4 e â

Hỡnh 4.2: Biờu diễn danh sỏch múc nổi đơn

Dấu E4 chỉ giỏ tri trudng next cua phan tt nay bang NULL

Giả sử cỏc phần tử trong danh sỏch cú kiểu dữ liệu là em Dưới đõy là khai bỏo cấu trỳc đữ liệu biờu diễn danh sỏch múc nối đơn

IIđịnh nghĩa kiểu dữ liệu Item (nếu cần) struct Node { {tem infor, Struct Node *next }

Struect Node *L; //Khai bỏo con trỏ L trỏ vào đầu danh sỏch L=NULL nếu danh sỏch rồng

Vi du: Khai bao danh sỏch lưu trữ thụng tin về sinh viờn:

Struct student l'Item ở đõy là student

{

char std_no[10}; /#Ma sinh viờn char std _name[30], /Họ tờn

int age; /Tuỗi

float avg_point: //Điểm trung bỡnh

}

struct Node

{

struct student infor struct Node *next: };

Trang 15

Chương 4: Danh sỏch tuyến tỉnh 97

3.2.2 Cỏc phộp toỏn trờn danh sỏch múc nỗi đơn

Bõy giờ chỳng ta sẽ xem xột một số phộp toỏn tỏc động trờn danh sỏch nồi đơn

Điều kiện để danh sỏch múc núi đơn rỗng là L = NULL, Do đú, đờ khởi tạo danh sỏch rỗng ta chỉ cần lệnh gỏn: L = NUI.L;

Danh sỏch múc nỗi chỉ đầy khi khụng cũn khụng gian nhớ dộ

cấp phỏt cho cỏc phần tử mới của danh sỏch Chỳng ta giả thiết điều

này khụng xảy ra, nghĩa là danh sỏch múc nối khụng bao giờ đầy Do

đú, phộp toỏn bố sung một phần tử vào danh sỏch luụn luụn được

thực hiện

a Bồ sung mội nỳt mới vào danh sỏch múc nồi ẩơn

Giả sử Q là một con trỏ, trừ vào một nỳt trong danh sỏch, ta cần

bổ sung một phần tử mới với thụng tin lưu trong biến X vào sau nỳt

được trỏ bởi Q Phộp toỏn này được thực hiện bởi thủ tục sau:

void InsertAfter(struct Node **L, struct Node *Q , Item X) { struct Node “p; //1 Tạo một nỳt mới p=(struct Node *)malloc(sizeof(struct Node)); p->infor = X;

/!2 Thực hiện bổ sung, nộu danh sach rộng thi bộ sung nỳt mới vào

Trang 16

98 Cấu trỳc dữ liệu và giải thuật ‘La Q al @ ` a2 | đơ " a3 | *đ a4 ——— a5 +

Hỡnh 4.3: M6 ta phộp bộ sung một phan tir vao sau nut trỏ bởi Q trong danh sach

Giả sử bõy giờ ta cần bỗ sung nỳt mới vào trước nỳt được trỏ bởi Q Phộp toỏn này phức tạp hơn Khú khăn là ở chỗ, nếu Q khụng phải là nỳt đầu tiờn của đanh sỏch (QzL) thỡ ta khụng thể xỏc định được nỳt

đứng trước Q để kết nối nú với nỳt mới Cú thể giải quyết khú khăn

bằng cỏch, đầu tiờn ta vẫn bổ sung nỳt mới vào sau Q, sau đú trao đụi giỏ trị chứa trong phan infor giữa nỳt mới và nỳt được trỏ bởi Q Thủ

tục thực hiện phộp toỏn này xin đành cho bạn đọc

b Loại bỏ một nỳt ra khỏi danh sỏch múc nối đơn

Cho đanh sỏch múc nỗi đơn được trỏ bởi L Q là một con trỏ, trỏ

vào một nỳt trong danh sỏch Giả sử ta cần loại bỏ nỳt được trỏ bởi Q

Ở đõy ta cũng gặp khú khăn là nếu Q khụng phải là nỳt đầu tiờn thỡ

khụng xỏc định được nỳt đứng trước Q Trong trường hợp này (a phải tỡm đến nỳt đứng trước Q và cho con trỏ R trỏ vào nỳt đú, tức là Q =R->next Sau đú ta mới thực hiện loại bỏ nỳt Q Ta cú thủ tục sau:

Trang 17

Chương 4: Danh sỏch tuyến tớnh 99 I2 Trường hợp nỳt trỏ bởi Q là nỳt đầu tiờn if (Q == *L) { *L = Q->next; free(Q); return 1; } //⁄3 Tỡm đờn nỳt đứng trước nỳt trỏ bởi Q R=”L; while (R->next != Q) R = R->next; 1/4 Loại bỏ nỳt trỏ bởi Q R->next = Q->next; free(Q); } Phộp toỏn loại bỏ được mụ tả bởi hỡnh 4.4 L R i acy at >| a2 | @4 >| a3 | #+ -" a4 —_ _ ] Hỡnh 4.4: Mụ tả phộp loại bỏ một phõn tử ra khỏi danh sỏch Â

c Ghộp hai danh sỏch múc nồi don

Giả sử cú hai danh sỏch múc nối đơn lần lượt được.trỏ bởi L1! và L2 Thủ tục sau thực hiện việc ghộp hai danh sỏch đú thành một danh sỏch mới được trỗ bởi LI

Trang 18

100 Cấu trỳc đữ liệu và giải thuật *L1 = L2; return: } 43, Tim đến nỳt cuối danh sỏch trỏ bởi P =*L1, while (R->next != NULL) R = R->next; 14 Ghộp R->next = L2; }

Nhận xột: Rừ ràng với cỏc danh sỏch tuyến tớnh mà kớch thước luụn biến động trong quỏ trỡnh xử lý hay thường xuyờn cú cỏc phộp bổ

sung và loại bỏ tỏc động, thỡ việc lưu trữ bằng danh sỏch múc nối như

trờn tỏ ra thớch hợp Tuy nhiờn, cỏch cài đặt này cũng cú những nhược

điểm nhất định:

Chỉ cú phần tử đầu tiờn trong danh sỏch được truy nhập trực

tiếp, cỏc phần tử khỏc chỉ được truy nhập sau khi đó di qua cỏc phần

tử đứng trước nú

Ở mỗi nỳt trong danh sỏch phải cú thờm trường ằex để lưu trữ

địa chỉ của nỳt tiếp theo, do đú với cựng một danh sỏch thỡ việc cài đặt bởi danh sỏch múc nối sẽ tốn bộ nhớ hơn so với cài đặt bằng mảng 3.3 Danh sỏch nối vũng

Một cải tiến của danh sỏch múc nối đơn là kiểu danh sỏch múc

nỗi vũng Nú khỏc với danh sỏch múc nối đơn ở chỗ: trường ứex/ của nỳt cuối cựng trong danh sỏch khụng phải bằng NULL,, mà nú trỏ đến

Trang 19

Chương 4: Danh sỏch tuyến tớnh 101 Cai tiễn này làm cho việc truy nhập vào cỏc nỳt trong danh sỏch

được lớnh hoạt hơn Ta cú thể truy nhập vào mọi nỳt trong danh sỏch

bất đầu từ nỳt nào cũng được, khụng nhất thiết phải từ nỳt đầu tiờn Điều đú cú nghĩa là nỳt nào cũng cú thể coi là nỳt đầu tiờn và con trỏ

L trỏ tới nỳt nào cũng được Như vậy, đối với danh sỏch múc nối vũng chỉ cõn cho biết con trỏ trỏ tới nỳt muốn loại bỏ ta sẽ thực hiện được

vỡ luụn tỡm được đến nỳt đứng trước đú Với phộp ghộp, phộp tỏch

cũng cú những thuận lợi nhất định

Tuy nhiờn, đanh sỏch nỗi vũng cú một nhược điểm rất rừ là trong

khi xử lý, nếu khụng cần thận sẽ dẫn tới một chu trỡnh khụng kết thỳc, bởi vỡ khụng biết được vị trớ kết thỳc danh sỏch

Đề khắc phục nhược điểm này, người ta đưa thờm vào danh sỏch

một nủt đặc biệt gọi là “nỳt đầu danh sỏch” Trường infor của nỳt này khụng chứa dữ liệu của phần tử nào và con trụ L bõy giờ trỏ tới nỳt đầu danh sỏch này Việc dựng thờm nỳt đầu danh sỏch đó khiến cho danh sỏch về mặt hỡnh thức khụng bao giờ rỗng Hỡnh ảnh của nú

minh họa như hỡnh 4.6 L 77) * ” a1 | đ a2 |*đ a3 | 4 Hỡnh 4.6: Mụ tả danh sỏch múc nồi vũng cú nỳt đầu

Sau đõy là đoạn giải thuật bổ sung một nỳt vào thành nỳt đầu

tiờn trong danh sỏch cú “nỳt đầu danh sỏch” trỏ bởi L P=(struct Node*) malloc(sizeof(struct Node)); P->infor = X;

P->next = L->next:

L->next = P;

3.4 Danh sỏch múc nụi hai chiờu

Khi làm việc với danh sỏch, cú những xử lý trờn mỗi nỳt của

Trang 20

102 Cộu tric dit liộu va giải thuật

những trường hợp như thế, để thuận tiện, người ta đưa vào mỗi nỳt

của danh sỏch hai con trỏ: Next Left trỏ đến nỳt đứng trước và

Next_Righ trỏ đến nỳt đứng sau nú Để truy nhập vào danh sỏch ta dựng hai con trỏ: con trỏ Z2/? trỏ vào nỳt đõu tiờn và con tro Right trd vào nỳt cuối cựng của đanh sỏch Hỡnh ảnh của danh sỏch múc nối hai chiều được minh họa trờn hỡnh 4.7 Left IM Right Ls

Hinh 4.7: M6 ta danh sỏch múc nồi đụi

Ta cú thờ khai bỏo cấu trỳc đữ liệu danh sỏch múc nỗi hai chiều như sau: lIKhai bỏo kiễu dữ liệu Item (nếu cõn) struct Node { {tem infor; struct Node *Next_Left, *Next_Right: )

Struct Node “Left, “Right;

Việc cài đặt danh sỏch múc nối hai chiều sẽ tiờu tốn nhiều bộ

nhớ hơn so với danh sỏch múc nối đơn Song bự lại, danh sỏch múc nổi đụi cú những ưu điểm mà danh sỏch múc nối đơn khụng thể cú

được, chăng hạn: khi xem xột danh sỏch múc nối đụi ta cú thờ lựi lại sau, hoặc tiến lờn trước

Cỏc phộp toỏn trờn danh sỏch múc nối hai chiều được thực hiện

dộ dang hơn Chẳng hạn, khi thực hiện phộp toỏn loại bỏ, với danh sỏch múc nỗi đơn, #a khụng thể thực hiện được nếu khụng biết nỳt

đứng trước nỳt cần loại bỏ Trong khi đú, ta cú thể tiến hành dễ dàng

trờn danh sỏch múc nối hai chiờu

Trang 21

Chương 4: Danh sỏch tuyến tinh 103

3.4.1 Phộp bỗ sung một nỳt mới

Cho hai con trỏ Lef và Right lõn lượt trỏ tới nỳt đầu và nỳt cuối của một danh sỏch múc nối hai chiờu, M là con trỏ trỏ tới một nỳt

trong danh sỏch này Giải thuật này thực hiện bộ sung mot nut mdi,

mà đữ liệu chứa ở biến X, vào trước nỳt trỏ bởi con trỏ M

Trang 22

104 Cấu trỳc đữ liệu và giai thuật

3.4.2 Logi bộ mot nỳt trờn danh sỏch

Cho hai con tro Left va Right lan lvot trộ tới nỳt đầu và nỳt cuối

của một danh sỏch múc nối hai chiều, M là con trỏ trỏ tới một nỳt

trong danh sỏch này Giải thuật này thực hiện loại bỏ nỳt trỏ bởi M ra khỏi danh sỏch int Loai_bo(struct Node **Left, struct Node **Right, struct Node *M) { if (“Left = = NULL) return 0; else if (*Left = = *Right) { *Left = NULL; *Right = NULL; } else if (M = = *Left) { “Left = (*Left)->Next_Right; (*Left)->Next_Left = NULL; } else if (M == Right) { Right = Right*.Next_Left: Right*.Next_Right = NULL; } else { M->Next_Left->Next_Right = M->Next_Right: M->Next_Right->Next_Left = M->Next_Left; } }

Chu y: Trong cac tmg dung, ngudi ta cũng thudng sir dung cdc

danh sỏch múc nối hai chiều vũng trũn, cú nỳt đầu danh sỏch Với loại

danh sỏch này, ta cú tất cả cỏc ưu điểm của danh sỏch múc nối hai

Trang 23

Chương 4: Danh sỏch tuyến tớnh 105

3.5 Ung dụng đanh sỏch múc nối: cỏc phộp tớnh số học trờn đa thức Trong mục này ta sẽ xột cỏc phộp tớnh số học cơ bản (cộng, trừ nhõn, chia) đối với đa thức một õn cú dạng:

A(X) = aX" + anX”” +, ẩ aIX + ao (1) Mỗi hạng thức của đa thức được đặc trưng bởi hệ số và số mũ

của x Giỏ sử cỏc bạng thức trong đa thức được sắp xếp theo thứ tự

giảm dần của số mũ, như trong đa thức (1) Ta thấy đa thức như một danh sỏch tuyến tớnh với cỏc phần tử của đanh sỏch là cỏc hạng thức của đa thức Khi ta thực hiện cỏc phộp toỏn trờn đa thức ta sẽ nhận

dược đa thức cú bậc khụng thể đoỏn trước được Ngay cả những da

thức cú bậc xỏc định thỡ số cỏc hạng thức của nú cũng biến đổi rất nhiều từ một đa thức này đến một đa thức khỏc Do đú phương phỏp tốt nhất là biờu điễn đa thức đưới đạng một danh sỏch múc nối Mỗi

nỳt của danh sỏch là một bản ghi gồm ba trường: coef chỉ hệ số, exp chỉ số mũ của x và con trỏ Next để trỏ tới nỳt tiếp theo Cấu trỳc đữ

liệu mụ tả một hạng thức (một nỳt) như sau: struct Node { float coef int exp; struct Node “Next; }

Vỡ những ưu điểm của danh sỏch vũng trũn cú nỳt đầu danh sỏch (Khụng cõn kiểm tra danh sỏch rỗng, mọi thành phần đều cú thành phần đi sau), ta chọn danh sỏch múc nối vũng trũn để biờu diễn đa

thức Với cỏch chọn này việc thực hiện cỏc phộp toỏn đa thức sẽ rất

ứọn Nỳt đầu danh sỏch là nỳt đặc biệt, cỏ exp = -1

Nhu vay voi da thitc: A(x) = 4x° - 2x? + 5x? + 6 sộ duoc biộu

Trang 24

106 Cấu trỳc đữ liệu và giải thuật ‘Ll 0 4 -2 5 6 > ô ô + a -1 5 3 2 0

Hỡnh 4.8: Danh sỏch múc nồi đụi biểu điờn đa thức

Sau đõy chỳng ta sẽ xột phộp cộng hai đa thức A(x) và B(x) Con trỏ A trỏ tới đầu danh sỏch biểu diễn đa thức A(x), con trỏ B trỏ tới đầu danh sỏch biểu diễn đa thức B(x) Sau khi thực hiện phộp cộng hai đa thức trờn ta được đa thức C(x) và con trỏ C trỏ tới đầu danh sỏch

biểu diộn C(x)

* Giải thuật

Trước hết cõn phải thấy răng để thực hiện phộp cộng đa thức

A(x) với đa thức B(x) ta phải tỡm đến từng hạng thức của cỏc đa thức

đú, nghĩa là phải dựng hai biến con trỏ P và Q để duyệt qua hai danh

sỏch tương ứng với hai đa thức A(x) và B(x) trong quỏ trỡnh tỡm này Ta thấy cú những trưởng hợp sau:

1, EXP(P) = EXP(Q), ta sẽ phải thực hiện cộng giả trị coef ở hai nỳt đỏ, nếu giỏ trị tổng khỏc khụng thỡ phải tạo ra nỳt mới thể hiện hạng thức tổng đú và găn vào danh sỏch ứng với C(x)

2 EXP(P) > EXP(Q) (hoặc ngược lại cũng tương tự): phải sao

chộp nỳt P và gắn vào danh sỏch của C(x)

3 Nếu một danh sỏch kết thỳc trước: phần cũn lại của danh sỏch

kia sẽ được sao chộp.và gắn vào danh sỏch của C(x)

Mỗi lần một nỳt mới được tạo ra đều phải gắn vào cuối danh sỏch

Trang 25

Chương 4: Danh sỏch tuyễn tinh 107

trường exp giỏ trị m (số mũ) và găn nỳt mới đú vào sau nỳt trỏ bởi con trỏ R void Attack(flaat h, int m, struct Node *R) { } struct Node *N; N=(struct Node*)malloc(sizeof(struct Node)); N->coef = h; N->exp = m; R->Next = N; R=N;

Sau day la thu tục cộng hai đa thức:

Trang 26

108 Cầu trỳc dữ liệu và giải thuật { Attack(P->coef, P->exp, R); P = P->Next: } while (Q->exp !=-1) /Danh sỏch ứng với A(x) đó hết { Attack(Q->coef, Q->exp, R): Q = Q->Next; } R->Next = *C; } 4 STACK VA QUEUE 4.1 Stack (Ngin xộp) 4.1.1 Khỏi niệm

Ngăn xếp (Staek) là một danh sỏch tuyến tớnh, trong đú phộp bổ sung một phần tử vào ngăn xếp và phộp loại bỏ một phần tử khỏi ngăn Xếp luụn luụn được thực hiện ở một đõu gọi là đỉnh (top)

Cú thờ hỡnh dung Ngăn xếp như cơ cầu của một hộp tiếp đạn

Việc đưa đạn vào hộp đạn hay lấy đạn ra khỏi hộp chỉ được thực hiện

Trang 27

Chương 4: Danh sỏch tuyến tớnh 109

4.1.2 Cài đặt ngăn xếp bởi mảng

Giả sử danh sỏch được biểu diễn là một ngăn xộp, cú độ dài tối đa là một số nguyờn dương N nào đú cỏc phần tử của ngăn xếp cú kiểu dữ liệu là em Hem cú thể là cỏc kiểu dữ liệu đơn, hoặc cỏc kiểu dữ liệu cú cõu trỳc Chỳng ta biểu điễn ngăn xếp bởi một bản, ghỉ gồm

2 trường Trường thứ nhất là mảng cỏc ƒem, trường thứ 2 ghi chỉ số

của thành phần mảng lưu trữ phần tử ở đỉnh của ngăn xếp Cấu trỳc dt

liệu biộu điễn ngăn xếp được khai bỏo theo mẫu sau: #define Max N Khai bao kiộu dộ liộu Item (nộu can) struct Stack { ftem E[Max]: unsigned int top; k

struct Stack S; //Khai bao ngan xộp S

Trang 28

110 Cấu trỳc dữ liệu và giải thuật { char ho_ten[25]; Ínf tuoi; } struct Stack {

struct Hoc_sinh E(max];

unsigned int top;

}

struct Stack S;

Khai bỏo ngăn xếp S cú thể chứa tối đa 100 phần từ, mỗi phần tử

(em) là một cõu trỳc Hoc_ sinh gồm 2 thành phõn ho _ten và tuoi Cỏc phỏp toản trờn ngăn xếp

Giả sử S là ngăn xếp, cỏc phần tử của nú cú kiờu ệfem và X là

một phõn tử cú cựng kiờu với cỏc phõn tử của ngăn xếp Ta cú cỏc phộp toỏn sau với ngăn xếp S

a Khởi tạo ngăn xếp rụng (ngần xếp khụng chứa phan tir nao)

void initialize (struct Stack *S)

{

S->top = 0;

}

b Kiểm tra ngăn xếp rỗng

int Empty (struct Stack S)

{

return (S.top = = 0):

}

Ham Empty nhan gid tri true nộu S rỗng va false nếu S khụng rồng c Kiộm tra ngan xộp day

int Full (struct Stack S) {

Trang 29

Chương 4: Danh sỏch tuyến tinh 111

Ham Full() nhan gia tri true nếu Đ day va false nộu khong

d Thờm một phõn tử mới vào đỉnh ngăn xếp

Đề bố sung phần tử X vào đỉnh của ngăn xếp S, trước hết kiểm

tra xem S cú đầy khụng Nếu Đ đõy thỡ bỗ sung khụng thực hiện được,

ngược lại X được bồ sung vào đỉnh của S Hàm PUSH trả về 1 nếu bổ

sung thành cụng, ngược lại trả về 0

int PUSH (struct Stack *S, [tem X) { if (Full(S)) return 0; else { S->top = S->top + 1; S->E[S->top] = X; return 1; } }

e Loại bỏ phõn tử ở định của ngăn xếp

Việc loại bỏ được thực hiện nếu S khụng rỗng, giỏ trị của phõn tử bị loại bỏ được gắn cho biờn X Hàm PểP(Q) trả về l nờu loại bỏ

thành cụng, ngược lại trả về 0

int POP (struct Stack *S; item *X) { if (Empty(*S)) return 0: else { *X = S->E[S->top]; S->top = S->top — 1; return 1; } }

4.1.3 Cài đặt ngăn xếp bởi danh sỏch múc nỗi đơn

Để cải đặt ngăn xếp bởi đanh sỏch múc nối đơn, ta sử dụng con

Trang 30

112 Cau trỳc dữ liệu và giải thuật 1 an õn-1 Poe ằ a4 NI Đỉnh Day

Hỡnh 4.11: Danh xỏch múc nối đơn biếu diễn ngăn xếp Cõu trỳc đữ liệu của ngăn xếp được khai bỏo như sau: struct Node { Item Infor: Node “Next, k Struct Node *S;

Trong cỏch cài đặt này, ngăn xếp rỗng khi S = NULL Ta giả sử việc cấp phỏt bộ nhớ động cho cỏc phần tử mới luụn thực hiện Do

đỏ, ngăn xếp khụng bao giờ đầy và phộp toỏn PUSH luụn thực hiện

thành cụng

*) Cac ham va thủ tục thực hiện cỏc phộp toỏn trờn ngăn xộp: a Khởi tạo ngăn xếp rỗng:

void Create(struct Node **S)

{ *S = NULL; }

b Kiểm tra ngăn xếp rỗng:

int Empty(struct Node *S) { return (S == NULL);

}

c Bồ sung một phản tử vào định ngăn xờp:

void PUSH(struct Node “*S, Item X)

{

Trang 31

Chuong 4: Danh sach tuyộn tinh 113 P = new Node: P->Infor = X; P->Next = NULL; if (S== NULL) *S =P; else { P->Next = *S; *S = P; } } 3 ane1 | * a, |e an | ep e+ ai i

d Lấy ra một phõn tử ở đỉnh ngăn xếp:

int POP(struct Node **S, Item *X)

Trang 32

114 Cấu trỳc dữ liệu và giải thuật To 8n An-1 n-2 oe a b P 4.1.4 Xử lý với nhiều ngăn xếp

Hỡnh 4.13: Minh hoạ thao tỏc POP

Cú những trường hợp cựng một lỳc ta phải xử lý nhiều ngăn xếp

trờn cựng một khụng gian nhớ Như vậy, cú thể xảy ra tỡnh trạng một

ngăn xếp này đó bị tràn trong khi khụng gian dự trữ cho ngăn xếp

khỏc vẫn cũn chỗ trống (tràn cục bộ) Làm thế nào để khắc phục được

tỡnh trạng này?

Nếu là hai ngăn xếp thỡ cú thể giải quyết đễ dàng Ta khụng qui

định kớch thước tối đa cho từng ngăn xếp nữa mà khụng gian nhớ dành ra sẽ được dựng chung Ta đặt hai ngăn xếp ở hai đầu sao cho hướng phỏt triển của chỳng ngược nhau, như hỡnh 4.14

77 mm

Đỏy 1 Đỉnh 1 Đỉnh 2 Đỏy 2

Hỡnh 4.14: Hai ngăn xếp trờn một khụng gian nhớ

Như vậy, cú thể một ngăn xếp này dựng lần sang quỏ nửa khụng

gian dự trữ nếu như ngăn xếp kia chưa dựng đến Do đú hiện tượng tràn chỉ xảy ra khi toàn bộ khụng gian nhớ dành cho chỳng đó được

dựng hết

Nhưng nếu số lượng ngăn xếp từ 3 trở lờn thỡ khụng thể làm theo

kiểu như vậy được, mà phải cú giải phỏp linh hoạt hơn nữa Chẳng

hạn cú 3 ngăn xếp, lỳc đầu khụng gian nhớ cú thể chia đều cho cả 3,

Trang 33

Chương 4: Danh sỏch tuyến tớnh 115

Lda

LÍ rot of

Day 1 Đỉnh 1 Day 2 Đỉnh 2 Đỏy 3 Đỉnh 3

Hỡnh 4.15: Ba ngăn xếp trờn một khụng gian nhớ

Nhưng nếu cú một ngăn xếp nào phỏt triển nhanh bị tràn trước

mà ngăn xếp khỏc vẫn cũn chỗ thỡ phải dồn chỗ cho nú bằng cỏch

hoặc đầy ngăn xếp đứng sau nú sang bờn phải hoặc lựi chớnh ngăn xếp đú sang trỏi trong trường hợp cú thể Như vậy thỡ đỏy của cỏc ngăn

xếp phải được phộp di động và dĩ nhiờn cỏc giải thuật bổ sung hoặc

loại bỏ phần tử đối với cỏc ngăn xếp hoạt động theo kiểu này cũng

phải thay đổi

4.1.5 Một số ứng dụng của ngăn xếp a Ứng dụng đổi cơ số

Ta biết rằng dữ liệu lưu trữ trong bộ nhớ của mỏy tớnh đều được

biểu diện dưới dạng mó nhị phõn Như vậy cỏc số xuất hiện trong chương trỡnh đều phải chuyển đổi từ hệ thập phõn sang hệ nhị phõn

trước khi thực hiện cỏc phộp xử lý

Khi đổi một số nguyờn từ hệ thập phõn sang hệ nhị phõn người

ta dựng phộp chia liờn tiếp cho 2 và lấy cỏc số dư (là cỏc chữ số nhị phõn) theo chiều ngược lại

Trang 34

116 Cấu trỳc dữ liệu và giải thuật

——> 11010111

Ta thấy trong cỏch biến đổi này cỏc số được tạo ra sau lại được

hiển thị trước Cơ chế sắp xếp này chớnh là cơ chế hoạt động của ngăn xếp Để thực hiện biến đổi ta sẽ dựng một ngăn xếp để lưu trữ cỏc số

Trang 35

Chương 4: Danh sỏch tuyến tinh 117 0 POP —> 1 7 0 0 0 1 1 0 1 0

Hỡnh 4 1ú.(b): Mụ tả hoạt động lấy dữ liệu từ ngăn xếp

Ta khai bỏo cầu trỳc dữ liệu cho bài toỏn này như sau:

#define Max 16 !/thực hiện đỗi số nguyờn cú kớch thước 2 byte typedef int Item; //Item là chữ số nhị phõn struct Stack { Item E[Max]; int top; } Sfruct Stack S; /“S là ngăn chứa cỏc số dư qua cỏc phộp chỡa trong quỏ trỡnh chuyờn đụi"!

Giải thuật sử dụng ngăn xếp thực hiện chuyờn đổi số nguyờn

Trang 36

118 Cấu trỳc đữ liệu và giải thuật

call POP(S, R); cout<<R;

}

b Ứng dụng định giỏ biểu thức số học theo ký phỏp nghịch đảo

Nhiệm vụ của bộ dịch là tạo ra cỏc chỉ thị mỏy cần thiết để thực hiện cỏc lệnh của chương trỡnh nguồn Một phần trong nhiệm vụ này

là tạo ra cỏc chỉ thị định giỏ cỏc biểu thức số học Chắng hạn cõu lệnh

gỏn X= A*B + C

Bộ dịch phải tạo ra cỏc chỉ thị mỏy tương ứng như sau:

1 - LOA A: Tim giỏ trị của A lưu trữ trong bộ nhớ và tải nú vào thanh ghi 2 - MUL B: Tỡm giỏ trị của B và nhõn nú với giỏ trị đang ở thanh ghi 3 - ADD C: Tim giỏ trị của C và cộng nú với giỏ trị trong thanh ghi 4 - STO X: Đưa giả trị trong thanh ghi vào lưu trữ ở vị trớ tương ứng của X, trong bộ nhớ

Trong cỏc ngụn ngữ lập trỡnh, biờu thức số học được viết như dạng thụng thường của toỏn học nghĩa là theo kớ phỏp trung tộ (infix notation) mỗi kớ hiệu của phộp toỏn hai ngụi được đặt giữa hai toản

hạng, cú thờ thờm dấu ngoặc

Chănghạn 5*(7+3)

Dấu ngoặc là cần thiết vỡ nếu viết 5*7 + 3 thỡ theo qui ước về thứ

tự ưu tiờn của phộp toỏn (mà cỏc ngụn ngữ lập trỡnh đều chấp nhận)

thỡ biờu thức trờn nghĩa là lấy 5 nhõn 7 được kết quỏ cộng với 3

Nhà logie học người Ba Lan Lukasiewicz đó đưa ra dạng biểu thức số học theo ký phỏp hậu tố (postủx notation) và tiờn tố (prefix

Trang 37

Chương 4: Danh sỏch tuyến tớnh 119 Ở dạng hậu tố cỏc toỏn tử đi sau cỏc toỏn hạng Như biểu thức 5*(7+3) sẽ cú dạng: Š 7 3 - * Cũn ở dạng tiờn tụ thỡ cỏc toỏn tử sẽ đi trước cỏc toỏn hạng Khi đú biểu thức 5*(7+3) cú dạng: * 5 - 7 3

ễng cũng khăng định rằng đối với cỏc dạng ký phỏp này dấu

ngoặc là khụng cần thiết

Nhiều bộ dịch khi định giỏ biểu thức số học thường thực hiện:

trước hết chuyển cỏc biểu thức dạng trung tố cú dấu ngoặc sang dạng hậu tố, sau đú mới tạo cỏc chỉ thị mỏy để định giỏ biểu thức ở dạng

hậu tố Việc biến đổi từ đạng trung tố sang dạng dạng hậu tế khụng

khú khăn gỡ, cũn việc định giỏ theo dạng hậu tổ thỡ để dàng hơn, “mỏy

múc” hơn so với dạng trung tụ

Đề minh hoa ta xột định giỏ của biểu thức sau: 15+841 *

tương ứng với biểu thức thụng thường: (1 + 5) * (8 - (4 - 1))

Biểu thức này được đọc tử trải sang phải cho tới khi tỡm ra một toỏn tử Hai toỏn hạng được đọc cuụi cựng, trước toỏn tử này, sẽ được kết hợp với nú Trong vớ dụ của chỳng ta thỡ toỏn tử đõu tiờn được đọc

là + và hai toỏn hạng tương ứng với nú là l và 5, sau khi kờt hợp biờu thực con này cú giả trị là ú, thay vào ta cú biờu thức rỳt gọn:

6841 *

Lại đọc từ trỏi sang phải, toỏn tử tiếp theo là - và ta xỏc định

được 2 toỏn hạng của nú là 4 và I Thực hiện phộp toỏn ta cú dạng rỳt

gon:

683-* Lại tiếp tục ta đi tới:

6S#

và cuối cựng thực hiện phộp toỏn * ta cú kết quỏ là 30

Phương phỏp định giỏ biểu thức hậu tế như trờn đũi hỏi phải lưu

Trang 38

120 Cấu trỳc dữ liệu và giải thuật

hai toỏn hạng cuối cựng phải được tỡm ra và kết hợp với toỏn tử này Như vậy ở đõy đó xuất hiện cơ chế hoạt động “vào sau ra trước” nghĩa là ta sẽ phải sử dụng tới ngăn xếp dộ lưu trữ cỏc toỏn hạng Cứ mỗi lần

đọc được một toỏn tử thỡ hai giỏ trị sẽ được lấy ra tỪ ngăn xếp để ỏp đặt toỏn tử đú lờn chỳng và kết quả lại được đõy vào ngăn xếp

Trang 39

Chương 4: Danh sỏch tuyến tớnh 12] Biểu thức Ngăn xếp 151+841 * 1 |<—T 15+841 " 5 |e—T t 1 +841 * B8 |<=T 8 |<—T 841 * 6 4 |=T 41 * 8 T 6 1 |<—T 1 * 4 8 6 3 |<—T ~~? 8 1 6 -* 5 |<—T T 6 * 30 |<—=T Chỳ thớch

Đẩy 1 vào ngăn xếp

Đẩy 5 vào ngăn xếp

Lấy 5 và 1 từ ngăn xếp cộng lại rồi đẩy

kết quả vào ngăn xếp

Đẩy 8 vào ngăn xếp

ĐẦy 4 vào ngăn xếp

Đẩy 1 vào ngăn xếp Lấy 1 và 4 từngăn xếp _ Thực hiện (4 - 1) rồi đẩy kết qua vào ngăn xếp Lẩy 3 và 8 từ ngăn xếp Thực hiện (8 - 3) rồi đẩy kết quả vào ngăn xếp

Lấy 5 và 6, thực hiện (5*6) rồi day

kết quả vào ngăn xếp

Trang 40

122 Cấu trỳc dữ liệu và giải thuật

4.2 Queue (Hàng đợi) 4.2.1 Khai niộm

Một kiểu dữ liệu trừu tượng quan trọng khỏc được xõy dựng trờn cơ sở mụ hỡnh đữ liệu danh sỏch tuyến tớnh là hàng đợi Hàng đợi là kiểu danh sỏch tuyến tớnh trong đú, phộp bổ sung một phần tử vào hàng đợi được thực hiện ở một đầu, gọi là lỗi sau (rear) và phộp loại bỏ một phần tử được thực hiện ở đầu kia, gọi là lối trước (#ont)

Như vậy, cơ cầu của hàng đợi là vào ở một đầu, ra ở đầu khỏc, phần tử vào trước thỡ ra trước, phõn tử vào sau thỡ ra sau Do đú, hàng đợi cũn được gọi là danh sỏch kiộu FIFO (First In First Out) Trong

thực tế ta cũng thấy cú những hinh ảnh giống hang doi, chang han,

hàng người chờ mua vộ tàu, học sinh xếp hàng đi vào lớp, v.v 4.2.2 Cài đặt hàng đợi bởi mảng

Ta cú thể biểu diễn hàng đợi bởi mảng với việc sử dụng hai chỉ sộ front dộ chỉ vi trớ đầu hàng đợi (lỗi trước) và rear dộ chi vi tri cudi

hàng đợt (lối sau) Cấu trỳc đữ liệu hàng đợi được biểu điễn như sau: #define maxN iiKhai bao kiểu dữ liệu ltem nếu cần struct Queue { unsigned int front, rear; ftem E[max]; k struct Queue Q;

!IKhai bỏo hàng đợi Q lưu trữ cỏc phõn tử của danh sỏch

Trong cỏch cài đặt như trờn, hàng đợi Q là rỗng nếu Q.rear = 0

Ngày đăng: 03/12/2015, 02:14

TỪ KHÓA LIÊN QUAN

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

TÀI LIỆU LIÊN QUAN