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

Con trỏ va danh sach liên kết

14 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 đề Con trỏ và Danh sách Liên kết
Chuyên ngành Computer Science
Thể loại Lecture Notes
Định dạng
Số trang 14
Dung lượng 421,96 KB

Nội dung

• Kích thước có thể thay đổi Hàm cấp phát bộ hớ của C void *mallocsize; //Trả về con trỏ chỉ đến một vung nhớ //size byte vừa được cấp phát void *callocn,size; //Trả về con trỏ chỉ đến

Trang 1

CON TRỎ VÀ DANH SÁCH LIÊN KẾT

I Kiễu dữ liệu con trỏ

1 Biến không động

• Khai báo tường minh

• Tồn tại khi vào phạm vi được khai báo và chỉ mất khi ra khỏi phạm vi này

• Được cấp phát vùng nhớ

• Kích thước không thay đổi trong suốt quá trình

VD:

int a;

char b[10];

//a b là các biến không động

2 Kiểu con trỏ

• Một con trỏ hay một biến con trỏ là:

✓ một biến chiếu đến một ô nhớ

✓ nó lưu vị trí/địa chỉ của ô nhớ đó

• Hai ứng dụng chính:

✓ Truy nhập gián tiếP

✓ Bộ nhớ động

VD :

int x, y ; //cho y=5 thi x bang 5

int* p ; //p la bien con tro

p = &y ; //tro p toi y (p luu dia chi cua y)

*p = x ; //ghi gia tri bien x vao bo nho tro toi p

• Đọc *p là biến mà p trỏ tới

Đọc &x là địa chỉ của x

& là toán tử địa chỉ (address of operator)

* là toán tử thâm nhập (dereferencing operator)

• Giả sử p1 = &x và p2 = &y, thì p1 trỏ tới x và p2 trỏ tới y

p1 = p2 Không tương đương với *p1 = *p2 p1 = p2 có hiệu quả trỏ p1 tới y,lệnh đó không thay đổi x Lệnh *p1 = *p2 ; tương đương với x=y;

• Cú pháp định nghĩa một kiểu con trỏ trong C

typedef <Kiểu con trỏ> *<kiểu cơ sở>

Trang 2

int *p, *q;

Ta có thể viết

typedef int* IntPtr;

IntPtr p, q; //p q deu la con tro

• Khi một biến con trỏ p lưu địa chỉ của đối tượng x ta nói p trỏ đến x

3 Biến động

• Không khai báo tường minh

• Có thể cấp phát hoặc giải phóng bộ nhớ theo yêu cầu

• Không theo quy tắc phạm vi

• Vung nhớ được cấp phát trong heap

heap: vùng bộ nhớ đặc biệt dành riêng cho các biến động Để tạo một biến động mới, hệ thống cấp phát không gian từ heap Nếu không còn bộ nhớ,

new không thể cấp phát bộ nhớ thì nó trả về gia trị Null Thực ra, NULL là giá trị 0, nhưng ta coi

nó là một giá trị đặc biệt vì còn sử

dụng cho trường hợp đặc biệt: con trỏ "rỗng"

• Kích thước có thể thay đổi

Hàm cấp phát bộ hớ của C

void *malloc(size); //Trả về con trỏ chỉ đến một vung nhớ

//size byte vừa được cấp phát

void *calloc(n,size); //Trả về con trỏ chỉ đến một vùng nhớ

//vừa được cấp phát gồm n phần tử

//Mỗi phần tử có kích thước size byte

Hàm cấp phát bộ hớ của C++

p = new int; //Cap phat mot o nho chua so nguyen va p tro toi o nho do

Hàm hủy một biến động do p chỉ đến

• Hàm free(p) hủy do lệnh malloc hoặc calloc

• Hàm delete p hủy do lệnh new

VD:

delete p;

➢ Trả lại vùng bộ nhớ trỏ bởi p, nhưng không sửa giá tri của p

Trang 3

➢ Sau khi thực thi delete p, giá trị của p không xác định

VD:

int* p1, p2;//cap phat vung nho cho mot bien dong kieu int

p1 = (int*)malloc(sizeof(int));

p1* = 5; //dat gia tri 5 cho bien dong p1

p2 = (int*)calloc(10, sizeof(int)); //cap phat bien dong kieu mang gom 10 phan tu keu int

(p2 + 3) *= 0; //dat gia tri 0 cho phan tu thu 4 cua mang p2

free(p1);

free(p2);

VD:

#include <iostream>

int main()

{

int *p1, *p2;

p1 = new int;

*p1 = 10;

p2 = p1;

cout << "*p1 = " << *p1 << endl; //*p1=10

cout << "*p2 = " << *p2 << endl << endl; //*p2=10

*p2 = 30;

cout << "*p1 = " << *p1 << endl; //*p1=30

cout << "*p2 = " << *p2 << endl << endl; //*p1=30

p1 = new int;

*p1 = 40;

cout << "*p1 = " << *p1 << endl; //*p1=40

cout << "*p2 = " << *p2 << endl << endl; //*p1=30

}

II Danh sách kiên kết

Các hình thức tổ chức danh sách

Có nhiều kiểu tổ chức liên kết giữa các phần tử trong danh sách như :

-Danh sách liên kết đơn: Mỗi phần tử liên kết với phần tử đứng sau nó trong danh sách

-Danh sách liên kết kép: Mỗi phần tử liên kết với phần tử đứng trước và sau nó trong danh sách

Trang 4

A B C D

-Danh sách liên kết vòng:Phần tử cuối danh sách liên kết với phần tử đầu danh sách

-Danh sách tổng quát

Hình thức liên kết này cho phép các thao tác thêm, huỷ phần tử trên danh sách được thực hiện dễ dàng hơn, phản ánh được bản chất linh động của danh sách

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

1 Kiến thức

Mỗi phần tử của danh sách đơn là một cấu trúc chứa hai thông tin :

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

• Thành phần mối liên kết: lưu trữ địa chỉ của phần tử kế tiếp trong danh sách, hoặc lưu trữ giá trị NULL nếu là phần tử cuối danh sách

Ta có định nghĩa tổng quát

struct Node

{

Data Info; //data là kiểu dữ liệu đã định nghĩa trước

Node* Next; //con trỏ chỉ đến cấu trúc Node

};

Trang 5

• Một phần tử trong danh sách đơn là một biến động sẽ được yêu cầu cấp phát khi cần Và

danh sách đơn chính là sự liên kết các biến động này với nhau do vậy đạt được sự linh động khi thay đổi số lượng các phần tử

• Nếu biết được địa chỉ của phần tử đầu tiên trong danh sách đơn thì có thể dựa vào thông tin next của nó để truy xuất đến phần tử thứ hai trong xâu, và lại dựa vào thông tin next của phần tử thứ 2 để truy xuất đến phần tử thứ 3, Nghĩa là để quản lý một xâu đơn chỉ cần biết địa chỉ phần tử đầu xâu Thường một con trỏ head sẽ được dùng để lưu trữ địa chỉ phần tử đầu xâu, ta gọi head là đầu xâu Ta có khai báo:

Node *head;

• Tuy về nguyên tắc chỉ cần quản lý xâu thông qua đầu xâu head, nhưng thực tế có nhiều trường hợp cần làm việc với phần tử cuối xâu, khi đó mỗi lần muốn xác định phần tử cuối xâu lại phải duyệt từ đầu xâu Ðể tiện lợi, có thể sử dụng thêm một con trỏ tail giữ địa chỉ phần tử cuối xâu Khai báo tail như sau :

Node *tail;

Xâu sẽ có

2 Các thao tác cơ bản trên danh sách liên kết đơn

✓ Giả sử có định nghĩa nút trong danh sách

✓ Giả sử có định nghĩa kiểu danh sách liên kết

1 Tạo phần tử

2 Khởi tạo danh sách rỗng

3 Kiểm tra danh sách rỗng

4 Duyệt danh sách

5 Tìm phần tử có thành phần dữ liệu x trong danh sách

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

7 Hủy một phần tử khỏi danh sách liên kết

8 Sắp xếp danh sách

Trang 6

9 Một số cấu trúc đặc biệt của danh sách liên kết đơn

//Giả sử có định nghĩa nút trong danh sách :

C1

struct Node //Khai báo một cấu trúc dữ liệu

{

int info;

node *pNext;

};

C2

typedef struct Node

{

int info;

Node *pNext;

}Node; //Kiểu của một phần tử trong danh sách

//Giả sử có định nghĩa kiểu danh sách liên kết :

struct list //Kiểu danh sách liên kết

{

Node *pHead; //con trỏ lưu địa chỉ đầu danh sách liên kết Node *pTail; //con trỏ lưu địa chỉ cuối danh sách liên kết

};

VD:

//Kiểu dữ liệu sinh viên

struct SinhVien

{

char Ten[30];

int MSSV;

};

//Kiểu phần tử trong danh sách

struct SinhVienNode

{

SinhVien Info;

SinhVien* pNext;

};

//Đổi lại tên kiểu phần tử trong danh sách

typedef SinhVien SVNode;

//Định nghĩa danh sách liên kết

Trang 7

struct List

{

SVNode* pHead;

SVNode* pTail;

};

Hoặc

//Kiểu dữ liệu sinh viên

struct SinhVien

{

char Ten[30];

int MSSV;

};

//Kiểu phần tử trong danh sách

struct SinhVienNode

{

SinhVien Info;

SinhVien* pNext;

};

//Định nghĩa danh sách liên kết

struct List

{

SinhVienNode* pHead;

SinhVienNode* pTail;

};

1.Tạo phần tử

// Hàm CreateNode(x): tạo một nút trong DSLK vời thành phần dữ liệu x, hàm trả về con trỏ lưu trữ địa chỉ của phần tử vừa tạo (nếu thành công)

Node* GetNode(int x)

{

Node *p;

p = new Node; //Cấp phát bộ nhớ cho phần tử

//Gộp Node* p=new Node;

if (p == NULL)

{

cout << "Khong du bo nho"; exit(1);

}

p->Info = x; //Gán thông tin cho phần tử p

p->pNext = NULL;

Trang 8

return p;

}

Sử dụng Node* New_ele = GetNode(x); : gán giá trị của hàm (là con trỏ) cho 1 biến con trỏ kiểu Node, phần tử này đặt tên là New_ele, giữ địa chỉ của phần tử đã tạo

2.Khởi tạo danh sách rỗng

//Hàm khởi tạo danh sách rỗng

void CreatList(List &l)

{

l.pHead = l.pTail = NULL;

}

3.Kiểm tra danh sách rỗng

//Kiểm tra danh sách rỗng, trả về 0 nếu rỗng trả về 1 nếu không rỗng

int IsEmpty(List )

{

if (l.pHead = NULL) //Danh sách rỗng

return 0;

return 1;

}

4.Duyệt danh sách

• Đếm các phần tử của danh sách

• Xuất dữ liệu các phần tử trong DS ra màn hình

• Tìm tất cả các phần tử thỏa điều kiện

• Hủy toàn bộ danh sách (và giải phóng bộ nhớ)

Thuật toán

B1: p = Head; //Cho p trỏ đến phần tử đầu danh sách

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

• B2.1 : Xử lý phần tử p;

B2.2 : p=p->pNext; // Cho p trỏ tới phần từ kế tiếp

//Hàm duyệt danh sách

void ProcessList(List l)

{

Node* p;

p = l.pHead;

while (p != NULL)

Trang 9

{

ProcessNode(p); //Xử lí cụ thể từng hàm

p = p->pNext;

}

}

//Hàm xuất dữ liệu ra màn hình

void Output(List )

{

Node* p;

if (IsEmpty(l)==0)

{

cout << "Danh sach rong"; return;

}

cout << "Du lieu danh sach";

p = l.pHead;

while (p!= NULL)

{

cout << p->info;

p = p->pNext;

}

}

4.Tìm phần tử có thành phần dữ liệu x trong danh sách

B1: p = Head; //Cho p trỏ đến phần tử đầu danh sách B2: Trong khi (p != NULL) và (p->Info != x ) thực hiện:

p = p->Next;// Cho p trỏ tới phần tử kế tiếp

B3:

Nếu p != NULL thì p trỏ tới phần tử cần tìm

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

//Hàm tìm phần tử có trong danh sách

Node* Search(List , int x)

{

Node* p;

p = l.pHead;

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

p = p->pNext;

return p;

}

Trang 10

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

Có ba vị trí để có thể chèn một phần tử New_ele 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 danh sách sau một phần tử q

C1: Chèn vào đầu danh sách

pHead

pTail

X

.

Giải thuật :

Nếu danh sách rỗng thì

B1.1: Head = New_elelment;

B1.2: Tail = Head;

Ngược lại

B2.1: New_ele->pNext = Head;

B2.1: Head = New_ele;

//Ham chen một phần tử vào đầu danh sách

void AddFirst(list &l, node *New_ele)

{

if (l.pHead == NULL) //xau rong

{

l.pHead = New_ele;

l.pTail = l.pHead;

}

else

{

New_ele->pNext = l.pHead;

l.pHead = New_ele;

}

}

//Tạo nút có dữ liệu x, sau đó chèn nút vừa tạo vào đầu danh sách, hàm trả về con trỏ lưu vị trí nút vừa tạo

Node* InsertHead(List &l, int x)

{

Node* New_ele = GetNode(x);

if (New_ele == NULL)

return NULL;

Trang 11

if (l.pHead == NULL)

{

l.pHead = New_ele;

l.pTail = l.pHead;

}

else

{

New_ele->pNext = l.pHead;

l.pHead = New_ele;

}

return New_ele;

}

Hoặc

Node* InsertHead(List &l, int x)

{

Node* New_ele = GetNode(x);

if (New_ele == NULL)

return NULL; AddHead(l, New_ele);

return New_ele;

}

C2: Chèn vào cuối danh sách

Thuật toán

Nếu danh sách rỗng thì

B1.1: Head = New_ele;

B1.2: Tail = Head;

Ngược lại

B2.1: Tail ->pNext = New_ele;

B2.2: Tail = New_ele ;

pHead

pTail

X

void AddTail(List &l, Node* New_ele)

{

Trang 12

if (l.pHead == NULL)

{

l.pHead = New_ele;

l.pTail = l.pHead;

}

else

{

l.pTail->pNext = New_ele;

l.pTail = New_ele;

}

}

Tạo nút có dữ liệu x, sau đó chèn nút vừa tạo vào cuối danh sách, hàm trả về con trỏ lưu vị trí nút vừa tạo

Node* InsertTail(List &l, int x)

{

Node* New_ele = GetNode(x);

if (New_ele == NULL)

return NULL;

if (l.pHead == NULL)

{

l.pHead = New_ele;

l.pTail = l.pHead;

}

else

{

l.pTail->pNext = New_ele;

l.pTail = New_ele;

}

return New_ele;

}

Hoặc

Node* InsertTail(List &l, int x)

{

Node* New_ele = GetNode(x);

if (New_ele == NULL)

return NULL; AddTail(l, New_ele);

return New_ele;

}

C2: Chèn một phần tử x vào danh sách sau một phần tử q

Trang 13

Nếu ( q != NULL) thì

B1: New_ele -> pNext = q->pNext;

B2: q->pNext = New_ele;

7 Hủy một phần tử khỏi danh sách liên kết

• Hủy phần tử đầu danh sách

• Hủy một phần tử đứng sau phần tử q trong danh sách

• Hủy một phần tử có dữ liệu x

8 Sắp xếp danh sách

9 Một số cấu trúc đặc biệt của danh sách liên kết đơn 9.1

A Danh sách liên kết kép

➢ Chèn một phần tử vào DSLK kép

1 Chèn đầu danh sách

2 Chèn cuối danh sách

3 Chèn nút vào sau một phần tử p

4 Chèn nút vào trước phần tử p

➢ Hủy một phần tử ra khỏi danh sách kép

1 Hủy phần tử đầu danh sách

2 Hủy phần tử đầu danh sách

3 Hủy phần tử sau phần tử q

4 Hủy phần tử trước phần tử q

Trang 14

5 Hủy phần tử có khóa k

Sắp xếp trên danh sách liên kết kép

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

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

  • Đang cập nhật ...

TÀI LIỆU LIÊN QUAN

w