Tác giả: Phan Đình Khôi - Đại học Bách Khoa Đà Nẵng
Reviewer: Nguyễn Khánh
Bài viết này sẽ giúp bạn tìm hiểu thêm về kỹ thuật hai con trỏ. Kỹ thuật này được sử dụng khá phổ biến, giúp chương trình tiết kiệm thời gian và không gian xử lý.
Cho hai dãy số nguyên đã được sắp xếp không giảm và lần lượt có và phần tử. Hãy ghép chúng thành dãy được bố trí theo thứ tự không giảm.
Giới hạn: và .
Hãy cùng xem ví dụ sau đây.
Cho trước hai dãy số và được sắp xếp không giảm:
Làm cách nào để có thể ghép chúng thành một dãy số cũng được sắp xếp không giảm ?
Trước tiên, hãy cùng xác định phần tử đầu tiên của dãy .
Vì dãy được bố trí theo thứ tự không giảm, cho nên phần tử đầu tiên của dãy phải là phần tử có giá trị nhỏ nhất trong cả hai dãy và .
Ta có thể so sánh hai phần tử nhỏ nhất của hai dãy , và đưa phần tử có giá trị nhỏ hơn vào vị trí đầu tiên của dãy .
Dãy và đã được sắp xếp không giảm, vì thế hai phần tử nhỏ nhất ở đây chính là hai phần tử ở vị trí đầu tiên ở mỗi dãy ( và ).
Bây giờ, phần tử tiếp theo của dãy sẽ là phần tử nhỏ nhất trong các phần tử chưa được đưa vào dãy .
Dãy và đã được sắp xếp không giảm, vì thế sau khi đưa vào dãy , là phần tử nhỏ nhất chưa được chọn ở dãy và là phần tử nhỏ nhất chưa được chọn ở dãy .
So sánh và , chọn phần tử có giá trị nhỏ hơn và đưa vào dãy .
Sau khi đưa vào dãy , trở thành phần tử nhỏ nhất chưa được chọn ở dãy .
Vẫn như thế, phần tử tiếp theo của dãy sẽ là phần tử nhỏ nhất trong các phần tử chưa được đưa vào dãy .
So sánh và , chọn phần tử có giá trị nhỏ hơn dãy và đưa vào dãy .
Sau khi đưa vào dãy , trở thành phần tử nhỏ nhất chưa được chọn ở dãy .
Ta nhận thấy rằng
Dựa vào những phân tích ta có giải pháp sử dụng hai con trỏ như sau:
Để hiểu rõ hơn, ta hãy cùng xem qua ví dụ sau đây:
Đặt và .
Vì nên ta đưa vào mảng và tăng vị trí lên một.
Vì nên ta đưa vào mảng và tăng vị trí lên một.
Vì nên ta đưa vào mảng và tăng vị trí lên một.
Vì nên ta có thể đưa bất kỳ một trong hai phần tử. Ở đây ta đưa phần tử vào và tăng vị trí lên một.
Vì nên ta đưa vào mảng và tăng vị trí lên một.
Vì nên ta đưa vào mảng và tăng vị trí lên một.
Vì nên ta đưa vào mảng và tăng vị trí lên một.
Vì nên ta đưa vào mảng và tăng vị trí lên một.
Vì tất cả các phần tử trong dãy đều đã được đưa vào dãy nên từ đưa lần lượt các phần tử chưa được chọn trong dãy vào trong dãy
Cài đặt
int i = 1, j = 1;
vector<int> c;
while (i <= n || j <= m){
if (j == m + 1 || (i <= n && a[i] <= b[j]))
c.push_back(a[i++]);
else
c.push_back(b[j++]);
}
for (auto it: c)
cout << it << " ";
Độ phức tạp
Vị trí con trỏ luôn tăng và tăng quá không quá lần, vị trí con trỏ cũng luôn tăng và tăng không quá lần.
Vì thế độ phức tạp của giải pháp là .
Cho một mảng số nguyên có phần tử, mảng này đã được sắp xếp tăng dần. Hãy tìm hai vị trí khác nhau bất kỳ sao cho tổng của hai phần tử ở hai vị trí đó có giá trị là .
Giới hạn: và
Hãy cùng xem ví dụ sau đây.
Cho trước mảng số được sắp xếp tăng dần và :
Làm cách nào để có thể tìm hai vị trí khác nhau mà tổng hai phần tử ở hai vị trí đó có tổng là ?
Trước tiên, ta có một chút nhận xét sau:
Có thể thấy, tổng của với các phần tử khác trong dãy đều lớn hơn . Vì thế ta không quan tâm đến nữa.
Có thể thấy, tổng của với các phần tử khác trong các phần tử ta quan tâm đều nhỏ hơn . Vì thế ta không quan tâm đến nữa.
Có thể thấy, tổng của với các phần tử khác trong các phần tử ta quan tâm đều lớn hơn . Vì thế ta không quan tâm đến nữa.
Như vậy, tại một thời điểm bất kỳ, những phần tử chúng ta cần quan tâm đến sẽ là các phần tử trong đoạn nào đó.
Ta có một số nhận xét sau:
Từ những phân tích vừa rồi ta có giải pháp sử dụng hai con trỏ như sau:
Để hiểu rõ hơn, ta hãy cùng xem qua một số ví dụ sau đây:
Ví dụ 1: và .
Đặt và .
Vì nên giảm vị trí đi một đơn vị.
Vì nên tăng vị trí lên một đơn vị.
Vì nên giảm vị trí đi một đơn vị.
Vì nên tăng vị trí lên một đơn vị.
Vì nên hai vị trí cần tìm là hai vị trí và .
Ví dụ 2: và .
Đặt và .
Vì nên giảm vị trí đi một đơn vị.
Vì nên tăng vị trí lên một đơn vị.
Vì tăng vị trí lên một đơn vị.
Vì giảm vị trí đi một đơn vị.
Vì giảm vị trí đi một đơn vị.
Vì tăng vị trí lên một đơn vị.
Vì nên không tìm được hai vị trí cần tìm.
Cài đặt
int i = 1, j = N;
while (i < j) {
if (a[i] + a[j] == x) {
cout << i << " " << j;
return 0;
}
if (a[i] + a[j] < x)
i += 1;
else
j -= 1;
}
cout << "No solution";
Độ phức tạp
Vị trí con trỏ luôn tăng, vị trí con trỏ thì luôn giảm.
Hơn nữa, sự thay đổi vị trí hai con trỏ này sẽ dừng lại khi tổng hai phần tử ở hai vị trí con trỏ có tổng là hay khi vị trí bằng vị trí .
Vì thế, việc thay đổi vị trí hai con trỏ sẽ không quá lần, độ phức tạp của giải pháp là .
Cho dãy số nguyên dương có phần tử. Hãy tìm độ dài đoạn con dài nhất trong dãy sao cho tổng các phần tử trong đoạn này không quá .
Dữ liệu đảm bảo các phần tử trong dãy đều có giá trị không quá .
Giới hạn: , và .
Để dễ dàng phân tích, ta tạm gọi
Qua đây, bài toán của chúng ta sẽ là tìm độ dài đoạn con "tốt" dài nhất.
Vì dãy là một dãy số nguyên dương cho nên
Với là một vị trí bất kỳ, nếu như là vị trí nhỏ nhất sao cho đoạn là một đoạn "tốt" thì
Từ đó, với mỗi từ đến , nếu ta xác định được vị trí , ta có thể biết được độ dài của đoạn con "tốt" dài nhất của dãy .
Hãy cùng nhận xét vị trí của với mỗi từ đến qua ví dụ sau đây:
Cho trước dãy và