0% found this document useful (0 votes)
33 views29 pages

Learn Code

Uploaded by

pompom
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
33 views29 pages

Learn Code

Uploaded by

pompom
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

c substring() trong Java được sử dụng để trích xuất một phần chuỗi con từ một

chuỗi ban đầu, dựa trên các chỉ số bắt đầu và kết thúc.
-Cú pháp: (chỉ lấy đến kí tự thứ endIndex-1).

Phương thức Integer.parseInt() trong Java được sử dụng để chuyển đổi một chuỗi
(String) thành một giá trị số nguyên (int).
-Cú pháp:

Sự khác biệt giữa == và .equals():


==:
Toán tử == trong Java so sánh địa chỉ vùng nhớ của hai đối tượng, tức là kiểm tra
xem hai biến có tham chiếu đến cùng một đối tượng hay không.
Khi sử dụng == để so sánh chuỗi, nó sẽ không kiểm tra nội dung của chuỗi mà
kiểm tra xem hai chuỗi có nằm cùng một vị trí trong bộ nhớ không.
equals():
Phương thức .equals() được định nghĩa để so sánh nội dung của hai chuỗi, nghĩa là
nó sẽ kiểm tra xem các ký tự trong hai chuỗi có giống nhau hay không.
Stack
Bạn có thể khởi tạo một Stack bằng cách sử dụng cú pháp sau:

1 số phương thức của stack


- Push(): thêm 1 phần tử vào đầu danh sách
- Pop(): lấy và xóa phần tử đc thêm vào gần đây nhất
- Peek():lấy phần tử đc thêm vào gần đây nhất nhưng ko xóa nó
- isEmpty(): kiểm tra xem stack có trống không
- search(Object o): tìm kiếm vị trí của 1 phần tử từ trên xuống dưới trong stack. Trả về vị trí của
phần tử hoặc -1 nếu ko tìm thấy
Cách lấy từng kí tự trong String
SORTING
Selection sort
Ý tưởng: xuât phát từ một mảng chưa sắp xếp, lần lặp thứ nhất ta duyệt từ đầu mảng để tìm phẩn
tử nhỏ nhất rồi chuyển nó lên vị trí đầu mảng bằng cách đổi chỗ nó với phần tử đứng đầu, lần lặp
thứ 2 duyệt từ vị trí thứ hai đến cuối mảng để tìm phần tử min rồi chuyển lên vị trí thứ 2 của
mảng, lặp lại như vậy cho đến hết mảng
Có một cách để hiểu hoạt động của thuật toán, đó là tìm hiểu các bất biến (invariant) của thuật
toán. Đối với selection sort, ta có một con trỏ, khi nãy nó là biến i của ta, con trỏ này chạy từ trái
sang phải. Ở đây nó được kí hiệu bằng mũi tên màu đỏ. Có hai bất biến sau: Thứ nhất, các ô bên
trái con trỏ không bao giờ thay đổi, và chúng đã đúng theo thứ tự tăng dần. Thứ hai, không có ô
nào ở bên phải con trỏ lại nhỏ hơn một ô nào ở bên trái con trỏ.
Các đặc điểm đó đúng với thời điểm thuật toán bắt đầu chạy, và thuật toán luôn gìn giữ các bất
biến đó khi lấy phần tử nhỏ nhất bên phải và đổi chỗ với vị trí tiếp theo (ô nằm ngay bên phải
con trỏ).

Độ phức tạp trong mọi trường hợp đều là O(n2) vì luôn phải duyệt hết mảng, so sánh để tìm ra
min và dùng vòng lặp lồng nhau

Thuật toán:

for (int i=0; i<a.length; i++) {


int min = i;
for (int j=i+1; j<a.length; j++) {
if(a[j] < a[min]) min = j;
}
If(min != i)
Swap(a[i], a[min];
}
Insertion sort
Danh sách ban đầu được chia làm 2 phần : 1 phần đã sắp xếp và 1 phần chưa sắp xếp. Bắt đầu
với phần tử đã sắp xếp là phần tử đầu tiên
-sử dụng vong for để lặp qua từng phần tử trong danh sách chưa sắp xếp, bắt đầu từ vị trí
index=1
-đối với mỗi phần tử đc gọi là key, thực hiện so sánh và chèn nó vào đúng vị trí trong phần đã
sắp xếp
-trong vòng lặp sử dụng 1 biến j để chỉ đến phần tử ngay trước key
-sử dụng while để so sánh key với các phần tử đã sắp xếp bên trái
-nếu arr[j]>k thì a di chuyển nó sang phải arr[j+1] = arr[j]
-tiếp tục giảm cho đến khi tìm đc vị trí thích hợp chèn key vào vị trí đó arr[j+1] = key
-Độ phức tạp: Tồi nhất O(n2) khi mảng đã được sắp xếp ngược
Tốt nhất O(n) khi mảng gần như đã đc sắp xếp, chỉ thực hiện 1 phép so sánh trong
mỗi lần
Mã giả:
For (int i=0; i<n; i++)
for (int j=i; j>0; j--)
if(a[j] < a[j-1]) swap(a[j], a[j-1]
else break;
Merge sort
Chia để trị
Chia mảng lớn thành các mảng con nhỏ hơn, sắp xếp các mảng con sau đó hợp nhất chúng lại
Nguyên lý hoạt động:
1. Chia
- Chia mảng thành 2 nửa bằng nhau (nếu số phần tử lẻ, một nửa sẽ lớn hơn 1 phần tử)
- Tiếp tục chia các nửa này cho đến khi mỗi mảng chỉ còn 1 phần tử
2. Trị
- Sắp xếp 2 mảng con đã chia bằng cách merge chúng sao cho mảng kếp quả đc sắp xếp
3. Kết hợp
- Sau khi các mảng con đc sắp xếp, merge chúng lại thành 1 mảng đã sx
Độ phức tạp trong mọi trường hợp là O(nlogn)
Quick sort
Phân hoạch lomuto (chọn phần tử cuối làm pivot)
Độ phức tạp O(n)

LomutoPartition(arr, low, high):


pivot = arr[high] // chọn phần tử cuối làm pivot
i = low – 1; // khởi tạo chỉ số để theo dỗi vị trí phần tử nhỏ hơn pivot

for j from low to high – 1:


if arr[j] <= pivot // nếu phần tử nhỏ hơn hoặc bằng pivot
i = i + 1 // tăng chỉ số i
swap(arr[i], a[j])
swap(arr[i+1], a[high]) // đưa pivot về đúng vị trí
return i + 1 // trả về chỉ số của pivot

Phân hoạch Hoare (chọn phần tử đầu làm pivot)


HoarePartition(arr, low, high):
pivot = arr[low]
i = low – 1
j = high + 1
while true:
repeat:
j=j–1
util arr[j] <= pivot

repeat:
i=i+1
util arr[i] >= pivot
if i >= j: break;
swap (arr[i], arr[j])

swap(arr[low], arr[j]
return j

Quick sort
QuickSort(arr, low, high):
If low < high
Pivot = partition(arr, low, high)
QuickSort(arr, low, pivot)
QuickSort(arr, pivot + 1, high)
Độ phức tạp của thuật toán quick sort best case là O(nlogn), worse case là O(n2) khi mảng đầu
vào đã được sx tăng dần hoặc giảm dần
Heap Sort
Sắp xếp ổn định là sắp xếp giữ nguyên thứ tự tương đối giữa các phần tử có giá trị bằng nhau
trong danh sách ban đầu
Ổn định: bubble sort, merge sort, insertion sort
Ko ổn định: quick sort, heap sort, selection sort

Max heap biểu diễn theo mảng, tìm vị trí của cha k/2, tìm vị trí của con 2k và 2k + 1
- Heap sort là 1 thuật toán sắp xếp dựa trên cấu trúc dữ liệu heap, là 1 loại cây nhị phân hoàn
chỉnh.
- là thuật toán sx ko ổn định
- thời gian thực thi O(nlogn) trong mọi trường hợp
- Nguyên tắc hoạt động: dựa trên max-heap hoặc min-heap
+ Max-heap : phần tử lớn nhất nằm ở gốc
+ Min-heap: phần tử nhỏ nhất nằm ở gốc
Comparator
ĐỒ THỊ
G(V, E)
-Đơn đồ thị: giữa 2 đỉnh chỉ có nhiều nhất 1 cạnh

-Đa đồ thị vô hướng: giữa 2 đỉnh có thể tồn tại nhiều cạnh

- 2 đỉnh đgl kề nhau nếu có 1 cạnh nối giữa 2 đỉnh đó, cạnh đó đgl cạnh liên thuộc giữa 2 đỉnh
-bậc của đỉnh (đt vô hướng): tổng số cạnh liên thuộc
VD: deg( C ) = 3
-đồ thị G vô hướng có m cạnh, khi đó tổng bậc các đinht trên đồ thị = 2m
-với đồ thị có hướng:
+ bán bậc ra: số cung hướng ra khỏi đỉnh u , deg+(u)
+ bán bậc vào: số cung hướng vào đỉnh u, deg-(u)
-trên đồ thị, tổng bán bậc ra = tổng bán bậc vào = tổng số cạnh
-đồ thị liên thông: luôn tìn được đường đi giữa 2 điểm bất kì

-biều diễn đồ thị bằng ma trận kề, đối xứng qua đường chéo chính
-Lưu đồ thị dạng danh sách cạnh

-đồ thị có trọng số


-Biểu diễn bằng danh sách kề

Thuật toán tìm kiếm theo chiều sâu


DFS(u) {
<Tham dinh u>
Visited[u] = true;
// duyệt các đỉnh kề với đỉnh u
For (v: adj[v] {
If(!visited[v]) DFS(v);
}
}
Duyệt theo chiều sâu (DFS): dùng đệ quy, stack
Bước 1: Đầu vào là một danh sách kề
Bước 2: Tạo 1 mảng visited để đánh dấu đỉnh nào đã được thăm
Bước 3: Duyệt từ đỉnh đầu tiên, đánh dấu nó là visited, thăm đỉnh này
Bước 4: Duyệt tất cả đỉnh kề của đỉnh này, nếu có đỉnh nào chưa được thăm (visited = false) thì
đệ quy với đỉnh đó
Thuật toán kết thúc khi tất cả các đỉnh đã được thăm
Độ phức tạp O(V + E)
Duyệt theo chiều rộng (BFS): dùng hàng đợi
Bước 1: Tạo 1 mảng visited có kích thước bằng số đỉnh của đồ thị (visited.size() = adj.size()) để
đánh dấu những đỉnh đã được thăm
Bước 2: Tạo 1 queue để lưu các đỉnh đang chờ để được thăm
Bước 3: đánh dấu đỉnh hiện tại là đã được thăm và thêm vào queue
Bước 4: Duyệt cho đến khi queue rỗng
-Lấy đỉnh đầu tiên ra khỏi queue
-Thăm đỉnh đó
-Duyệt qua tất cả các đỉnh kề của đỉnh đó, nếu có đỉnh nào chưa được thăm thì thêm nó
vào queue và đánh dấu là visited
Thuật toán kết thúc khi queue rỗng
Độ phức tạp O(V + E)
Thuật toán tìm số thành phần liên thông
Là số lần phải chạy dfs hoặc là bfs

Thuật toán truy vết đường đi giữa 2 đỉnh u, v


Ý tưởng: Duyệt qua đồ thị bắt đầu từ đỉnh u, cho đến khi tìm thấy đỉnh v và lưu lại đỉnh cha của
nó vào 1 mảng parent -> sau đó truy vết bằng cách kiểm tra xem đỉnh v đã được thăm chưa, nếu
chưa được thăm thì giữa u và v không có đường đi. Nếu v được thăm rồi thì tạo 1 danh sách,
duyệt từ đỉnh v cho tới khi v = u, đẩy v vào danh sách sau đó gán v = parent[v], reverse danh
sách
Thuật toán Dijkstra
1.Đánh dấu nút nguồn với khoảng cách hiện tại là 0 và phần còn lại là vô cực
2.Đặt nút chưa đc thăm có khoảng cách hiện tại nhỏ nhất làm nút hiện tại
3.Đối với mỗi đỉnh kề với nó, kiểm tra xem nếu khoảng cách của nút hiện tại cộng với khoảng
cách của nút đó đến nút hiện tại nhỏ hơn khoảng cách hiện tại của nút đó thì đặt khoảng cách
hiện tại là khoảng cách của nút đó
4.Đánh dấu nút hiện tại là visited
5.Quay lại bước 2 cho đến khi tất cả các nút đã đc truy cập
Độ phức tạp là O((V+E)logV)
Duyệt cây
Inorder (duyệt theo thứ tự trung gian)
Duyệt cây con trái, duyệt gốc, duyệt cây con phải
Nếu đây là cây tìm kiếm nhị phân thì sẽ in ra các nút theo thứ tự từ nhỏ đến lớn

Mã giả:
Inorder(root.left)
Cout << root.key
Inorder(root.right)

Preorder (duyệt tiền tự)


Duyệt nút gốc -> cây con trái -> cây con phải

Mã giả:
Cout << root.key
Inorder(root.left)
Inorder(root.right)

Postorder(Duyệt hậu tự)


Duyệt cây con trái -> cây con phải -> nút gốc

Mã giả:
Inorder(root.left)
Inorder(root.right)
Cout << root.key
Levelorder(Duyệt theo tầng)
Duyệt từ trên xuống dưới, từ trái sang phải
Thêm nút gốc vào hàng đợi -> duyệt nút gốc -> thêm nút con trái và phải vào hàng đợi

Mã giả:
Void levelOrder(Node root):
If(root == null) return
Queue.add(root)
While(!queue.isEmpty())
Node current = queue.poll()
Cout << current.data
If(current.left != null) queue.add(current.left)
If(current.right != null) queue.add(current.right)
Max-heap tree: là cây có nút gốc luôn lớn hơn nút con
Min-heap tree ngược lại
Cách insert 1 node vào max-heap tree
Insert từ trái sang phải, so sánh với node cha, nếu lớn hơn node cha thì đổi chỗ rồi tiếp tục so
sánh với node cha đến khi nó nhỏ hơn node cha
Cách xóa node gốc khỏi max-heap tree
Hoán đổi node gốc với node bên phải dưới cùng của cây, xóa nút gốc rồi heapify lại cái cây

Cách xóa 1 node khỏi BST:


-Nếu nó là nút là thì xóa luôn
-Nếu nó có 1 con thì nút con đó thế vào chỗ của cha nó
-Nếu nó có 2 con:
 Tìm nút nhỏ nhất ở cây con bên phải (inorder successor) hoặc nút lớn nhất ở cây
con bên trái (inorder predecessor).
 Sao chép giá trị của nút đó vào nút cần xóa.
 Xóa nút vừa sao chép ở cây con bên phải hoặc bên trái (lặp lại từ bước 1).
Successor là nút có giá trị nhỏ nhất trong cây con phải của nút đó hoặc là nút kế tiếp
theo thứ tự inorder
Predecessor là nút có giá trị lớn nhất trong cây con trái của nút đó, hoặc là nút đứng
trước nó trong thứ tự duyệt inorder của cây.
Cách xoay cây BST:
a. Xoay phải:
1. Xác định nút gốc ban đầu x và nút gốc mới y = x.left
2. Di chuyển cây con bên phải của y thành cây con bên trái của x
3. Gán x thành cây con phải của y
4. Gán y làm gốc mới

Mã giả:

Node rotateRight(Node x):


Node y = x.left
Node T3 = y.right
y.right = x
x.left = T3
Heap Sort
Sắp xếp ổn định là sắp xếp giữ nguyên thứ tự tương đối giữa các phần tử có giá trị bằng nhau trong danh
sách ban đầu
Ổn định: bubble sort, merge sort, insertion sort
Ko ổn định: quick sort, heap sort, selection sort

Max heap biểu diễn theo mảng, tìm vị trí của cha k/2, tìm vị trí của con 2k và 2k + 1
- Heap sort là 1 thuật toán sắp xếp dựa trên cấu trúc dữ liệu heap, là 1 loại cây nhị phân hoàn
chỉnh.
- là thuật toán sx ko ổn định
- thời gian thực thi O(nlogn) trong mọi trường hợp
- Nguyên tắc hoạt động: dựa trên max-heap hoặc min-heap
+ Max-heap : phần tử lớn nhất nằm ở gốc
+ Min-heap: phần tử nhỏ nhất nằm ở gốc
Bảng băm
Các phương pháp giải quyết xung đột (collision)
1. Open addressing (đánh địa chỉ mở)
Trong pp này nếu xảy ra xung đột phần tử mới sẽ được lưu trữ ở 1 vị trí khác trong cùng bảng,
dựa trên 1 quy tắc tìm kiếm
+ Linear probing (thăm dò tuyến tính): tìm kiếm tuần tự các vị trí tiếp theo trong bảng cho đến
khi tìm thấy 1 vị trí trống
+ Quadratic probing (thăm dò bình phương): khoảng cách giữa các lần kiểm tra tăng theo bình
phương
VD: lần 0: k mod n -> collision
Lần 1: (k+12) mod n -> collision
Lần 3: (k+22) mod n ->
+ Double hashing (băm kép) : sử dụng 2 hàm băm để giải quyết xung đột
Công thức: hash(k,i)=(h1(k)+i⋅h2(k))mod m
Hàm băm phụ: h2(k) = 1 + (k mod (m-1))
VD: m = 7, k = 15
H1(15) = 15 mod 7 = 1
H2(15) = 1 + (15 mod 6) = 4
Vị trí 1 đã bị chiếm -> lần thử 1: hash(15, 1) = (1 + 1.4 ) mod 7 = 5
Vị trí 5 đã bị chiếm -> lần thử 2: hash(15,2) = (1 + 2.4) mod 7 = 2
Vị trí 2 đã bị chiếm -> lần thử 3: hash(15, 3) = (1 + 3.4)……

2. Separate chaining (xâu băm liên kết)


Nếu xung đột thì thêm vào danh sách liên kết đơn tại vị trí đó
Tính duy nhất: Nếu tất cả các cạnh đều có trọng số khác nhau thì chỉ có duy một cây khung
nhỏ nhất. Ngược lại, nếu một vài cạnh có trọng số giống nhau thì có thể có nhiều hơn một
cây khung nhỏ nhất.
Thuật toán Kruskal (union-find)
Mã giả
1. Sắp xếp các cạnh của đồ thị theo trọng số tăng dần
2. Khởi tạo 1 tập hợp các tập (union-find) để theo dỗi các tập con của đồ thị
3. Khởi tạo MST là 1 tập rỗng
4. Duyệt qua từng cạnh trong danh sách đã sắp xếp:
a. lấy cạnh hiện tại (u, v) với trọng số w
b. nếu u và v thuộc 2 tập con khác nhau (ko tạo chu trình), thêm cạnh (u, v) vào MST và
hợp nhất 2 tập con u và v
c. Nếu u và v thuộc cùng 1 tập con, bỏ qua cạnh này
5. Kết thúc khi số lượng cạnh trong MST = số đỉnh – 1
6. Trả về MST
VD:
 Cạnh (A, C, 5): A và C thuộc các tập con khác nhau → thêm vào MST và hợp nhất {A, C}.
 Cạnh (A, B, 10): A và B thuộc các tập con khác nhau → thêm vào MST và hợp nhất {A, B,
C}.
 Cạnh (B, C, 15): B và C thuộc cùng một tập con → bỏ qua.
 Cạnh (C, D, 20): C và D thuộc các tập con khác nhau → thêm vào MST và hợp nhất {A, B,
C, D}.
 Cạnh (D, E, 25): D và E thuộc các tập con khác nhau → thêm vào MST và hợp nhất {A, B,
C, D, E}.
 Cạnh (A, E, 30): A và E thuộc cùng một tập con → bỏ qua.
Thuật toán prim (priority queue)
Mã giả:
1. Khởi tạo MST rỗng, chọn 1 đỉnh bất kì làm điểm bắt đầu
2. Khởi tạo các tập đỉnh chưa được thăm, bao gồm tất cả các đỉnh trừ đỉnh bắt đầu
3. Lặp lại cho đến khi tất cả các đỉnh đều có trong MST
a. Tìm 1 cạnh có trong số nhỏ nhất nối đỉnh trong MST với đỉnh ngoài MST
b. Them cạnh đó vào MST
c. Thêm đỉnh mới vào MST và loại bỏ nó khỏi tập các đỉnh chưa được thăm
4. Trả về MST
độ phức tạp thuật toán Prim O ((E+V)logV)
Danh sách và danh sách liên kết
Độ phức tạp khi cài đặt danh sách bằng mảng
a. Thêm vào cuối (append)
Best case: ko cần resize: O(1)
Worst case: cần resize: O(n), ít khi xảy ra do kích thước mảng tăng dần theo cấp số nhân
b. Thêm vào vị trí bất kì (insert)
Di chuyển tất cả phần tử sau vị trí chèn lên 1 vị trí, thêm phần tử vào vị trí xác định
Độ phức tạp O(n)
c. Xóa vị trí bất kì
Độ phức tạp O(n)
Nếu phần tử ở cuối mảng thì là O(1)
d. Truy nhập phần tử
Truy nhập phần tử tại index đã cho(get): O(1)
Cập nhập giá trị tại index đã cho : O(1)
HashSet
1. Đặc điểm của hashset
 Không lưu trữ trùng lặp: Mỗi phần tử trong HashSet chỉ xuất hiện một lần. Nếu bạn
thêm một phần tử đã tồn tại, nó sẽ không được thêm vào.
 Không đảm bảo thứ tự: HashSet không duy trì thứ tự của các phần tử. Các phần tử
có thể được lưu trữ theo bất kỳ thứ tự nào.
 Cấu trúc dữ liệu: HashSet sử dụng bảng băm (hash table) để lưu trữ các phần tử, cho
phép việc tìm kiếm, thêm và xóa phần tử nhanh chóng.
 Cho phép phần tử null: HashSet hỗ trợ chứa một phần tử null (nếu cấu trúc băm cho
phép).
2. Cách khai báo
VD: HashSet<Integer> set = new HashSet<>();
3. Các phương thức
- Add(key) : nếu phần tử đã có thì không làm gì
- Remove(key)
- (Boolean) contains(key)
- Size(): số phần tử
- isEmpty()
- clear()
Substring
Bài toán tìm xâu con pattern trong 1 xâu text cho trước
Mã giả thuật toán Brute-Force
Độ phức tạp m*n
BruteForce(text, pattern):
n = length(text)
m = length(pattern)
result = [] // danh sách lưu các vị trí tìm đc
for i = 0 to n-m
match = true
for j = 0 to m-1
if text[i+j] !=pattern[j]
match = false
break
if match:
result.add(i)
return result
HashMap
Lưu các cặp key value; key là duy nhất còn value có thể trùng lặp
Các phương thức
+ put(key, value): thêm 1 cặp key value
+ get(key): trả về value tương ứng với key
+ remove(key): xóa cặp key value
+ containsKey(key): kiểm tra xem key có tồn tại trong map không
+ containsValue(value): kiểm tra xem value có tồn tại trog map không
+ isEmpty()
+ size()

You might also like