You are on page 1of 96

TRƯỜNG ĐẠI HỌC SƯ PHẠM KỸ THUẬT HƯNG YÊN

KHOA CÔNG NGHỆ THÔNG TIN

BÀI TẬP THỰC HÀNH

HỌC PHẦN: CẤU TRÚC DỮ LIỆU


VÀ GIẢI THUẬT

Trình độ đào tạo : Đại học


Hệ đào tạo : Chính quy

Hưng Yên năm 2012


Bài tập thực hành CTDL & GT Trang 2
MỤC LỤC

BÀI THỰC HÀNH 1. KIỂU DỮ LIỆU CÓ CẤU TRÚC ................................................................................ 3


BÀI THỰC HÀNH 2. ĐỘ PHỨC TẠP TÍNH TOÁN .................................................................................. 16
BÀI THỰC HÀNH 3+4. DANH SÁCH NỐI ĐƠN......................................................................................... 22
BÀI THỰC HÀNH 5. NGĂN XẾP - STACK ............................................................................................. 42
BÀI THỰC HÀNH 6. HÀNG ĐỢI - QUEUE ........................................................................................... 53
BÀI THỰC HÀNH 7. DANH SÁCH LIÊN KẾT KÉP ................................................................................. 59
BÀI THỰC HÀNH 8. DANH SÁCH LIÊN KẾT VÒNG ............................................................................. 70
BÀI THỰC HÀNH 9. CÂY NHỊ PHÂN .................................................................................................. 81
BÀI THỰC HÀNH 10. Kiểm tra thực hành .......................................................................................... 96
Bài tập thực hành CTDL & GT Trang 3

BÀI THỰC HÀNH 1. KIỂU DỮ LIỆU CÓ CẤU TRÚC

* Mục tiêu
Hệ thống lại kiến thức về kiểu dữ liệu mảng, xâu, tệp, cấu trúc: Cách khai báo, các
thao tác trên, cách thức tổ chức bộ nhớ của mảng, xâu, tệp, cấu trúc
* Yêu cầu:
A. Bài tập mẫu
Bài 1: Cho n số nguyên dương a0,a1,a2,...,an-1.
a.Chèn phần tử x vào vị trí k của dãy.
b.Xóa tất cả các số nguyên tố trong dãy.
Gợi ý:
a.Khởi tạo một mảng trung gian tmp có độ dài là (a.Length+1) sau đó gán các giá
trị : tmp[i]=a[i] với i từ 0 ->k-1 (k-1 là vị trí trước vị trí cần chèn)
tmp[k]=giá trị cần chèn
tmp[i+1]=a[i] với i từ k->a.Length-1
Cuối cùng gán mảng a=tmp.
b.Thực hiện duyệt các phần tử trong mảng kiểm tra xem phần tử đó có phải là số
nguyên tố hay không (số nguyến tố là số chỉ chia hết cho 1 và chính nó) :
+ Nếu là số nguyên tố: Duyệt mảng từ trái sang phải.Từ vị trí số nguyên tố đó tiến
hành dời các phần tử về phía trước cho đến khi kết thúc mảng, sau đó giảm kích
thước mảng
+Ngược lại, chuyển sang kiểm tra phần tử kế tiếp
Lời giản mẫu
using System;
class VD
{ static int[] a;
static void Nhap()
{ int n;
Console.Write("Nhap n=");
n = int.Parse(Console.ReadLine());
a = new int[n];
Bài tập thực hành CTDL & GT Trang 4
for (int i = 0; i < a.Length; ++i)
{
Console.Write("a[" + i + "]=");
a[i] = int.Parse(Console.ReadLine());
}
}
static void Chen(int x, int k)
{
if (k >= 0 && k <= a.Length - 1)
{
int[] tmp = new int[a.Length + 1];
for (int i = 0; i < k; ++i)
tmp[i] = a[i];
tmp[k] = x;
for (int i = k; i < a.Length; ++i)
tmp[i + 1] = a[i];
a = tmp;
}
else Console.WriteLine("Du lieu khong hop le");
}
static bool ngto(int x)
{
bool ok = true;
for (int i = 2; i < x - 1; ++i)
if (x % i == 0) { ok = false; break; }
return ok;
}
static void Xoa()
{
int n = a.Length;
for (int i = 0; i < n; )
if (ngto(a[i]))
Bài tập thực hành CTDL & GT Trang 5
{
for (int j = i; j < a.Length - 1; ++j)
a[j] = a[j + 1];
n = n - 1;
}
else ++i;
int[] tmp = new int[n];
for (int i = 0; i < n; ++i)
tmp[i] = a[i];
a = tmp;
}
static void Hien()
{
for (int i = 0; i < a.Length; ++i)
Console.Write(a[i] + "\t");
Console.WriteLine();
}
static void Main()
{
Nhap();
Chen(333, 1);
Hien();
Xoa();
Hien();
Console.ReadKey();
}
}
Bài 2: Cho một danh sách lưu trữ thông tin về các nhân viên trong một công ty, thông tin
gồm :
- Mã nhân viên
- Họ và tên
- Năm sinh (số nguyên)
Bài tập thực hành CTDL & GT Trang 6
- Phòng ban
- Lương cơ bản (số nguyên)
- Thưởng (số nguyên)
- Thực lĩnh (số nguyên, trong đó thực lĩnh = lương cơ bản + thưởng )
Hãy thực hiện các yêu cầu sau:
a.Tính tổng thực lĩnh tháng của tất cả nhân viên trong công ty.
b.In danh sách những nhân viên có mức lương cơ bản thấp nhất.
Gợi ý:
Để lưu trữ thông tin của từng nhân viên ta khai báo một cấu trúc lưu trữ các thông tin
struct NhanVien
{
public string MaNV;
public string Hoten;
public int Namsinh;
public string Phongban;
public int LuongCB;
public int Thuong;
}
class VD
{
static NhanVien[] DS;
a.Thực hiện duyệt từng nhân viên : tính thực lĩnh của từng người sau đó cộng các
thực lĩnh lại với nhau
b.Trước tiên ta tìm mức lương cơ bản thấp nhất bằng cách :
+ Gán một giá trị min bằng lớn vô cùng : int min = int.MaxValue;
+Duyệt qua từng nhân viên nếu nhân viên nào có mức lương nhỏ hơn min thì
gán min bằng mức lương đó
Cuối cùng in ra thông tin của các nhân viên có mức lương bằng min
Lời giản mẫu
using System;
struct NhanVien
{
Bài tập thực hành CTDL & GT Trang 7
public string MaNV;
public string Hoten;
public int Namsinh;
public string Phongban;
public int LuongCB;
public int Thuong;
}
class VD
{
static NhanVien[] DS;
static void Nhap()
{
int n;
Console.Write("Nhap n="); n = int.Parse(Console.ReadLine());
DS = new NhanVien[n];
for (int i = 0; i < DS.Length; ++i)
{
Console.WriteLine("Nhap thong tin NV thu:" + i);
Console.Write("Ma nhan vien:"); DS[i].MaNV = Console.ReadLine();
Console.Write("Ho ten:"); DS[i].Hoten = Console.ReadLine();
Console.Write("Nam sinh:"); DS[i].Namsinh = int.Parse(Console.ReadLine());
Console.Write("Phong ban:"); DS[i].Phongban = Console.ReadLine();
Console.Write("Luong co ban:"); DS[i].LuongCB =
int.Parse(Console.ReadLine());
Console.Write("Thuong:"); DS[i].Thuong = int.Parse(Console.ReadLine());
}
}
static int ThucLinh(int i)
{
return DS[i].Thuong + DS[i].LuongCB;
}
static void HienMin()
Bài tập thực hành CTDL & GT Trang 8
{
int min = int.MaxValue;
for (int i = 0; i < DS.Length; ++i)
if (min > DS[i].LuongCB) min = DS[i].LuongCB;
Console.WriteLine("Danh sach luong cb min");
for (int i = 0; i < DS.Length; ++i)
if (DS[i].LuongCB == min)
Console.WriteLine(DS[i].MaNV + "\t" + DS[i].Hoten + "\t" + DS[i].Namsinh
+ "\t" + DS[i].Phongban + "\t" + DS[i].LuongCB + "\t" + DS[i].Thuong + "\t" +
ThucLinh(i));
}
static void TongLuongCTy()
{
int t = 0;
for (int i = 0; i < DS.Length; ++i)
t += ThucLinh(i);
Console.WriteLine("Tong luong cty:" + t);
}
static void Main()
{
Nhap();
TongLuongCTy();
HienMin();
Console.ReadKey();
}
}
B. Bài tập tự giải
Bài 1: Cho n số nguyên dương a0,a1,a2,...,an-1.
a.Chèn phần tử x vào vị trí k của dãy.
b.Xóa tất cả các số nguyên tố trong dãy.
c.Kiểm tra dãy có tăng dần hay không ?
d.Tìm số nhỏ nhất chia hết cho tất cả các số của dãy.
Bài tập thực hành CTDL & GT Trang 9
e.Tìm các cặp số nguyên tố cùng nhau (hai số nguyên dương được gọi là nguyên
tố cùng nhau nếu ước số chung lớn nhất của chúng bằng 1).
f.Tìm tần số xuất hiện của các số trong dãy.
Gợi ý:
a. Khởi tạo một mảng trung gian tmp có độ dài là (a.Length+1) sau đó gán các
giá trị : tmp[i]=a[i] với i từ 0 ->k-1 (k-1 la vị trí trước vị trí cần chèn)
tmp[k]=giá trị cần chèn
tmp[i+1]=a[i] với i từ k->a.Length-1
Cuối cùng gán mảng a=tmp.
b. Thực hiện duyệt các phần tử trong mảng kiểm tra xem phần tử đó có phải la số
nguyên tố hay không (số nguyến tố là số chỉ chia hết cho 1 và chính nó) :
+ Nếu là số nguyên tố: Duyệt mảng từ trái sang phải. Từ vị trí số nguyên tố đó tiến
hành dời các phần tử về phía trước cho đến khi kết thúc mảng,sau đó giảm kích
thước mảng
+ Ngược lại,chuyển sang kiểm tra phần tử kế tiếp
c. Duyệt qua các phần tử a[i] của mảng với i từ 0->n-2, tại mỗi phần tử kiểm tra
xem nó có nhỏ hơn các phần tử a[j] kế tiếp không với j=i+1->n-1
+ Nếu không, thoát khỏi vòng lặp trả ra giá trị False (không tăng dần)
+ Ngược lại, kiểm tra phần tử a[i] kế tiếp
d. Đầu tiên ta xây dựng một hàm BCNN(int x,int y) tính BCNN của 2 số. Sau đó
ta khởi tạo giá trị int bc = 1 rồi thực hiện tính BCNN của bc với từng phần tử của
mảng, kết quả thu được đưa vào bc
e. Xây dựng một hàm tìm UCLN của 2 số
Thực hiện duyệt qua từng phần tử a[i] của mảng với i từ 0->n-2, với mỗi phần tử
ta tìm UCLN của phần tử đó với từng phần tử a[j] kế tiếp nó với j từ i+1 -> n-1 .
nếu UCLN =1 ta in ra cặp a[i],a[j] đó.
f. Ta tạo 2 mảng mới: mảng b chứa các phần tử không trùng nhau của mảng a,
mảng c chứa tần số xuất hiện của các số tương ứng ở mảng b ( khởi tạo các phần
tử của c =0)
Khởi tạo b[0]=a[0]. Duyệt qua các phần tử của mảng a:
+Nếu a[i] = b[i] thì c[i] =c[i]+1
+Nếu a[i] !=b[i] thì b[i+1]=a[i]
Bài tập thực hành CTDL & GT Trang 10
Bài 2: Cho ma trận vuông n dòng và n cột; các phần tử là các số nguyên (0≤ n < 100).
Viết các hàm thực hiện các yêu cầu sau:
a.Tính tổng tất cả các phần tử của ma trận.
b.Tìm giá trị dương nhỏ nhất của ma trận.
c.Tính tổng các phần tử nằm trên đường chéo phụ.
d.Kiểm tra xem các phần tử nằm trên đuờng chéo chính có tăng dần hay không ?
(theo chiều từ góc trên bên trái xuống góc dưới bên phải).
Gợi ý:
a. Khởi tạo 1 giá trị t=0.Sau đó duyệt qua từng phần tử của ma trận , thực hiện
cộng t với từng phần tử trong ma trận:
b. Khởi tạo giá trị min bằng dương vô cùng int min = int.MaxValue;
Thực hiện duyệt qua các phần tử của ma trận,nếu thấy phần tử đó nhỏ hơn
min và lớn hơn 0 thì gán vào min
c.Ta nhận thấy rằng các phần tử a[i,j] thuộc đường chéo chính của ma trận cấp n
thì có j=(n-1)-i với i ,j chạy từ 0 ->n-1
Để tính tổng các phần tử đường chéo phụ ta duyệt qua các phần tử a[i,j] của
ma trận nếu phần tử nào thỏa mãn j=(n-1)-i thì ta thực hiện cộng các phần tử đó lại
d.Ta thấy trong ma trận cấp n thì số phần tử trên đường chéo chính la n.
+ Đầu tiên , ta thực hiện việc khai báo một mảng một chiều []tg có n phần tử chứa
các phần tử a[i,j] trên đường chéo chính của ma trận(với i=j) theo chiều từ góc bên
trái phía trên đến bên phải phía dưới
+ Duyệt qua các phần tử tg[i] của mảng tg với i từ 0->n-2, tại mỗi phần tử kiểm tra
xem nó có nhỏ hơn các phần tử tg[j] kế tiếp không với j=i+1 ->n-1
*Nếu không ,thoát khỏi vòng lặp trả ra giá trị False (không tăng dần)
*Ngược lại,kiểm tra phần tử tg[i] kế tiếp
Bài 3: Cho ma trận vuông n dòng n cột; mỗi phần tử của ma trận là một phân số (giả thiết
rằng tử số và mẫu số của các phân số này là các số nguyên). Hãy thực hiện các yêu cầu
sau:
a.Tìm phân số có giá trị nhỏ nhất nằm trong khoảng.(0;1).
b.Đếm số lượng phân số nằm trong ma trận tam giác trên có giá trị nằm trong
khoảng (0,1)
Bài tập thực hành CTDL & GT Trang 11
c.Sắp xếp các phân số trong ma trận tăng dần từ trái qua phải và từ trên xuống
dưới.
Gợi ý:
Để lưu trữ thông tin của từng phần tử phân số ma trận ta khai báo một trường lưu
trữ các thông tin của một phân số(tử số và mẫu số)
struct Phanso
{
public int Tu;
public int Mau;
}
class VD
{
static Phanso[] DS;
a.Khởi tạo giá trị nhỏ nhất double min = 1 .
+Thực hiện duyệt qua các phần tử của ma trận.Tại mỗi phần tử thực hiện so sánh
giá trị của tử số chia cho mẫu số với min ,nếu nhỏ hơn min va lớn hơn 0 thì gán
min bằng giá trị đó:
+Hiện ra phần tử có giá trị tử số chia mẫu số bằng min .Dùng một biến đếm int
d=0 đếm các giá trị bằng min,nếu ra khỏi vòng lặp mà d=0 ta kết luận không có
phân số cần tìm
b.Ta thấy các phần tử nằm trong ma trận tam giác trên thì luôn có vị trí hàng nhỏ
hơn vị trí cột (i<j) .Từ đó ta thực hiện việc đếm các phần tử của mảng o vi trí
DS[i,j] thỏa mãn i<j và có giá trị Tử chia cho mẫu trong khoảng (0,1):
c.Thực hiện duyệt các phần tử DS[i,j] của mảng với i từ 0->n-1 và j từ 0->n-2,
Với mỗi phần tử ta so sánh với từng phần tử kế tiếp nó nếu phần tử kế tiếp có giá
trị nhỏ hơn thì thực hiện đổi chỗ 2 phần tử đó với nhau:
Bài 4: Cho một danh sách lưu trữ thông tin về các nhân viên trong một công ty, thông tin
gồm :
- Mã nhân viên
- Họ và tên
- Năm sinh (số nguyên)
- Phòng ban
Bài tập thực hành CTDL & GT Trang 12
- Lương cơ bản (số nguyên)
- Thưởng (số nguyên)
- Thực lĩnh (số nguyên, trong đó thực lĩnh = lương cơ bản + thưởng )
Hãy thực hiện các yêu cầu sau:
a.Tính tổng thực lĩnh tháng của tất cả nhân viên trong công ty.
b. In danh sách những nhân viên có mức lương cơ bản thấp nhất.
c. Đếm số lượng nhân viên có mức thưởng >= 1200000.
d. In danh sách các nhân viên tăng dần theo phòng ban, nếu phòng ban trùng nhau
thì giảm dần theo mã nhân viên.
e. Cập nhật tăng lương của tất cả các nhân viên lên 5%
Gợi ý: Để lưu trữ thông tin của từng nhân viên ta khai báo một trường lưu trữ các thông
tin
struct NhanVien
{
public string MaNV;
public string Hoten;
public int Namsinh;
public string Phongban;
public int LuongCB;
public int Thuong;
}
class VD
{
static NhanVien[] DS;

a.Trước hết ta xây dựng một hàm thuclinh(int x) để tính thực lĩnh của một nhân
viên bất kỳ. Sau đó ta duyệt từ đầu đến cuối danh sách nhân viên rồi gọi hàm
thuclinh(i) ra. Khi đó ta sẽ được tổng số thực lĩnh tháng của tất cả các nhân viên
trong công ty.

b.Đầu tiên ta gán một biến min=int.MaxValue(giá trị dương vô cùng).


Bài tập thực hành CTDL & GT Trang 13
+Tiếp theo duyệt qua tất cả các nhân viên kiểm tra xem min>DS[i].LuongCB hay
không?

-Nếu thoả mãn min = DS[i].LuongCB

-Ngược lại,tiếp tục duyệt các nhân viên tiếp theo.

+Khi kết thúc vòng lặp kiểm tra đó ta sẽ tìm được danh sách những người có mức
lương cơ bản thấp nhất.

+Tiếp tục

c. +Khởi tạo biến dem=0,duyệt qua các phần tử của danh sách. Tại mỗi phần tử
kiểm tra xem DS[i].Thuong >= 1200000 nếu thoả mãn thì dem++.

d. Duyệt qua tất cả các phần tử trong mảng. Tại mỗi phần tử ta thực hiện in ra các
thông tin của nhân viên đó. Riêng ở phần thông tin về lương thì ta tăng thêm
5%nữa
Bài 5:Viết chương trình tạo tập tin văn bản có tên là “BANGSO.INP” có cấu trúc như
sau:
-Dòng đầu tiên ghi hai số m và n (m, n là các số nguyên dương nhập từ bàn phím)
-Trong m dòng tiếp theo mỗi dòng ghi n số nguyên ngẫu nhiên trong phạm vi từ 0
đến 1000 (các số cách nhau ít nhất một dấu cách)
Hãy thực hiện các công việc sau:
a.Hãy cho biết chỉ số các dòng có chứa số nguyên tố (giả thiết các dòng trong tập
tin văn bản được đánh số từ 0 đến m-1).
b.Xoay vòng các cột qua phải một vị trí (cột 0 sẽ qua cột 1, cột 1 qua cột 2,... cột
n-1 về cột 0).
c.Sắp xếp các phần tử tăng dần trên từng cột.
Hãy ghi các kết quả trên vào file văn bản có tên là “BANGSO.OUT”.
Bài 6: Cho mảng vuông n. Hãy tìm phần tử lớn nhất trên mỗi đường chéo song song với
đường chéo chính.
Gợi ý: Xây dựng 2 hàm tính max của đường chéo thuộc ma trận đường chéo trên có phần
tử đầu tiên thuộc cột jvà max của đường chéo thuộc ma trận đường chéo dưới có phần tử
đầu tiên thuộc hàng i:
Bài tập thực hành CTDL & GT Trang 14
*Với đường chéo thuộc ma trận đường chéo trên.Khởi tạo giá trị
max=int.MinValue.Duyệt qua các phần tử của mảng tại mỗi phần tử a[i ,j] có j-i =địa chỉ
cột nếu a[i ,j]>max gán max =a[i,j].
* Với đường chéo thuộc ma trận đường chéo dưới.Khởi tạo giá trị
max=int.MinValue.Duyệt qua các phần tử của mảng tại mỗi phần tử a[i ,j] có i-j =địa chỉ
hàng nếu a[i ,j]>max gán max =a[i,j].
Tại hàm Main() thực hiện:
+Duyệt qua số cột của ma trận ,tại mỗi cột gọi hàm tính max chéo trên của cột đó va in ra
+Duyệt qua số hang của ma trận ,tại mỗi hang gọi hàm tính max chéo dưới của hàng đó
và in ra
C. Bài tập làm thêm
Bài 1: Cho mảng một chiều gồm n tọa độ điểm (giả sử hoành độ và tung độ của các
điểm là các số nguyên).
a.Hãy tìm một điểm trong mảng xa gốc tọa độ nhất.
b.Hãy tìm tọa độ hai điểm gần nhau nhất.
c.Hãy xác định tọa độ của hình chữ nhật nhỏ nhất bao hết cả n điểm trên (tọa độ
góc trên bên trái và tọa độ góc dưới bên phải của hình chữ nhật).
Ví dụ n = 5 và tọa độ 5 điểm là: (0,0); (0,3); (3,3); (4,1); (4,4).
Thì kết quả câu a là điểm (4,4), kết quả câu b là (3,3) và (4,4), kết quả câu c là
(0,4); 4(,0).
Bài 2: Viết chương trình tạo một tập tin văn bản có tên là “DAYSO.INP” có cấu trúc như
sau: -Dòng đầu tiên ghi n (n là số nguyên dương nhập từ bàn phím).
-Trong các dòng tiếp theo ghi n số nguyên ngẫu nhiên trong phạm vi từ 1 đến
10000, mỗi dòng 10 số (các số cách nhau ít nhất một dấu cách).
Hãy thực hiện các công việc sau đây:
a.Tìm giá trị lớn nhất của các số trong tập tin DAYSO.INP.
b.Đếm số lượng số chẵn, số lượng số lẻ trong tập tin DAYSO.INP.
c.Hãy đếm số lượng số nguyên tố, số chính phương, số hoàn hảo, số Amstrong
trong tập tin DAYSO.INP.
Hãy ghi kết quả của các câu a,b,c trên vào tập tin văn bản có tên là “DAYSO.OUT”.
Bài tập thực hành CTDL & GT Trang 15
Bài 3: Một đơn vị quan tâm về dân số lặp một bản thống kê số lượng người sinh trong
từng năm, kể từ năm 1920 đến 1970 và lưu trữ bảng đó trong máy tính tính bằng một
mảng một chiều N[1920..1970] với N[k] có giá trị bằng số người sinh trong năm k.
Hãy viết giải thuật thực hiện:
a. In ra những năm mà không có người nào được sinh ra
b. Tính số lượng những năm mà số người sinh ra không quá 10
c. Tính số người trên 50 tuổi tính đến năm 1985.
Bài tập thực hành CTDL & GT Trang 16

BÀI THỰC HÀNH 2. ĐỘ PHỨC TẠP TÍNH TOÁN


* Mục tiêu
Lựa chọn thuật toán tốt để tiết kiện được thời gian, không gian nhớ tốt nhất cho
mỗi bài toán
* Yêu cầu:
A. Bài tập mẫu
Bài 1: Giả sử n ≥ 0 và x là số thực.Hãy tính giá trị của biểu thức sau đây.

Gợi ý:
Algorithms1: O(N2)
double s=1;
for (int i=1;i<=n;i++)
s=s+pow(x,i)/giaithua(i);// giaithua(i) = i!=1.2.3….i
Độ phức tạp theo cách này là O(N2), tuy nhiên chương trình không thể thực hiện
được khi n lớn; chẳng hạn n =100 - do phép tính giai thừa của n không thể thực
hiện..
Algorithms2: O(N2)
double s=1,p;
for (int i=1; i<=n;i++)
{
p=1;
for (int j=1; j<=i;j++)
p=p*x/j;
s=s+p;
}
Độ phức tạp theo cách này vẫn là là O(N2), tuy nhiên chương trình đã không
cần tính giai thừa của n.
Algorithms3: O(N) – độ phức tạp tuyến tính
double s=1,p=1;
for (int i=1;i<=n;i++)
{
Bài tập thực hành CTDL & GT Trang 17
p=p*x/i;
s=s+p;
}
Lời giản mẫu
using System;
class VD
{
static void Main()
{
double s = 1, p = 1, x;
int n;
Console.Write("Nhap n="); n = int.Parse(Console.ReadLine());
Console.Write("Nhap x="); x = double.Parse(Console.ReadLine());
for (int i = 1; i <= n; i++)
{
p = p * x / i;
s = s + p;
}
Console.WriteLine("Ket qua:" + s);
Console.ReadKey();
}
}

Bài 2: Cộng hai số nguyên lớn a và b, trong đó số a có m chữ số và số b có n chữ số.


Số nguyên lớn ở đây là số có thể có đến vài trăm chữ số. Để lưu trữ các số nguyên
lớn này ta có thể dùng chuỗi (mỗi ký tự của chuỗi là một chữ số) hoặc dùng mảng một
chiều (mỗi phần tử của mảng một chiều là một chữ số).
Gợi ý :Giả sử có 2 số a,b nhập vào ở dạng xâu so1 và so2
Đầu tiên ta thực hiện chuẩn hóa 2 số như sau : kiểm tra xem xâu nào có độ dài nhỏ
hơn thì thêm 0 vào đầu xâu cho đến khi thu được 2 xâu so1 và so2 có độ dài như nhau
Thực hiện tính tổng :Khai báo một biến nhớ =0; Thực hiện duyệt qua các phần tử
của 2 xâu từ phần tử cuối đến phần tử đầu, tại mỗi phần
Bài tập thực hành CTDL & GT Trang 18
Lời giản mẫu
using System;
using System.Text;
class BaiXau
{
static void chuanHoaSo(ref string so1, ref string so2)
{
StringBuilder s1 = new StringBuilder(so1);
StringBuilder s2 = new StringBuilder(so2);
if (s1.Length > s2.Length)
while (s2.Length < s1.Length)
s2.Insert(0, "0");
else
if (s2.Length > s1.Length)
while (s1.Length < s2.Length)
s1.Insert(0, "0");
so1 = s1.ToString();
so2 = s2.ToString();
}

static string tinhTong(string so1, string so2)


{
string s;
int t, a, b, nho = 0;
int n;
chuanHoaSo(ref so1, ref so2);
StringBuilder kq = new StringBuilder("");
n = so1.Length - 1;
while (n >= 0)
{
a = so1[n] - 48;
b = so2[n] - 48;
Bài tập thực hành CTDL & GT Trang 19
t = a + b + nho;
s = t.ToString();
if (s.Length == 1) s = "0" + s;
nho = s[0] - 48;
kq.Insert(0, s[1]);
n = n - 1;
}
if (nho == 1) kq.Insert(0, "1");
return kq.ToString();
}
static void Main()
{
string so1, so2;
StringBuilder x = new StringBuilder("");
StringBuilder y = new StringBuilder("");
StringBuilder s = new StringBuilder("");
Console.WriteLine("\t\t BAN HAY NHAP VAO HAI SO NGUYEN");
Console.Write(" "); so1 = Console.ReadLine();
Console.WriteLine("+");
Console.Write(" "); so2 = Console.ReadLine();

x.Append('-', so1.Length > so2.Length ? so1.Length : so2.Length);


y.Append(' ', Math.Abs(so1.Length - so2.Length));

s.AppendFormat("\t\t{0}\n\n", "BAN HAY NHAP VAO HAI SO NGUYEN");


if (so1.Length < so2.Length)
s.AppendFormat(" {0}{1}\n", y.ToString(), so1);
else
s.AppendFormat(" {0}\n", so1);
s.AppendFormat("{0}\n", "+");
if (so2.Length < so1.Length)
s.AppendFormat(" {0}{1}\n", y.ToString(), so2);
Bài tập thực hành CTDL & GT Trang 20
else
s.AppendFormat(" {0}\n", so2);
s.AppendFormat(" {0}\n", x.ToString());
s.AppendFormat(" {0}", tinhTong(so1, so2));
Console.Clear();
Console.WriteLine(s);
Console.ReadKey();
}
}

B. Bài tập tự giải


Bài 1: Giả sử n ≥ 0 và x là số thực.Hãy tính giá trị của biểu thức sau đây.

Gợi ý:
Ta nhận thấy (x ^ n)/n!=(x.x.x…x)/(n(n-1)(n-2)…2.1) .Để tính S, khai báo 2 biến
p và s
Duyệt qua các số tự nhiên từ 1->n ,tại mỗi số ta thực hiện tính tích của thương x
và số đó với thương x và các số trước nó (p=p*x/i) sau đó thực hiện cộng dồn các kết
quả đó lại ta được tổng S
Bài 2: Cho dãy n số nguyên a0,a1,...,an-1. Hãy chuyển k phần tử đầu tiên của dãy về cuối
dãy.
Gợi ý: Sử dụng một mảng tg chứa k phần tử và mảng tmp chứa n-k phần tử. Duyệt qua
các phần tử a[i] của mảng (i: 0->n):
+Nếu i < k tg[i]=a[i] (chuyển k phần tử đầu sang tg
+Ngược lai, tmp[j++] =a[i] ( khởi tạo j=0) chuyển các phần tử còn lại sang tmp
Sau đó thực hiện ghép 2 mảng tmp và tg với nhau
Bài 3: Cho dãy n số nguyên {ai, ở đây giả sử i=1..n} Dãy con liên tiếp là dãy mà thành
phần của nó là các thành phần liên tiếp nhau trong {a}, ta gọi tổng của dãy con là tổng tất
cả các thành phần của nó. Tìm tổng lớn nhất trong tất cả các tổng của các dãy con của
{a}. Ví dụ nếu n = 7;
4 –5 6 –4 2 3 -7
Bài tập thực hành CTDL & GT Trang 21
Thì kết quả tổng là 7.
Bài 4. Giả sử n ≥1 và x là số thực. Hãy viết hàm tính giá trị của biểu thức sau đây
(với độ phức tạp tuyến tính):

Gợi ý: Xây dựng một hàm tính biểu thức mẫu với đối số là một số tự nhiên bất kỳ, giả sử
ta có hàm Mau(int n) trả giá trị 1+1/2+…+1/n
Tại hàm Main ta duyệt qua các số tự nhiên i từ 1->n.Tại mỗi số i ta tính tích của (-
1)mũ i với x mũ i và chia cho hàm Mau (với đối số là số i đó), đồng thời tiến hành cộng
dồn các kết quả đó lại được S

C. Bài tập làm them

Bài 1: Cộng hai số nguyên lớn a và b, trong đó số a có m chữ số và số b có n chữ số. Số


nguyên lớn ở đây là số có thể có đến vài trăm chữ số. Để lưu trữ các số nguyên lớn này ta
có thể dùng chuỗi (mỗi ký tự của chuỗi là một chữ số) hoặc dùng mảng một chiều (mỗi
phần tử của mảng một chiều là một chữ số). Tuy nhiên trong hai phương án này thì
phương án dùng mảng một chiều để lưu trữ sẽ có thuật toán tốt hơn.
Bài 2: Tìm số hạng thứ n của dãy Fibonasci (giải quyết khi n là một số lớn – khi đó ta
không thể sử dụng đệ quy và cũng không thể sử dụng mảng để lưu trữ).
Bài 3: Cho dãy n số nguyên a0,a1,...,an-1.Hãy tìm dãy con liên tiếp tăng dài nhất.
Bài 4: Cho dãy n số nguyên a0,a1,...,an-1.Hãy tìm đoạn con dài nhất chứa toàn số 0.
Bài 5: Cho dãy n số nguyên a0,a1,...,an-1.Hãy tìm dãy con tăng chứa nhiều số
Bài tập thực hành CTDL & GT Trang 22

BÀI THỰC HÀNH 3+4. DANH SÁCH NỐI ĐƠN

* Mục tiêu
Hệ thống lại những kiến thức liên quan đến danh sách liên kết và các phép toán
trên nó
* Yêu cầu:
A. Bài tập mẫu
Bài 1: Cho một danh sách liên kết đơn l (l chứa địa chỉ nút đầu tiên của danh sách), mỗi
nút là một số nguyên dương.
a.Đếm xem trong danh sách có bao nhiêu số bằng x ?
b.Tìm phần tử dương nhỏ nhất trong danh sách.
c. Xóa phần tử x xuất hiện đầu tiên trong danh sách.
Gợi ý: Khởi tạo một danh sách toàn cục l
a.Khởi tạo một biến đếm d=0.Duyệt qua từng phần tử của danh sách, nếu phần tử
nào có giá trị là x thì tăng d=d+1;
b.Khởi tạo biến minduong=int.MaxValue .Duyệt qua từng phần tử của danh sách
nếu phần tử nào nhỏ hơn min thì gán bằng min
c.Sử dụng 2 nút p và t ,với p=l ,t trỏ vào nút phía trước nút p.Duyệt qua các phần
tử của danh sách đến khi gặp phần tử có giá trị x .Lúc này p trỏ vào phần tử cần
xóa ,ta xóa nút p bằng cách cho nút t nắm thông tin của nút sau nút p
*Chú ý:
+ Có 3 trường hợp p trỏ vào l (phần tử đầu), p trỏ vào cuối (p=null), p trỏ vào
giữa
+ Để quan sát kết quả ta nhập dữ liệu cho danh sách liên kết l
Lời giản mẫu
using System;
class Node
{
public int info;
public Node link;
}
class DSLKD
Bài tập thực hành CTDL & GT Trang 23
{
static Node l = new Node(); // l dùng để chứa địa chỉ của nút đầu tiên trong danh sách
static void Nhap()
{
Console.WriteLine("Nhap vao day so nguyen");
int i = 0; l = null; Node tg; char kt;
do
{
Console.WriteLine("Nhap so nguyen thu:" + ++i);
tg = new Node();
Console.Write("Info="); tg.info = int.Parse(Console.ReadLine());
tg.link = null;
// bổ sung nút tg vào đầu danh sách l
if (l == null) l = tg;
else { tg.link = l; l = tg; }
Console.WriteLine("Ban nhap tiep C/K"); kt = char.Parse(Console.ReadLine());
} while (char.ToUpper(kt) == 'C');
}
static void Hien()
{
Console.WriteLine("Cac phan tu co trong danh sach");
Node tg = l;
while (tg != null)
{
Console.Write(tg.info + "\t");
tg = tg.link;
}
Console.WriteLine();
}
static int Dem(int x)
{
int d = 0;
Bài tập thực hành CTDL & GT Trang 24
Node tg = l;
while (tg != null)
{
if (x == tg.info) d++;
tg = tg.link;
}
return d;
}
static int MinDuong()
{
if (l == null) throw new Exception("Danh sach rong");
else
{
int m = int.MaxValue;
Node tg = l;
while (tg != null)
{
if (m > tg.info && tg.info >= 0) m = tg.info;
tg = tg.link;
}
return m;
}
}
static void Xoa(int x)
{
Node p = l;
Node t = p;
while (p != null && p.info != x)
{
t = p;
p = p.link;
}
Bài tập thực hành CTDL & GT Trang 25
if (p == null) Console.WriteLine("x khong ton tai trong danh sach");
else
{
if (p == l) l = l.link;
else if (p.link == null) t.link = null;
else t.link = p.link;
}
}
//Ham thuc hien cac chuc nang cua chuong trinh
static void Main()
{
Nhap();
Xoa(3);
Hien();
Console.WriteLine("Min duong nho nhat:" + MinDuong());
Console.ReadKey();
}
}
Bài 2: Hãy viết phần khai báo cấu trúc dữ liệu để mô tả một danh sách liên kết đơn l mà
mỗi phần tử chứa một số nguyên. Viết các hàm thực hiện các yêu cầu sau:
a.Tạo một danh sách l1 chỉ chứa các số chẵn và chia hết cho 3 từ danh sách l.
b.Sắp xếp các phần tử của danh sách theo chiều giảm dần.
Gợi ý: Khởi tạo 1 danh sách toàn cục l
a.Tạo một danh sách l1. Sử dụng một nút tg duyệt qua từng phần tử của danh sách
l nếu có phần tử thỏa mãn là số chẵn và chia hết cho 3 thì ta gán 1 nút tmp =tg và
tmp.link=null và đưa tmp vào danh sách l1 :
+Nếu l1 =null thì l=tmp
+Ngược lại them tmp vào danh sách l1 ( tmp.link=l1;l1=tmp)
b.Thực hiện so sánh từng nút i của danh sách với các nút j ở phía sau nó nếu giá trị
của nút i > nút j ta đổi giá trị của 2 nút cho nhau
Lời giải mẫu
using System;
Bài tập thực hành CTDL & GT Trang 26
class Node
{
public int info;
public Node link;
}
class DSLKD
{
static Node l;
static void Nhap()
{
Console.WriteLine("Nhap vao day so nguyen");
int i = 1; l = null; Node tg; char kt;
do
{
Console.WriteLine("Nhap so nguyen thu:" + i);
tg = new Node();
Console.Write("Info="); tg.info = int.Parse(Console.ReadLine());
tg.link = null;
if (l == null) l = tg;
else { tg.link = l; l = tg; }
Console.WriteLine("Ban nhap tiep C/K"); kt = char.Parse(Console.ReadLine());
} while (char.ToUpper(kt) == 'C');
}
static void Hien(Node l)
{
Console.WriteLine("Cac phan tu co trong danh sach");
Node tg = l;
while (tg != null)
{
Console.Write(tg.info + "\t");
tg = tg.link;
}
Bài tập thực hành CTDL & GT Trang 27
Console.WriteLine();
}
static Node Tach()
{
Node tg = l, tmp, l1 = null;
while (tg != null)
{
if (tg.info % 3 == 0 && tg.info % 2 == 0)
{
tmp = new Node();
tmp.info = tg.info;
tmp.link = null;
//bổ sung nút tmp vào đầu danh sách l1
if (l1 == null) l1 = tmp;
else tmp.link = l1;
l1= tmp;
}
tg = tg.link;
}
return l1;
}
static void SapXep()
{
Node i, j;
i = l;
while (i.link != null)
{
j = i.link;
while (j != null)
{
if (i.info > j.info)
{ int tg = i.info; i.info = j.info; j.info = tg; }
Bài tập thực hành CTDL & GT Trang 28
j = j.link;
}
i = i.link;
}
}
//Ham thuc hien cac chuc nang cua chuong trinh
static void Main()
{
Nhap();
SapXep();
Hien(l);
Node l1 = Tach();
Hien(l1);
Console.ReadKey();
}
}
Bài 2: Dùng danh sách liên kết đơn cộng hai đa thức bất kỳ
R(x) =P(x)+Q(x)
Với
P(x)=a0xn+a1xn-1+…+an
Q(x)=b0xm+b1xm-1+…+bm
Gợi ý: Khai báo danh sách gồm 2 trường số mũ và hệ số để lưu trữ từng phần tử của đa
thức
Khai báo các danh sách toàn cục P,Q, R
Nhập vào danh sách P va Q
Xây dựng một hàm thêm 1 nút tg vào danh sách l:
+Nếu l=null thì l=tg;
+Ngược lại l!=null : Duyệt qua các phần tử của danh sách l bằng một nút p
*Nếu p.số mũ =tg.số mũ thì cộng hệ số của p với hệ số của tg đưa kết quả vào p.hệ số
*Nếu trong ds l không có số mũ = số mũ của tg ta thực hiện chèn tg vào sau nút cuối
cùng có số mũ nhỏ hơn tg ( có 3 th :chèn đầu –chèn cuối –chèn giữa)
Xây dựng hàm tính tổng như sau: khai báo R=null
Bài tập thực hành CTDL & GT Trang 29
Duyệt qua các nút của danh sách chứa đa thức P và Q tại mỗi phần tử ta gọi hàm thêm
nút với tham số truyền vào là ds R và nút đang duyệt
Lời giải mẫu
using System;
class Node
{
public int hs;
public int mu;
public Node next;
}
class DaThuc
{
static Node P, Q, R; // nút P, Q, R lần lượt chứa địa chỉ nút đầu tiên của các danh sách
tương ứng
static void Nhap(out Node l)
{
Console.WriteLine("Nhap thong tin cho da thuc");
int i = 1; l = null; Node tg; char kt;
do
{
Console.WriteLine("Nhapa thong tin cho phan tu thu:" + i);
tg = new Node();
Console.Write("He so:"); tg.hs = int.Parse(Console.ReadLine());
Console.Write("Mu:"); tg.mu = int.Parse(Console.ReadLine());
tg.next = null;
Add(ref l, tg); // bổ sung nút tg vào vị trí thích hợp theo số mũ
Console.WriteLine("Ban nhap tiep C/K"); kt = char.Parse(Console.ReadLine());
} while (char.ToUpper(kt) == 'C');
}
static void Add(ref Node l, Node tg)
{
if (l == null) l = tg;
Bài tập thực hành CTDL & GT Trang 30
else
{
Node p = l, t = l;
while (p != null && p.mu < tg.mu)
{
t = p;
p = p.next;
}
//Neu mu da co
if (p != null && tg.mu == p.mu)
p.hs = p.hs + tg.hs;
else
//Them vao dau
if (p == l)
{
tg.next = l;
l = tg;
}
//Them vao sau
else if (p == null && t.next == null) t.next = tg;
//Them vao giua
else
{
tg.next = p;
t.next = tg;
}
}
}
static void Hien(Node l)
{
Node tg = l;
string kq = "";
Bài tập thực hành CTDL & GT Trang 31
while (tg != null)
{
if (tg.mu == 0) kq = kq + tg.hs;
else if (tg.mu == 1) kq = kq + (tg.hs > 0 ? "+" : "-") + tg.hs + "X";
else kq = kq + (tg.hs > 0 ? "+" : "-") + tg.hs + "X^" + tg.mu;
tg = tg.next;
}
Console.WriteLine(kq);
}
static void Tong()
{
R = null;
Node tmp = P, tg;
while (tmp != null)
{
tg = new Node();
tg.hs = tmp.hs;
tg.mu = tmp.mu;
tg.next = null;
Add(ref R, tg);
tmp = tmp.next;
}
tmp = Q;
while (tmp != null)
{
tg = new Node();
tg.hs = tmp.hs;
tg.mu = tmp.mu;
tg.next = null;
Add(ref R, tg);
tmp = tmp.next;
}
Bài tập thực hành CTDL & GT Trang 32
}
//Ham thuc hien cac chuc nang cua chuong trinh
static void Main()
{
Console.WriteLine("Nhap thong tin da thuc P");
Nhap(out P);
Hien(P);
Console.WriteLine("Nhap thong tin da thuc Q");
Nhap(out Q);
Hien(Q);
Console.WriteLine("Tong hai da thuc");
Tong();
Hien(R);
Console.ReadKey();
}
}
B.Bài tập tự giải
Buổi 1
Bài 1: Cho một danh sách liên kết đơn l, mỗi nút là một số nguyên dương.
a.Tìm phần tử lớn nhất danh sách l.
b.Tính tổng các phần tử của danh sách l.
c.Đếm xem trong danh sách l có bao nhiêu số nguyên tố ?
d.Đếm xem trong danh sách có bao nhiêu số âm ? bao nhiêu số bằng 0 ? bao nhiêu
số dương ?
e.Đếm xem trong danh sách có bao nhiêu số bằng x ?
f.Tìm phần tử dương nhỏ nhất trong danh sách.
Gợi ý: Khai báo danh sách toàn cục l
a.Khởi tạo giá trị max=int.MinValue.Duyệt qua các phần tử của danh sách tại mỗi
phần tử so sánh với max, nếu lớn hơn max gán max bằng phần tử đó
b.Khởi tạo t=0.Duyệt qua các phần tử của danh sách và thực hiện cộng dồn các giá
trị của nó vào t
Bài tập thực hành CTDL & GT Trang 33
c.Xây dựng một hàm kiểm tra một số bất kì có phải là số nguyên tố không ( giả sử
hàm KTNT(int x) .Sau đó duyệt qua các phần tử của danh sách với mỗi phần tử ta
gọi hàm KTNT( giá trị phần tử) nếu trả giá trị True ta tăng biến đếm lên 1 ( với
khởi tạo biến đếm =0)
d.Khởi 3 biến đếm âm ,dương và 0 bằng 0.Duyệt qua các phần tử của danh sách
tại mỗi phần tử tiến hành kiểm tra:
+Nếu có giá trị <0 tăng biến đếm âm lên 1
+Nếu có giá trị >0 tăng biến đếm dương lên 1
+Nếu có giá trị =0 tăng biến đếm 0 lên 1
e.Duyệt qua các phần tử của danh sách ,tại mỗi phần tử nếu có giá trị = x ta thực
hiện tăng giá trị biến đếm lên 1 (với khởi tạo biến đếm =0)
f.Khởi tạo giá trị minduong=int.MaxValue .Thực hiện duyệt qua các phần tử của
danh sách ,nếu có giá trị nhỏ hơn minduong và lớn hơn 0 thì gán min= giá trị đó
Bài 2: Cho một danh sách liên kết đơn l, mỗi nút là một số nguyên dương.
a.Xóa phần tử đầu tiên trong danh sách.
b.Xóa phần tử cuối cùng trong danh sách.
c.Xóa một phần tử được trỏ bởi con trỏ q.
d.Xóa một phần tử ngay trước phần tử được trỏ bởi con trỏ q.
e.Xóa một nút có giá trị k.
Gợi ý: Khai báo danh sách toàn cục l
a.Muốn xóa phần tử đầu ta cho phần tử đầu nắm thông tin của phần tử kế tiếp
nó(l=l.next)
b.Để xóa phần tử cuối ta cho phần tử đó trỏ vào null
c.Xóa con trỏ q:
+Nếu q trỏ vào đầu danh sách ta thực hiện xóa đầu ( l=l.next –câu a)
+Nếu q trỏ vào cuối danh sách ta thực hiện xóa cuối ( cho phần tử cuối trỏ vào
null –câu b)
+Còn lai, ta cho con trỏ t ( với t trỏ vào phần tử phía trước q) nắm bắt thong tin
của phần tử mà q nắm bắt (t.next =q.next)
d.Sử dụng 2 con trỏ t và p ( t trỏ vào phần tử trước p,p=l) duyệt qua các phần tử
của danh sách đến khi p.next =q.Sau khi thoát khỏi vòng lặp p là nút cần xóa:
+Nếu p=l thực hiện xóa đầu
Bài tập thực hành CTDL & GT Trang 34
+Nếu q=null thực hiện xóa cuối
+Ngược lại,cho t nắm bắt thông tin của q
e.Sử dụng 2 con trỏ t và p( t trỏ vào phần tử trước p,p=l) duyệt qua các phần tử của
danh sách đến khi p=null hoặc giá trị của p =k.Thoát khỏi vòng lặp p trỏ vào nút
có giá trị k và t trỏ vào nút trước p
+Nếu p=l thực hiện xóa đầu
+Nếu p.next=null thực hiện xóa cuối
+Ngược lại,cho t nắm bắt thông tin của nút sau p (t.next =p.next)
Bài 3: Cho một danh sách liên kết đơn l, mỗi nút là một số nguyên dương.
a.Hãy tạo danh sách l1 chỉ chứa các số nguyên tố từ danh sách l.
b.Tách danh sách l thành 2 danh sách: một danh sách chứa toàn số chẵn, một
danh sách chứa toàn số lẻ.
Gợi ý: Khởi tạo danh sách toàn cục l
a.Xây dựng hàm kiểm tra 1 số bất kỳ có là số nguyên tố không giả sử là hàm
KTNT(int x).Khai báo danh sách l1=null và con trỏ p=l1.Duyệt qua các phần tử
của danh sách ,tại mỗi phần tử gọi hàm KTNT(phần tử đó) nếu giá trị trả về True
ta đưa nó vào danh sách l1 ( p.info=giá trị phần tử đó và p..next=null)
b.Khởi tạo 2 danh sách l1=null , l2=null và 2 con trỏ p=l1 ,q=l2 .Duyệt qua các
phần tử của danh sách l:
Nếu phần tử đó là chẵn đưa vào l1: p.info=giá trị phần tử đó và p.next=null
Nếu phần tử đó là lẻ đưa vào l2: q.info=giá trị phần tử đó và q.next=null
Bài 4: Cho một danh sách liên kết đơn l, mỗi nút là một số nguyên dương.
a.Trộn hai danh sách tăng dần thành một danh sách tăng dần.
b.Sắp xếp các phần tử của L giảm dần theo phương pháp chọn trực tiếp
Gợi ý: Khai báo danh sách toàn cục l
a.Trước tiên ta xây dựng hàm thêm 1 nút tg vào danh sách l sao cho danh sách
tăng dần:
+Nếu l=null thì l=tg ,l.next=null
+Ngược lại .Sử dụng 1 con trỏ p duyệt qua các phần tử của l cho đến khi danh
sách rỗng hoặc gặp giá trị lớn hơn giá trị nút tg .Sau vòng lặp:
*Nếu p=null
Bài 5: Viết chương trình thực hiện các yêu cầu sau:
Bài tập thực hành CTDL & GT Trang 35
a.Khai báo cấu trúc dữ liệu của một danh sách liên kết đơn các tỉnh. Biết rằng
thông tin của mỗi tỉnh bao gồm: tên tỉnh, diện tích, dân số
b.Cài đặt các thao tác cơ bản cho danh sách liên kết đơn các tỉnh (thêm, sửa , xóa,
duyệt).
c.Tính tổng diện tích của tất cả các tỉnh trong danh sách liên kết
d.Tìm địa chỉ của node chứa tính có diện tích lớn nhất trong danh sách liên kết.
e.Tìm một tỉnh có dân số lớn nhất
f.Sắp xếp danh sách tăng dần theo diện tích.
Gợi ý : Khai báo danh sách toàn cục l
a.Khai báo lớp Node bao gồm 3 trường thông tin giá trị tên tỉnh (string) ,diện tích
và dan số (int) và địa chỉ nút kế tiếp
b.Các thao tác :
*Thêm :Khai báo nút tg chứa thông tin tỉnh cần thêm và tg.next=null. Sử dụng con
trỏ t và p=l ( t trỏ vào nút phía trước p) duyệt qua các phần tử của danh sách cho
đến vị trí cần thêm :
+Nếu p=null báo lỗi vị trí
+Ngược lại ,cho t nắm thông tin của tg và tg nắm thông tin của p
*Sửa: Sử dụng một nút tg =l duyệt qua các phần tử của danh sách đến khi gặp vị
trí cần sửa
+Nếu tg=null báo lỗi
+Ngược lại,gán các trường thông tin giá trị của nút tg trỏ vào bằng các giá trị mới
*Xóa: Khai báo nút tg chứa thông tin tỉnh cần thêm và tg.next=null. Sử dụng con
trỏ t và p=l ( t trỏ vào nút phía trước p) duyệt qua các phần tử của danh sách cho
đến vị trí cần xóa :
+Nếu p=null báo lỗi
+Ngược lại, xóa nút p :
Nếu p=l, thực hiện xóa nút đầu
Ngược lại, cho t nắm thông tin của nút sau p (xóa giữa và cuối)
c. Sử dụng con trỏ tg =l duyệt qua các phần tử trong danh sách và thực hiện cộng
dồn giá trị thông tin Diện tích của từng phần tử vào một biến tổng
d. Khai báo một biến max có giá trị là giá trị Diện tích của phần tử đầu tiên trong
danh sách và một biến vị trí =0. Thực hiện duyệt qua các phần tử trong danh sách
Bài tập thực hành CTDL & GT Trang 36
nếu phần tử nào có giá trị diện tích lớn hơn max thì gán bằng max và tăng biến vị
trí lên 1
e. Khai báo một biến max có giá trị là giá trị Dân số của phần tử đầu tiên trong
danh sách. Thực hiện duyệt qua các phần tử trong danh sách nếu phần tử nào có
giá trị dân số lớn hơn max thì gán bằng max. Để hiện ra tỉnh có dân số max ta
duyệt qua các phần tử của danh sách nếu phần tử nào có giá trị dân số = max thì
hiện các thông tin của phần tử đó ra màn hình
f. Thực hiện duyệt qua các phần tử của danh sách tại mỗi phần tử so sánh giá trị
diện tích của phần tử đó với từng phâng tử kế tiếp nó nếu lớn hơn thực hiện đổi
chỗ toàn bộ các giá trị (tên tỉnh –dân số - diện tích)của 2 nút đó với nhau

Buổi 2
Bài 6: Cho danh sách nối đơn, có con trỏ LIST trỏ tới nút đầu tiên của danh sách này.
Hãy viết giải thuật thực hiện:

a. Cộng thêm một số A vào số đang chứa tại trường INFO của mỗi nút.
b. Đếm số lượng các nút có trong danh sách đó.
c. Đếm số lượng các nút đang chứa số dương thuộc danh sách đó (giả sử các số chứa
trong mỗi nút là số đại số khác không).
Bài 7: Hãy viết phần khai báo cấu trúc dữ liệu để mô tả một danh sách liên kết đơn mà
mỗi phần tử chứa một số nguyên. Viết các hàm thực hiện các yêu cầu sau:
a.Đếm các số nguyên tố của danh sách
b.Tạo một danh sách l1 chỉ chứa các số chẵn và chia hết cho 3 từ danh sách l.
c.Sắp xếp các phần tử của danh sách theo chiều giảm dần.

Gợi ý:
Trước tiên ta khai báo 1 lớp node gồm trường lưu trữ thông tin ,trường chứa địa
chỉ và nhập một danh sách mới:
class node
{
public int x;
public node link;
}
Bài tập thực hành CTDL & GT Trang 37
Sau đó với mỗi yêu cầu ta thực hiện như sau:
a. Ta phải xây dựng một hàm kiểm tra xem một số bất kỳ nào đó có phải là số
nguyên tố không giả sử hàm đó là bool kiemtraNT(int n). Sau đó chúng ta duyệt
qua tất cả các phần tử của danh sách, với mỗi phần tử tg.info chúng ta gọi hàm
kiemtraNT ra thực hiện nếu kiemtraNT (tg.info) trả về giá trị là True nghĩa là
phần tử tg.info chúng ta đang kiểm tra là số nguyên tố ta tăng biến đếm khởi tạo
bằng 0 lên 1. Số nguyên tố bằng giá trị của biến đếm đó.
b. Ta khai báo mới danh sách mới l1=null,một node c=null và node tg=l Duyệt các
phần tử trong danh sách tg cho đến khi tg=null,kiểm tra nếu phần tử đó chia hết
cho 3 và chia hết cho 2(số chia hết cho 2 là số chẵn) thì ta thêm phần tử đó vào
l1 bằng cách:khai báo 1 node tmp mới để chứa phần tử đang xét,rồi kiểm tra,nếu
l1=null thì ta gán l1=tmp,ngược lại c.link = tg;c = tmp;
c. Khai báo node i,j,gán i=l,ta sử dụng 2 vòng lặp while. Trong khi i.link!=null ta
gán j=i.link,rồi duyệt j cho đến khi j=null. Kiểm tra giá trị của phần tử i.info nếu
lớn hơn j.info thì ta hoán đổi giá trị của hai phần tử này qua một biến trung gian
nào đó. Kết thúc công việc đó ta được 1 danh sách giảm dần.
Bài 8: Hãy khai báo kiểu danh sách liên kết đơn có tên LIST mà mỗi phần tử chứa các
thông tin về một sinh viên bao gồm một số nguyên dương chỉ mã số sinh viên và một
chuỗi ký tự cho biết họ và tên của sinh viên.
Thực hiện các công việc sau:
a.Xây dựng hàm sắp xếp danh sách sinh viên theo thứ tự tăng dần của mã số sinh
viên.
b.Xây dựng hàm thêm một sinh viên mới vào danh sách(đã có thứ tự) sao cho vẫn
bảo đảm thứ tự tăng dần của mã số.
Gợi ý: Đầu tiên ta khai báo cấu trúc sinhvien gồm họ tên và mã sinh viên,sau đó khai 1
lớp node gồm thành phần dữ liệu info kiểu sinhvien,thành phần lưu trữ địa chỉ phần tử kế
tiếp trong danh sách.
a.Khai báo 2 node mới i=l(l là danh sách toàn cục)và j Trong khi phần tử tiếp theo
của i chưa bằng null thì ta sẽ gán j=i.next(nghĩa là j sẽ chạy trước i,j bằng phần tử kế
tiếp của i),trong khi j khác null thì kiểm tra nếu giá trị của i.masv.info<j.masv.info
thì đổi chỗ i.masv.info và j.masv.info với nhau qua 1 biến trung gian nào đó.
Bài tập thực hành CTDL & GT Trang 38
b. Khai báo 2 node mới p=l,t=l. Trong khi p.masv.info<mã sinh viên mới nhập vào
và p chưa rỗng thì gán t=p. Khởi tạo 1 node tg mới gán tg.masv.info=mã sinh viên
mới nhập vào. Kiểm tra nếu l rỗng gán l=tg luôn,nếu p=l thì thêm vào đầu gán phần
tử tiếp theo của tg:tg.next=l;l=tg.,nếu p rỗng thì them vào sau gán t.next=tg,ngược
lại tất cả điều trên ta thêm vào giữa.
Bài 9: Cho một danh sách liên kết, mỗi nút chứa một số nguyên.
a.Thêm một phần tử có giá trị x vào đầu danh sách
b.Chỉ giữ lại một giá trị trong số các giá trị giống nhau.
c.Kiểm tra xem danh sách có được sắp xếp tăng dần hay không?
d.Đảo ngược danh sách.
e.Sắp xếp các số chẵn trong danh sách theo thứ tự tăng, sắp xếp các số lẻ
theo thứ tự giảm dần, các số 0 giữ nguyên vị trí.
Gợi ý: Trước tiên ta khai báo 1 lớp node gồm trường lưu trữ thông tin ,trường chứa địa
chỉ và nhập một danh sách mới, sau đó với mỗi yêu cầu ta thực hiện như sau:
a.Kiểm tra danh sách nếu rỗng thì ta gán l=tg nghĩa là đó là phần tử đầu tiên của
danh sách,ngược lại ta cho địa chỉ tiếp theo của tg nắm bắt lấy l(tg.next=l)và gán
l=tg.
b.Đầu tiên kiểm tra xem danh sách nếu rỗng(l=null) thì in ra thông báo danh sách
rỗng. Ngược lại khởi tạo node i=l,j,ta sử dụng 2 vòng lặp. Thực hiện các công việc
sau cho đến khi j=l:gán j=i.next,trong khi j!=l thì kiểm tra nếu i.info=j.info thì xóa
j đi.
c. Kiểm tra xem danh sách nếu rỗng(l=null) thì in ra thông báo danh sách rỗng.
Ngược lại khai báo node tg=l,kiểm tra xem phần tử bên phải của tg không bằng
l(tg.next!=l) và giá trị của tg lớn hơn giá trị của phần tử tiếp theo(tg.next.info) thì
gán ok=false(ok là biến theo dõi tính đúng hay sai) rồi thoát khỏi vòng lặp. Nếu
ok=true thì dãy tăng dần ngược lại là dãy k tăng dần.
d. Đầu tiên ta đếm xem danh sách đó có bao nhiêu phần tử. Sau đó ta duyệt ngược
danh sách đó cho 1 vòng lặp lồng trong 1 vòng lặp khác. Vòng lặp bên trong chạy
sau vòng lặp ngoài 1 phần tử ta hoán vị 2 phần tử kế nhau cho đến khi nào hết
danh sách thì thôi.
e.Khởi tạo thêm 2 node l1,l2 rỗng. duyệt hết danh sách nếu phần tử chia hết cho 2
thì them vào danh sách l1,không chia hết them vào danh sách l2,bằng 0 thì giữ
Bài tập thực hành CTDL & GT Trang 39
nguyên. Sau đó sắp xếp tăng dần cho l1,giảm dần cho l2. Cuối cùng ta chèn l1,l2
vào l.
C. Bài tập làm thêm
Bài 1:.Viết chương trình thực hiện các yêu cầu sau:
a.Khai báo cấu trúc dữ liệu của một danh sách liên kết đơn để lưu tọa độ các đỉnh
của một đa giác lồi trong mặt phẳng OXY.
b.Tính chu vi của đa giác.
c.Tính diện tích của đa giác.
Gợi ý: Tạo một lớp node gồm 2 trường info lưu trữ thông tin tung độ, hoành độ và 1 con
trỏ chỉ đến cấu trúc node để lưu tọa độ các đỉnh của một đa giác lồi. Sau đó thực hiện
việc nhập danh sách với các tọa độ của các đỉnh đa giác đó.
a. Tính độ dài vectơ các cạnh của đa giác. Sau đó áp dụng công thức tính chu vi
đa giác bằng tổng các cạnh của đa giác đó.
b. Diện tích của 1 đa giác lồi áp dụng công thức s=1/2(x1.y2-x2.y1+x2y3-
x3y2+….+(xn-1)yn-xn(yn-1)+xny1-x1yn).
Ghi chú: Đa giác lồi (Convex polygon): toàn bộ đa giác nằm về một phía của đường
thẳng chứa cạnh bất kỳ nào của đa giác.
Bài 2: Cho một danh sách nối đơn có con trỏ P trỏ tới nút đầu tiên của nó. Biết rằng danh
sách này không rỗng. Hãy viết giải thuật:
a. Bổ sung một nút mới vào sau nút có địa chỉ T (hay : con trỏ T trỏ tới nó) đang có
trong danh dách đó.
b. Bổ sung một nút mới vào trước nút trỏ bởi T đang có trong danh sách đó.
Biết rằng phần thông tin dành cho nút mới đang chứa trong ô có địa chỉ là X.
c. Bổ sung một nút vào vị trí thứ k của danh sách.
d. Tìm xem trong danh sách có nút mang giá trị là Y hay không? Nếu thấy thì trả ra
địa chỉ của nút đó, ngược lại trả ra giá trị null.
Bài 3: Cho một danh sách nối đơn có con trỏ Q trỏ tới nút đầu tiên của danh sách. Biết
rằng danh sáhc này không rỗng. Hãy viết giải thuật:
a. Loại bỏ nút đầu tiên của danh sách.
b. loại bỏ nút trỏ bởi T đang có trong danh sách. Biết rằng T không phải là đại chỉ nút
đầu tiên và cũng không phải là đại chỉ nút cuối cùng.
c. Loại bỏ nút thứ k của danh sách.
Bài tập thực hành CTDL & GT Trang 40
Bài 4: Cho ba danh sách nối đơn, lần lượt có nút đầu tiên được trỏ bởi L1, L2, L3. Hãy
viết giải thuật:
a. Ghép danh sách L2 vào sau danh sách L1 và cho L trỏ tới danh sách tổng hợp.
b. Ghép danh sách L3 vào trước L2 và L1 vào sau L2 và cho L trỏ tới danh sách tổng
hợp.
Giả sử rằng cả ba danh sách này đều không rỗng.
Bài 5: Cho một danh sách nối đơn có con trỏ P trỏ tới nút đầu tiên của nó, danh sách này
không rỗng. Cho con tỏ Q trỏ tới một nút đang có trong danh sách. Hãy viết giải thuật
tách danh sách này thành hai danh sách con. Danh sách thứ nhất sẽ được trỏ bởi P.
Danh sách thứ hai sẽ trỏ bởi Q (nút trỏ bởi Q sẽ là nút đầu tiên của danh sách thứ hai).
Bài 6: Cho danh sách nối đơn, không rỗng, có con trỏ LIST trỏ tới nút đầu tiên. Biết rằng
trường INFO của mỗi nút đều chứa một số dương. Hãy viết giải thuật:
Tính giá trị trung bình của các số chứa trong danh sách.

Bài 7: Viết khai báo và các thủ tục cài đặt danh sách bằng mảng. Dùng các thủ tục này để
viết:

a. Thủ tục nhận một dãy các số nguyên nhập từ bàn phớm, lưu trữ nó trong danh
sách theo thứ tự nhập vào.
b. Thủ tục nhận một dãy các số nguyên nhập từ bàn phím, lưu trữ nó trong danh sách
theo thứ tự ngược với thứ tự nhập vào.
c. Viết thủ tục in ra màn hình các phần tử trong danh sách theo thứ tự của nó trong
danh sách.
Bài 8: Viết thủ tục thêm một phần tử trong danh sách liên kết đã có thứ tự sao cho ta vẫn
có một danh sách có thứ tự.

Bài 9: Viết thủ tục nhận vào từ bàn phím một dãy số nguyên, lưu trữ nó trong một danh
sách có thứ tự không giảm, theo cách sau: với mỗi phần tử được nhập vào thủ tục phải
tìm vị trí thích hợp để xen nó vào danh sách cho đúng thứ tự. Viết thủ tục trên cho trường
hợp danh sách được cài đặt bằng mảng và cài đặt bằng con trỏ.

Bài 10: Viết thủ tục loại bỏ các phần tử trùng nhau (giữ lại duy nhất 1 phần tử) trong một
danh sách có thứ tự không giảm, trong hai trường hợp: cài đặt bằng mảng và cài đặt bằng
con trỏ.
Bài tập thực hành CTDL & GT Trang 41
Bài 11: Viết thủ tục nhận vào từ bàn phím một dãy số nguyên, lưu trữ nó trong một danh
sách có thứ tự tăng không có hai phần tử trùng nhau, theo cách sau: với mỗi phần tử được
nhập vào thủ tục phải tìm kiếm xem nó có trong danh sách chưa, nếu chưa có thì xen nó
vào danh sách cho đúng thứ tự. Viết thủ tục trên cho trường hợp danh sách được cài đặt
bằng mảng và cài đặt bằng con trỏ.

Bài 12: Viết thủ tục trộn hai danh sách liên kết chứa các số nguyên theo thứ tự tăng để
được một danh sách cũng có thứ tự tăng.

Bài 13: Viết thủ tục xóa khỏi danh sách lưu trữ các số nguyên các phần tử là số nguyên
lẻ, cũng trong hai trường hợp: cài đặt bằng mảng và bằng con trỏ.

Bài 14: Viết thủ tục tách một danh sách chứa các số nguyên thành hai danh sách: một
danh sách gồm các số chẳn còn cái kia chứa các số lẻ.

Bài 15: Cho đa thức P(x)= anxn+ an-1xn-1+... + a1x + a0 được lưu trữ trong máy tính
dưới dạng một danh sách liên kết mà mỗi phần tử của danh sách là một record có ba
trường lưu giữ hệ số, số mũ, và trưòng NEXT trỏ đến phần tử kế tiếp. Chú ý cách lưu trữ
đảm bảo thứ tự giảm dần theo số mũ của từng hạng tử của đa thức.

Ví dụ: đa thức 5x4 - x + 3 được lưu trữ trong danh sách có 3 phần tử như sau:

a. Hãy viết chương trình thực hiện được sự lưu trữ này.
b. Dựa vào sự cài đặt ở trên, viết thủ tục thưc hiện việc cộng hai đa thức.
c. Viết thủ tục lấy đạo hàm của đa thức.
Bài tập thực hành CTDL & GT Trang 42

BÀI THỰC HÀNH 5. NGĂN XẾP - STACK

* Mục tiêu
- Hệ thống lại những kiến thức liên quan đế Stack và các phép toán trên nó
- Cài đặt được các phép toán trên Stack sử dụng mảng và danh sách liên kết
* Yêu cầu:
A. Bài tập mẫu
Bài 1: Cài đặt các phép toán trên Stack bằng mảng gồm các phép toán:
- Kiểm tra Stack rỗng không IsEmpty()
- Kiểm tra Stack có đầy không IsFull()
- Thêm một phần tử vào Stack Push(x)
- Loại một phần tử khỏi Stack Pop()
- Khởi tạo Stack Init()
Gợi ý: Trước tiên khai báo mảng s và biến top toàn cục.
-Stack rỗng khi top=-1(top là biến theo dõi đỉnh của stack), vậy ta chỉ cần trả về giá trị
của top=-1.
-Stack đầy khi top bằng hoặc lớn hơn độ dài của stack, ta trả về giá trị của
top>=s.length.
-Đầu tiên ta kiểm tra xem stack đã đầy chưa, nếu đầy rồi ta in ra 1 thông báo không
thể thêm phần tử vào stack nữa,ngược lại gán s[++top]=x.
-Kiểm tra xem stack có rỗng không? Nếu rỗng không có gì lấy cả thì in ra 1 thông báo
ngoại lệ, ngược lại ta trả về giá tri s[top--].
-Khởi tạo stack bằng cách tạo 1 mảng mới với độ dài tùy ý và gán top=-1;
Lời giải mẫu
using System;
class Stack
{
public int top;
public int[] s;
public bool isEmpty()
{
return top == -1;
Bài tập thực hành CTDL & GT Trang 43
}
public bool isFull()
{
return top >= s.Length;
}
public void Init()
{
s = new int[20];
top = -1;
}
public void Push(int x)
{
if (!isFull()) s[++top] = x;
else Console.Write("Stack tran");
}
public int Pop()
{
if (isEmpty()) throw new Exception("Tack Empty");
else return s[top--];
}
}
Bài 2: Chuyển một số từ hệ đếm thập phân sang hệ đếm thập lục phân
Gợi ý: Đầu tiên ta phải xây dựng 1 lớp stack gồm các hàm kiểm tra rỗng, kiểm tra đầy,
thêm 1 phần tử, lấy ra 1 phần tử, khởi tạo stack.
-Tại hàm Main ta thực hiện các công việc sau:
1. Khởi tao 1 stack t mới.
2. Nhập số cần chuyển đổi n.
3. Khai báo string st = "0123456789ABCDEF";
4. Trong khi n!=0 ta dùng hàm push(hàm thêm 1 phần tử vào stack) để thêm giá trị
của phép tính n%16 vào stack ,sau đó gán n=n/16;
5. In ra kết quả bằng cách thực hiện:trong khi stack chưa rỗng thì dùng hàm pop(hàm
lấy 1 phần tử trong stack) để lấy các giá trị có trong stack ra.
Bài tập thực hành CTDL & GT Trang 44
Lời giải mẫu
using System;
class Stack
{
public int top;
public int[] s;
public bool isEmpty()
{
return top == -1;
}
public bool isFull()
{
return top >= s.Length;
}
public void Init()
{
s = new int[20];
top = -1;
}
public void Push(int x)
{
if (!isFull()) s[++top] = x;
else Console.Write("Stack tran");
}
public int Pop()
{
if (isEmpty()) throw new Exception("Tack Empty");
else return s[top--];
}
}
class App
{
Bài tập thực hành CTDL & GT Trang 45
static void Main()
{
Stack t = new Stack();
int n;
Console.Write("Nhap vao so can doi:");
n = int.Parse(Console.ReadLine());
t.Init();
string st = "0123456789ABCDEF";
while (n != 0)
{
t.Push((int)st[n % 16]);
n = n / 16;
}
Console.Write("Ket qua chuyen sang he thap luc phan:");
while (!t.isEmpty())
{
Console.Write("{0}", (char)t.Pop());
}
Console.ReadKey();
}
}
Bài 3: Xây dựng chương trình nhập vào một biểu thức bất kỳ sau đó tính giá trị của
chúng
Gợi ý: Đầu tiên ta phải xây dựng 1 lớp stack gồm các hàm kiểm tra rỗng. Kiểm tra đầy,
thêm 1 phần tử, lấy ra 1 phần tử, khởi tạo stack, khai báo mảng s và biến top toàn cục.
-Sau đó là lớp chương trình, ta tạo 1 hàm kiểm tra độ ưu tiên của toán tử với phép cộng
và trừ là 1, phép nhân và chia là 2.
-Xây dựng 1 hàm postfix để thêm hoặc lấy ra 1 phần tử trong stack.
+Trước hết, ta tạo 1 stack t mới, khởi tạo 1 xâu E1 rỗng, duyệt từ đầu cho đến hết xâu E
(xâu được nhập vào từ bàn phím) kiểm tra E[i] nếu là số thì thêm vào bên phải xâu E1,
nếu là “(“ thì đẩy vào stack,nếu “)” thì sẽ lấy các phần tử trong stack thêm vào bên phải
E1 cho đến khi gặp “(“ thì mới thôi lấy ra, nếu gặp toán tử thì gọi hàm kiểm tra độ ưu tiên
Bài tập thực hành CTDL & GT Trang 46
để so sánh độ ưu tiên giữa toán tử đang xét với toán tử trong stack, nếu độ ưu tiên của
toán tử đang xét nhỏ hơn toán tử ở đỉnh stack thì lấy toán tử đầu stack thêm vào E1 đồng
thời đẩy toán tử đang xét vào stack.
+ Sau đó trong khi stack chưa rỗng thì lấy hết các phần tử trong đó ra và thêm vào E1.
+ Tiếp đến là công viêc tính toán:đầu tiên tạo 1 satck mới, khai báo 2 biến lưu trư 2 toán
hạng lấy ra, 1 biến lưu trữ kết quả kiểu số thực. Duyệt từ đầu cho hết E1 nếu là số thì đẩy
vào satck,ngược lại thì lấy hai toán hạng đầu satck ra rồi thực hiện phép toán tương ứng,
được kết quả ta lại đẩy vào stack. Cuối cùng ta trả giá giá trị kết quả là phần tử lấy ra cuối
cùng của stack.
Lời giải mẫu:
using System;
using System.Collections.Generic;
using System.Text;
public class Stack
{
public int top;
public object[] s;
public bool isEmpty()
{
return top == -1;
}
public bool isFull()
{
return top >= s.Length;
}
public void Init()
{
s = new object[20];
top = -1;
}
public object Top()
{
Bài tập thực hành CTDL & GT Trang 47
if (isEmpty()) throw new Exception("Tack Empty");
else return s[top];
}
public void Push(object x)
{
if (!isFull()) s[++top] = x;
else Console.Write("Stack tran");
}
public object Pop()
{
if (isEmpty()) throw new Exception("Tack Empty");
else return s[top--];
}
}

public class TBT


{
static int Pri(char k)
{
int kq = 0;
switch (k)
{
case '+': kq = 1; break;
case '-': kq = 1; break;
case '*': kq = 2; break;
case '/': kq = 2; break;
}
return kq;
}
static string PostFix(string E)
{
Stack t = new Stack(); t.Init();
Bài tập thực hành CTDL & GT Trang 48
string E1 = "";
for (int i = 0; i < E.Length; ++i)
if (E[i] >= '0' && E[i] <= '9')
E1 = E1 + E[i].ToString();
else if (E[i] == '(') t.Push('(');
else if (E[i] == ')')
{
while ((char)t.Top() != '(')
{
E1 = E1 + t.Pop();
}
t.Pop();
}
else
{ while (!t.isEmpty() && Pri((char)t.Top()) > Pri(E[i]))
{

E1 = E1 + t.Pop();
}
t.Push(E[i]);
}
while (!t.isEmpty()) E1 = E1 + t.Pop();
return E1;
}
static double Calculator(string E)
{
double y, x, z;
Stack t = new Stack(); t.Init();
for (int i = 0; i < E.Length; ++i)
if (E[i] >= '0' && E[i] <= '9') t.Push((double)E[i] - 48);
else switch (E[i])
{
Bài tập thực hành CTDL & GT Trang 49
case '+':
y = (double)t.Pop();
x = (double)t.Pop();
z = x + y;
t.Push((double)z);
break;
case '-':
y = (double)t.Pop();
x = (double)t.Pop();
z = x + y;
t.Push((double)z);
break;
case '*':
y = (double)t.Pop();
x = (double)t.Pop();
z = x + y;
t.Push((double)z);
break;
case '/':
y = (double)t.Pop();
x = (double)t.Pop();
z = x + y;
t.Push((double)z);
break;
}
return (double)t.Pop();
}
//Ham thuc hien cac chuc nang cua chuong trinh
static void Main()
{
string E = "5+(5+3*2)+3+6/2";
string E1 = PostFix(E);
Bài tập thực hành CTDL & GT Trang 50
Console.WriteLine(Calculator(E1));
Console.ReadKey();
}
}
A. Bài tự làm
Bài 1: Cài đặt các phép toán trên Stack bằng danh sách liên kết gồm các phép toán:
- Kiểm tra Stack rỗng không IsEmpty()
- Thêm một phần tử vào Stack Push(x)
- Loại một phần tử khỏi Stack Pop()
- Khởi tạo Stack Init()
Gợi ý: Trước tiên khai báo 1 lớp node gồm 1 trường info lưu trữ thông tin của phần tử,1
node link lưu trữ địa chỉ kế tiếp trong danh sách. Sau đó trong lớp satck khai báo 1 node s
toàn cục.
-Stack rỗng khi s==null(s là theo dõi phần tử đầu tiên của danh sách),vậy ta chỉ cần
trả về giá trị của s==null.
-Khởi tạo 1 node tg mới chứa phần tử cần thêm vào stack,gán giá trị
tg.info=x;tg.link=null(địa chỉ kế tiếp trong danh sách tg là null),kiểm tra xem danh
sách có rỗng không? Nếu rỗng thì gán s=tg;ngược lại tg.link=s;s=tg.
-Kiểm tra xem stack có rỗng không? Nếu rỗng không có gì lấy cả thì in ra 1 thông báo
stack rỗng,ngược lại ta gán int kq=s.x;s=s.link sau đó trả ra giá trị kết quả.
-Khởi tạo stack bằng cách tạo 1 danh sách rỗng s=null.
Bài 2: Dùng Stack để chuyển một số nguyên bất kỳ sang hệ đếm thập lục phân, nhị phân
Gợi ý: Đầu tiên ta phải xây dựng 1 lớp stack gồm các hàm kiểm tra rỗng.kiểm tra
đầy,thêm 1 phần tử,lấy ra 1 phần tử.
-Tại hàm Main ta thực hiện các công việc sau:
1. 1.Khởi tao 1 stack t mới.
2. Nhập số cần chuyển đổi n,nhập hệ cần đổi k.
3. Khai báo string st = "0123456789ABCDEF";
4. Trong khi n!=0 ta dùng hàm push(hàm thêm 1 phần tử vào stack) để thêm giá trị
của phép tính n% k vào stack ,sau đó gán n=n/k;
5. In ra kết quả bằng cách thực hiện:trong khi stack chưa rỗng thì dùng hàm pop(hàm
lấy 1 phần tử trong stack) để lấy các giá trị có trong stack ra.
Bài tập thực hành CTDL & GT Trang 51
Bài 3: Dùng Stack kiểm tra tính hợp lệ của một dãy các dấu ( và ) trong một biểu thức
Ví dụ 1: (()()()()) là hợp lệ
Ví dụ 1: (())()()) là không hợp lệ
Gợi ý: Đầu tiên ta phải xây dựng 1 lớp stack gồm các hàm kiểm tra rỗng.kiểm tra
đầy,thêm 1 phần tử,lấy ra 1 phần tử,khởi tạo stack. Khai báo mảng s và biến top toàn cục.
-Gọi hàm khởi tạo 1 stack t mới init(),duyệt từ 0 đến s.length
+kiểm tra nếu s[i]=’(‘ thì gọi hàm push(s[i])(ham thêm 1 phần tử vào stack),ngược
lại kiểm tra nếu stack rỗng thì gán false (kt 1 biến kiểm tra nào đó và được khởi
tạo là true )và thoát khỏi vòng lặp,ngược lại gọi hàm thêm 1 phần tử vào pop().
+Kiểm tra,nếu kt=false in ra không hợp lệ,ngược lại kiểm tra nếu stack rỗng thì in
ra man hình hợp lệ,ngược lại là không hợp lệ.
Bài 4: Tính giá trị của một biểu thức dạng chuỗi ký tự bao gồm các chữ số và các phép
toán +,-,*,/ , % và dấu đóng mở ngoặc đơn.
Ví dụ: (( 2 + 3 )*2) – 4/2 = 12
Gợi ý: Đầu tiên ta phải xây dựng 1 lớp stack gồm các hàm kiểm tra rỗng.kiểm tra
đầy,thêm 1 phần tử,lấy ra 1 phần tử,khởi tạo stack,khai báo mảng s và biến top toàn cục.
-Sau đó là lớp chương trình,ta tạo 1 hàm kiểm tra độ ưu tiên của toán tử với phép cộng và
trừ là 1,phép nhân và chia là 2.
-Xây dựng 1 hàm postfix để thêm hoặc lấy ra 1 phần tử trong stack.
+Trước hết,ta tạo 1 stack t mới,khởi tạo 1 xâu E1 rỗng,duyệt từ đầu cho đến hết xâu
E(xâu được nhập vào từ bàn phím) kiểm tra E[i] nếu là số thì thêm vào bên phải xâu
E1,nếu là “(“ thì đẩy vào stack,nếu “)” thì sẽ lấy các phần tử trong stack thêm vào bên
phải E1 cho đến khi gặp “(“ thì mới thôi lấy ra,nếu gặp toán tử thì gọi hàm kiểm tra độ ưu
tiên để so sánh độ ưu tiên giữa toán tử đang xét với toán tử trong stack,nếu độ ưu tiên của
toán tử đang xét nhỏ hơn toán tử ở đỉnh stack thì lấy toán tử đầu stack thêm vào E1 đồng
thời đẩy toán tử đang xét vào stack.
+Sau đó trong khi stack chưa rỗng thì lấy hết các phần tử trong đó ra và thêm vào E1.
+Tiếp đến là công viêc tính toán:đầu tiên tạo 1 satck mới,khai báo 2 biến,2 biến lưu trư 2
toán hạng lấy ra,1 biến lưu trữ kết quả kiểu số thực.Duyệt từ đầu cho hết E1 nếu là số thì
đẩy vào satck,ngược lại thì lấy hai toán hạng đầu satck ra rồi thực hiện phép toán tương
ứng,được kết quả ta lại đẩy vào stack. Cuối cùng ta trả giá giá trị kết quả là phần tử lấy ra
cuối cùng của stack.
Bài tập thực hành CTDL & GT Trang 52
Bài 5: Hãy cho biết nội dung của Stack sau mỗi thao tác trong dãy :
Ví dụ cho dãy: EAS*Y**QUE***ST***I*ON
Với một chữ cái tượng trưng cho thao tác thêm chữ cái tương ứng vào stack, dấu * tượng
trưng cho thao tác lấy nội dung một phần tử trong stack in lên màn hình.
Hãy cho biết sau khi hoàn tất chuỗi thao tác, những gì xuất hiện trên màn hình ?
Gợi ý: Đầu tiên ta phải xây dựng 1 lớp stack gồm các hàm kiểm tra rỗng.kiểm tra
đầy,thêm 1 phần tử,lấy ra 1 phần tử,khởi tạo stack,khai báo mảng s và biến top toàn cục.
-Duyệt hết dãy được nhập vào, kiểm tra nếu là chữ cái thì đẩy vào stack, còn là dấu “*”
thì lấy 1 chữ cái đầu stack ra rồi thêm vào bên phải 1 xâu mới. Kết quả là 1 dãy chữ
cái(xâu mới).
Bài tập thực hành CTDL & GT Trang 53

BÀI THỰC HÀNH 6. HÀNG ĐỢI - QUEUE

* Mục tiêu
Hệ thống lại những kiến thức liên quan đến hàng đợi Queue và các phép toán trên

* Yêu cầu:
A. Bài tập mẫu

Cài đặt các phép toán trên Queue bằng danh sách gồm các phép toán:
- EnQueue(O): thêm một đối tượng O - Object vào đuôi hàng đợi;
- DeQueue(): lấy ra một đối tượng ở đầu hàng đợi và trả về trị của nó, nếu hàng đợi
rỗng sẽ gặp lỗi;
- EmptyQueue(): kiểm tra xem hàng đợi có rỗng hay không;
- Front(): Trả về trị của phần tử ở đầu hàng đợi mà không loại nó khỏi hàng đợi, nếu
hàng đợi rỗng sẽ gặp lỗi.
Gợi ý: Khai báo con trỏ first trỏ vào phần tử đầu tiên của Queue
- EmptyQueue(): Hàng đợi rỗng khi con trỏ first bằng null
- EnQueue(O): Thêm một đối tượng O vào đuôi hàng đợi, ta khởi tạo một nút tg có chứa
giá của đối tượng O, cho tg chứa thông tin của con trỏ first và gán first bằng tg
- DeQueue():Thực hiện kiểm tra hàng đợi :
+Nếu hàng đợi rỗng báo lỗi
+Ngược lại,kiểm tra :
*Nếu hàng đợi có 1 phần tử (first.next =null) đối tượng lấy ra có giá trị bằng first và first
bằng null
*Ngược lại, dùng một con trỏ c duyệt qua các phần tử của Queue ( khởi tạo c=first duyệt
đến c.next.next=null) ,đối tượng lấy ra có giá trị bằng phần tử duyệt đến sau cùng của
Queue (c.next) và xóa phần tử đó ra khỏi Queue (c.next=null)
-Front():Kiểm tra hàng đợi
+Nếu hàng đợi rỗng báo lỗi
+Ngược lại , kiểm tra :
*Nếu hàng đợi có 1 phần tử (first.next =null) phần tử đầu hàng đợi có giá trị bằng first
Bài tập thực hành CTDL & GT Trang 54
* Ngược lại, dùng một con trỏ c duyệt qua các phần tử của Queue ( khởi tạo c=first duyệt
đến c.next.next=null) ,phần tử đầu hàn đợi có giá trị bằng phần tử duyệt đến sau cùng của
Queue (c.next)
Lời giải mẫu
using System;
class Node
{
public int value;
public Node next;
}
class Queue
{
public Node first;
public bool EmptyQueue()
{
return first == null;
}
public void EnQueue(int x)
{
Node tg = new Node();
tg.value = x;
tg.next = first;
first = tg;
}
public int DeQueue()
{
int value;
if (first == null) throw new Exception("Queue empty");
else
{
if (first.next == null)
{
Bài tập thực hành CTDL & GT Trang 55
value = first.value;
first = null;
}
else
{
Node c = first;
while (c.next.next != null) c = c.next;
value = c.next.value;
c.next = null;
}
return value;
}
}
public void Cleanup()
{
first = null;
}
public int Front()
{
int value;
if (first == null) throw new Exception("Queue empty");
{
if (first.next == null)
{
value = first.value;
}
else
{
Node c = first;
while (c.next.next != null) c = c.next;
value = c.next.value;
}
Bài tập thực hành CTDL & GT Trang 56
return value;
}
}
}
class App
{
static void Main()
{
Queue t = new Queue();
int x; char kt;
do
{
Console.Write("Nhap mot so nguyen:");
x = int.Parse(Console.ReadLine());
t.EnQueue(x);
Console.Write("Ban co nhap tiep C/K"); kt = char.Parse(Console.ReadLine());
} while (char.ToUpper(kt) == 'C');
Console.WriteLine("Cac so nguyen da nhap");
while (!t.EmptyQueue())
Console.Write(t.DeQueue() + "\t");
Console.WriteLine();
Console.ReadKey();
}
}
B. Bài tự làm

Bài 1: Cài đặt các phép toán trên Queue bằng mảng gồm các phép toán:
- EnQueue(O): thêm một đối tượng O vào đuôi hàng đợi;
- DeQueue(): lấy ra một đối tượng ở đầu hàng đợi và trả về trị của nó, nếu hàng đợi
rỗng sẽ gặp lỗi;
- EmptyQueue(): kiểm tra xem hàng đợi có rỗng hay không;
- Front(): Trả về trị của phần tử ở đầu hàng đợi mà không loại nó khỏi hàng đợi, nếu
hàng đợi rỗng sẽ gặp lỗi.
Bài tập thực hành CTDL & GT Trang 57
Bài 2: Cài đặt các phép toán trên Queue bằng danh sách liên kết gồm các phép toán:
- EnQueue(O): thêm một đối tượng O vào đuôi hàng đợi;
- DeQueue(): lấy ra một đối tượng ở đầu hàng đợi và trả về trị của nó, nếu hàng đợi
rỗng sẽ gặp lỗi;
- EmptyQueue(): kiểm tra xem hàng đợi có rỗng hay không;
- Front(): Trả về trị của phần tử ở đầu hàng đợi mà không loại nó khỏi hàng đợi, nếu
hàng đợi rỗng sẽ gặp lỗi.
Gợi ý: Khai báo con trỏ first trỏ vào phần tử đầu tiên của Queue
-EmptyQueue():Hàng đợi rỗng khi con trỏ first bằng null
-EnQueue(O):Thêm một đối tượng O vào đuôi hàng đợi, ta khởi tạo một nút tg có chứa
giá của đối tượng O, cho tg nắm thông tin của con trỏ first và gán first bằng tg
-DeQueue():Thực hiện kiểm tra hàng đợi :
+Nếu hàng đợi rỗng báo lỗi
+Ngược lại,kiểm tra :
*Nếu hàng đợi có 1 phần tử (first.next =null) đối tượng lấy ra có giá trị bằng first và first
bằng null
*Ngược lại, dùng một con trỏ c duyệt qua các phần tử của Queue ( khởi tạo c=first duyệt
đến c.next.next=null) ,đối tượng lấy ra có giá trị bằng phần tử duyệt đến sau cùng của
Queue (c.next) và xóa phần tử đó ra khỏi Queue (c.next=null)
-Front():Kiểm tra hàng đợi
+Nếu hàng đợi rỗng báo lỗi
+Ngược lại , kiểm tra :
*Nếu hàng đợi có 1 phần tử (first.next =null) phần tử đầu hàng đợi có giá trị bằng first
* Ngược lại, dùng một con trỏ c duyệt qua các phần tử của Queue ( khởi tạo c=first duyệt
đến c.next.next=null) ,phần tử đầu hàn đợi có giá trị bằng phần tử duyệt đến sau cùng của
Queue (c.next)
Bài 3: Sắp xếp danh sách số nguyên dùng Queue
Gợi ý: Khai báo một Queue toàn cục Q
Trước tiên xây dựng một hàm thêm một số bất kỳ vào một Queue sao cho thứ tự
sắp xếp trong Queue không đổi ví dụ là hàm Them(int a,Queue P), Thực hiện kiểm tra:
+Nếu P rỗng thêm a vào P
+Ngược lại,kiểm tra phần tử ở đầu Queue P
Bài tập thực hành CTDL & GT Trang 58
*Nếu P.Front()>a thêm a vào P
*Ngược lai,sử dụng một Queue TG ,chuyển hết các phần tử trong P sang TG đến khi gặp
số lớn hơn a, rồi đưa a vào P.Sau đó đưa toàn bộ các phần tử ở TG sang P
Tại hàm Main() thực hiện duyệt qua danh sách các số đã nhập, tại mỗi số ta gọi hàm
Them ( số đó ,Q) .Kết thúc hiện ra Q ta được dãy số giảm dần
Bài 4: Dùng Queue mô phỏng dòng chữ chạy trên màn hình
Bài 5: Hãy cho biết nội dung của hàng đợi sau mỗi thao tác trong dãy :
Ví dụ cho dãy: EAS*Y**QUE***ST***I*ON
Với một chữ cái tượng trưng cho thao tác thêm chữ cái tương ứng vào hàng đợi,
dấu * tượng trưng cho thao tác lấy nội dung một phần tử trong hàng đợi in lên màn hình.
Hãy cho biết sau khi hoàn tất chuỗi thao tác, những gì xuất hiện trên màn hình ?
Gợi ý: Khởi tạo một Queue lưu trữ các kí tự
Nhập vào xâu s dãy ký tự. Sau đó thực hiện thêm từng ký tự trong xâu s vào
Queue
Thực hiện duyệt qua các phần tử của Queue đến khi Queue rỗng .Tại mỗi phần tử
của Queue nếu kí tự trả ra có mã ASCII thuộc vào khoảng mã ASCII tương ứng của các
chữ cái A->Z và a->z thì hiện kí tự đó ra màn hình.
Bài tập thực hành CTDL & GT Trang 59

BÀI THỰC HÀNH 7. DANH SÁCH LIÊN KẾT KÉP

* Mục tiêu
Hệ thống lại những kiến thức liên quan đế danh sách liên kết kép và các phép toán
trên nó
* Yêu cầu:
A. Bài tập mẫu
Bài 1: Cho một danh sách liên kết đôi, mỗi nút là một số nguyên dương.
a.Đếm xem trong danh sách có bao nhiêu số bằng x ?
b.Tìm phần tử dương nhỏ nhất trong danh sách.
c. Xóa phần tử x xuất hiện đầu tiên trong danh sách.
Gợi ý: Khai báo 2 nút toàn cục pHead, pTail lần lượt chứa địa chỉ nút đầu và nút cuối
danh sách
a.Khởi tạo biến d=0 đếm các số =x. Duyệt qua các phần tử trong danh sách bằng
một nút tg (khởi tạo tg=pHead), tại mỗi phần tử so sánh giá trị của nó với x nếu
bằng ta tăng giá trị biến d=d+1.
b. Khởi tạo giá trị min bằng lớn vô cùng (MaxValue). Duyệt qua các phần tử trong
danh sách bằng một nút tg (khởi tạo tg=pHead), tại mỗi phần tử nếu có giá trị nhỏ
hơn min va lớn hơn 0 thì gán min bằng phần tử đó.
c. Sử dụng một nút p (Khởi tạo p=pHead), duyệt qua các phần tử của danh sách
đến khi gặp phần tử có giá trị x
+Nếu p=null báo lỗi không có x trong danh sách
+Ngược lại, p sẽ trỏ vào nút cần xóa:
*Nếu p=pHead, gán pHead bằng phần tử đứng sau pHead (pHead=pHead.next)
*Nếu p=pTail (hay p.next=null tức p trỏ vào phần tử cuối), ta cho p trỏ vào null
*Ngược lại, ta cho nút đứng trước p chứa địa chỉ nút đứng sau p và nút đứng sau p
chứa địa chỉ nút đứng trước p. Khi đó, p không còn liên kết trong danh sách.

Lời giải mẫu


using System;
class Node
{
Bài tập thực hành CTDL & GT Trang 60
public int info;
public Node pre, next;//chứa địa chỉ nút đứng trước, đứng sau
}
class DSLKK
{
static Node L, R;// chứa địa chỉ nút đầu tiên, cuối cùng của danh sách
static void Nhap()
{
Console.WriteLine("Nhap vao day so nguyen");
int i = 1; Node tg; char kt;
L =R = null;
do
{
Console.WriteLine("Nhap so nguyen thu:" + i);
tg = new Node();
Console.Write("Info="); tg.info = int.Parse(Console.ReadLine());
tg.pre = null;
tg.next = null;
if (L == null && R == null) L = R = tg;
else
{
tg.pre = R;
R.next = tg;
R = tg;
}
Console.WriteLine("Ban nhap tiep C/K"); kt = char.Parse(Console.ReadLine());
} while (char.ToUpper(kt) == 'C');
}
static void Hien()
{
Console.WriteLine("Cac phan tu co trong danh sach");
Node tg = L;
Bài tập thực hành CTDL & GT Trang 61
while (tg != null)
{
Console.Write(tg.info + "\t");
tg = tg.next;
}
Console.WriteLine();
}
public static int Dem(int x)
{
int d = 0;
Node tg = L;
while (tg != null)
{
if (x == tg.info) d++;
tg = tg.next;
}
return d;
}
static int MinDuong()
{
if (L == null && R == null) throw new Exception("Danh sach rong");
else
{
int m = int.MaxValue;
Node tg = L;
while (tg != null)
{
if (m > tg.info && tg.info >= 0) m = tg.info;
tg = tg.next;
}
return m;
}
Bài tập thực hành CTDL & GT Trang 62
}
static void Xoa(int x)
{
Node p = L;
while (p != null && p.info != x) p = p.next;
if (p == null) Console.WriteLine("x khong ton tai trong danh sach");
else
{
if (p == L) L = L.next;
else if (p.next == null) p.pre.next = null;
else
{
p.pre.next = p.next;
p.next.pre = p.pre;
}
}
}
//Ham thuc hien cac chuc nang cua chuong trinh
static void Main()
{
Nhap();
Xoa(3);
Hien();
Console.WriteLine("Min duong trong danh sach:" + MinDuong());
Console.ReadKey();
}
}
Bài 2: Hãy viết phần khai báo cấu trúc dữ liệu để mô tả một danh sách liên kết đôi mà
mỗi phần tử chứa một số nguyên. Viết các hàm thực hiện các yêu cầu sau:
a. Đưa ra màn hình các phần tử là số nguyên tố có trong danh sách
b. Sắp xếp các phần tử của danh sách theo chiều giảm dần.
Bài tập thực hành CTDL & GT Trang 63
c. Giả sử danh sách đã được sắp xếp giảm dần, hãy thêm một phần tử vào danh
sách sao cho thứ tự trong danh sách không thay đổi
Gợi ý: Khai báo 2 nút toàn cục pHead, pTail chứa địa chỉ nút đầu và nút cuối danh sách
a. Xây dựng hàm kiểm tra một số bất kỳ có phải số nguyên tố hay không, giả sử là hàm
KTNT(int x). Thực hiện duyệt qua các phần tử cả danh sách tại mỗi phần tử gọi hàm
KTNT nếu trả giá trị True ta hiện giá trị của phần tử đó ra màn hình
b. Duyệt qua từng phần tử của danh sách, tại mỗi phần tử thực hiện so sánh với từng phần
tử phía sau nó nếu lớn hơn thực hiên hoán đổi vị trí của 2 phần tử cho nhau,
c. Khởi tạo một nút tg chứa giá trị cần thêm vào có địa chỉ nút đứng trước và đứng sau là
null,
+Nếu danh sách rỗng gán pHead=pTail=tg
+Ngược lại,sử dụng nút p =pHead duyệt qua các phần tử của danh sách đến khi gặp phần
tử nhỏ hơn giá trị thêm
*Nếu p=null tức giá trị thêm vào là nhỏ nhất ta thực hiện thêm vào bên phải danh sách
*Nếu p=pHead giá trị thêm vào là nhỏ nhất ta thêm vào đầu danh sách
*Ngược lại, ta thực hiện thêm tg vào vị trí nút p đang trỏ đến
Lời giải mẫu:
using System;
class Node
{
public int info;
public Node pre, next;//chứa địa chỉ nút đứng trước, đứng sau
}
class DSLKK
{
static Node pHead, pTail;
static void Nhap()
{
Console.WriteLine("Nhap vao day so nguyen");
int i = 1; Node tg; char kt;
pHead = pTail = null;
Bài tập thực hành CTDL & GT Trang 64
do
{
Console.WriteLine("Nhap so nguyen thu:" + i);
tg = new Node();
Console.Write("Info="); tg.info = int.Parse(Console.ReadLine());
tg.pre = null;
tg.next = null;
if (pHead == null && pTail == null) pHead = pTail = tg;
else
{
tg.pre = pTail;
pTail.next = tg;
pTail = tg;
}
Console.WriteLine("Ban nhap tiep C/K"); kt = char.Parse(Console.ReadLine());
} while (char.ToUpper(kt) == 'C');
}
static void Hien()
{
Node tg = pHead;
while (tg != null)
{
Console.Write(tg.info + "\t");
tg = tg.next;
}
Console.WriteLine();
}
//Hàm kiểm tra n có phải là số nguyên tố không?
static bool NguyenTo(int n)
{
bool ok = true;
for (int i = 2; i <= n - 1; ++i)
Bài tập thực hành CTDL & GT Trang 65
if (n % i == 0) { ok = false; break; }
return ok;
}
static void HienNT()
{
Console.WriteLine("Cac phan tu la so nguyen to co trong danh sach");
Node tg = pHead;
while (tg != null)
{
if (NguyenTo(tg.info))
Console.Write(tg.info + "\t");
tg = tg.next;
}
Console.WriteLine();
}
static void SapXep()
{
Node i = pHead;
while (i.next != null)
{
Node j = i.next;
while (j != null)
{
if (i.info < j.info)
{
int tg = i.info;
i.info = j.info;
j.info = tg;
}
j = j.next;
}
i = i.next;
Bài tập thực hành CTDL & GT Trang 66
}
}
static void Them(int x)
{
Node tg = new Node();
tg.info = x;
tg.pre = tg.next = null;
if (pHead == null && pTail == null) pHead = pTail = tg;
else
{
Node p = pHead;
while (p != null && p.info > tg.info) p = p.next;
if (p == pHead)//Them đầu danh sách
{
tg.next = pHead;
pHead.pre = tg;
pHead = tg;
}
else if (p == null)//Them cuối danh sách
{
tg.pre = pTail;
pTail.next = tg;
pTail = tg;
}
else//Them o giua
{
tg.next = p;
tg.pre = p.pre;
p.pre.next = tg;
p.pre = tg;
}
}
Bài tập thực hành CTDL & GT Trang 67
}
//Ham thuc hien cac chuc nang cua chuong trinh
static void Main()
{
Nhap();
Console.WriteLine("Danh sach luc dau");
Hien();
Console.WriteLine("Danh sach sau khi sap xep");
SapXep();
Hien();
Console.WriteLine("Danh sach sau khi chen");
Them(5);
Hien();
Console.WriteLine("Danh sach cac so nguyen to");
HienNT();
Console.ReadKey();
}
}

B. Bài tập tự giải


Bài 1: Dùng danh sách liên kết kép viết chương trình lưu trữ một danh sách các số
nguyên, sắp xếp danh sách theo thứ tự (tăng hoặc giảm), trộn 2 danh sách có thứ tự để
được một danh sách mới có thứ tự.
Yêu cầu chi tiết:
1. Viết các khai báo cần thiết để cài đặt một danh sách các số nguyên.
Gợi ý: Khai báo nút mang thông tin là số nguyên và địa chỉ nút trước, sau của nút
Khai báo nút đầu và cuối của danh sách
2. Viết thủ tục khởi tạo một danh sách rỗng.
Gợi ý: Danh sách rỗng khi nút đầu và cuối của danh sách là rỗng (null)
3. Viết hàm kiểm tra danh sách rỗng.
Gợi ý: Hàm kiểm tra trả giá trị True khi nút đầu và cuối cùng rỗng (null)
4. Viết thủ tục nhập một danh sách.
Bài tập thực hành CTDL & GT Trang 68
Gợi ý: Khai báo 2 nút đầu, cuối của danh sách pHead, pTail
Khởi tạo danh sách rỗng và một nút tg chứa giá trị cần nhập với địa chỉ nút đứng
trước và sau của tg là null. Thực hiện thêm tg vào danh sách
+Nếu danh sách rỗng gán pHead = pTail =tg
+Ngược lại, thêm tg vào trước nút cuối của danh sách
5. Viết thủ tục hiển thị danh sách ra màn hình.
Gợi ý: Khởi tạo nút tg=pHead duyệt qua các phần tử bên phải tg của danh sách ,tại mỗi
phần tử hiện ra giá trị của nó
6. Viết thủ tục sắp xếp danh sách theo thứ tự (tăng hoặc giảm).
Gợi ý: Khởi tạo nút tg=pHead duyệt qua các phần tử bên phải tg của danh sách,tại mỗi
phần tử thực hiện so sánh với từng phần tử bên phải nó nếu lớn hơn thực hiên hoán đổi
giá trị của 2 phần tử cho nhau,
7. Xen một phần tử mới x vào danh sách sau cho danh sách mới vẫn bảo đảm thứ tự.
Gợi ý: (Giả sử cho danh sách tăng dần)
Khởi tạo một nút tg chứa giá trị x với địa chỉ trước và sau của tg là null
+Nếu danh sách là rỗng ta gán nút đầu và cuối danh sách bằng tg
+ Ngược lại dùng nút p=pHead duyệt qua các phần tử đứng sau tg của danh sách đến khi
gặp nút có giá trị lớn hơn x. Sau khi duyệt p trỏ vào vị trí cần thêm
*Nếu p=pHead thực hiện thêm vào đầu danh sách (x là nhỏ nhất so với danh sách)
*Nếu p=null thêm vào phải danh sách (x là lớn nhất)
*Ngược lại, thêm vào vị trí nút p dang trỏ đến
8. Xóa một phần tử x ra khỏi danh sách sao cho danh sách mới vẫn bảo đảm thứ tự.
Gợi ý : Khi xóa một phần tử không làm thay đổi thứ tự danh sách.
Xóa một phần tử như sau:
+ Nếu danh sách rỗng hoặc không có x trong danh sách: báo lỗi
+ Ngược lại :
*Nếu x=pHead, pHead=pHead.next
*Nếu x=pTail, pTail=pTail.pre
*Ngược lại, cho nút đứng trước x nắm thông tin nút đứng sau x và nút đứng sau x nắm
thông tin nút đứng trước x. Khi đó, x không còn liên kết trong danh sách.
9. Viết thủ tục trộn 2 danh sách đã có thứ tự thành một danh sách mới sao cho danh sách
mới vẫn bảo đảm thứ tự.
Bài tập thực hành CTDL & GT Trang 69
Gợi ý: Xây dựng một hàm thêm một số bất kỳ vào một danh sách sao cho thứ tự không
đổi ( cách làm như câu 7)
Sau đó khai báo một danh sách kq, rồi duyệt qua các phần tử của 2 danh sách, tại
mỗi phần tử ta gọi hàm thêm vào ở trên với đối số là phần tử đang xét và danh sách kq

Bài 2: Dùng danh sách liên kết kép cộng hai đa thức bất kỳ
R(x) =P(x)+Q(x)
Với
P(x)=a0xn+a1xn-1+…+an
Q(x)=b0xm+b1xm-1+…+bm
Sau đó tính giá trị của một đa thức R tại điểm x0
Gợi ý: Khai báo danh sách gồm 2 trường số mũ và hệ số để lưu trữ từng phần tử của đa
thức
Khai báo 2 nút toàn cục pHead, pTail chứa địa chỉ đầu và cuối danh sách
Khai báo các danh sách toàn cục P,Q,KQ
Nhập vào danh sách P và Q
Xây dựng một hàm thêm 1 nút tg vào danh sách P:
+Nếu P rỗng thì pHead = pTail =tg;
+Ngược lại P!=null : Duyệt qua các phần tử của danh sách P bằng một nút p
*Nếu p.số mũ =tg.số mũ thì cộng hệ số của p với hệ số của tg đưa kết quả vào p.hệ số
*Nếu trong ds P không có số mũ = số mũ của tg ta thực hiện chèn tg vào sau nút cuối
cùng có số mũ nhỏ hơn tg ( có 3 th :chèn trái–chèn phải –chèn giữa danh sách)
Xây dựng hàm tính tổng như sau: khai báo danh sách KQ rỗng
Duyệt qua các nút của danh sách chứa đa thức P và Q tại mỗi phần tử ta gọi hàm thêm
nút với tham số truyền vào là nút đang duyệt và danh sách KQ
Bài tập thực hành CTDL & GT Trang 70

BÀI THỰC HÀNH 8. DANH SÁCH LIÊN KẾT VÒNG

* Mục tiêu
Hệ thống lại những kiến thức liên quan đế danh sách liên kết vòng và các phép
toán trên nó
* Yêu cầu:
A. Bài tập mẫu

Bài 1: Hãy viết phần khai báo cấu trúc dữ liệu để mô tả một danh sách liên kết đơn vòng
mà mỗi phần tử chứa một số nguyên. Viết các hàm thực hiện các yêu cầu sau:
a.Tách danh sách ban đầu thành hai danh sách: l1 chứa các số chẵn, l2 chứa các số lẻ
b.Tính tổng các phân tử là số dương và chia hết cho 3 có ở trong danh sách
Gợi ý: Trước tiên ta phải khởi tạo và nhập giá trị cho danh sách l.
a. Khởi tạo 2 danh sách l1, l2 mới rỗng. Sau đó kiểm tra danh sách l nếu rỗng thì in
ra 1 dòng thông báo danh sách rỗng. Ngược lại, khởi tạo 1 node p=l, trong khi chưa
hết phần tử trong p, kiểm tra nếu p.info chia hết cho 2 (số chia hết cho 2 là số chẵn)
thì thêm p.info vào l1, ngược lại thêm p.info vào l2.
b. Khởi tạo t=0(1 biến để lưu trữ phần tử là số dương và chia hết cho 3). Khai báo 1
node tg=l, trong khi chưa duyệt hết các phần tử trong tg ta kiểm tra nếu phần tử nào
là số dương (tg.info>0) và chia hết cho 3 (tg.info%3==0) thì ta cộng giá trị phần tử
đó vào biến t (t=t+tg.info).
Lời giải mẫu:
using System;
class Node
{
public int info;
public Node link;
}
class DSLKDV
{
static Node l, l1, l2;
static void Nhap()
{
Bài tập thực hành CTDL & GT Trang 71
Console.WriteLine("Nhap vao day so nguyen");
int i = 1; l = null; Node tg; char kt;
do
{
Console.WriteLine("Nhap so nguyen thu:" + i);
tg = new Node();
Console.Write("Info="); tg.info = int.Parse(Console.ReadLine());
tg.link = null;
ThemDau(ref l, tg);//Hoac ThemCuoi(ref l,tg);
Console.WriteLine("Ban nhap tiep C/K"); kt = char.Parse(Console.ReadLine());
} while (char.ToUpper(kt) == 'C');
}
static void ThemDau(ref Node l, Node tg)
{
Node c;
if (l == null) { l = tg; c = tg; }
else
{
c = l;
//Tim dia chi phan tu cuoi
while (l != null && c.link != l) c = c.link;
tg.link = l;
l = tg;
}
c.link = l;
}
static void ThemCuoi(ref Node l, Node tg)
{
if (l == null) l = tg;
else
{
Node c = l;
Bài tập thực hành CTDL & GT Trang 72
//Tim dia chi phan tu cuoi
while (l != null && c.link != l) c = c.link;
c.link = tg;
}
tg.link = l;
}
static void Hien(Node l)
{
if (l == null)
Console.WriteLine("Danh sach rong");
else
{
Node tg = l;
do
{
Console.Write(tg.info + "\t");
tg = tg.link;
} while (tg != l);
Console.WriteLine();
}
}
static void Tong()
{
int t = 0;
if (l == null)
Console.WriteLine("Danh sach rong");
else
{
Node tg = l;
do
{
if (tg.info > 0 && tg.info % 3 == 0)
Bài tập thực hành CTDL & GT Trang 73
t = t + tg.info;
tg = tg.link;
} while (tg != l);
Console.WriteLine("Ket qua la:" + t);
}
}

static void Tach()


{
l1 = l2 = null;
//l1 chua cac so chan, l2 chua cac so le
if (l == null)
Console.WriteLine("Danh sach rong");
else
{
Node p = l, tg;
do
{
tg = new Node();
tg.info = p.info;
tg.link = null;
if (p.info % 2 == 0) ThemDau(ref l1, tg);
else ThemDau(ref l2, tg);
p = p.link;
} while (p != l);
Console.WriteLine();
}
}
//Ham thuc hien cac chuc nang cua chuong trinh
static void Main()
{
Nhap();
Bài tập thực hành CTDL & GT Trang 74
Console.WriteLine("Danh sach luc dau");
Hien(l);
Tach();
Console.WriteLine("Danh sach so chan");
Hien(l1);
Console.WriteLine("Danh sach so e");
Hien(l2);
Tong();
Console.ReadKey();
}
}
Bài 2: Cho một danh sách liên kết đôi, mỗi nút chứa một số nguyên.
a.Rut gọn danh sách và chỉ giữ lại một giá trị trong số các giá trị giống nhau.
b.Kiểm tra xem danh sách có được sắp xếp tăng dần hay không?
Gợi ý: Trước tiên ta phải khởi tạo và nhập giá trị cho danh sách PHead, pTail. Tạo 1 lớp
node gồm 1 trường lưu trữ thông tin info kiểu int, và 2 trường lưu trữ địa chỉ pre,next
(pHead.pre lưu trữ thông tin nút đứng trước của pHead còn next lưu trữ thông tin nút
đứng sau của pTail). Khai báo danh sách pHead, pTail toàn cục.
a. Đầu tiên kiểm tra xem danh sách nếu rỗng (pHead=null và pTail=null) thì in ra
thông báo danh sách rỗng. Ngược lại khởi tạo node i=pHead,j, ta sử dụng 2 vòng
lặp. Thực hiện các công việc sau cho đến khi j=pHead:gán j=i.right,trong khi
j!=pHeadthì kiểm tra nếu i.info=j.info thì xóa j đi.
b. Kiểm tra xem danh sách nếu rỗng (pHead=null và pTail=null) thì in ra thông
báo danh sách rỗng. Ngược lại khai báo node tg=pHead, kiểm tra xem phần tử
đứng trước của tg không bằng pHead (tg.next!=pHead nghĩa là chưa duyệt hết
vòng) và giá trị của tg lớn hơn giá trị của phần tử tiếp theo(tg.next.info) thì gán
ok=false(ok là biến theo dõi tính đúng hay sai) rồi thoát khỏi vòng lặp. Nếu
ok=true thì dãy tăng dần ngược lại là dãy k tăng dần.
Lời giải mẫu:
using System;
class Node
{
Bài tập thực hành CTDL & GT Trang 75
public int info;
public Node pre, next;
}
class DSLKKV
{
static Node pHead, pTail;
static void Nhap()
{
Console.WriteLine("Nhap vao day so nguyen");
int i = 1; Node tg; char kt;
pHead = pTail = null;
do
{
Console.WriteLine("Nhap so nguyen thu:" + (i++));
tg = new Node();
Console.Write("Info="); tg.info = int.Parse(Console.ReadLine());
tg.pre = null;
tg.next = null;
ThemDauTrai(tg);
Console.WriteLine("Ban nhap tiep C/K"); kt = char.Parse(Console.ReadLine());
} while (char.ToUpper(kt) == 'C');
}
static void ThemDauTrai(Node tg)
{
if (pHead == null && pTail == null)
{
pHead = pTail = tg;
tg.next = pHead;
tg.pre = pTail;
}
else
{
Bài tập thực hành CTDL & GT Trang 76
tg.next = pHead;
tg.pre = pTail;
pHead.pre = tg;
pTail.next = tg;
pHead = tg;
}
}
static void Rutgon()
{
if (pHead == null && pTail == null)
Console.WriteLine("Danh sach rong");
else
{
Node i = pHead, j;
do
{
j = i.next;
do
{
if (i.info == j.info) Xoa(j);
j = j.next;
} while (j != pHead);
i = i.next;
} while (i != pHead);
Console.WriteLine();
}
}
static void Xoa(Node p)
{
if (pHead == null && pTail == null) return;
else
{
Bài tập thực hành CTDL & GT Trang 77
if (p == pHead && p == pTail) pHead = pTail = null;
else if (p == pHead)
{
pTail.next = pHead.next;
pHead.next.pre = pTail;
pHead = pHead.next;
}
else if (p == pTail)
{
pHead.pre = pTail.pre;
pTail.pre.next = pHead;
pTail = pTail.pre;
}
else
{
p.pre.next = p.next;
p.next.pre = p.pre;
}
}
}
static void KiemTra()
{
bool ok = true;
if (pHead == null && pTail == null)
Console.WriteLine("Danh sach rong");
else
{
Node tg = pHead;
do
{
if (tg.next != pHead && tg.info > tg.next.info) { ok = false; break; }
tg = tg.next;
Bài tập thực hành CTDL & GT Trang 78
} while (tg != pHead);
Console.WriteLine();
}
if (ok) Console.WriteLine("Day da cho la day tang dan");
else Console.WriteLine("Day da cho khong la day tang dan");
}
static void Hien()
{
if (pHead == null && pTail == null)
Console.WriteLine("Danh sach rong");
else
{
Node tg = pHead;
do
{
Console.Write(tg.info + "\t");
tg = tg.next;
} while (tg != pHead);
Console.WriteLine();
}
}
//Ham thuc hien cac chuc nang cua chuong trinh
static void Main()
{
Nhap();
Console.WriteLine("Day ban dau");
Hien();
Rutgon();
Console.WriteLine("Day sau khi rut gon");
Hien();
KiemTra();
Console.ReadKey();
Bài tập thực hành CTDL & GT Trang 79
}
}

B.Bài tập tự giải


Dùng danh sách liên kết vòng viết chương trình lưu trữ một danh sách các số
nguyên, sắp xếp danh sách theo thứ tự (tăng hoặc giảm), trộn 2 danh sách có thứ tự để
được một danh sách mới có thứ tự.
Yêu cầu chi tiết:
1. Viết các khai báo cần thiết để cài đặt một danh sách các số nguyên.
2. Viết thủ tục khởi tạo một danh sách rỗng.
3. Viết hàm kiểm tra danh sách rỗng.
4. Viết thủ tục nhập một danh sách.
5. Viết thủ tục hiển thị danh sách ra màn hình.
6. Viết thủ tục sắp xếp danh sách theo thứ tự (tăng hoặc giảm).
7. Xen một phần tử mới x vào danh sách sau cho danh sách mới vẫn bảo đảm thứ tự.
8. Xóa một phần tử x ra khỏi danh sách sao cho danh sách mới vẫn bảo đảm thứ tự.
9. Viết thủ tục trộn 2 danh sách đã có thứ tự thành một danh sách mới sao cho danh sách
mới vẫn bảo đảm thứ tự.
Gợi ý:
1. Khai báo lớp node gồm thành phần lưu trữ thông tin info kiểu dữ liệu int,thành phần
lưu trữ địa chỉ phần tử kế tiếp next.
2. Danh sách rỗng phần tử đầu bằng phần tử cuối và bằng null.
3. Trả về giá trị phần tử đầu bằng phần tử cuối và bằng null.
4. Nối đơn (thêm vào đầu): nếu danh sách rỗng pHead=null thì gán pHead=tg; c=tg,
ngược lại tg.next=pHead, pHead=tg.(c.next=pHead). Nối kép (thêm vào đầu):danh
sach rỗng pHead=null và pTail=null thì gán pHead = pTail=tg, ngược lại
tg.next=pHead;pHead.pre=tg; pHead=tg. Sau đó gán pTail.next=pHead;
pTail.pre=pTail.
5. Duyệt các phần tử trong danh sách trong khi chưa đi giáp vòng ta in ra giá trị phần tử
tương ứng.
6. Khai báo node i, j, gán i=pHead, ta sử dụng 2 vòng lặp while. Trong khi
i.next!=null ta gán j=i.next, rồi duyệt j cho đến khi j=null. Kiểm tra giá trị của
Bài tập thực hành CTDL & GT Trang 80
phần tử i.info nếu lớn hơn(nhỏ hơn) j.info thì ta hoán đổi giá trị của hai phần tử
này qua một biến trung gian nào đó. Kết thúc công việc đó ta được 1 danh sách
giảm dần(tăng dần) đối với danh sách nối đơn, danh sách nối kép tương tự.
7. Khai báo 2 node mới p=l, t=l. Trong khi p.info<phần tử mới nhập vào và p chưa
rỗng thì gán t=p. Khởi tạo 1 node tg mới gán tg.info=phần tử mới nhập vào. Kiểm
tra nếu l rỗng gán l=tg luôn, nếu p=l thì thêm vào đầu gán phần tử tiếp theo của
tg:tg.next=l;l=tg., nếu p rỗng thì thêm vào sau gán t.next=tg, ngược lại tất cả điều
trên ta thêm vào giữa. (Đây là cách đảm bảo thứ tự theo chiều tăng dần của danh
sách nối đơn, đối với danh sách nối kép hoăc theo chiều giảm dần tương tự).
8. Xóa một phần tử x ra khỏi danh sách sao cho danh sách mới vẫn bảo đảm thứ tự.
Khi xóa một phần tử không làm thay đổi thứ tự danh sách.
Xóa một phần tử như sau:
+ Nếu danh sách rỗng hoặc không có x trong danh sách: báo lỗi
+ Ngược lại, cho nút bên trái x nắm thông tin nút bên phải x và nút bên phải x nắm
thông tin nút bên trái x
9. Xây dựng một hàm thêm một số bất kỳ vào một danh sách sao cho thứ tự không
đổi (cách làm như câu 7)
Sau đó khai báo một danh sách kq, rồi duyệt qua các phần tử của 2 danh
sách, tại mỗi phần tử ta gọi hàm thêm vào ở trên với đối số là phần tử đang xét và
danh sách kq
Bài tập thực hành CTDL & GT Trang 81

BÀI THỰC HÀNH 9. CÂY NHỊ PHÂN


* Mục tiêu
- Hệ thống lại kiến thức về kiểu dữ liệu cây nhị phân như: Cách khai báo, các phép
duyệt trên cây nhị phân
- Cài đặt được các phép duyệt và một số thao tác trên cây nhị phân.
* Yêu cầu:
A. Bài tập mẫu
Bài 1: Tạo cây nhị phân, sau đó hiển thị các nút trên cây theo phép duyệt thứ tự giữa
Lời giải mẫu
using System;
class Node
{
public int Data;
public Node Left, Right;
}
class Trees
{
static void InSert(ref Node root, int newdata)
{
if (root == null)
{
root = new Node();
root.Data = newdata;
root.Left = null;
root.Next = null;
}
else if (newdata < root.Data) InSert(ref root.Left, newdata);
else if (newdata > root.Data) InSert(ref root.Right, newdata);
else Console.WriteLine("Error: Trung lap du lieu");
}
static void Duyet(Node nut)
{
Bài tập thực hành CTDL & GT Trang 82
Console.Write(nut.Data + "->");
}
static void Pinorder(Node nut)
{
if (nut != null)
{
Pinorder(nut.Left);
Duyet(nut);
Pinorder(nut.Right);
}
}
//Ham thuc hien cac chuc nang cua chuong trinh
static void Main()
{
Node root = null; int newdata;
do
{
Console.Write("Ket thuc nhap nhan Enter");
newdata = int.Parse("0" + Console.ReadLine());
if (newdata != 0) InSert(ref root, newdata);
} while (newdata != 0);
Console.WriteLine("Duyet thu tu giua_LNR su dung de quy:");
Pinorder(root);
Console.ReadKey();
}
}
Câu 2: Thực hiện những thao tác sau với cây nhị phân tìm kiếm
a) Tạo cây nhị phân tìm kiếm với các khóa cho trước
b) Khiểm tra xem một khóa bất kỳ có trong cây không?
c) Hiện thị kết quả khi duyệt cây theo thứ tự giữa
d) Đếm số node trong cây
e) Đếm số nút có một con
Bài tập thực hành CTDL & GT Trang 83
f) Đếm số nút có hai con
g) Đếm số lá của cây
h) Chiều cao của cây
Lời giải mẫu
using System;
class Node
{
public int value;
public Node left, right;
}
class Tree
{
static Node Root = null;//Goc cua cay nhi phan
//Phuong thuc them mot nut co khoa la X vao cay nhi phan p
static void Insert(ref Node Root, int X)
{
if (Root == null)
{
Root = new Node();
Root.value = X;
Root.left = null;
Root.right = null;
}
else
{
if (Root.value < X) Insert(ref Root.right, X);
else if (Root.value > X) Insert(ref Root.left, X);
}
}
static void Search(Node Root, int x, ref Node p)
{
//Nếu tìm thấy thì p chỉ tới nút có trường khoá bằng x, ngược lại p = null}
p = Root;
if (p != null)
if (x < p.value) Search(Root.left, x, ref p);
else if (x > p.value) Search(Root.right, x, ref p);
else Console.WriteLine("Da tim thay nut co gia tri"+x);
else
Console.WriteLine("Khong tim thay nut co gia tri" + x);

//Phuong thuc duyet cay nhi phan tho thu tu giua


static void LNR(Node Root)
{
if (Root != null)
Bài tập thực hành CTDL & GT Trang 84
{
LNR(Root.left);
Console.Write("{0} -> ", Root.value);
LNR(Root.right);
}
}
//Dem so nut cua cay nhi phan, luu trong bien dem
static void CountNode(Node Root,ref int dem)
{
if (Root != null)
{
dem++;
CountNode(Root.left, ref dem);
CountNode(Root.right, ref dem);
}
}
static int Count1Node(Node Root)
{
if (Root == null)
return 0;
else if ((Root.left == null) && (Root.right == null))
return 0;
else if (Root.left == null)
return 1 + Count1Node(Root.right);
else if (Root.right == null)
return 1 + Count1Node(Root.left);
else
return Count1Node(Root.left) + Count1Node(Root.right);
}
static int Count2Node(Node Root)
{
if (Root == null)
return 0;
else if ((Root.left == null) && (Root.right == null))
return 0;
else if (Root.left == null)
return Count2Node(Root.right);
else if (Root.right == null)
return Count2Node(Root.left);
else
return 1 + Count2Node(Root.left) + Count2Node(Root.right);
}
static int CountLeaf(Node Root)
{
if (Root == null)
return 0;
else if ((Root.left == null) && (Root.right == null))
return 1;
Bài tập thực hành CTDL & GT Trang 85
else return CountLeaf(Root.left) + CountLeaf(Root.right);
}
static int max(int a, int b)
{
int max;
if (a > b)
max = a;
else
max = b;
return max;
}
static int high_tree(Node Root)
{
if (Root == null)
return 0;
else
return 1 + max(high_tree(Root.left), high_tree(Root.right));
}
static void Main()
{
int[] A = new int[] { 6, 13, 25, 14, 51, 42, 12, 31, 56, 67, 34 };
//Lan luot them cac phan tu cua mang A vao cay nhi phan thoa man dieu kien la cay
nhi phan tim kiem
for (int i = 0; i < A.Length; i++)
Insert(ref Root, A[i]);
//In ra cay nhi phan bang cach duyet theo thu tu giua
LNR(Root);
Console.WriteLine();
Node p = null;
Search(Root, 56, ref p);
//Dem so luong nut tren cay nhi phan
int dem = 0;
CountNode(Root, ref dem);
Console.Write("\nSo nut tren cay nhi phan {0}", dem);
Console.Write("\nSo node co mot con: {0}", Count1Node(Root));
Console.Write("\nSo node co hai con: {0}", Count2Node(Root));
Console.Write("\nSo la: {0}", CountLeaf(Root));
Console.Write("\nChieu cao: {0}", high_tree(Root));
Console.ReadKey();
}
}

Câu 3: Viết chương trình cài đặt cây biểu thức với các yêu cầu sau:
a) Tạo cây biểu thức
b) Tính giá trị của cây biểu thức
c) Hiển thị giá trị các node theo thứ tự sau
Gợi ý
Bài tập thực hành CTDL & GT Trang 86
Thuật toán này yêu cầu sử dụng 2 stack:
- OperatorStack: Chứa các toán tử
- NodeStack: Chứa các node tạo nên cấu trúc cây (node gốc của các cây con được xây
dựng từ dưới lên)
Các bước tiến hành thuật toán
Tạo một phương thức phụ CreateSubTree() có nhiệm vụ tạo một cây biểu thức
gồm 3 node. Node gốc là toán tử Pop ra từ OperatorStack, hai node lá là toán hạng Pop
từ NodeStack. Cuối cùng đưa node gốc vào lại NodeStack.
Lặp qua từng token trong biểu thức infix
- Nếu là toán hạng: Push vào NodeStack
- Nếu là dấu mở ngoặc “(“: Push vào OperatorStack
- Nếu là dấu đóng ngoặc “)”:
+ Lặp cho đến khi lấy được dấu mở ngoặc “(“ trong OperatorStack, mỗi lần lặp
gọi phương thức CreateSubTree().
+ Pop dấu mở ngoặc ra khỏi OperatorStack.
- Nếu là toán tử:
+ Lặp cho đến khi OperatorStack rỗng hoặc độ ưu tiên của toán tử ở đỉnh
OperatorStack nhỏ hơn độ ưu tiên của toán tử hiện tại. Mỗi lần lặp gọi phương
thức CreateSubTree()
+ Push toán tử vào OperatorStack.
Khi hết vòng lặp, nếu OperatorStack còn phần tử, gọi phương thức CreateSubTree() cho
đến khi OperatorStack rỗng.
Node cuối cùng nằm trong NodeStack là node gốc của cây.
Ví dụ chuyển biểu thức infix sau thành cây biểu thức: (a+b)*c-d/e

Token OperatorStack NodeStack Description

( ( {Empty} Push “(“ vào OperatorStack

a ( a Push “a” vào NodeStack

+ (+ a Push “+” vào OperatorStack

b (+ ab Push “b” vào NodeStack

) {Empty} + Cho “a”, “b” ra thành node con của “+”


Push “+” vào NodeStack

* * + Push “*” vào OperatorStack

c * +c Push “c” vào NodeStack

- - * Cho “+”, “c” thành node con của “*”


Push “*” vào node Stack
Push “-“ vào OperatorStack

d - *d Push “d” vào NodeStack


Bài tập thực hành CTDL & GT Trang 87
/ -/ *d Push “/” vào OperatorStack

e -/ *de Push “e” vào NodeStack

-/ *de Kết thúc vòng lặp

- */ Cho “d” và “e” thành node con của “/”


Push “/” vào NodeStack

{Empty} - Cho “*” và “/” thành node con của “-“


Push “-“ vào NodeStack

Lời giải mẫu

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
class BinaryTreeNode
{
public BinaryTreeNode LeftChild;
public BinaryTreeNode RightChild;
public string Value;

public bool IsLeaf


{
get { return this.LeftChild == null && this.RightChild == null; }
}

public BinaryTreeNode(string value)


{
Value = value;
}

}
class Expression
{

static string Infix2Prefix(string infix)


{
List<string> prefix = new List<string>();
Stack<string> stack = new Stack<string>();
for(int i=0;i<infix.Length;++i)
{
if (IsOperator(infix[i]))
prefix.Add(infix[i].ToString());
else if (infix[i] == ')')
Bài tập thực hành CTDL & GT Trang 88
stack.Push(infix[i].ToString());
else if (infix[i] == '(')
{
string x = stack.Pop();
while (x != ")")
{
prefix.Add(x);
x = stack.Pop();
}
}
else
{
while (stack.Count > 0 && Priority(infix[i].ToString()) <
Priority(stack.Peek()))
prefix.Add(stack.Pop());
stack.Push(infix[i].ToString());
}
}

while (stack.Count > 0)


prefix.Add(stack.Pop());

StringBuilder str = new StringBuilder();


for (int i = prefix.Count - 1; i >= 0; i--)
{
str.Append(prefix[i]);
}

return str.ToString();
}

static double EvaluateExpressionTree(BinaryTreeNode node)


{
double t = 0;
if (node.IsLeaf)
t = double.Parse(node.Value);
else
{
double x = EvaluateExpressionTree(node.LeftChild);
double y = EvaluateExpressionTree(node.RightChild);

switch (node.Value)
{
case "+": t = x + y; break;
case "-": t = x - y; break;
case "*": t = x * y; break;
case "/": t = x / y; break;
case "%": t = x % y; break;
Bài tập thực hành CTDL & GT Trang 89
}
}
return t;
}

static void CreateSubTree(Stack<BinaryTreeNode> opStack,


Stack<BinaryTreeNode> nodeStack)
{
BinaryTreeNode node = opStack.Pop();
node.RightChild = nodeStack.Pop();
node.LeftChild = nodeStack.Pop();
nodeStack.Push(node);
}

static BinaryTreeNode Infix2ExpressionTree(string infixExpression)


{
List<string> prefix = new List<string>();
Stack<BinaryTreeNode> operatorStack = new Stack<BinaryTreeNode>();
Stack<BinaryTreeNode> nodeStack = new Stack<BinaryTreeNode>();
for (int i = 0; i < infixExpression.Length; ++i)
{
if (IsOperator(infixExpression[i]))
{
while (operatorStack.Count > 0 && Priority(operatorStack.Peek().Value) >=
Priority(infixExpression[i].ToString()))
CreateSubTree(operatorStack, nodeStack);
operatorStack.Push(new BinaryTreeNode(infixExpression[i].ToString()));
}
else if (infixExpression[i] == '(')
operatorStack.Push(new BinaryTreeNode(infixExpression[i].ToString()));
else if (infixExpression[i] == ')')
{
while (operatorStack.Peek().Value != "(")
CreateSubTree(operatorStack, nodeStack);
operatorStack.Pop();
}
else
nodeStack.Push(new BinaryTreeNode(infixExpression[i].ToString()));
}

while (operatorStack.Count > 0)


CreateSubTree(operatorStack, nodeStack);

return nodeStack.Peek();
}
static bool IsOperator(char str)
{
return str == '+' || str == '-' || str == '*' || str == '/';
Bài tập thực hành CTDL & GT Trang 90
}

static bool IsOperand(char str)


{
return str >= '0' && str <= '9';
}
public static int Priority(string op)
{
if (op == "*" || op == "/")
return 2;
if (op == "+" || op == "-")
return 1;
return 0;
}

static void LRN(BinaryTreeNode p) //phuong thuc duyet cay nhi phan


{
if (p != null)
{
LRN (p.LeftChild);
LRN (p.RightChild);
Console.Write("{0} -> ", p.Value);
}
}
static void Main()
{

string x = "(5-3)*(3+1)/2";
BinaryTreeNode s = Expression.Infix2ExpressionTree(x);
Console.WriteLine(Expression.EvaluateExpressionTree(s));
Expression. LRN (s);
Console.ReadKey();

}
}

Bài tập tự giải


Bài 1: Viết chương trình cài đặt một cây biểu thức, tính trị của cây biểu thức này.
Yêu cầu chi tiết:
1. Viết phần khai báo để cài đặt một cây biểu thức.
2. Viết thủ tục khởi tạo cây rỗng.
3. Viết hàm kiểm tra cây rỗng.
Bài tập thực hành CTDL & GT Trang 91
4. Thiết kế hàm tạo cây từ cây con trái L, cây con phải R và nhản của nút n, bằng cách
xem đây là có nút gốc là n và 2 cây con tương ứng là L (con trái) và R (con phải).
5. Viết các thủ tục duyệt cây:
Duyệt tiền tự, trung tự, hậu tự .
Duyệt theo mức.
6. Viết hàm xác định số nút trong cây.
7. Thiết kế hàm xác định chiều cao của cây.
8. Viết hàm tính giá trị của cây biểu thức.
9. Viết hàm xác định mức của một nút trong cây.
Gợi ý:
1.Trước tiên ta khai báo 1 lớp node gồm 1 thành phần lưu trữ thông tin, 2 thành
phần chứa địa chỉ bên trái và bên phải và khai báo 1 node root(gốc)mới.
class node
{
public int x;
public node left, right;
}
2. Cây rỗng khi không có gốc nghĩa là root=null. Cây con trái rỗng root.left=null,
cây con phải rỗng root.next=null.
3.Trả về giá trị root=null.
4.-Đối với phép toán hai ngôi(chẳng hạn +.-,*,/) được biểu diễn gốc chứa toán tử,
cây con trái biểu diễn toán hạng bên trái, cây con phải biểu diễn toán hạng bên
phải.
-Đối với phép toán 1 ngôi như “phủ định” hoặc “lấy giá trị tuyệt đối”,hoặc các
hàm chuẩn như exp() hoặc cos()…thì cây con trái rỗng.
-Còn với các phép toán 1 toán hạng như phép “lấy đạo hàm” ()’ hoặc “hàm giai
thừa” ()! Thì cây con phải rỗng.
5. -Duyệt tiền tự: thăm gốc->duyệt cây con trái theo thứ tự trước->duyệt cây con
phải theo thứ tự trước.
-Duyệt trung tự: duyệt cây con trái theo thứ tự giữa->thăm gốc->duyệt cây con
phải theo thứ tự giữa
Bài tập thực hành CTDL & GT Trang 92
-Duyệt hậu tự: duyệt cây con trái theo thứ tự sau->duyệt cây con phải theo thứ tự
sau->thăm gốc.
-Duyệt theo mức: ta sử dụng kiểu hàng đợi queue để duyệt. Nếu cây không rỗng
thì thực hiện đưa cây con vào queue, trong khi queue chưa rỗng thì in ra giá trị
trong queue, nếu cây con trái không rỗng thì duyệt mức cây bên trái,ngược lại
duyệt mức cây bên phải.
6. Duyệt cho đến khi cây rỗng số nút trong cây=gốc+cây con bên trái+cây con bên
phải.
7.Chiều cao của cây được xác định bằng công thức h=[log2(n+1)], trong đó n là số
nút trên cây đó.
8.Đầu tiên ta phải duyệt cây theo thứ tự trước để chuyển biểu thức về dạng tiền tố
hoặc duyệt theo thứ tự sau để chuyển biểu thức về dạng hậu tố. Sau đó ta thực hiện
tính toán giá trị biểu thức giống như bài tính toán biểu thức ở phần stack.

Bài 2: Viết chương trình cài đặt một cây tìm kiếm nhị phân (nhãn của mỗi nút được nhập
từ bàn phím) .
Yêu cầu chi tiết:
1. Viết phần khai báo để cài đặt một cây tìm kiếm nhị phân.
2. Viết thủ tục khởi tạo cây rỗng.
3. Viết hàm kiểm tra cây rỗng.
4. Viết thủ tục xen một nút vào cây tìm kiếm nhị phân.
5. Viết thủ tục xóa một nút trong cây tìm kiếm nhị phân.
6. Viết thủ tục nhập một cây tìm kiếm nhị phân với nhản của các nút của cây được nhập
vào từ bàn phím.
7. Viết các thủ tục duyệt cây:
Duyệt tiền tự, trung tự, hậu tự .
Duyệt theo mức.
8. Viết hàm xác định số nút trong cây.
9. Thiết kế hàm xác định chiều cao của cây.
10. Viết hàm xác định mức của một nút trong cây.
Gợi ý:
Bài tập thực hành CTDL & GT Trang 93
1.Trước tiên ta khai báo 1 lớp node gồm 1 thành phần lưu trữ thông tin ,2 thành
phần chứa địa chỉ bên trái và bên phải và khai báo 1 node root (gốc)mới.
class node
{
public int info;
public node left, right;
}
2.Cây rỗng khi không có gốc nghĩa là root=null.cây con trái rỗng root.left=null,
cây con phải rỗng root.next=null.
3.Trả về giá trị root=null.
4.Ta có thể xen một nút vào cây nhị phân vào nhiều chỗ khác nhau nhưng thêm
vào một nút lá là thuận tiện nhất. Trước tiên ta tạo 1 node p mới và gán p.info=x(x
là giá trị của nút muốn thêm vào). Sau đó kiểm tra nếu gốc rỗng thì gán luôn nút
mới là gốc, cây con trái,phải của gốc bằng null, ngược lại nếu x mà lớn hơn giá trị
của gốc thì ta them vào cây con bên phải, nhỏ hơn them vào cây con bên trái.
5.Xóa nút trên cây có thể xảy ra 3 trường hợp:
-x là nút lá
-x là nút nửa lá(chỉ có một con trái hoặc con phải)
-x có đủ hai con(trường hợp tổng quát)
Nếu nút muốn xóa không có cây con trái và cây con phải thì ta chỉ cần xóa
nút đó là xong, ngược lại nếu có 1 cây con trái hoặc cây con phải thì thì cho cây
con trái hoặc cây con phải móc nối với cây cha của nút đó.
Nếu nút muốn xóa có cả 2 cây con phải và cây con trái thì ta so sánh cây
con phải và cây con trái của nút đó với cây cha nếu cây con trái nhỏ hơn cây cha
của nút cần xóa thì cho chúng móc nối với nhau, ngược lại ta cho cây con phải
móc nối với cây cha của nút cần xóa.
6.Nếu cây rỗng thì gán luôn giá trị nhập vào bằng gốc, ngược lại so sánh với gốc
nếu nhỏ hơn thì gán bằng cây con trái của gốc, ngược lại gán bằng cây con phải
của gốc,c ứ như vậy khi nào kết thúc việc nhập vào thì thôi.
7. Duyệt tiền tự: thăm gốc->duyệt cây con trái theo thứ tự trước->duyệt cây con
phải theo thứ tự trước.
Bài tập thực hành CTDL & GT Trang 94
-Duyệt trung tự: duyệt cây con trái theo thứ tự giữa->thăm gốc->duyệt cây con
phải theo thứ tự giữa
-Duyệt hậu tự: duyệt cây con trái theo thứ tự sau->duyệt cây con phải theo thứ tự
sau->thăm gốc.
-Duyệt theo mức: ta sử dụng kiểu hàng đợi queue để duyệt. Nếu cây không rỗng
thì thực hiện đưa cây vào queue,trong khi queue chưa rỗng thì in ra giá trị trong
queue,nếu cây con trái không rỗng thì duyệt mức cây bên trái,ngược lại duyệt mức
cây bên phải.
8. Duyệt cho đến khi cây rỗng số nút trong cây=gốc+cây con bên trái+cây con bên
phải.
9. Chiều cao của cây được xác định bằng công thức h=[log2(n+1)],trong đó n là số
nút trên cây đó.
Bài 3: Viết chương trình cài đặt một cây tổng quát ( dùng mảng hay con trỏ ).
Yêu cầu chi tiết:
1. Viết phần khai báo để cài đặt một cây.
2. Viết thủ tục khởi tạo cây rỗng.
3. Viết hàm kiểm tra cây rỗng.
4. Viết thủ tục nhập một cây với nhản của các nút của cây được nhập từ bàn phím.
5. Viết các thủ tục duyệt cây:
Duyệt tiền tự.
Duyệt trung tự.
Duyệt hậu tự.
Duyệt theo mức.
6. Viết hàm xác định số nút trong cây.
7. Thiết kế hàm xác định chiều cao của cây.
8. Viết hàm xác định mức của một nút trong cây (nút đầu tiên có nhản cần xác định).
9. Thiết lập một hàm kiểm tra sự hiện hữu của một nút nào đó trong cây.
10. Thiết lập hàm xác định nhản của nút con trái nhất của một nút với nhản của nút được
nhập từ bàn phím.
11. Thiết lập hàm xác định nhản của nút anh ruột phải của một nút với nhản của nút
được nhập từ bàn phím.
Bài tập thực hành CTDL & GT Trang 95
12. Thiết lập hàm xác định nhản của nút cha của một nút với nhản của nút với nhản của
nút được nhập từ bàn phím.
Gợi ý:
1.Khai báo một lớp node gồm thành phần lưu trữ kiểu giá trị của từng nút,1 trường lưu
trữ địa chỉ của các nút con.
Class node {
Public int x;
Public node con1;
Public node con2;
….
}
2.Cây khai báo rỗng khi nút gốc được khai báo rỗng
3.Hàm kiểm tra cây rỗng trả giá trị True khi nút gốc là rỗng (root=null)
4.Sử dụng đệ quy .Thực hiện kiểm tra cây
+ Nếu rỗng (root=null) gán gốc có giá trị vừa nhập và các nút con trỏ vào null
+Ngược lại,Kiểm tra cây con thứ nhất (root.con1) nếu rỗng gán cho giá trị vừa nhập và
các nút con của nó trỏ vào null,nếu không rỗng thực hiện kiểm tra các nút kế tiếp việc
gán thực hiện tương tự
5.Xây dựng một hàm duyệt hiển thị giá trị của một nút. Trong các thuật toán duyệt cây ta
sử dụng hàm đệ quy
*Duyệt tiền tự : gọi hàm duyệt hiển thị nút gốc ,rồi duyệt đệ quy lần lượt các nút con của

*Duyệt trung tự :
*Duyệt hậu tự: gọi hàm duyệt đệ quy từng nút con của nút gốc và gọi hàm duyệt hiển thị
nút gốc
*Duyệt mức:khởi tạo một QUEUE ,duyệt qua từng nút của cây,tại mỗi nút thực hiện đưa
tất cả các nút con của nó vào Queue và hiện ra các phần tử trong Queue.Tiếp đến thực
hiện gọi hàm đệ quy Duyệt mức của các cây con tiếp theo
6.Duyệt qua các nút trong cây ,qua mỗi nút ta thực hiện tăng giá trị của một biến đếm nút
với khởi tạo bằng 0
7.hàm xác định chiều cao của cây trả giá trị loga cơ số 2 của( số nút +1)
8.
Bài tập thực hành CTDL & GT Trang 96

BÀI THỰC HÀNH 10. Kiểm tra thực hành

* Yêu cầu:
- Cài đặt được các thuật toán trên các kiểu dữ liệu
- Có khả năng áp dụng các thuật toán để lập trình giải các bài toán theo yêu cầu

You might also like