Download as pdf or txt
Download as pdf or txt
You are on page 1of 50

GIỚI THIỆU MÔN HỌC

• Nắm được các kiểu dữ liệu để tổ chức,


khai báo cho chương trình được hiệu
quả. Các kiểu dữ liệu như: danh sách,
ngăn xếp, hàng đợi, cây, bảng băm, …
• Đánh giá độ phức tạp thuật toán.
• Các thuật toán được mô tả bằng giả C
Nội dung môn học
1. NHẬP MÔN CTDL
2. DANH SÁCH
3. NGĂN XẾP VÀ HÀNG ĐỢI
4. CÂY
5. SẮP XẾP VÀ TÌM KIẾM
6. BẢNG BĂM
TÀI LIỆU THAM KHẢO
1. Bài giảng Cấu trúc dữ liệu, Khoa CNTT
2. Cấu trúc dữ liệu và giải thuật, Đỗ Xuân Lôi
3. Cấu trúc dữ liệu ứng dụng và cài đặt bằng C,
Nguyễn Hồng Chương
4. Cấu trúc dữ liệu, Nguyễn Trung Trực
5. Data Structures and Algorithms, Alfred
V.Aho, John E.Hopcroft, Jeffrey D. Ullman
Chương 1
NHẬP MÔN CTDL
1.1. Các khái niệm
1.2. Kiểu dữ liệu cơ bản và kết hợp
1.3. Hàm đệ qui
1.4. Độ phức tạp thuật toán
1.5. Bài tập
1.1. Các khái niệm
• Dữ liệu: Thông tin được lưu trữ và xử lý
trên máy tính
• Kiểu dữ liệu: Tập hợp các giá trị cùng
tính chất
• Cấu trúc dữ liệu: Cách tổ chức, lưu
trữ dữ liệu sao cho sử dụng được hiệu quả
• Thuật toán: Các bước thực hiện để giải
quyết vấn đề.
5
1.2. Kiểu dữ liệu cơ bản và kết hợp
1.2.1. Kiểu dữ liệu cơ bản
1.2.2. Kiểu dữ liệu kết hợp

6
1.2.1. Kiểu dữ liệu cơ bản
Kiểu kí tự
Kiểu char
Biểu diễn các kí tự trong bảng mã ASCII
Ví dụ
‘A’ có giá trị mã ASCII là 65
‘2’ có giá trị mã ASCII là 50
Kiểu kí tự đồng thời cũng là kiểu số nguyên
Có hai kiểu char: signed char và unsinged char
Kiểu kí tự Kích thước Miền giá trị
signed char 1 Byte -128..127
unsigned char 1 Byte 0..255 7
1.2.1. Kiểu dữ liệu cơ bản
• Kiểu số nguyên
Kiểu số nguyên Kích thước Miền giá trị
(signed) short 2 Byte -32768..32767
(signed) int/long 4 Byte -2147483648..
2147483647

unsigned short 2 Byte 0..65535


unsigned int/long 4 Byte 0..4294967295

Ngầm định thì các kiểu số nguyên đều có dấu


(signed) nên từ khóa signed ít dùng.

8
1.2.1. Kiểu dữ liệu cơ bản
• Kiểu số thực

Kiểu số thực Kích thước Miền giá trị


float 4 Byte 3.4x10-38..3.4x1038
double 8 Byte 1.7x10-308..1.7x10308

long double 10 Byte 3.4x10-4932..1.1x104932

9
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu mảng
Khi làm việc với các cấu trúc dữ liệu dạng dãy hay danh sách
các phần tử, ta sử dụng kiểu mảng (array)
Mảng 1 chiều: một vec-tơ các phần tử
Mảng nhiều chiều: một bảng các phần tử

Mảng một chiều


Dãy các phần tử có cùng kiểu dữ liệu
Các phần tử được sắp xếp theo trật tự nhất định

10
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu mảng
Cú pháp khai báo mảng một chiều
kiểu_dữ_liệu tên_mảng[Kich_thuoc];
Ví dụ
int a[10];
float x[100];
Kich_thuoc là số phần tử mảng được xác định khi khai báo
Sử dụng toán tử [ ] để truy cập phân tử của mảng
Ví dụ: a[2], x[10], …
Chỉ số các phần tử mảng được đánh số từ 0
 Việc truy cập phần tử có chỉ số ngoài phạm vi thì chương trình
không kiểm tra 11
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu mảng
Ví dụ
Nhập danh sách các giá trị nguyên vào một mảng, sau đó tìm phần
tử có giá trị nhỏ nhất trong mảng
#include <stdio.h>
#define N 10
main()
{
int x[N], i, min;

for (i=0; i < N; i++){


printf(" x[%d]= ", i);
scanf("%d", &x[i]);
}
min = x[0];
for (i=1; i < N; i++)
if ( x[i]<min) min = x[i];
printf("\n min= %d", min);
}
12
1.2.2. Kiểu dữ liệu kết hợp
Khởi tạo mảng
• Kiểu mảng
Mảng có thể được khởi tạo giá trị ngay khi khai báo
Cú pháp
kiểu_dữ_liệu tên_mảng[số_phần_tử_của_mảng] =
{danh_sách_các_giá_trị_khởi_tạo};
Ví dụ
int a[3] = {2, 4, 5};
Hoặc có thể bỏ qua kích thước
int a[] = {2, 4, 5}; /*không khai báo số phần tử mảng*/
. Nếu giá trị đầu ít hơn kích thước thì các phần tử còn lại nhận giá trị 0
int a[100] = {2, 3, 5};

13
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu mảng
Định nghĩa kiểu mới – từ khóa typedef
Có thể sử dụng từ khóa typedef để định nghĩa các kiểu dữ liệu mới
Kiểu dữ liệu mới sẽ được sử dụng để khai báo dữ liệu
Ví dụ
typedef int SONGUYEN;
typedef float DAYSO [10];

Khai báo biến


SONGUYEN n, a[100];
DAYSO x, y;

14
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu mảng
Mảng và địa chỉ
Toán tử & dùng để lấy địa chỉ một biến
Toán tử & cũng được dùng để lấy địa chỉ của một phần tử mảng
Các phần tử trong mảng được bố trí các ô nhớ liên tiếp nhau trên
bộ nhớ
Nếu biết được địa chỉ phần tử thú i sẽ xác định được địa chỉ phần tử
thú i+1
Địa chỉ phần tử đầu tiên là địa chỉ của mảng
Tên mảng là địa chỉ của phần tử đầu mảng
Cũng có thể truy cập phần tử của mảng qua con trỏ

15
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu mảng
Mảng và địa chỉ
Ví dụ
float a[100];
float *p;
Các cách viết sau là tương đương:
a  &a[0]
a + i  &a[i]
*(a + i)  a[i]
Các phép gán hợp lệ
p = a;
p = &a[0];

16
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu mảng
Mảng là tham số của hàm
Khi sử dụng mảng là tham số của hàm, ta có thể khai báo, chẳng hạn:
int a[]
Hoặc
int *a
Như thế, hai cách sau là tương đương:
f(int a[]) { … }
f(int *a) { … }
Khi sử dụng, có thể gọi:
f(a);
Hoặc
f(&a[0]);

17
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu mảng
Mảng là tham số của hàm

void nhap_mang(int *x, int n)


{
int i;

for (i=0; i < n; i++)


{
printf(" x[%d]= ", i);
scanf("%d", &x[i]);
}
}
18
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu mảng
Mảng là tham số của hàm

void xuat_mang(const int x[ ], int n)


{
int i;

for (i=0; i <= n-1; i++)


printf(" x[%d]= %d\n", i, x[i]);
}

Khi truyền mảng cho hàm thì hàm luôn xử lý trực tiếp với mảng nên giá
trị gốc của mảng có thể thay đổi theo.
Để hàm không được làm thay đổi giá trị thì tham số được khai báo với
từ khóa const 19
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu mảng
Mảng nhiều chiều
Ví dụ, khai báo mảng hai chiều
int a[4][10];
là mảng có 4 hàng, 10 cột
Truy cập các phần tử của mảng
a[0][0], a[0][1], a[i][j]
Để truy cập đến tất cả các phần tử của mảng 2 chiều thường
dùng 2 vòng lặp for lồng nhau

20
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu mảng
Mảng nhiều chiều

/* Hàm nhập mảng các số nguyên */


void nhap_ma_tran(int a[][Max_Cot], int m, int n)
{
int i, j;

for (i=0; i < m; i++){


printf("\n Nhap hang thu %2d\n", i);
for (j=0; j < n; ++j){
printf(“pt[%d][%d]”, i, j);
scanf("%d", &a[i][j] );
}
}
}

21
1.2.2. Kiểu dữ liệu kết hợp
• Xâu kí tự
• Xâu kí tự là một mảng các phần tử kiểu char, kết thúc bởi kí tự ‘\0’
hay NULL
Thao tác trên xâu kí tương tự như thao tác trên mảng một chiều
Khai báo xâu kí tự
char xau[100]; /* xâu chứa tối đa 100 */
Hoặc
char *xau; /* khai báo con trỏ đến xâu kí tự */
Khởi tạo
char xau[100] = {‘a’, ‘b’, ‘c’, ‘\0’};
char xau[100] = “abc”;
char xau[] = “abc”;
char *xau = “abc”;

22
1.2.2. Kiểu dữ liệu kết hợp
• Xâu kí tự
Một số hàm thao tác trên xâu kí tự được khai báo trong
<string.h>
• int strlen(char *str)
Chép xâu kí tự
• char * strcpy(char *str1, char *str2)
Không sử dụng phép gán (=) để gán xâu kí tự cho con trỏ xâu kí tự
Phải sử dụng hàm strcpy
• char *strcat(char *dest, char *source)
Ghép xâu source vào cuối xâu dest

23
1.2.2. Kiểu dữ liệu kết hợp
• Xâu kí tự
Một số hàm thao tác trên xâu kí tự được khai báo trong
<string.h>
• int strcmp(char *str1, char *str2)
So sánh hai xâu kí tự
Hàm trả về
0 nếu str1 == str2
> 0 nếu str1 > str2
< 0 nếu str1 < str2
• char *strstr(char *str1, char *str2)
Tìm một xâu trong một xâu khác
Hàm trả về con trỏ trỏ đến vị trí xâu str2 trong str1 nếu tìm thấy, nếu
không sẽ trả về NULL
• char *strchr(char *str, char ch)
Tìm một ký tự trong một xâu
Hàm trả về con trỏ trỏ đến vị trí ký tự ch trong str nếu tìm thấy, nếu
không sẽ trả về NULL

24
1.2.2. Kiểu dữ liệu kết hợp
• Xâu kí tự
Một số hàm chuyển đổi giữa chuỗi và số được khai báo trong
<stdlid.h>
• double atof (const char *str)
Chuyển chuỗi str thành số thực kiểu double
• int atoi (const char *str)
Chuyển chuỗi str thành số nguyên kiểu int
• long int atol (const char *str)
Chuyển chuỗi str thành số nguyên kiểu long

25
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu cấu trúc
• Kiểu cấu trúc cho phép tạo ra kiểu dữ liệu mới gồm các
thành phần có thể có kiểu khác nhau
• Kiểu cấu trúc (structure) hay còn được gọi là kiểu bản ghi
(record)
• Mỗi thành phần được gọi là các trường (field)
• Dùng từ khóa struct để định nghĩa kiểu cấu trúc

26
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu cấu trúc
Ví dụ: dùng kiểu cấu trúc mô tả dữ liệu là địa chỉ
Địa chỉ gồm các thông tin: số nhà, tên đường, tên thành phố

struct dia_chi
{
int so_nha;
char duong[40];
char thanh_pho[30];
}ongA, baB;

struct dia_chi anhC, chiD;

27
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu cấu trúc
• Để truy cập trường f của biến cấu trúc S dùng cú pháp:
S.f
Ví dụ: theo khai báo trên
ongA.sonha= baB.sonha;
strcpy(anhC.duong, chiD.duong);

Có thể dùng phép gán cho biến struct

Ví dụ:
ongA= baB;
anhC=chiD; 28
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu cấu trúc
• Hàm có tham số kiểu cấu trúc
• Ví dụ:
void nhap(dia_chi *dc)
{
dia_chi x;

printf(“So nha:"); scanf("%d", &x.sonha);


printf(“Duong:"); gets(x.duong);
printf(“TPho:"); gets(x.thanh_pho);
*dc = x;
}

29
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu cấu trúc
• Để truy cập trường f của biến cấu trúc mà con trỏ P chỉ
đến dùng cú pháp: P‒>f
• Ví dụ: có thể nhập trực tiếp cho

void nhap(dia_chi *x)


{

printf(“So nha:"); scanf("%d", &x ‒> sonha);


printf(“Duong:"); gets(x ‒> duong);
printf(“TPho:"); gets(x ‒> thanh_pho);
}
30
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu cấu trúc
• Hàm có thể trả về kiểu cấu trúc
• Ví dụ: có thể viết lại hàm trên bằng cách khác
struct dia_chi nhap()
{
dia_chi x;

printf(“So nha:"); scanf("%d", &x.sonha);


printf(“Duong:"); gets(x.duong);
printf(“TPho:"); gets(x.thanh_pho);
return x;
}

31
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu con trỏ
• khai báo biến con trỏ
typedef kiểudữliệu *kiểucontrỏ ;
kiểucontrỏ biếncontrỏ ;
• hoặc ta có thể khai báo trực tiếp biến con trỏ như sau:
kiểudữliệu *biếncontrỏ ;
• Để truy cập giá trị con trỏ p chỉ đến dùng cú pháp: *p

Ví dụ: int a=123, b=456, *p=&a;


printf("\na=%d, *p=%d", a, *p);
*p=0;
printf("\na=%d, *p=%d", a, *p);
p=&b; 32
printf("\nb=%d, *p=%d", b, *p);
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu tham chiếu
• Dấu & để khai báo biến bằng tham chiếu
kiểudữliệu &biếnthamchiếu;
• Sau đó chỉ dùng tên biến tham chiếu để truy cập giá
Ví dụ: int a=123, &r = a;// r va a cung 1 vung nho
printf("\nr=%d, a=%d", r, a);
printf("\n&r=%p, &a=%p", &r, &a);
r=456;
printf("\nr=%d, a=%d", r, a);
• Tham chiếu thường dùng cho tham số của hàm thay
cho tham số là con trỏ. Trong hàm thuận tiện hơn bằng
cách dùng tên tham số thay vì dùng con trỏ (*). Và khi
gọi hàm thì truyền tên biến, giá trị gốc của biến thay 33
đổi theo.
1.2.2. Kiểu dữ liệu kết hợp
• Kiểu tham chiếu
Ví dụ:
#include <stdio.h>
void TongTich(int x, int y, int &s, int *p)
{
s = x + y;
*p = x*y;
}
main()
{
int s, p;
TongTich (2, 3, s, &p);
printf("\n 2+3 =%d", s);
printf("\n 2*3 =%d", p);
}

34
1.3. Hàm đệ qui
Số n! có hệ thức truy hồi:
n! = (n-1)!n và 0!=1.
n=0 gọi là giá trị neo.
Có hàm đệ qui tính n! như sau

int Fact ( unsigned n)


{
if (n == 0) return 1;
else return Fact (n-1)*n;
}

35
1.3. Hàm đệ qui
Số Fibonacci thứ n, Fn, có hệ thức truy hồi:
Fn =Fn-1 +Fn-2 và F0 =F1=1.
Có hàm đệ qui tính Fn như sau
int Fib ( unsigned n)
{
if (n == 0 || n==1) return 1;
else return Fib (n-1)+Fib (n-2);
}

36
1.3. Hàm đệ qui
Viết hàm đệ qui cần lưu ý giá trị neo
Ví dụ: Hàm lỗi
int Bad(unsigned n)
{
if (n == 0) return 0;
else return Bad(n/3+1);
}

Thử vẽ sơ đồ gọi Bad(5) 37


1.4. Độ phức tạp thuật toán

38
1.4. Độ phức tạp thuật toán
Tính độ phức tạp dựa vào các thao tác:
• phép gán/đọc/ghi: O(1)
• gọi/trả về của hàm: O(1)
• lệnh if: thời gian kiểm tra cộng với
O(max của hai nhánh).
• lệnh lặp: tổng toàn bộ các bước lặp
trên số thao tác của mỗi bước.

39
1.4. Độ phức tạp thuật toán
Các quy tắc
Quy tắc tổng:
T1(n)+T2(n) = O(f(n)+g(n))
= O(max{f(n), g(n)})
Quy tắc tích:
T1(n).T2(n)=O(f(n).g(n))

40
1.4. Độ phức tạp thuật toán
Trong mô tả thuật toán:
• Không khai báo kiểu hàm, kiểu tham
số
• Không khai báo biến cục bộ
• Đánh chỉ số từng dòng để dễ mô tả
việc tính độ phức tạp
• Đánh giá theo ký pháp O_lớn
• Đánh giá theo kích thước nhập n
41
1.4. Độ phức tạp thuật toán
Ví dụ: Thuật toán tính n!
Fact(n)
{
1. p=1;
2. for (i=1; i≤n; i++) p*=i;
3. return p;
}
Dòng 1: O(1)
Dòng 2: O(n)
Dòng 3: O(1)
42
Theo quy tắc tổng: T(n)=O(n+2)=O(n)
1.4. Độ phức tạp thuật toán
Ví dụ: Thuật toán nhập ma trận vuông cấp n
NhapMT (A, n)
{
1. for (i=0; i<n; i++)
2. for (j=0; j<n; j++) scanf(“%d”, &A[i][j]);
}
Dòng 1: O(n)
Dòng 2: O(n)
Dòng 1+2: O(n2), theo quy tắc tích
T(n)=O(n2)
43
1.4. Độ phức tạp thuật toán
Ví dụ: Thuật toán đệ qui tính Fn
Fib (n)
{
if (n == 0 || n==1) return 1;
else return Fib (n-1)+Fib (n-2);
}
Vẽ cây nhị phân gọi đệ qui. Đây là cây nhị phân
không đầy đủ. Số nút của cây nhị phân đầy đủ
chiều cao n là 2n-1.
Vậy độ phức tạp thuật toán là T(n) = O(2n). 44
n
1.4. Độ phức tạp thuật toán
Ví dụ: Thuật toán lặp tính Fn
Fib (n)
{
x=y=z=1;
for (i=2; i≤n; i++){
z=x+y; x=y; y=z;
}
return z;
}
T(n) = O(n).
45
1.5. Bài tập
Bài 1:
a) Độ phức tạp thuật toán là gì?
b) Cho ví dụ một thuật toán có độ phức tạp
thời gian O(n2)
Bài 2: Viết hàm đệ quy tính n!. Đánh giá độ
phức tạp thuật toán.
Bài 3: Viết hàm đệ quy tính số tổ hợp C(n,k).
Xem k=O(n). Đánh giá độ phức tạp thuật toán
theo n. 46
1.5. Bài tập
Bài 4: Viết chương trình đọc số nguyên, rồi
hiển thị số đảo ngược. Ví dụ, 463 đảo thành
364.(Lưu ý các số chẵn chục như: 1230, 3400)
Bài 5: Viết chương trình chuyển số Ả Rập
sang số La Mã. Số Ả Rập tương ứng số La Mã
như sau:
I = 1, IV = 4, V = 5, IX = 9, X = 10, XL = 40, L = 50,
XC = 90, C = 100, CD = 400, D = 500, CM = 900, M
= 1000.
Ví dụ: 1234 →MCCXXXIV 47
1.5. Bài tập
Bài 6: Số hoàn thiện là số tự nhiên bằng tổng
các ước thực sự của nó. Ví dụ 6=1+2+3. Viết
chương trình nhập số tự nhiên, rồi kiểm tra có
phải là số hoàn thiện hay không.
Bài 7:Một chuỗi ký tự gọi là palindrome nếu
đảo ngược các ký tự của nó thì nhận được chuỗi
ban đầu. Ví dụ, “madam” là palindrome. Viết
chương trình nhập một chuỗi ký tự, rồi kiểm tra
có phải là palindrome hay không.
48
1.5. Bài tập
Bài 8: Hai chuỗi ký tự gọi là anagram nếu một
chuỗi là hoán vị của chuỗi kia. Ví dụ, “read” và
“dear” là anagram của nhau. Viết chương trình
nhập hai chuỗi ký tự, rồi kiểm tra có phải là
anagram của nhau hay không.
Bài 9: Viết chương trình nhập số tự nhiên n>0
từ bàn phím và dùng thuật toán quay lui (đệ
quy) để liệt kê tất cả các chuỗi nhị phân độ dài
n.
49
1.5. Bài tập
Bài 10:
Cho tập X là tập các số tự nhiên nhỏ hơn 1000,
X={0, 1, 2, …, 999}. Kiểu tập hợp con của X
được khai báo là mảng các bit với ý nghĩa: k
thuộc tập A khi và chỉ khi A[k] = 1.
Hãy khai báo kiểu tập hợp và định nghĩa các
hàm thao tác trên tập hợp như: phép thuộc,
phép hợp, phép giao, phép hiệu, …
50

You might also like