Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 36 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
36
Dung lượng
648,9 KB
Nội dung
Trần Quốc Chiến Cấutrúcliệu giải thuật CHƯƠNG II CẤUTRÚCDỮLIỆUDANHSÁCH Chương II Cấutrúcliệudanhsách II.1 Trần Quốc Chiến Cấutrúcliệu giải thuật I TỔNG QUAN VỀ DANHSÁCH Định nghĩa Danhsách (List) dãy phần tử a1, , an (n 1) với tính chất: biết biết ai+1 Danhsách xếp tuyến tính theo vị trí chúng Số phần tử n gọi độ dài danhsách Nếu n = 0, ta có danhsách rỗng Ví dụ - Danhsách nhân viên quan - Danhsách sinh viên lớp - Danhsách môn học - Danhsách số tự nhiên không 100 Các phép toán danhsách Để tạo lập, cập nhật, khai thác danh sách, người ta định nghĩa phép toán sau a Phép duyệt danhsách Duyệt danhsách duyệt qua tất phần tử danhsách thoả mãn để thực cơng việc (chẳng hạn hiển thị phần tử) Ví dụ: Liệt kê danhsách nữ nhân viên quan b Phép tìm kiếm Tìm kiếm thao tác tìm phần tử danhsách thoả mãn điều kiện Kết phép tìm kiếm tìm thấy khơng tìm thấy Sau tìm thấy phần tử cần tìm ta thực phép tốn phần tử sửa đổi hay xố Ví dụ: Trong danhsách nhân viên quan, tìm nhân viên có tên “Trần Công Minh” c Thêm phần tử vào danhsách Đây thao tác thêm phần tử vào danhsách Phần tử thêm vào cuối danh sách, đầu danhsách chèn vào danhsách Nếu danhsách đầy, tức số phần tử danhsách số cực đại cho phép phép tốn thêm khơng Chương II Cấutrúcliệudanhsách II.2 Trần Quốc Chiến Cấutrúcliệu giải thuật thực Nếu danhsách chưa đầy thêm phần tử vào danhsách độ dài (số phần tử) danhsách tăng thêm Ví dụ: Trong danhsách nhân viên quan, thêm nhân viên hợp đồng có tên “Trần Cơng Minh” d Loại bỏ phần tử khỏi danhsách Đây thao tác loại bỏ phần tử khỏi danhsách Trước loại bỏ phần tử, chưa xác định phần tử loại bỏ, phải thực phép tìm kiếm Nếu khơng tìm thấy khơng thể thực phép loại bỏ Nếu tìm thấy thực phép loại bỏ độ dài (số phần tử) danhsách giảm Phần tử loại bỏ cuối danh sách, đầu danhsáchdanhsách Ví dụ Trong danhsách nhân viên quan, loại bỏ nhân viên cắt hợp đồng có tên “Trần Công Minh” e Sửa đổi phần tử danhsách Đây thao tác hiệu chỉnh phần tử danhsách Trước hiệu chỉnh phần tử, chưa xác định phần tử hiệu chỉnh, phải thực phép tìm kiếm Nếu khơng tìm thấy khơng thể thực phép hiệu chỉnh Nếu tìm thấy thực phép hiệu chỉnh Số phần tử danhsách không thay đổi Phần tử hiệu chỉnh cuối danh sách, đầu danhsáchdanhsách Ví dụ Trong danhsách nhân viên quan, hiệu chỉnh mức lương cho nhân viên có tên “Trần Cơng Minh” f Sắp xếp thứ tự danhsách Đây thao tác xếp lại thứ tự phần tử danhsách theo khố Thứ tự phần tử thay đổi, số phần tử giữ nguyên Ví dụ: Sắp xếp danhsách nhân viên quan theo họ tên theo mức lương g Tách danhsách thành nhiều danhsách Đây thao tác tách phần lấy tất phần tử danhsách đưa sang danhsách khác Ví dụ Từ danhsách nhân viên quan tách riêng danhsách nữ nhân viên h Ghép nhiều danhsách thành danhsách Đây thao tác lấy tất phần tử từ danhsách khác tạo thành danhsách Các phần tử danhsách xếp cạnh Chương II Cấutrúcliệudanhsách II.3 Trần Quốc Chiến Cấutrúcliệu giải thuật Ví dụ Từ danhsách nhân viên phòng ghép lại thành danhsách nhân viên toàn quan i Trộn nhiều danhsách thành danhsách Đây thao tác lấy tất phần tử từ danhsách khác tạo thành danhsách theo cấutrúc Các phần tử danhsách bị xáo trộn Ví dụ Từ danhsách nhân viên phòng xếp theo họ tên trộn lại thành danhsách nhân viên toàn quan xếp theo họ tên Chương II Cấutrúcliệudanhsách II.4 Trần Quốc Chiến Cấutrúcliệu giải thuật II DANHSÁCH ĐẶC Định nghĩa khai báo a Định nghĩa Danhsách đặc (condensed list) hay gọi danhsách kề (contiguous list) danhsách mà phần tử lưu trữ nhớ, phần tử thứ i+1 đứng sau phần tử thứ i Hình sau mơ tả danhsách đặc, phần tử có kích thước xếp a1 add[1] a2 ai+1 add[i] d an1 an add[n] Nếu phần tử có độ dài d byte ký hiệu địa phần tử i add[i], ta sử dụng cơng thức tính địa sau: add[i] = add[1] + (i1)*d b Khai báo Trong PASCAL: Const ElemNo = ; Type InfoType = ; KeyType = ; Element = record key : KeyType; {khoá} info : InfoType; {dữ liệu} end; ListType = array[1 ElemNo] of Element; Var List: ListType; ListNum: integer; {số phần tử danh sách, không vượt ElemNo} Trong C: #define ElemNo typedef InfoType; typedef KeyType; struct Element { KeyType key; //khoá InfoType info; // liệu }; struct Element List[ElemNo+1]; int ListNum; // số phần tử danh sách, không vượt ElemNo Chương II Cấutrúcliệudanhsách II.5 Trần Quốc Chiến Cấutrúcliệu giải thuật Các phép toán a Khởi tạo danhsách Ban đầu danhsách rỗng, nên ta khởi tạo đặt độ dài danhsách ListNum = Trong PASCAL procedure Initialize; begin ListNum := 0; end; Trong C void Initialize() { ListNum = 0; } b Duyệt danhsách Duyệt danhsách thoả mãn để Trong PASCAL procedure Traverse; var i: integer; {biến cục bộ} begin for i := to ListNum if then end; Trong C void Traverse() { int i; //biến cục for ( i = 1; i next2 = p; p->next2 = q; return p; Chương II Cấutrúcliệudanhsách II.22 Trần Quốc Chiến Cấutrúcliệu giải thuật } d Loại phần tử khỏi danhsách Xét danhsách đa liên kết có hai vùng liên kết next1 next2 xếp theo thứ tự tăng dần theo info1 info2 ghi kiểu InfoType Giả sử ta cần loại bỏ phần tử có nội dung DelInfo Thủ tục Delete_Element cài đặt thuật toán xoá phần tử khỏi danhsách Trong PASCAL: Procedure Delete_Element(DelInfo : InfoType); var q , {* phần tử phụ trợ dò tìm *} q1 , {* phần tử tìm thấy theo next1 *} before {* phần tử đứng trước q *} : NodePointer; begin q := plist1; { dò tìm theo vùng next1 } while (q nil) and (q^.info DelInfo) if q^.info.info1 > DelInfo.info1 then q := nil else begin before := q; q := q^.next1 end; if q nil then { tìm thấy phần tử cần loại bỏ } begin q1 := q; { ngắt next1 } if q = plist1 then plist1 := q^.next1 else before^.next1 := q^.next1; { dò tìm theo vùng next2 } q := plist2; while q q1 begin before := q; q := q^.next2 end; if q = plist2 then plist2 := q^.next2 else before^.next2 := q^.next2; Chương II Cấutrúcliệudanhsách II.23 Trần Quốc Chiến Cấutrúcliệu giải thuật end; dispose(q) end; Trong C: void Delete_Element(InfoType DelInfo) { NodePointer q , /* phần tử phụ trợ dò tìm */ q1 , /* phần tử tìm thấy theo next1 */ before; /* phần tử đứng trước q */ q = plist1; // dò tìm theo vùng next1 while ((q != NULL) && (q->info != DelInfo)) if (q->info.info1 > DelInfo.info1) q = NULL; else { before = q; q = q->next1; } if (q != NULL) // tìm thấy phần tử cần loại bỏ { q1 = q; // ngắt next1 if (q == plist1) plist1 = q->next1; else before->next1 = q->next1; //dò tìm theo vùng next2 q = plist2; while (q != q1) { before = q; q = q->next2; } if (q == plist2) plist2 = q->next2; else before->next2 = q->next2; } free(q); } Chương II Cấutrúcliệudanhsách II.24 Trần Quốc Chiến Cấutrúcliệu giải thuật V DANHSÁCH LIÊN KẾT KÉP Định nghĩa Danhsách liên kết kép (doubly linked list) danhsách mà phần tử có hai vùng liên kết Một vùng liên kết đến phần tử đứng trước nó, gọi liên kết ngược (previous) vùng liên kết đến phần tử đứng sau nó, gọi liên kết thuận (next) Ví dụDanhsách thêm vào lấy hai đầu Tổ chức danhsách liên kết kép Mỗi phần tử danhsách liên kết kép bao gồm vùng thông tin info vùng liên kết previous next ứng với vùng liên kết ta có điểm đầu first điểm cuối last Phần tử danhsách liên kết kép có dạng: previous info next Tổ chức danhsách liên kết kép sau: first last Chương II Cấutrúcliệudanhsách II.25 Trần Quốc Chiến Cấutrúcliệu giải thuật Khai báo Trong PASCAL Trong C Type InfoType = record typedef struct data Info1 : ; { … info1; Info2 : ; … info2; … end; } InfoType; NodePointer = ^Node; struct Node { Node = record InfoType info; info : InfoType; struct Node *previous; previous : NodePointer; struct Node *next; next : NodePointer; }; end; typedef struct Node *NodePointer; Var first, last : NodePointer;{ trỏ đầu NodePointer first, last, … ;// trỏ đầu trỏ cuối danh sách} trỏ cuối danhsách Các phép toán a Khởi tạo: danhsách rỗng Trong PASCAL procedure Initialize; begin first := nil; last := nil; end; Trong C void Initialize() { first = NULL; last = NULL; } b Duyệt danhsách Duyệt danhsách theo liên kết thuận theo liên kết ngược, giải thuật giống giải thuật duyệt danhsách liên kết đơn c Chèn phần tử vào danh sách: Giả sử ta chèn phần tử có nội dung NewInfo Trường hợp thêm phần tử vào đầu danhsách Hàm Insert_First trả địa phần tử thêm vào Chương II Cấutrúcliệudanhsách II.26 Trần Quốc Chiến Trong PASCAL Cấutrúcliệu giải thuật Trong C function Insert_First(NewInfo: InfoType): NodePointer Insert_First(InfoType NodePointer; NewInfo) var p {*phần tử *}: NodePointer; { begin NodePointer p ;//phần tử new(p); p = (NodePointer)malloc(sizeof(struct p^.info := NewInfo;{* ghi liệu vào Node)); phần tử p *} p->info = NewInfo;// ghi liệu vào p^.previous := nil; phần tử p p^.next := first; p->previous = NULL; if first nil then p->next = first; first^.previous := p; if (first != NULL) first := p; first->previous = p; if last = nil then first = p; last := p; if (last = NULL) Insert_First := p last = p; end; return p; } Trường hợp thêm phần tử vào sau phần tử q Hàm Insert_Element trả địa phần tử thêm vào Trong PASCAL Trong C function Insert_Element(NewInfo: NodePointer Insert_Element(InfoType InfoType; q: NodePointer): NodePointer; NewInfo, NodePointer q) Var p, {*phần tử *} r :{ NodePointer; NodePointer p , /*phần tử */ r; begin p = (NodePointer)malloc(sizeof(struct new(p); Node)); p^.info := NewInfo;{* ghi liệu vào p->info = NewInfo;// ghi liệu vào phần tử p *} phần tử p p^.next := q^.next; p->next = q->next; p^.previous := q; p->previous = q; q^.next := p; q->next = p; r := p^.next; r = p->next; if r nil then {thêm vào danh if (r != NULL) /*thêm vào danh sách} sách */ r^.previous := p r->previous = p; else {thêm vào cuối danh sách} else /*thêm vào cuối danhsách */ last := p; last = p; Insert_Element := p return p; end; } Chương II Cấutrúcliệudanhsách II.27 Trần Quốc Chiến Cấutrúcliệu giải thuật Trường hợp thêm phần tử vào danhsách có thứ tự Hàm Insert_Element trả địa phần tử thêm vào Trong PASCAL: function Insert_Element(NewInfo: InfoType): NodePointer; var p , {*phần tử thêm vào*} q, before : NodePointer; begin new(p); p^.info := NewInfo; {* ghi liệu vào phần tử p *} {tìm phần tử before trước phần tử p} q := first; while (q nil) and (q^.info < NewInfo) begin before := q; q := q^.next; end; if q = first then {thêm vào đầu danh sách} begin p^.next := first; p^.previous := nil; if first nil then first^.previous := p; first := p; if last = nil then last := p; end else {thêm vào sau before} begin p^.next := before^.next; p^.previous := before; before^.next := p; if q nil then {thêm vào danh sách} q^.previous := p else {thêm vào cuối danh sách} last := p; end Insert_Element := p end; Trong C: NodePointer Insert_Element(InfoType NewInfo) { Chương II Cấutrúcliệudanhsách II.28 Trần Quốc Chiến Cấutrúcliệu giải thuật NodePointer p , /*phần tử thêm vào*/ q, before; p = (NodePointer)malloc(sizeof(struct Node)); p->info = NewInfo;// ghi liệu vào phần tử p {tìm phần tử before trước phần tử p} q = first; while ((q != NULL) && (q->info < NewInfo)) { before = q; q = q->next; } if (q == first) //thêm vào đầu danhsách { p->next = first; p->previous = NULL; if (first != NULL) first->previous = p; first = p; if (last == NULL) last = p; } else /*thêm vào sau before */ { p->next = before->next; p->previous = before; before->next = p; if (q != NULL) //thêm vào danhsách q->previous = p; else //thêm vào cuối danhsách last = p; } return p; } d Loại phần tử khỏi danhsách Giả sử ta cần loại bỏ phần tử có địa p Thủ tục Delete_Element xoá phần tử khỏi danhsách Chương II Cấutrúcliệudanhsách II.29 Trần Quốc Chiến Trong PASCAL Cấutrúcliệu giải thuật Trong C procedure Delete_Element(p : NodePointer); void Delete_Element(NodePointer p) var { before, {* phần tử đứng trước p *} NodePointer before, /* phần tử after {* phần tử đứng sau p *} đứng trước p */ : NodePointer; after ; // phần tử đứng sau p begin before = p->previous; before := p^.previous; after = p->next; after := p^.next; if (before != NULL) if before nil then before->next = p->next; before^.next := p^.next; if (after != NULL) if after nil then after->previous = p->previous; after^.previous := p^.previous; if (p == first) if p = first then first = after; first := after; if (p == last) if p = last then last = before; last := before; free(p); dispose(p) } end; e Tìm kiếm phần tử danhsách Có thể tìm theo vùng next điểm first (thứ tự tăng dần) theo vùng previous điểm last (thứ tự giảm dần) Giải thuật giống danhsách liên kết đơn Chương II Cấutrúcliệudanhsách II.30 Trần Quốc Chiến Cấutrúcliệu giải thuật VI DANHSÁCH LIÊN KẾT VỊNG Định nghĩa Danhsách liên kết vòng danhsách liên kết mà vùng liên kết phần tử cuối trỏ đến phần tử đầu Có thể biểu diễn phần tử danhsách liên kết vòng sau: first Khai báo Trong PASCAL Trong C Type InfoType = ; InfoType info; NodePointer = ^Node; struct Node *next; Node = record }; info : InfoType; typedef struct Node *NodePointer; next : NodePointer; NodePointer first, p;// first trỏ danh end; sách Var first, p: NodePointer ;{ first trỏ p = (NodePointer)malloc(sizeof(struct danh sách} Node));//cấp phát nút động cho p Các phép toán danhsách Các phép toán duyệt, tìm kiếm, chèn, xố tương tự danhsách liên kết đơn Tuy nhiên cần ý vùng liên kết phần tử cuối trỏ đến phần tử đầu Danhsách liên kết vòng thích hợp với phép tách ghép danhsách Chương II Cấutrúcliệudanhsách II.31 Trần Quốc Chiến Cấutrúcliệu giải thuật Thủ tục chèn phần tử vào cuối danhsách Trong PASCAL Trong C procedure Insert(NewInfo: InfoType); void Insert(InfoType NewInfo) var p, q : NodePointer; { begin NodePointer p, q; {tạo nút p} p = (NodePointer)malloc(sizeof(struct new(p); Node)); p^.info := NewInfo; p->info = NewInfo; p^.next := nil; p->next = NULL; {tìm nút cuối danhsách q} // tìm nút cuối danhsách q q := first; q = first; while (q^.next first) while (q->next != first) q := q^.next; q = q->next; q^.next := p; {liên kết nút q đến p} q->next = p;// liên kết nút q đến p; p^.next := first;{ p trở thành nút cuối p->next = first;// p trở thành nút cuối danh sách} danhsách end; } Thủ tục loại phần tử sau phần tử q Trong PASCAL procedure Delete(q : NodePointer); var p,r : NodePointer; begin p := q^.next; if p nil then begin q^.next := p^.next; end; if p = first then begin {tìm nút cuối danhsách r} r := first; while (r^.next first) r := r^.next; first := p^.next; r^.next := first; end; dispose(p); end; Trong C void Delete(NodePointer q) { NodePointer p, r; p = q->next; if (p != NULL) { q->next = p->next; } if (p ==first) { r = first; while (r->next != first) r = r->next; first = p->next; r->next = first; } free(p); } Ví dụ: Bài tốn Josephus Chương II Cấutrúcliệudanhsách II.32 Trần Quốc Chiến Cấutrúcliệu giải thuật Một nhóm n binh sĩ bị kẻ thù bao vây họ phải chọn người cầu cứu viện binh Việc chọn người thực sau Cho trước số nguyên s gọi bước đếm Các binh sĩ xếp thành vòng tròn đánh số 1, 2, , n người huy Đếm từ người thứ nhất, đạt đến s binh sĩ tương ứng bị loại khỏi vòng Lại bắt đầu đếm từ người tiếp theo, đạt đến s binh sĩ tương ứng bị loại khỏi vòng Q trình tiếp tục lại binh sĩ, người người điều cầu cứu viện binh Sau chương trình cài đặt giải thuật toán Trong PASCAL Type NodePointer = ^Node; Node = record info : integer; next : NodePointer; end; Var q, p, first : NodePointer; n, {số binh sĩ} s, {bước đếm} i : integer; Procedure Initialize; {*** khởi tạo ***} begin first := nil; write(„cho số binh sĩ : „); readln(n); write(„cho số bước đếm: „); readln(s); end; Procedure CreateRing; {Tạo danh sách} var p, last : NodePointer; begin {tạo nút đầu tiên} new(first); first^.info := 1; first^.next := nil; {nối tiếp n-1 phần tử } last := first; for i := to n begin new(p); p^.info := i; Chương II Cấutrúcliệudanhsách Trong C struct node { int v;/*dinh ke*/ struct node *link; }; typedef struct node *nodeptr; int n, s; nodeptr first, last; void addnode(int i); void init(void); /*** khởi tạo ***/ main() { nodeptr p1, p; int k, i; clrscr(); printf("nhap n, s = "); scanf("%d %d", &n, &s); // Tạo danhsách init(); for (k = 2; k link; p1->link = p->link; free(p); II.33 Trần Quốc Chiến Cấutrúcliệu giải thuật p^.next := nil; } last^.next := p; printf("\n người lại: %d", p1->v); last := p; } end; void addnode(int i) {kết nối phần tử cuối đến phần tử đầu} { last^.next := first; nodeptr p; end; p = (nodeptr)malloc(sizeof(struct BEGIN node)); Initialize; p->v = i; CreateRing; last->link = p; {loại dần n - binh sĩ} p->link = first; new(p); last = p; p^.next := first; } while p p^.next void init(void) begin { {di chuyển đến phần tử thứ s-1} nodeptr p; for i := to s-1 p = (nodeptr)malloc(sizeof(struct p := p^.next; node)); { loại phần tử thứ s khỏi vòng} p->v = 1; q:=p^.next; first = p; p^.next := q^.next; last = p; disposei(q) last->link = first; end; } write(„Người chọn : ‟, p^.info); END Chương II Cấutrúcliệudanhsách II.34 Trần Quốc Chiến Cấutrúcliệu giải thuật BÀI TẬP Viết chương trình nhập số nguyên n, a, b (0 < n 100, a < b , n