Hướng dẫn giải của Thêm phần tử vào mảng


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.

Lời giải này đang bị ẩn cho đến khi bạn chọn mở ra.

Chúng tôi khuyên bạn nên tự thử giải bài trước. Việc mở lời giải có thể làm lộ mất ý tưởng chính trước khi bạn có cơ hội tự giải.

Bạn phải đăng nhập để mở lời giải này.

Đăng nhập

Tác giả: Hiếu Nguyễn, 2uockhanh29, duykhanh100106, LamThanhVu

Editorial for ptit059: Thêm phần tử vào mảng

Hiểu bài toán

Bài toán yêu cầu chèn một số nguyên x vào một mảng tăng dần gồm N phần tử sao cho mảng thu được vẫn tăng dần. Mảng ban đầu được đảm bảo đã được sắp xếp tăng dần. Sau khi chèn, mảng sẽ có N+1 phần tử. Vấn đề chính là tìm vị trí thích hợp để chèn x và đảm bảo các phần tử sau vị trí đó được dịch chuyển.

Các cách tiếp cận

Cách Tìm vị trí và chèn bằng dịch chuyển mảng
#include<stdio.h>

int main() {
    int n, a[10005];
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    int k, pos = -1;
    scanf("%d", &k);
    // Tìm vị trí đầu tiên có phần tử lớn hơn k
    for (int i = 0; i < n; i++) {
        if (k < a[i]) {
            pos = i;
            break;
        }
    }
    // Nếu không tìm thấy (k lớn hơn tất cả), chèn vào cuối
    if(pos != -1) {
        // Dịch chuyển các phần tử từ pos về sau sang phải
        for (int i = n; i > pos; i--) {
            a[i] = a[i - 1];
        }
        a[pos] = k;
    } else {
        a[n] = k;
    }
    // In mảng kết quả
    for (int i = 0; i < n + 1; i++) {
        printf("%d ", a[i]);
    }
    return 0;
}
  • Time Complexity: O(n)
  • Space Complexity: O(n)

Cách tiếp cận này tìm vị trí chèn (pos) bằng cách duyệt mảng từ đầu đến cuối. Nếu tìm thấy phần tử đầu tiên lớn hơn x, ta ghi nhận vị trí đó và dừng lại. Nếu không tìm thấy (x lớn hơn tất cả các phần tử), pos sẽ là cuối mảng (hoặc một flag đặc biệt). Sau đó, ta cần dịch chuyển các phần tử từ vị trí pos trở về sau sang phải 1 đơn vị để giải phóng chỗ cho x. Thao tác dịch chuyển này có độ phức tạp O(n) trong trường hợp xấu nhất (chèn vào đầu mảng). Cuối cùng, gán a[pos] = x và in ra mảng.

Cách Sử dụng mảng động và chèn vào cuối rồi sắp xếp
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

int main() {
    int n, x, i, j;
    scanf("%d", &n);
    int a[n+1]; // Mảng đủ chỗ cho N+1 phần tử
    for (i=0; i<n; i++) {
        scanf("%d", &a[i]);
    }
    scanf("%d", &x);
    a[n]=x; // Đưa x vào cuối mảng
    // Sử dụng Bubble Sort hoặc Insertion Sort một bước để đưa x về đúng vị trí
    // Code mẫu sử dụng logic tương tự Insertion Sort/Bubble Sort
    for (i=0; i<n; i++) {
        if (a[i] > a[n]) {
            int tmp = a[i];
            a[i] = a[n];
            a[n] = tmp;
        }
    }
    for (i=0; i<n+1; i++) {
        printf("%d ", a[i]);
    }
    return 0;
}
  • Time Complexity: O(n)
  • Space Complexity: O(n)

Phương pháp này tận dụng việc mảng ban đầu đã được sắp xếp. Đầu tiên, ta đưa phần tử x vào vị trí cuối cùng của mảng (đã cấp phát đủ kích thước N+1). Sau đó, thực hiện một vòng lặp duyệt từ đầu mảng. Nếu gặp phần tử nào lớn hơn x, ta hoán đổi nó với x. Đây về cơ bản là bước đầu tiên của thuật toán Insertion Sort hoặc Bubble Sort để 'đẩy' x về đúng vị trí. Vì mảng ban đầu đã tăng dần, x sẽ chỉ cần 'đẩy' lui một đoạn duy nhất cho đến khi gặp phần tử lớn hơn nó hoặc hết mảng. Độ phức tạp vẫn là O(n) trong trường hợp xấu nhất.

Cách Sử dụng mảng tĩnh và chỉ số duyệt
#include<stdio.h>
#include<stdlib.h>
#include<math.h>

#define ll long long
#define sc scanf
#define pr printf

int main() {
    int n, x; sc("%d", &n);
    int a[100005]; // Mảng tĩnh lớn
    for(int i=0; i<n; i++) sc("%d", &a[i]);
    sc("%d",&x);

    int vitrichen = n; // Mặc định chèn vào cuối
    for(int i=0; i<n; i++){
        if(x < a[i]){
            vitrichen = i;
            break;
        }
    }
    // Dịch chuyển phần tử
    for(int j=n; j>vitrichen; j--){
        a[j] = a[j-1];
    }
    a[vitrichen] = x;
    // In kết quả
    for(int k=0; k<n+1; k++)
        pr("%d ", a[k]);
    return 0;
}
  • Time Complexity: O(n)
  • Space Complexity: O(n)

Đây là biến thể trực tiếp nhất của phương pháp dịch chuyển mảng. Nó dùng một biến vitrichen để lưu vị trí cần chèn. Vòng lặp đầu tiên tìm vị trí này. Vòng lặp thứ hai (duyệt ngược) thực hiện dịch chuyển các phần tử. Cách này rõ ràng và dễ hiểu, xử lý được các trường hợp đặc biệt (chèn đầu, chèn cuối, chèn giữa) một cách gọn gàng.

Phân tích độ phức tạp

Cách tiếp cận Time Space Tên
1 O(n) O(n) Tìm vị trí và chèn bằng dịch chuyển mảng
2 O(n) O(n) Sử dụng mảng động và chèn vào cuối rồi sắp xếp
3 O(n) O(n) Sử dụng mảng tĩnh và chỉ số duyệt

Bài học kinh nghiệm

  • Mảng ban đầu đã được sắp xếp tăng dần là thông tin quan trọng nhất giúp giảm độ phức tạp.
  • Vị trí chèn là chỉ số đầu tiên mà tại đó phần tử của mảng lớn hơn x. Nếu không có, vị trí chèn là N.
  • Để chèn vào mảng tĩnh, ta bắt buộc phải dịch chuyển các phần tử hiện có để tạo khoảng trống.

Lỗi thường gặp

  • Quên cấp phát mảng có kích thước N+1 dẫn đến tràn bộ nhớ (buffer overflow) khi chèn phần tử mới.
  • Lỗi logic khi duyệt để tìm vị trí chèn (ví dụ: sử dụng > thay vì >= có thể thay đổi hành vi nếu có phần tử bằng x, mặc dù đề bài không rõ ràng về số trùng lặp, nhưng thường chèn ngay sau phần tử bằng hoặc trước đều được, cần nhất quán).
  • Lỗi dịch chuyển mảng: duyệt từ đầu về cuối sẽ ghi đè dữ liệu chưa xử lý, phải duyệt từ cuối về đầu.

Bình luận

Please read the guidelines before commenting.


Không có bình luận tại thời điểm này.