Triển khai Các thuật toán ArrayList
Đã giới thiệu| English | Ý nghĩa |
|---|---|
| shift/ʃɪft/ | dịch chuyển |
| delete/dɪˈliːt/ | xóa |
| insert/ˈɪnsɜːt/ | chèn |
Xóa các phần tử liền kề tiết lộ lỗi bỏ sót phần tử
- Bắt đầu từ
[0, 0, 5], xóa chỉ mục 0 rồi tăng i. Số 0 thứ hai đã dịch chuyển vào index 0, nên vòng lặp tiến tới sẽ bỏ sót nó và giữ lại[0, 5]. - Đối với vòng lặp while tiến trình được dạy ở đây, hãy giữ nguyên chỉ số sau khi xóa và chỉ tăng lên khi giữ lại một phần tử. Khi đó nó sẽ loại bỏ cả hai số không và kết thúc với
[5].
Giữ nguyên chỉ mục tiến tới hiện tại sau khi xóa
- Sau khi
remove(i), mọi phần tử sau đó sẽ dịch chuyển một vị trí sang trái. Nếu một phần tử hiện nằm ở vị trí i, thì nó chưa được kiểm tra; hãy xem xét nó trong lần lặp tiếp theo. - Vòng lặp dưới đây chấp nhận một danh sách Integers khác null và xóa mọi số 0 tại chỗ. Nó sử dụng kích thước hiện tại trong mỗi lần lặp và không thực hiện get nào trên một danh sách rỗng.
import java.util.ArrayList;
public class RemoveZeros {
public static void removeZeros(ArrayList<Integer> list) {
int i = 0;
while (i < list.size()) {
if (list.get(i) == 0) {
list.remove(i);
} else {
i++;
}
}
}
}
Giải thích tại sao vòng lặp này kết thúc
- Mỗi lần lặp hoặc xóa một phần tử và giảm size, hoặc giữ lại một phần tử và tăng i. Do đó
size() - i, số vị trí còn lại cần kiểm tra, giảm đi một đơn vị trong mỗi lần lặp. - Bắt đầu từ
[0, 0, 5], các trạng thái lần lượt là[0, 5]tại i=0,[5]tại i=0, sau đó là[5]tại i=1. Điều kiện cuối cùng là sai, do đó không xảy ra truy cập ngoài biên.
Một vòng lặp ngược tuân theo quy tắc khác
- Vòng lặp ngược bắt đầu từ chỉ mục tồn tại cuối cùng:
for (int i = list.size() - 1; i >= 0; i--). Xóa các phần tử trùng khớp bằngremove(i); giá trị i tiếp theo luôn nhỏ hơn một đơn vị. - Xóa tại i chỉ dịch chuyển các chỉ mục phía sau, những chỉ mục này đã được kiểm tra trước đó. Các phần tử chưa được kiểm tra phía trước vẫn giữ nguyên chỉ mục của chúng, vì vậy giảm i sau khi xóa là đúng đắn ở đây; “không bao giờ tăng sau khi xóa” không phải là quy tắc phổ quát.
Chọn cách cập nhật chỉ mục phù hợp với hướng duyệt. Trong while-loop tiến tới, hãy giữ nguyên sau khi xóa; trong for-loop ngược, tiếp tục giảm. Thay đổi cấu trúc trực tiếp trong khi duyệt nâng cao là một mẫu không an toàn riêng biệt, ngay cả khi không xuất hiện ngoại lệ fail-fast.
Trong vòng lặp while theo hướng trước được hiển thị ở đây, chỉ tiến hành i khi bạn...
Độ khó 1/5 · 100 xu + một phần thưởng nhỏ cho lần hoàn thành đầu tiên trong ngày
Sau remove(i), phần tử tiếp theo chuyển vào vị trí i — đừng bỏ qua nó.
Vòng lặp theo hướng đơn giản trên [0, 0, 5] xóa tại i=0 rồi tăng i sẽ bỏ qua số không tiếp theo.
Độ khó 1/5 · 100 xu + một phần thưởng nhỏ cho lần hoàn thành đầu tiên trong ngày
Số không còn lại dịch chuyển vào chỉ mục 0, nhưng bài kiểm tra tiếp theo diễn ra tại chỉ mục 1. Một vòng lặp ngược sử dụng quy tắc chỉ mục đúng khác.
Một cách thay thế cho việc xóa tại chỗ tránh việc dịch chuyển là...
Độ khó 1/5 · 100 xu + một phần thưởng nhỏ cho lần hoàn thành đầu tiên trong ngày
Thêm các phần tử giữ lại vào một danh sách mới tránh được việc dịch chuyển.
Các thuật toán ArrayList tái sử dụng các mẫu mảng thông qua...
Độ khó 1/5 · 100 xu + một phần thưởng nhỏ cho lần hoàn thành đầu tiên trong ngày
Thay thế [] và length của mảng bằng get(i) và size().
Tính chất có thể thay đổi kích thước của một ArrayList cho phép bạn chèn và xóa phần tử, khác với mảng cố định.
Độ khó 1/5 · 100 xu + một phần thưởng nhỏ cho lần hoàn thành đầu tiên trong ngày
add và remove thay đổi size của danh sách.
Một vòng xóa theo hướng ngược bắt đầu từ size()-1 phải giữ nguyên cùng chỉ mục sau mỗi lần xóa.
Độ khó 1/5 · 100 xu + một phần thưởng nhỏ cho lần hoàn thành đầu tiên trong ngày
Sai: các phần tử chưa được kiểm tra ở trước sẽ giữ nguyên chỉ số của chúng, do đó vòng lặp ngược sẽ giảm đi bình thường.
Xây dựng một danh sách mới khi danh sách gốc phải được giữ nguyên
- Để giữ nguyên danh sách gốc, hãy tạo một kết quả mới và thêm từng phần tử khác không vào khi đọc từ danh sách gốc. Cách này giữ nguyên thứ tự ban đầu và kích thước của nó; tuy nhiên sẽ tốn thêm bộ nhớ cho danh sách.
- Đối với danh sách các đối tượng có thể thay đổi, việc sao chép chỉ giữ lại tham chiếu chứ không tạo ra bản sao của các đối tượng đó. Một cấu trúc danh sách mới vẫn có thể chia sẻ các đối tượng phần tử với danh sách gốc; hãy phân biệt giữa danh tính của danh sách và danh tính của các phần tử.
Xóa theo hướng trước giữ nguyên chỉ mục sau khi xóa
Hai số không liền kề được kiểm tra tại chỉ mục 0 trước khi 5 được giữ lại.
Kiểm tra các trường hợp làm lộ sai sót của thuật toán
- Kiểm tra danh sách rỗng, toàn số không, không có số không, các số không liền kề và một số không ở chỉ mục cuối cùng. Với việc loại bỏ tại chỗ, các tham chiếu khác trỏ đến cùng danh sách sẽ thấy sự thay đổi đó; một kết quả mới sẽ giữ nguyên cấu trúc gốc.
- Một ArrayList có thể chèn hoặc xóa phần tử; các thao tác này có thể dịch chuyển các chỉ mục sau. Tìm kiếm có thể trả về chỉ mục tìm thấy hoặc -1 khi không có; giá trị tối đa yêu cầu chính sách cho danh sách rỗng rõ ràng. Tái sử dụng mẫu mảng thông qua get/size là hữu ích, nhưng mỗi thuật toán vẫn cần giới hạn và hợp đồng riêng.
Việc loại bỏ sẽ dịch chuyển các chỉ mục. Giải thích phần tử nào bị bỏ qua và chọn chỉ mục tiếp theo tương ứng. Giữ nguyên hoặc tăng (tiến) và giảm (lùi) đều là các mô hình hợp lệ; một danh sách mới chứa các phần tử được giữ lại sẽ bảo toàn cấu trúc gốc nhưng có thể chia sẻ các đối tượng.
Order the states for the corrected removal loop on [0, 0, 5].
Độ khó 3/5 · 200 xu + một phần thưởng nhỏ cho lần hoàn thành đầu tiên trong ngày
Each removal rechecks the shifted value; keeping 5 finally advances the index.
Phương thức removeZeros theo chiều ngang được hiển thị sẽ thực hiện bao nhiêu lần gọi get trên một danh sách rỗng?
Độ khó 2/5 · 140 xu + một phần thưởng nhỏ cho lần hoàn thành đầu tiên trong ngày
Điều kiện ban đầu 0 < 0 là sai, nên không có lần gọi get nào xảy ra.