Tài nguyên Thư viện

Thành viên trực tuyến

3 khách và 0 thành viên

Thống kê

  • truy cập   (chi tiết)
    trong hôm nay
  • lượt xem
    trong hôm nay
  • thành viên
  • Menu Thư viện

    Gốc > Kiến thức tin học > Lập trình Pascal >

    Con trỏ: DANH SÁCH ĐƯỢC GHÉP NỐI


         Khi ta muốn lập một danh sách, ví dụ danh sách Nhan_su, mà biết trước được số người thì ta có thể sử dụng cấu trúc dữ liệu kiểu mảng cho công việc lập trình được đơn giản. Song nhiều khi số phần tử của danh sách lại không được biết trước nên nếu ta sử dụng các mảng thì bao giờ ta cũng phải cho số chiều cực đại có thể dùng tới để khai báo mảng. Đương nhiên khi đó nếu chúng ta dùng không hết thì phí ô nhớ đang thiếu cho việc khác. Nếu chúng ta sử dụng biến động thì cần đến đâu chúng ta sẽ tạo ra đến đấy, tất nhiên lúc này bị chỉ hạn chế bởi kích thước của bộ nhớ trong của máy. Mặt khác nhiều khi ô nhớ bị hạn chế, cho nên ta không thể đồng thời sử dụng nhiều biến tĩnh cùng một lúc(phạm vi ô nhớ dành cho tất cả biến tĩnh trên máy vi tính họ IBM là 64 KB). Một trong các biện pháp khắc phục là dùng biến con trỏ để xây dựng một danh sách các phần tử được móc nối với nhau.    

    1.  Tạo danh sách được ghép nối:        

                        Type

                           PointerN = ^Nhan_su ;

                           Nhan_su = Record

                              Ten : String[ 30 ] ;

                              Tuoi : Integer ;

                              Next : PointerN ;  (* Để trỏ vào phần tử bên cạnh *)

                           End ;

                        Var

                           Last, Ptr, P, Q : PointerN ;

                           HeapTop : ^Integer ;  (* Con trỏ đánh dấu của Mark *)

                           Name : String[ 30 ] ;

                        BEGIN

                           (* Đọc danh sách vào cho đến khi ấn Return : Name = ' ' *)

                           Last := Nil ;

                           Mark(HeapTop);  (* Đánh dấu vị trí *)

                           Repeat

                              Writeln ;

                              Write (' Ho va ten : ') ; Readln(Name);

                              If  Name <> ' '  Then

                              Begin

                                 New(Ptr);

                                 Ptr^.Ten := Name ;

                                 Write (' Tuoi : ') ; Readln(Ptr^.Tuoi);

                                 Ptr^.Next := Last ;

                                 Last := Ptr ;

                              End ;

                           Until  Name = ' ' ;

                           (* Đọc lại toàn bộ danh sách *) ;

                           Writeln (' DANH SACH NGUOI : ') ;

                           Ptr := Last ;   (* Ptr trỏ vào người cuối cùng trong danh sách *) ;

                           While  Ptr <> Nil  Do

                           Begin

                              Writeln (' Ho va ten : ', Ptr^.Ten);

                              Writeln (' Tuoi : ', Ptr^.Tuoi);

                              Writeln ;

                              Ptr := Ptr^.Next ;  (* Ptr trỏ vào người bên cạnh *) ;

                           End ;

                           Release(HeapTop);

                        END. 

         HeapTop là biến con trỏ được dùng để đánh dấu vị trí trong các thủ tục MarkRelease. Vì vậy ta chỉ cần định nghĩa sao cho HeapTop là con trỏ, còn trỏ vào kiểu nào thì ta không cần quan tâm  

         Hoàn toàn có thể được khi ta định nghĩa :  

                        HeapTip : ^Byte ;

         hoặc       HeapTop : ^Nhan_su ; 

         Last  là biến con trỏ luôn luôn trỏ vào phần tử cuối cùng của danh sách (ở đây phần tử là một bản ghi kiểu Nhan_su). Khi mới vào chương trình, Last được gán gí trị Nil để báo rằng danh sách chưa có ai cả.

         Name là một biến trung gian để đọc tên và làm phép thử kết thúc việc vào dữ liệu bằng cách ấn Return khi máy hỏi  Ho va ten. Việc vào dữ liệu là một vòng lặp không bị hạn chế số lượng trước nhờ có vòng lặp Repeat.

         Nếu  Name <> ' ', tức là có thêm một người cần đưa vào danh sách, thủ tục  New(Ptr) sẽ tạo ra một biến động. Việc gán tên và đọc tuổi không có gì mới lạ cả. Riêng có một điều là biến động này có một trường là biến con trỏ (Next) và ta muốn rằng trường Next của phần tử mới được tạo ra sẽ luôn luôn trỏ vào phần tử được tạo ra trước nó, việc này được thực hiện nhờ dòng lệnh : 

                        Ptr^.Next := Last ; 

         Sau mỗi lần dùng thủ tục  New(Ptr) thì Ptr lại không trỏ vào các biến động được tạo ra trước đây, tuy nhiên ta không bị mật địa chỉ của chúng nhờ có trường con trỏ Next của Ptr giữ lại. Như vậy các phần tử của danh sách được ghép nối với nhau nên ta gọi đây là danh sách được ghép nối. Dòng lệnh Last := Ptr ;  sẽ làm cho Last trỏ vào phần tử cuối cùng của danh sách mà lúc nào Ptr cũng trỏ vào.

         Đoạn tiếp theo của chương trình trong ví dụ trên là đọc lại danh sách đã có. Việc đọc danh sách được bắt đầu tiến hành từ phần tử vào cuối cùng. Phần tử này được con trỏ Last trỏ vào. Còn Ptr là biến con trỏ được sử dụng như là một biến trung gian để nháp, nghĩa là có thể dùng nó làm các  việc khác nữa.Việc đọc biến tiến hành qua vòng lặp While để kiểm tra xem Ptr còn trỏ vào phần tử nào không, nếu có thì đọc. Sau khi đọc xong một phần tử, ta phải chuyển sang một phần tử trước đấy bằng dòng lệnh :              

                        Ptr := Ptr^.Next ;  (* Ptr trỏ vào người bên cạnh *)  

         Cuối cùng câu lệnh  Release(HeapTop) sẽ giải phóng hết các ô nhớ. 

    2.  Xen vào:

         Bây giờ ta xét thêm một thao tác nữa với danh sách được ghép nối : xen một phần tử vào một chỗ mong muốn. Ví dụ xen vào trước phần tử có tên là Hai. Giả thiết rằng ta vẫn dùng các mô tả ở trên và Last là con trỏ trỏ vào phần tử cuối cùng như đã trình bày. 

                        (* To phần tử mới để xen vào *)

                        New(Q);

                        Q^.Ten := ' Ba ' ;

                        Q^.Tuoi := 3 ;

                        (* Tìm vị trí cần xen *) ;

                        Ptr := Last ;

                        While(Ptr <> Nil)and(Ptr.Ten <> ' Hai ') Do

                           Ptr := Ptr^.Next ;

                        (* Ghép nối vào chổ cần thiết *) ;

                        Q.Next := Ptr^.Next ;

                        Ptr^.Next := Q ; 

    3.  Tháo đi:

         Trái ngược với thao tác xen vào là một phần tử ra khỏi danh sách. Ví dụ tháo đi phần tử có tên là Ba. Khi này ta phải dùng Q ngay trong lúc tìm kiếm. Khi tìm xong rồi ta phải tháo trong  hai trường hợp khác nhau: 

                        (* Tìm kiếm phần tử cần tháo *)

                        P := Last ;

                        While(P <> Nil)and(P^.Ten <> ' Ba ') Do

                        Begin

                           Q := P ;

                           P := P^.Next ;

                        End ;

                        (* Tháo đi *)

                        If  P = Last  Then  Last := P^.Next ;

                        Else  Q^.Next := P^.Next ; 

    4.  Chương trình con dùng biến con trỏ : 

         Biến con trỏ cũng có thể được đưa vào như là tham số của chương trình con. Ví dụ ta có thể lập mốt chương trình con Xen như sau : 

                        Procedure Xen(Var Q : PointerN);

                        Begin

                         ...

                        End ; 

         Nhu ta đã biết, giá trị kết quả của một Function là kiểu vô hướng. Tuy nhiên ta cũng có thể làm chương trình con kiểu Function có kết quả được mô tả kiểu con trỏ. Ví dụ chương trình con sau được dùng để đảo các con trỏ của một danh sách được ghép nối. Tham số của nó là con trỏ trỏ vào phần tử nằm cuối danh sách, tức là trỏ vào phần tử cuối cùng. 

                        Function Dao(Cuoi : Pointern): PointerN ;

                        Var

                           P1, P2 : PointerN ;

                        Begin

                           P1 := Cuoi ;

                           Cuoi := Nil ;

                           Repeat

                              P2 := P1^.Next ;

                              P1^.Next := Cuoi ;

                              Cuoi := P1 ;

                              P1 := P2 ;

                           Until P1 = Nil ;

                           Dao := Cuoi ;

                        End ;


    Nhắn tin cho tác giả
    Đỗ Trung Thành @ 10:49 13/09/2009
    Số lượt xem: 1544
    Số lượt thích: 0 người
     
    Gửi ý kiến