Professional Documents
Culture Documents
dạng những câu hỏi ngắn như câu hỏi lý thuyết hoặc viết 1 chương trình
ngắn, tìm chiều cao tìm số nút
thi cuối kỳ trắc nghiệm, sử dụng tài liệu giấy (dự kiến không dc sd)
nắm vững lý thuyết,công thức, cấp độ độ phức tạp của các thuật toán,câu hỏi
lý thuyết, nắm được những giải thuật chính,40-50c
BÀI TẬP :
Bài tập 1. Đệ quy
Hiện thực giải thuật đệ quy Euclid tính ước số chung lớn nhất của 2 số
nguyên dương.
#include<stdio.h>
if(b == 0)
return a;
if ( a%b==0)
return b;
int main(){
int a,b;
scanf("%d",&a);
scanf("%d",&b);
printf("UCLN(%d,%d)=%d",a,b,UCLN(a,b));
}
1. Viết hàm
void Swap(List *list, int p1, int p1) {
....
}
thực hiện việc hoán đổi 2 phần tử ở vị trí p1 và p2 của danh sách đặc
(ArrayList).
Ví dụ:
list=(1,2,3,4), p1=0, p2=2 => list = (3,2,1,4)
list=(1,2,3,4), p1=0, p2=3 => list = (4,2,3,1)
list=(1,2,3,4), p1=1, p2=1 => list = (1,2,3,4)
1.
int temp=0;
temp=list->data[p1];
list->data[p1] = list->data[p2];
list->data[p2] = temp;
}
2. Viết hàm
void Reverse(List *list) {
...
}
thực hiện việc đảo ngược vị trí các phần tử trong danh sách.
Ví dụ:
list=(1,2,3,4) => list=(4,3,2,1)
list=(1) => list=(1)
2.
void Reverse(List *list) {
int i;
for (i=0; 2*i<list->length-1;i++)
Swap(list , i , list->length- i-1); }
Buổi 1:
Chương 1: Tổng quan giải thuật ứng dụng trong kinh doanh
Nội dung học:
20% thi giữa kì
20% tiểu luận nhóm
50% thi cuối kỳ trắc nghiệm
tổng cộng 7 chương
-Vai trò của tổ chức dữ liệu, tại sao phải tổ chức dữ liệu?
+dữ liệu: là những số liệu rời rạc của một sự vật hiện tượng cần quản lý.cd:
nhân viên: mã, họ tên, địa chỉ, sdt,lương…
+Thao tác trên dữ liệu: tìm kiếm, sửa đổi, xóa, biến đổi dữ liệu, lập báo cáo…
-Mối quan hệ giữa cấu trúc dữ liệu và giải thuật:
-một số vidu:
+thuật toán bubblesort: sắp xếp dãy,
+biến temp: biến tạm
-Phân tích các hàm đệ quy:
+hàm tính giai thừa:
Trắc nghiệm:
CÂU HỎI VÀ BÀI TẬP CHƯƠNG 1
Câu 1: Cấu trúc dữ liệu là …
d. Cách lưu trữ dữ liệu trong bộ nhớ truy cập ngẫu nhiên (RAM), sao cho nó
có thể được sử dụng một cách hiệu quả
Câu 2: Bằng cách chạy thử 1 thuật toán với 1 bộ dữ liệu, ta có thể khẳng
định điều gì?
b. Khẳng định thuật toán sai nếu cho kết quả sai
Câu 3: Hàm thể hiện độ phức tạp có dạng thường gặp là gì?
a. log2n, n, nlog2n
b. n^2 , n^3
c. 2^n, 3^n , n! , n^n
d. Cả ba câu trên đều đúng
Câu 4: Khi đánh giá độ phức tạp của câu lệnh If ta cần đánh giá điều gì?
c. Độ phức tạp của việc kiểm tra điều kiện và độ phức tạp của câu lệnh bên
trong thân If
Câu 5: Độ phức tạp trong trường hợp xấu nhất của thuật toán sau là gì?
boolean S(int A[], int n, int X)
{ for (int i = 0; i<n; i++)
{ if (A[i] == X)
return true;
}
return false;
}
b. O(n)
Câu 6: Thời gian thực hiện của một thuật toán được tính toán ra kết quả
là T(n) = 2*n + n*logn. Ký hiệu O của độ phức tạp thuật toán trên là gì?
c. O(nlogn)
Câu 7: Phát biểu nào sau đây sai?
c. O(2n) có độ phức tạp nhỏ hơn O(3n+5)
Câu 8: Độ phức tạp của thuật toán tính giá trị của biểu thức 1+2+3+...+n
là gì?
b. O(n)
Câu 9: Độ phức tạp của thuật toán tính giá trị của biểu thức n²+35n+6 là
gì?
b. O(n^2)
Câu 10: Thuật ngữ nào dưới đây được sử dụng để mô tả một thuật toán
có độ phức tạp là O(n)?
b. Độ phức tạp tuyến tính (tuần tự)
Câu 11: Biểu diễn công thức (n - 2)*(n - 4) sử dụng ký hiệu O để biểu
diễn độ phức tạp là gì?
d. Cả ba câu trên đều sai
Câu 12: Thường ta coi T(n) là thời gian thực hiện chương trình trong
trường hợp xấu nhất trên dữ liệu đầu vào có kích thước n, tức T(n) là
gì?
d. Thời gian lớn nhất để thực hiện chương trình đối với mọi dữ liệu vào có
cùng kích thước n
Câu 13: Công cụ nào sau đây được dùng để diễn đạt thuật toán?
a. Ngôn ngữ lập trình
b. Lưu đồ (sơ đồ khối)
c. Ngôn ngữ tự nhiên
d. Cả ba câu trên đều đúng
Câu 14: Phát biểu nào sau đây là sai khi chọn để điền vào dấu … trong
phát biểu sau: "Thuật toán là một dãy hữu hạn các thao tác, trong đó
…"?
c. Thứ tự thao tác không quan trọng
Câu 15: Cho biết kết quả khi thực hiện thuật toán sau với a[]= {-3, -3, 15,
-3}; n= 4; x= -3:
int FindX(int a[], int n, int x)
{ int i;
for (i= n-1; i >= 0; i--)
if (a[i]==x) return (i);
return (-1);
}
=> =3
Câu 16: Quá trình giải quyết bài toán được thực hiện bằng việc chia bài
toán lớn thành các bài toán nhỏ hơn để giải quyết, được áp dụng theo
chiến lược nào sau đây?
a. Chia để trị
Câu 17: Cho mảng các số nguyên A[] và thuật toán sau, cho biết độ
phức tạp T(n) của thuật toán là gì?
Alg1(int A[], int n)
{ for (k= 0; k<n-1, k++)
for (j=k; j<=k+1;j++)
A[j] = A[k] + 1
}
=> O(n^2)
Câu 18: Lý thuyết thuật toán quan tâm điều gì?
d. Cả ba câu trên đều đúng
Câu 19: Giả sử ta có hai thuật toán P1 và P2 với thời gian thực hiện
tương ứng là T1(n) =100n^2 và T2(n) = 5n^3. Với n < 20, thuật toán nào
sẽ thực hiện nhanh hơn?
d. Thuật toán P2
Câu 20: Trong đoạn chương trình có hai vòng lặp for lồng nhau, qui tắc
nào sau đây là chính xác để đánh giá độ phức tạp của thuật toán?
b. Qui tắc nhân
CHƯƠNG 2:
-Hàm đệ quy:
+Hàm (phương thức) được gọi là đẹ quy nếu quá trình thực hiện nó tự gọi
đến chính mình
+đệ quy trực tiếp: nằm bên trong hàm (phương thức)(*)
+đệ quy gián tiếp: Nếu hàm(phương thức) gọi tới hàm(phương thức) khác,
mà hàm (phương thức) này lần lượt gọi tới hàm (phương thức) ban đầu.
+Tính xác định
+tính hữu hạn( tính dừng): hàm đệ quy hay gặp khó khăn, không biết liệu hàm
có dừng hay không hoặc chạy bao lâu mới dừng (điểm yếu).Để thực hiện
dừng, phải có 2 phần: phần đệ qui(yêu cầu gọi đẹ qui, thực hiện lại giải thuật
ở cấp độ thấp hơn,thường được đặt trong một điều kiện), phần không đệ qui
(phần cơ sở-phần làm dừng tính đệ qui,gồm các trường hợp không cần thực
hiện lại giải thuật, dừng trực tiếp giải quyết được bài toán)
+tính đúng đắn
+ưu điểm: mạnh ở tính lặp,diễn giải đệ quy có thể ngắn gọn hơn, định nghĩa
một tập hợp rất lớn các đối tượng bằng 1 số các câu lệnh hữu hạn
+khuyết điểm: không xác định được bước lặp (không xác định được tính
dừng), gây tràn đệ quy (tràn bộ nhớ đệ quy)=>máy tính báo lỗi
=>tìm cách xử lý (khử đệ quy), chỉ khi nào không thể dùng giải thuật lặp ta
mới nên sử dụng thuật toán đệ qui.(đệ qui: Mạnh, dễ gọi nhưng khó quản lý,
có thể gây tràn bộ nhớ nếu máy tính yếu,vòng lặp quá lớn)
-cơ chế hoạt động của đệ qui:
+hoạt động theo cơ chế LIFO(last in first out) ( khác cơ chế FIFO)
+Dùng stack để lưu vết dữ liệu và chỉ thị lệnh,
+biến cục bộ: được gọi lại khi đang lưu giữ trạng thái
-Phân loại đệ qui:
+đệ qui tuyến tính: có duy nhất 1 lời gọi hàm gọi lại chính nó
+đệ qui nhị phân: trong thân hàm có 2 lời gọi hàm gọi lại chính nó một cách
tường minh
+đệ qui phi tuyến: Trong thân của hàm có lời gọi hàm gọi lại chính nó được
đặt bên trong vòng lặp
+đệ qui tương hỗ: Trong thân của hàm này có lời gọi hàm đến hàm kia và
trong thân của hàm kia có lời gọi hàm tới hàm này.
-Khử đệ qui:
+nên dùng vòng lặp vì đệ qui rất tốn bộ nhớ, xử lý chậm: khử đệ qui bằng giai
thừa, số fibonacci
BUỔI 3:
Nắm dc 5 chương cslt
chương 1:
CHƯƠNG 2:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
int main()
{
++x:
x++:
printf: yêu cầu nhập
scanf:
int a,b;
printf("nhap 2 so a va b: ");
scanf("%d%d",&a,&b);
printf("tong a cong b: %d ",a+b);
return 0;
}
Chương 3:
Cấu trúc điều kiện:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
float a,b,x;
printf ("nhap hai so a,b: ");
scanf("%f%f",&a,&b);
if (a==0)
if (b==0) printf("vo so nghiem.\n");
else printf("vo nghiem.\n");
else{
x=-b/a;
printf("mot nghiem x=%8.2f\n",x);
}
return 0;
}
int main()
{
int i,n;
printf("nhap vao so n: ");
scanf("%d",&n);
for (i=1;i<=n;i++)
printf("%d",i);
return 0;
}
int main()
{
int k, dem=0;
scanf("%d",&k);
while (k!=0)
{
dem++;
scanf("%d",&k);
}
printf("so luong cac so nguyen da nhap la: %d",dem);
getch();
}
CHƯƠNG 4
Chương trình con:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
int a=2912, b=1706;
int sum= Tong(a,b);
printf("tong la: %d",sum);
return 0;
}
int Tong(int a, int b)
{
return a+b;
}
chụp màn hình máy tính màu đen, nhập vào dòng đầu tiên “chương trình của
nguyễn văn a”, trả lời câu hỏi lý thuyết, câu hỏi thực hành
bài 1:Viết chương trình tính giai thừa của số tự nhiên n, với n được
nhập từ bàn phím slide 13
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
int Giaithua(int n)
{
if((n==1)||(n==0)) { return 1;}
return Giaithua(n-1)*n;
}
int main(){
int Giaithua(int n);
}
bài 2: slide 19
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
int main(){
int a,b;
printf("chuong trinh tinh fibonaci cua: le tu quynh nhi \n");
a=5;/*thay bang lenh print yeu cau nhap so a*/
b= Fibonaci (a);
printf("%d",b);
getch();
return 0;
}
—------------
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
int main(){
int a,b;
printf("chuong trinh tinh fibonaci cua: le tu quynh nhi \n");
printf("nhap a: ");
scanf("%d",&a);
b= Fibonaci (a);
printf("%d",b);
getch();
return 0;
}
—------------
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
int main(){
int a,b;
printf("chuong trinh tinh fibonaci cua: le tu quynh nhi \n");
printf("nhap a: ");
scanf("%d",&a);
b= Fibonaci (a);
printf("giai thua fibonaci thu %d la: %d",a,b);
getch();
return 0;
}
bài 3: Viết chương trình tính tổng của n số tự nhiên đầu tiên, với n được
nhập từ bàn phím(slide 17)
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
● Độ sâu tối đa của hàm đệ quy (vì nó đưa vào stack). Độ sâu ít thì nhanh hơn
int main()
{
printf("chuong trinh cua le tu quynh nhi\n");
int n,t;
printf("nhap so: ");
scanf("%d",&n);
void H10toH2(int n)
{
if (n <= 0) return;
int t = n%2;
H10toH2(n/2);
printf("%d",t);
—-----------------------------------------------
#include<iostream>
using namespace std;
int C(int k, int n) {
if (k == 0 || k == n) return 1;
if (k == 1) return n;
return C(k - 1, n - 1) + C(k, n - 1);
}
int main(){
int n, k;
cout << "Nhap k: ";
cin >> k;
cout << "Nhap n: ";
cin >> n;
cout << "To hop bang: " << C(k, n);
return 0;
}
Bài 6: Viết chương trình minh họa bài toán Tháp Hà Nội bằng đệ quy
Bài 7: Viết chương trình tính ước số chung lớn nhất của 2 số. Minh họa quá
trình hoạt động của giải thuật.
Bài 8: Viết chương trình sắp xếp mảng số nguyên tăng dần. Minh họa quá
trình hoạt động của giải thuật.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
Bài 14:
#include <conio.h>
#include <stdio.h>
int max(int a[],int n)
{
if(n==1) return a[0];
if (a[n-1]>max(a,n-1)) return a[n-1];
return max(a,n-1);
}
int min(int a[],int n)
{
if (n==1) return a[0];
if (a[n-1]<min(a,n-1)) return a[n-1];
return min(a,n-1);
}
int main()
{
int a[50],n,i;
printf("CHUONG TRINH TIM MAX VA MIN CUA 1 MANG SO
NGUYEN CUA LETUQUYNHNHI\n");
printf("so luong phan tu: ");
scanf ("%d",&n);
-Danh sách là tập hợp các phần tử có kiểu dữ liệu xác định và giữa chúng có
một mối quan hệ nào đó. Số phần tử của danh sách gọi là chiều dài của DS.
Danh sách có chiều dài bằng 0 gọi là DS rỗng
-Danh sách đặc là danh sách mà không gian bộ nhớ lưu trữ các phần tử
được đặt liên tiếp nhau.
+elem A[spt] <=> int A[10] Ví dụ: int a[10]; int float b[20];
+Mảng được quản lý bằng một địa chỉ trong bộ nhớ máy tính và các phần tử
được bố trí tuần tự liên tiếp theo chỉ số. bắt đầu từ số 0, địa chỉ đối với danh
sách đặt không thay đổi, kích thước cố định, nội dung có thể thay đổi. dữ liệu
liên tiếp cô đặc lại với nhau gọi là danh sách đặc
+Ưu điểm: – Mật độ sử dụng 100% – Dễ dàng truy xuất đến từng phần tử:
+Nhược điểm: – Độ phức tạp thuật toán thêm/bớt phần tử vào/ra danh sách
là khá cao T(n)max=O(n) (insert vào tốn chi phí lớn) – Lãng phí khi trong danh
sách có nhiều phần tử cùng giá trị (không loại bỏ được các phần tử trùng
nhau)
+khai báo cấu trúc danh sách đặc:
● cấu trúc phức hợp: vd: sinh viên có mssv, tên, địa chỉ…
● khai báo:
#define MAXLIST 200
typedef struct DanhSachKe{
int num (chiều dài hiện tại của dsach liền kề);
int nodes[MAXLIST];
}List;
● Khởi tạo danh sách:
void Init(List &plist){ (đọc và ghi lại dữ liệu)
plist.num=0;
}
● xác định số nút (số phần tử)
int ListSize(List plist){
return plist.num;
}
● Kiểm tra danh sách rỗng
int IsEmpty(List plist){
return (plist.num==0); // Nếu rỗng trả về giá trị 1 (true), ngược lại trả về
giá trị 0 (false)
}
● Kiểm tra danh sách đầy
int IsFull(List plist){
return (plist.num==MAXLIST); // Nếu đầy trả về giá trị 1 (true), ngược lại
trả về giá trị 0 (false)
}
● Truy xuất một phần tử trong danh sách
int Retrieve(đọc)(List plist, int pos){
if(pos<0 || pos>=ListSize(plist))
{
printf("Vi tri %d khong hop le",pos); return -1;}
else
{ if(IsEmpty(plist))
printf("danh sach bi rong!!!");
else
return plist.nodes[pos];
}
}
● Thêm một phần tử vào danh sách:
void Insert(List &plist, int pos, int x){ int i;
if (pos<0 || pos>plist.num)
{ printf("Vi tri chen khong hop le !"); return; }
else{ if(IsEmpty(plist) ){
printf("Danh sach rong !");
return; }
else{
for (i=plist.num-1;i>=pos;i-- )
{ plist.nodes[i+1]=plist.nodes[i]; }
plist.nodes[pos]=x;
plist.num++; }
}
}
vdchuong3:
#include <stdio.h>
#include <conio.h>
#include <string.h>
//Khoi tao
void Init(List &plist){
plist.num=0;
}
// Xac dinh so nut cua danh sách:
int ListSize(List plist){
return plist.num;
}
// Kiem tra danh sách rong:
int IsEmpty(List plist){
return (plist.num==0);//Ham tra ve mot gia tri, neu =1 tuc la ds
rong, =0 la ds khong rong
}
// Kiem tra danh sách day
int IsFull(List plist){
return (plist.num==MAXLIST);
}
//Nhap danh sach
void Input(List &plist)
{
int i;
do
{
printf("\nNhap so phan tu: ");
scanf("%d",&plist.num); //1D array: scanf("%d",&n);
} while (plist.num<0);// neu so phan tu <0 thi yeu cau nhap lai
//INSERT
void Insert(List &plist, int pos, int x)
{ int i;
if (pos<0||pos>plist.num){ printf("Vi tri chen khong hop le !"); return; }
else{ if(IsEmpty(plist) ){
else {
for (i=plist.num-1;i>pos;i-- )
{ plist.nodes[i]=plist.nodes[i-1]; }
plist.nodes[pos]=x;
plist.num++; }
}
//truy xuat mot phan tu trong danh sach
int Retrieve(List plist, int pos){
if(pos<0 || pos>=ListSize(plist))
{ printf("Vi tri %d khong hop le",pos); return 0;}
else
{ if(IsEmpty(plist))
}
//xoa mot phan tu ra khoi danh sach
int Remove(List &plist, int pos){
int i;
int x;
if(pos <0 || pos>=ListSize(plist))
else{
x=plist.nodes[pos];
for(i=pos;i<ListSize(plist)-1;i++){
plist.nodes[i]=plist.nodes[i+1];
}
plist.num--;
return x;
}
}
//tim kiem 1 phan tu trong danh sach
int Search(List plist, int x){
int vitri=0;
while(plist.nodes[vitri]!=x && vitri<plist.num) vitri++;
if(vitri==plist.num) return -1;
return vitri;
}
//sap xep danh sach
void Sort1(List &plist){
int tam,i,j;
for(i=0;i<plist.num-1;i++)
for(j=i+1;j<plist.num;j++)
if(plist.nodes[i]>plist.nodes[j]) // Doi cho 2 phan tu nay
{
tam=plist.nodes[i];
plist.nodes[i]=plist.nodes[j];
plist.nodes[j]=tam;
}
}
//sap xep danh sach 2
void SelectionSort(List &plist){
int i,j,vitrimin,min;
for(i=0;i<plist.num-1;i++){
min=plist.nodes[i];
vitrimin=i;
for(j=i+1;j<plist.num;j++){
if(min >plist.nodes[j]){
min=plist.nodes[j];
vitrimin=j;
}
}
plist.nodes[vitrimin]=plist.nodes[i];
plist.nodes[i]=min;
}
}
//Hàm chính
int main(int argc, char** argv) {
bỏ ngày sinh, trộn 2 bài qlsv với danh sách liên kết
*NGĂN XẾP STACK:
#define MAXSTACK 100
struct stack{
int top;
int nodes[MAXSTACK];
};
typedef struct stack Stack;
-tác vụ IsEmpty(Stack s){
return (s.top==-1)
}
-tác vụ push:
void Push(Stack &s,int x){
s.nodes[++(s.top)]=x;
}
-tác vụ pop:
int Pop(Stack &s){
if(IsEmpty(s)){
printf(“Stack bi rong”); return 0
}
return s.nodes[s.top–];
}
-khai báo cấu trúc stack:
typedef struct node
{
DataType info;
struct node * next;
} Node;
typedef Node* NODEPTR;
typedef NODEPTR STACk;
STACK s;
-hiện thực stack bằng danh sách liên kết:
+khởi tạo stack:
void Init(STACK &s){
s=NULL;
}
+kiểm tra stack rỗng:
int IsEmpty(STACK s){
return (s==NULL);
}
+thêm một phần tử vào stack:
void Push(STACK &s, DataType x){
NODEPTR p = new Node;
p->info=x;
p->next=s;
s=p;
}
+lấy một phần tử ra khỏi stack:
int Pop(STACK &s,DataType &x)
{
if(isEmpty(s))
return 0;
NODEPTR p=s;
x=p->info;
s=s->next;
delete p;
return 1;
}
-giới thiệu về stack: cơ chế lifo, push và pop
-giới thiệu về queue (hàng đợi): cơ chế fifo, front và rear
+xử lí dịch vụ của ngân hàng, máy in
+insert: thêm nút mới vài cuối hàng đợi (luôn đưa vào rear), front không đổi
rear tăng, tối đa hàng đợi, nếu hàng đợi bị đầy k insert dc nữa
+remove: dùng để xóa (đưa vào front), front tăng rear không đổi, hàng đợi k bị
rỗng mới lấy ra được
+con trỏ front và rear ban đầu dc gán là -1, nếu có phần tử dc gán vào thì
front và rear mới nhảy lên vị trí 0
=>xu hướng rear tăng lên, nếu rear chạm đáy, dùng mảng vòng để hiện thực
hàng đợi
+mảng vòng:
● khai báo cấu trúc:
#define MAXQUEUE 100
struct queue{
int front , rear;
int nodes[MAXQUEUE];
}
● tác vụ khởi động:
void Init(struct queue &pq){
pq.front=pq.rear=MAXQUEUE-1;
}
● tác vụ kiểm tra hàng đợi trống:
int IsEmpty(struct queue &pq){
return (pq.front == pq.rear);
}
● tác vụ thêm vào hàng đợi:
void Insert(struct queue &pq, int x){
if(pq.rear==MAXQUEUE-1)
pq.rear = 0;
else
pq.rear++;
if(pq.rear ==pq->front)
printf(“hang doi bi day”);
else
pq.nodes[pq.rear]=x;
}
● Lấy một phần tử ra khỏi hàng đợi:
int Remove(struct queue &pq){
if(IsEmpty(pq))
printf(“hang doi bi day”);
else{
if(pq.front==MAXQUEUE-1)
pq.front=0;
else
pq.front++;
return pq.nodes[pq.front];
}
}
+dùng danh sách liên kết hiện thực hàng đợi
-Hàng đợi có ưu tiên:
CHƯƠNG 4: CÂY - TREE
-nút vuông: toán tử, nút tròn toán hạng
-nút lá: nút tận cùng, không có con nào phía dưới
-nút trong: internal node
-cây nhị phân đầy đủ:đầy đủ từ mức 1 đến mức h-1
-cây nhị phân gần đầy: đầy đủ nhưng mức h không đủ->sd mảng 1c không
phù hợp, nên sử dụng con trỏ
-chỉ có 1 trường dữ liệu là số nguyên và 2 con trỏ pleft, pright
typedef struct nodetype{
int data;
struct nodetype *pleft;
struct nodetype *pright;
}Node;
typedef Node * NODEPTR;
-duyệt cây nhị phân: phép xử lý các nút trên cây, duyệt qua 1l
+ Duyệt cây theo thứ tự trước (Preorder) (thông dụng nhất, hay còn được gọi
là Node left right):
● N trước LR(NLR): nếu duyệt xong F về C, kiểm tra C có cây con bên
phải không,các nút chỉ ghi 1 lần duy nhất, mỗi lần qua 1 nút là kiểm tra
2 cửa trái và phải
+TH2: nút có khóa x trên cây có 1 nút con(trái hoặc phải): tương đối không
phức tạp
+TH3:nút x có một cây hai con trái và phải
● cách 1: hủy gián tiếp lấy 15 dán lên 18, tìm nút lớn nhất bên trái
CHƯƠNG 5: ĐỒ THỊ-GRAPH
-đồ thị liên thông: tất cả các cặp nút luôn có con đường nối với nhau
-đồ thị có trọng số: mỗi cung được gán một giá trị số đặc trưng
3.Duyệt đồ thị
-tìm kiếm theo chiều sâu (DFS): đi tới không được thì phải lùi lại
-tìm kiếm theo chiều rộng (BFS):
-khái niệm nghịch thế: trong mảng từ trái qua phải xuất hiện cặp số mà con số
bên phải nhỏ hơn số bên trái thì gọi là một cặp nghịch thế : i<j và Mi>Mj. danh
sách có thứ tự sẽ không chứa nghịch thế
2.Phương pháp nổi bọt (bubble sort)
-ý tưởng: làm cho nổi bật, nhẹ thì nổi lên trên, nhỏ nhất nổi lên trên đầu
-trong quá trình sắp xếp sẽ đi từ trái qua phải, người bên trái lớn hơn người
bên phải thì đó là nghịch thế => đảo vị trí
-có 3 bước chính:
+Bước 1 : i = 0; // lần xử lý đầu tiên
+Bước 2 : j = N-1; //Duyệt từ cuối dãy ngược về vị trí i
● Trong khi (j > i) thực hiện:
● nếu M[j]<M[j-1]: M[j]↔M[j-1 ];//xét cặp phần tử kế cận
● j = j-1;
+Bước 3 : i = i+1; // lần xử lý kế tiếp
+Nếu i >=N-1: Hết dãy. Dừng
+Ngược lại : Lặp lại Bước 2.
-cài đặt giải thuật:
void BubbleSort(int M[], int n)
{
int i, j;
for (i = 0; i < n - 1; i++)
{
for (j = n - 1; j > i; j--)
{
if (M[j] < M[j - 1])// nếu có nghịch thế
{
int temp = M[j];
M[j] = M[j - 1];
M[j - 1] = temp;
}}}}
Bài tập: Hãy giải thích từng bước quá trình chạy Bubble Sort cho danh
sách dưới đây:
int[] M = {10,15,2,8,7};
BubbleSort(M);
giải:
void BubbleSort(int M[], int n)
{
int i, j;
for (i = 0; i < n - 1; i++)
{
for (j = n - 1; j > i; j--)
{
if (M[j] < M[j - 1])// nếu có nghịch thế
{
int temp = M[j];
M[j] = M[j - 1];
M[j - 1] = temp;
}}}}
int[] M = {10,15,2,8,7};
BubbleSort(M);
for1: (for1- lần 1)Khởi tạo i=0, kiểm tra i<n-1 ⇔i<5-1⇔0<4 đúng
for 2: (for2- lần 1)Khởi tạo j=n-1=4, kiểm tra j>i⇔ 4>0=>đúng
If: M[j] <M[j-1] <=> M[4]<M[3]
7<8 đúng, hoán đổi vị trí của hai phần tử này, ta có:
M = {10,15,2,7,8};
…
-khi chạy tới i=2 và j=3 thì trạng thái lúc đó ntn
-Độ phức tạp:
+xấu nhất và trung bình: O(n*n)
+tốt nhất: O(n)
-khuyết điểm:
+Không nhận diện được tình trạng dãy đã có thứ tự hay có thứ tự từng phần.
+Các phần tử nhỏ được đưa về vị trí đúng chỗ rất nhanh, trong khi các phần
tử lớn lại được đưa về vị trí đúng rất chậm.
3.Phương pháp chọn(Selection Sort)
-ý tưởng: mô phỏng một trong những cách sắp xếp tự nhiên nhất trong thực
tế, một mảng có i->n, tìm từ Ni đến N-1 vị trí nào nhỏ nhất thì đưa về vị trí i,
dãy hiện hành tiến về bên phải và thu nhỏ lại
-các bước:
+Bước 1 : i = 0;
+Bước 2 : Tìm phần tử M[min] nhỏ nhất trong dãy hiện hành từ M[i] đến M[N-
1]
+Bước 3 : Nếu min ≠ i, Hoán vị M[min] và M[i]
+Bước 4 : Nếu i < N-1 thì i = i+1; Lặp lại Bước 2
+Ngược lại: Dừng. //N-1 phần tử đã nằm đúng vị trí.
-câu lệnh:
void SelectionSort(int M[], int n)
{
int min;
for(int i=0;i<n-1;i++)
{
min = i;
for(int j=i+1;j<n;j++)
{
if (M[j] < M[min])
min = j;
}
if(min!=i)
{
int temp = M[i];
M[i] = M[min];
M[min] = temp;
}
}
}
Bài tập: Hãy giải thích từng bước quá trình chạy Selection Sort cho danh
sách dưới đây (sau khi chạy for1 lần thứ 3 kết quả ntn)
Int M[] = {10,15,2,8,7};
SelectionSort(M);
for1 lần 1: 2,10,15,8,7
for1 lần 2: 2,7,10,15,8
for1 lần 3: 2,7,8,10,15
-đánh giá giải thuật: số lượng phép so sánh không phụ thuộc vào tình trạng
của dãy số ban đầu
+tốt nhất:
● số lần so sánh: N(N-1)/2
● số lần gán: 0 (min hoán vị với i)
+xấu nhất:
● số lần so sánh: N(N-1)/2
● số lần gán: 3N (mảng bị sắp xếp ngược)
4.Phương pháp chèn(Insertion sort)
-mảng bên tay trái: được sắp xếp nội bộ (từ nhỏ đến lớn), xuất hiện nghịch
thế thì đánh dấu điểm đó,rút ra từ mảng bên phải chèn vào mảng bên trái, sau
khi insert vào vị trí thích hợp thì danh sách tăng lên, chèn vào mảng đã được
sắp xếp => chèn trực tiếp
-phương pháp chèn trực tiếp:
● Xét dãy n phần tử: M0, M1,…, Mn-1
● Xem dãy gồm 1 phần tử là M0 dãy có thứ tự
● Thêm M1 vào dãy có thứ tự M0 sao cho dãy mới M0,M1 là dãy có thứ
tự. Nếu M1 <M0 ta đổi chỗ M1 với M0.
● Thêm M2 vào dãy có thứ tự M0, M1 sao cho dãy mới M0, M1, M2 là
dãy có thứ tự
● Tiếp tục như thế đến n-1 bước ta sẽ có dãy có thứ tự M0, M1, …., Mn-1
-i xuất phát bằng 1 (khác các phương pháp khác)
-phương trình:
void InsertionSort(int M[],int n)
{
//lưu vị trí cần chèn
int pos=0;
int x;
//xem đoạn M[0] đã sắp
for (int i=1;i<n;i++)
{
x=M[i];//lưu trữ giá trị M[i] tránh bị ghi đè khi dời chỗ các phần tử
for(pos=i;(pos>0) &&( M[pos-1]>x); pos–)
{
M[pos]=M[pos-1];
}
M[pos]=M[pos -1];
}
M[pos]=x;//chèn x vào dãy
}
}
-đánh giá giải thuật:
+Các phép so sánh xảy ra trong mỗi vòng lặp tìm vị trí thích hợp pos, và mỗi
lần xác định vị trí đang xét không thích hợp, sẽ dời chỗ phần tử M[pos] tương
ứng.
+Giải thuật thực hiện tất cả N-1 vòng lặp tìm pos, do số lượng phép so sánh
và dời chỗ này phụ thuộc vào tình trạng của dãy số ban đầu, nên chỉ có thể
ước lượng trong từng trường hợp
5.Phương pháp đổi chỗ(Interchange sort)
-Để sắp xếp một dãy số, ta có thể xét các nghịch thế có trong dãy và làm triệt
tiêu dần chúng đi.
-Ý tưởng chính:
+ Xuất phát từ đầu dãy, tìm tất cả nghịch thế chứa phần tử này, triệt tiêu
chúng bằng cách đổi chỗ phần tử này với phần tử tương ứng trong cặp
nghịch thế.
+ Lặp lại xử lý trên với các phần tử tiếp theo trong dãy
-Bước 1 : i = 0;// bắt đầu từ đầu dãy
-Bước 2 : j = i+1;//tìm các phần tử M[j] < M[i], j>i
-Bước 3 :
Trong khi j ≤ N thực hiện
Nếu M[j]<M[i]: M[i]↔M[j];//xét cặp phần tử M[i],
M[j]
j = j+1;
-Bước 4 : i = i+1;
Nếu i < n: Lặp lại Bước 2.
Ngược lại: Dừng.
-trong một dãy số chưa được sắp xếp, tồn tại ít nhất một cặp số ở tình trạng
nghịch thế, tìm cách triệt tiêu dần,xuất phát từ đầu dãy
-phương trình:
void InterchangeSort(int[] M, int n) { int i, j; for (i = 0; i < n - 1; i++) { for (j = i +
1; j < n; j++) { if (M[j] < M[i]) { int temp = M[i]; M[i] = M[j]; M[j] = temp; } } } }
6.Phương pháp sắp xếp nhanh(Quick sort)
Ðể sắp xếp dãy M0, M1, ..., Mn-1 giải thuật QuickSort dựa trên việc phân
hoạch dãy ban đầu thành hai phần :
Dãy con 1: Gồm các phần tử M0.. Mi có giá trị không lớn hơn pivot
Dãy con 2: Gồm các phần tử Mi .. Mn-1 có giá trị không nhỏ hơn pivot với
pivot là giá trị của một phần tử tùy ý trong dãy ban đầu. Sau khi thực hiện
phân hoạch, dãy ban đầu được phân thành 3 phần:
1. Mk < pivot , với k = 0..i
2. Mk = pivot , với k = i..j
3. Mk > pivot , với k = j..N-1
Trong đó dãy con thứ 2 đã có thứ tự, Nếu các dãy con 1 và 3 chỉ có 1 phần
tử thì chúng cũng đã có thứ tự, khi đó dãy ban đầu đã được sắp. Ngược lại,
nếu các dãy con 1 và 3 có nhiều hơn 1 phần tử thì dãy ban đầu chỉ có thứ tự
khi các dãy con 1, 3 được sắp. Ðể sắp xếp dãy con 1 và 3, ta lần lượt tiến
hành việc phân hoạch từng dãy con theo cùng phương pháp phân hoạch dãy
ban đầu vừa trình bày .
Giải thuật phân hoạch dãy Ml, Ml+1, ., Mr thành 2 dãy con:
• Bước 1 : Chọn tùy ý một phần tử M[k] trong dãy là giá trị mốc, l ≤ k ≤ r: pivot =
M[k]; i = l; j = r;
• Bước 2 : Phát hiện và hiệu chỉnh cặp phần tử M[i], M[j] nằm sai chỗ :
• Bước 2a : Trong khi (M[i]pivot) j--;
• Bước 2c : Nếu i<= j // M[i] ≥ pivot ≥ M[j] mà M[j] đứng sau M[i] Hoán vị
(M[i],M[j]);
• Bước 3 : Nếu i < j: Lặp lại Bước 2.//chưa xét hết mảng Nếu i ≥ j: Dừng
-lấy số giữa làm pivot,bên phải nhỏ hơn hoặc bằng pivot thì dừng lại,trái nhỏ
hơn pivot, phải lớn hơn pivot
chạy tour lần 1: 3 5 8 1 4 12 8 23 33
-con trỏ từ bên trái chạy qua,i từ trái chạy qua,j từ phải chạy về, khi nào 2 con
trỏ đụng nhau thì dừng lại 1, và đảo vị trí cho nhau
-chương trình:
void QuickSort(int M,int left, int right)
{
if (left >= right) return;
int pivot = M[(left + right) / 2];
int i = left, j = right ;
do{
while (M[i] < pivot) i++;
while (M[j] > pivot) j--;
if (i <= j){
int temp = M[i];
M[i] = M[j];
M[j] = temp;
i++;
j--;
}
} while (i < j);
QuickSort(M,left, j);
QuickSort(M,i,right);
}
btn: quản lý sinh viên trên danh sách liên kết
=>Vậy giải thuật tìm tuyến tính có độ phức tạp tính toán cấp n: T(N) = O(N)
*tìm kiếm nhị phân:
-Đặc trưng dữ liệu: Đã được sắp xếp theo một thứ tự nào đó (nhỏ-> lớn)
-Tính chất của dữ liệu được sắp là :
Mi-1<= Mi<=Mi+1
=> Vậy nếu x> Mi thì x chỉ có thể xuất hiện trong đoạn [Mi+1 ,MN-1] của dãy,
ngược lại nếu x< Mi thì x chỉ có thể xuất hiện trong đoạn [M0 ,Mi-1] của dãy -
Giải thuật tìm nhị phân áp dụng nhận xét trên đây với tư tưởng chia để trị để
tìm cách giới hạn phạm vi tìm kiếm sau mỗi lần so sánh x với một phần tử
trong dãy.
-cài đặt giải thuật:
Bước 1: left = 0; right = N-1; // tìm kiếm trên tất cả các phần tử
Bước 2:
mid = (left+right)/2; // lấy mốc so sánh
So sánh M[mid] với k, có 3 khả năng :
M[mid] = k: Tìm thấy. Dừng
M[mid] > k: Tìm tiếp x trong dãy con M[left] .. M[mid -1] : right =mid - 1;
M[mid] < k: Tìm tiếp x trong dãy con M[mid +1] .. M[right] : left = mid + 1;
Bước 3: Nếu left <= right : Còn phần tử chưa xét tìm tiếp.
Lặp lại Bước 2.
Ngược lại: Dừng; //Ðã xét hết tất cả các phần tử, không tìm thấy.
-đoạn chương trình:
int BinarySearch(int M[],int n, int x)
{
int left = 0, right = n-1;
int mid;
do
{
mid = (left + right) / 2;
if (x == M[mid]) return mid;//Thấy x tại mid
else if (x < M[mid])
right = mid - 1;
else
left = mid + 1;
} while (left <= right);
return -1; // Tìm hết dãy mà không có x
}
vd: int M[] = {1, 5, 15, 19, 25, 27, 29, 31,33,45,55,88,100};
int kq= BinarySearch(M,13, 19);
tìm phần tử X=19