Professional Documents
Culture Documents
CH 1 PDF
CH 1 PDF
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
8
1.2.1. Kiểu dữ liệu cơ bản
• Kiểu số thực
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ử
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;
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];
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
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
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;
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);
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;
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
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
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
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);
}
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