ในบทความนี้ เราได้รับปัญหา เราได้รับอาร์เรย์ และมีสองประเภทของคิวรีที่เราต้องตอบ:
- ประเภท 0 - เราต้องคำนวณจำนวนองค์ประกอบที่มากกว่าหรือเท่ากับ x (ค่าที่กำหนด)
- ประเภท 1 - เราต้องคำนวณจำนวนองค์ประกอบที่มากกว่า x (ค่าที่กำหนด) อย่างเคร่งครัด
นี่คือตัวอย่างง่ายๆ:
Input : arr[] = { 10, 15, 30, 40, 45 } และ Q = 3
Query 1: 0 50
Query 2: 1 40
Query 3: 0 30
Output : 0 1 3
คำอธิบาย: x = 50, q = 0 : ไม่มีองค์ประกอบมากกว่าหรือเท่ากับ 50
x = 40, q = 1 : 45 มากกว่า 40
x = 30, q = 0 : สามองค์ประกอบ 30, 40, 45 มากกว่าหรือเท่ากับ 30
วิธีการแก้ปัญหา
เราสามารถใช้สองวิธีที่แตกต่างกันเพื่อหาคำตอบ แรกเราจะใช้วิธี Brute Force แล้วตรวจสอบว่ามันสามารถทำงานกับข้อจำกัดที่สูงกว่าได้หรือไม่ ถ้าไม่ได้ เราจะดำเนินการเพิ่มประสิทธิภาพวิธีแก้ปัญหาของเรา
วิธี Brute Force
ในวิธีนี้ เราจะเดินทางผ่านอาร์เรย์สำหรับคิวรีทั้งหมด q และหาตัวเลขที่ตรงตามเงื่อนไขที่กำหนด
ตัวอย่าง
#include <bits/stdc++.h>
using namespace std;
void query(int *arr, int n, int type, int val) {
int count = 0; // คำตอบ
if(!type) { // เมื่อถามคิวรีประเภท 0
for(int i = 0; i < n; i++) {
if(arr[i] >= val)
count++;
}
} else { // เมื่อถามคิวรีประเภท 1
for(int i = 0; i < n; i++) {
if(arr[i] > val)
count++;
}
}
cout << count << "\n";
}
int main() {
int ARR[] = { 10, 15, 30, 40, 45 };
int n = sizeof(ARR)/sizeof(ARR[0]); // ขนาดของอาร์เรย์
query(ARR, n, 0, 50); // คิวรี 1
query(ARR, n, 1, 40); // คิวรี 2
query(ARR, n, 0, 30); // คิวรี 3
return 0;
}
ผลลัพธ์
0 1 3
ในวิธีข้างต้น เราเพียงแค่เดินทางผ่านอาร์เรย์และคำนวณคำตอบสำหรับคิวรี วิธีนี้ทำงานสำหรับตัวอย่างที่กำหนด แต่ถ้าเราพบข้อจำกัดที่สูงกว่า วิธีนี้จะล้มเหลว เพราะความซับซ้อนด้านเวลาทั้งหมดของโปรแกรมคือ O(N×Q) โดยที่ N คือขนาดของอาร์เรย์ และ Q คือจำนวนคิวรี ดังนั้นตอนนี้เราจะเพิ่มประสิทธิภาพวิธีนี้ให้ทำงานได้กับข้อจำกัดที่สูงกว่าด้วย
วิธีที่มีประสิทธิภาพ (Efficient Approach)
ในวิธีนี้ เราจะใช้ Binary Search เพื่อหา upper bound และ lower bound ของค่าที่กำหนด เราจัดเรียงอาร์เรย์ของเราก่อนโดยใช้ binary search แล้วจึงนำฟังก์ชัน lower bound และ upper bound ไปใช้ตามลำดับ
ตัวอย่าง
#include <bits/stdc++.h>
using namespace std;
void lowerbound(int *arr, int n, int val) {
int l = -1, r = n;
while(r - l > 1) { // binary search คำตอบ
int mid = (l+r)/2;
if(arr[mid] >= val)
r = mid;
else
l = mid;
}
if(r == n) // ถ้า r ไม่เคลื่อนไหว หมายความว่าไม่มีองค์ประกอบที่ตรงตามเงื่อนไข
cout << "0\n";
else
cout << n - r << "\n";
}
void upperbound(int *arr, int n, int val) {
int l = -1, r = n;
while(r - l > 1) { // binary search คำตอบ
int mid = (l+r)/2;
if(arr[mid] > val)
r = mid;
else
l = mid;
}
if(r == n)// ถ้า r ไม่เคลื่อนไหว หมายความว่าไม่มีองค์ประกอบที่ตรงตามเงื่อนไข
cout << "0\n";
else
cout << n - r << "\n";
}
void query(int *arr, int n, int type, int val) {
if(!type) // ถ้า type == 0 เรียกฟังก์ชัน lower bound
lowerbound(arr, n, val);
else // ถ้า type == 1 เรียกฟังก์ชัน upperbound
upperbound(arr, n, val);
}
int main() {
int arr[] = { 1, 2, 3, 4 };
int n = sizeof(arr)/sizeof(arr[0]); // ขนาดของอาร์เรย์
sort(arr, arr+n); // จัดเรียงอาร์เรย์
query(arr, n, 0, 5); // คิวรี 1
query(arr, n, 1, 3); // คิวรี 2
query(arr, n, 0, 3); // คิวรี 3
return 0;
}
ผลลัพธ์
0 1 2
โค้ดข้างต้นทำงานบน binary search ที่ลดความซับซ้อนด้านเวลาลงอย่างมาก ดังนั้นความซับซ้อนสุดท้ายของเราจะกลายเป็น O(N log N) โดยที่ N คือขนาดของอาร์เรย์
คำอธิบายโค้ด
ในวิธีนี้ เราจะใช้ binary search เพื่อหา upper bound และ lower bound ของค่าที่กำหนด ตอนนี้สำหรับ binary search เราจัดเรียงอาร์เรย์ของเราก่อนเพราะมันทำงานได้เฉพาะกับอาร์เรย์ที่จัดเรียงแล้ว เราสร้างฟังก์ชัน lower bound และ upper bound ที่ช่วยให้เราหาตัวเลขแรกที่ตรงตามเงื่อนไข type 0, type 1 ตามลำดับ ตอนนี้เนื่องจากเราจัดเรียงอาร์เรย์แล้ว เราพบตัวเลขแรกที่ช่วยให้เงื่อนไขเป็นจริง ดังนั้นองค์ประกอบหลังจากองค์ประกอบนี้ก็จะทำตามเงื่อนไขเช่นกัน ดังนั้นเราพิมพ์ความแตกต่างของดัชนีขององค์ประกอบนี้กับ N (ขนาดของอาร์เรย์)
สรุป
ในบทความนี้ เราแก้ปัญหา Queries for greater than and not less than โดยใช้ Binary search เรายังเรียนรู้โปรแกรม C++ สำหรับปัญหานี้และวิธีการแก้ปัญหาแบบเต็มรูปแบบ (ธรรมดาและมีประสิทธิภาพ) ที่เราใช้แก้ปัญหานี้ เราสามารถเขียนโปรแกรมเดียวกันในภาษาอื่นๆ เช่น C, Java, Python และภาษาอื่นๆ ได้ เราหวังว่าบทความนี้จะเป็นประโยชน์