3.2-bob: Massiv va String algoritmlari
3-QISM — Algoritm va ma'lumotlar tuzilmasi · 2-mavzu
1. Kirish va motivatsiya
Big-O 3.1-bob o'lchovini bildik; endi uni amalda qo'llaymiz. Massiv 2.7-bob va string 2.6-bob — dasturlashda eng ko'p uchraydigan ma'lumot. Ular ustidagi klassik algoritm naqshlari (ikki ko'rsatkich, sliding window, prefiks yig'indi) — intervyularning va kundalik ishning asosi.
Bu naqshlar — alohida masalalar emas, balki fikrlash shablonlari 0.6-bob. Bittasini o'zlashtirsangiz, yuzlab masalani yechasiz: "ikki ko'rsatkich" — palindrom, juftliklar, saralangan massivni birlashtirish; "sliding window" — eng katta ketma-ketlik, substring masalalari.
O'xshatish: bu naqshlar — duradgorning asosiy harakatlari (arralash, randalash, biriktirish). Har bir mebel boshqacha, lekin asosiy harakatlar bir xil. Algoritm naqshlarini bilsangiz — yangi masala "tanish" ko'rinadi.
Nega muhim?
- Massiv/string — har bir dasturda (ro'yxat, matn, ma'lumot qayta ishlash).
- Bu naqshlar O(n²) yechimni ko'pincha O(n) ga tushiradi 3.1-bob.
- LeetCode/intervyu masalalarining katta qismi shu naqshlarga tayanadi 3.13-bob.
2. Nazariya — chuqur tushuntirish
2.1. Massiv — xotirada qanday yashaydi (0.1)
Massiv — xotirada ketma-ket joylashgan elementlar (0.1: RAM kataklari). Shuning uchun indeks bo'yicha murojaat O(1) — manzil = boshlanish + indeks × element_hajmi (to'g'ridan-to'g'ri hisoblanadi):
Indeks: 0 1 2 3 4
┌────┬────┬────┬────┬────┐
Massiv: │ 10 │ 20 │ 30 │ 40 │ 50 │ ketma-ket xotirada 0.1-bob
└────┴────┴────┴────┴────┘
arr[3] boshlanish + 3 × hajm DARROV (O(1))Lekin o'rtaga qo'shish/o'chirish O(n) — qolgan elementlarni surish kerak (3.1 jadvali).
2.2. Ikki ko'rsatkich (two pointers) naqshi
Ikki ko'rsatkich — massiv/stringda ikkita indeks bilan ishlash; ko'pincha O(n²) ni O(n) ga tushiradi:
- Qarama-qarshi (ends): biri boshdan, biri oxiridan — markazga qarab (palindrom, juftlik).
- Bir yo'nalishli (fast/slow): ikkalasi boshdan, har xil tezlikda (dublikat o'chirish).
Palindrom tekshirish (qarama-qarshi):
"k a n o k"
chap va o'ng taqqoslanadi, markazga yaqinlashadi
chap o'ngfunction palindromMi(s) {
let chap = 0, ong = s.length - 1;
while (chap < ong) {
if (s[chap] !== s[ong]) return false; // mos kelmasa — palindrom emas
chap++; ong--; // markazga
}
return true;
}
// O(n) vaqt, O(1) xotira (3.1)2.3. Sliding window (suriluvchi oyna) naqshi
Sliding window — uzluksiz ketma-ketlik (subarray/substring) ustida ishlash; "oyna"ni surib, qayta hisoblamasdan yangilash:
Eng katta 3 ta uzluksiz son yig'indisi:
[2, 1, 5, 1, 3, 2]
└─────┘ oyna=8 (2+1+5)
└─────┘ oyna surildi: -2 +1 = 7
└─────┘ -1 +3 = 9 eng kattafunction engKattaOyna(arr, k) {
let oyna = 0;
for (let i = 0; i < k; i++) oyna += arr[i]; // birinchi oyna
let max = oyna;
for (let i = k; i < arr.length; i++) {
oyna += arr[i] - arr[i - k]; // yangi kel, eski ket (qayta hisoblamasdan!)
max = Math.max(max, oyna);
}
return max;
}
// O(n) — har element bir marta (ichma-ich tsiklsiz — 3.1)Nega kuchli: har oynani noldan hisoblasak — O(n×k). Surib yangilash — O(n). Bu — qayta hisoblashdan qochish 3.1-bob.
Kadane algoritmi — eng katta yig'indili uzluksiz subarray (oyna kengligi qat'iy emas, o'zgaruvchan). Har bir indeksda savol: "shu elementni oldingi yig'indiga qo'shamizmi yoki undan yangi boshlaymizmi?" Manfiy sonlar bo'lganda ham ishlaydi:
[-2, 1, -3, 4, -1, 2, 1, -5, 4]
└──────────┘ eng katta yig'indi = 6 (4-1+2+1)function engKattaYigindi(arr) {
let joriy = arr[0], max = arr[0]; // bo'sh massivni alohida tekshir (0.6)
for (let i = 1; i < arr.length; i++) {
joriy = Math.max(arr[i], joriy + arr[i]); // yangi boshla yoki davom et
max = Math.max(max, joriy); // eng katta yig'indini yangilab bor
}
return max;
}
engKattaYigindi([-2, 1, -3, 4, -1, 2, 1, -5, 4]); // 6 — O(n), O(1) xotiraG'oya: agar oldingi yig'indi manfiy bo'lsa — uni tashlab, shu elementdan yangi boshlash foydaliroq. Bu — DP ning 3.12-bob eng sodda namunalaridan biri.
2.4. Prefiks yig'indi (prefix sum) naqshi
Oldindan yig'indilarni hisoblab saqlash — keyin istalgan oraliq yig'indisini O(1) da olish:
function prefiksYigindi(arr) {
const prefiks = [0];
for (let i = 0; i < arr.length; i++)
prefiks[i + 1] = prefiks[i] + arr[i]; // har joygacha yig'indi
return prefiks;
}
// [3,1,4,1,5] prefiks [0,3,4,8,9,14]
// oraliq [i..j] yig'indisi = prefiks[j+1] - prefiks[i] O(1)!2.5. Hash map bilan tezlashtirish (2.9)
Ko'p massiv masalasi Map/Set bilan O(n²) dan O(n) ga tushadi (3.1, 2.9):
// "Two Sum" — yig'indisi target bo'lgan ikki son indeksi
function twoSum(arr, target) {
const korilgan = new Map(); // qiymat indeks
for (let i = 0; i < arr.length; i++) {
const kerakli = target - arr[i];
if (korilgan.has(kerakli)) // juftini ko'rganmizmi? O(1) (2.9)
return [korilgan.get(kerakli), i];
korilgan.set(arr[i], i);
}
return null;
}
// O(n) — Map bilan (ichma-ich tsikl O(n²) o'rniga — 3.1)2.6. Asosiy massiv amallari (in-place vs yangi)
- In-place — asl massivni o'zgartirish (O(1) qo'shimcha xotira), lekin mutatsiya 2.7-bob.
- Yangi massiv — immutable (2.7, 2.15), lekin O(n) xotira.
// In-place ag'darish (ikki ko'rsatkich — 2.2)
function agdar(arr) {
let l = 0, r = arr.length - 1;
while (l < r) { [arr[l], arr[r]] = [arr[r], arr[l]]; l++; r--; } // swap (2.8)
return arr;
}
// Immutable: [...arr].reverse() yoki arr.map(...) (2.7)2.7. String algoritmlari (2.6)
String — o'zgarmas 2.6-bob, shuning uchun ko'p amal uchun massivga aylantirib ishlanadi:
const harflar = [...s]; // yoki s.split("") (2.6)
const tartiblangan = [...s].sort().join(""); // string saralash (2.6, 2.7)Klassik string masalalari: palindrom 2.2-bob, anagram (harf sanog'i), substring qidirish, belgi chastotasi (Map — 2.9).
// Anagram tekshirish — harf chastotasi (Map — 2.9)
function anagramMi(a, b) {
if (a.length !== b.length) return false; // chegaraviy holat (0.6)
const sanoq = {};
for (const ch of a) sanoq[ch] = (sanoq[ch] || 0) + 1;
for (const ch of b) {
if (!sanoq[ch]) return false;
sanoq[ch]--;
}
return true;
}
// O(n) — chastotani Map'da sanash (saralash O(n log n) o'rniga)3. Asosiy naqshlar — tez ma'lumotnoma
IKKI KO'RSATKICH — palindrom, juftlik, saralangan birlashtirish (O(n))
SLIDING WINDOW — uzluksiz subarray/substring (O(n))
KADANE — eng katta yig'indili uzluksiz subarray (O(n))
PREFIX SUM — oraliq yig'indilari (hisob O(n), so'rov O(1))
HASH MAP/SET — qidiruv/dublikat/chastota O(n) ga tushirish 2.9-bob
IN-PLACE SWAP — ag'darish/tartiblash O(1) xotira4. Batafsil kod namunalari
Misol 1 — Ikki ko'rsatkich: saralangan massivda juftlik (2.2)
// Saralangan massivda yig'indisi target bo'lgan juftlik
function juftlikTopib(arr, target) {
let l = 0, r = arr.length - 1; // qarama-qarshi (2.2)
while (l < r) {
const yigindi = arr[l] + arr[r];
if (yigindi === target) return [arr[l], arr[r]];
yigindi < target ? l++ : r--; // kichik bo'lsa chapni surib, katta bo'lsa o'ngni
}
return null;
}
juftlikTopib([1, 3, 5, 7, 9], 12); // [3, 9] — O(n), O(1) xotiraMisol 2 — Sliding window: takrorsiz eng uzun substring (2.3)
function engUzunTakrorsiz(s) {
const korilgan = new Set(); // oynadagi belgilar (2.9)
let chap = 0, max = 0;
for (let ong = 0; ong < s.length; ong++) {
while (korilgan.has(s[ong])) { // takror bo'lsa — chapni surib torayt
korilgan.delete(s[chap]); chap++;
}
korilgan.add(s[ong]);
max = Math.max(max, ong - chap + 1); // oyna kengligi
}
return max;
}
engUzunTakrorsiz("abcabcbb"); // 3 ("abc") — O(n)Misol 3 — Hash map: chastota va eng ko'p uchragan (2.5, 2.9)
function engKopUchragan(arr) {
const sanoq = new Map();
for (const x of arr)
sanoq.set(x, (sanoq.get(x) || 0) + 1); // chastota (2.9)
let max = null, maxSon = 0;
for (const [qiymat, son] of sanoq) // eng ko'rini top
if (son > maxSon) { max = qiymat; maxSon = son; }
return max;
}
engKopUchragan([1, 3, 1, 3, 1]); // 1 — O(n)Misol 4 — In-place: noldan keyin surish (2.6)
// Barcha 0 larni oxiriga surish (in-place, tartibni saqlab)
function nollarniSur(arr) {
let yoz = 0; // nol bo'lmaganlarni yozish indeksi
for (let i = 0; i < arr.length; i++)
if (arr[i] !== 0) arr[yoz++] = arr[i]; // nol bo'lmaganni oldinga
while (yoz < arr.length) arr[yoz++] = 0; // qolganini nol bilan to'ldir
return arr;
}
nollarniSur([0, 1, 0, 3, 12]); // [1, 3, 12, 0, 0] — O(n), O(1) xotira5. To'g'ri va noto'g'ri holatlar
1) Ichma-ich tsikl (naqsh o'rniga)
// O(n²) — har juftlikni tekshirish (3.1)
for (let i = 0; i < arr.length; i++)
for (let j = i + 1; j < arr.length; j++)
if (arr[i] + arr[j] === target) return [i, j];
// O(n) — hash map (2.5)
const m = new Map();
for (let i = 0; i < arr.length; i++) {
if (m.has(target - arr[i])) return [m.get(target - arr[i]), i];
m.set(arr[i], i);
}2) Stringni tsiklda += bilan qurish (katta hajmda)
// string o'zgarmas — har += yangi string (sekin — 2.6)
let r = "";
for (const ch of s) r += ch.toUpperCase();
// massivga yig'ib, join (2.7)
const r = [...s].map(ch => ch.toUpperCase()).join("");3) Sliding window'ni har safar noldan hisoblash
// O(n×k) — har oynani qayta yig'ish
// surib yangilash: +yangi -eski (O(n) — 2.3)
oyna += arr[i] - arr[i - k];4) Chegaraviy holatlarni unutish (0.6)
// bo'sh massiv, bitta element, uzunlik teng emas — tekshirilmagan
// boshida tekshir
if (arr.length === 0) return null;
if (a.length !== b.length) return false; // anagram (2.7)6. Keng tarqalgan xatolar va yechimlari
Xato 1 — Off-by-one (chegara xatosi)
Sababi: < vs <=, indeks chegarasi (0.6, 2.2). Yechimi: qo'lda kichik misolda tekshiring; ikki ko'rsatkichda while (l < r) (teng bo'lganda to'xtaydi).
Xato 2 — undefined element (chegaradan tashqari)
Sababi: arr[i] mavjud bo'lmagan indeks 2.7-bob. Yechimi: indeks arr.length ichida ekanini tekshiring; sliding window'da i - k >= 0.
Xato 3 — String'ni massiv kabi o'zgartirish
s[0] = "X"; // ishlamaydi — string o'zgarmas (2.6)Sababi: string immutable 2.6-bob. Yechimi: [...s] (massivga), o'zgartiring, join 2.7-bob.
Xato 4 — Katta nda O(n²) muzlash
Sababi: ichma-ich tsikl 3.1-bob. Yechimi: ikki ko'rsatkich/sliding window/hash map naqshi (3-bo'lim).
Xato 5 — Mutatsiya kutilmagan natija
Sababi: in-place algoritm asl massivni o'zgartirdi 2.7-bob. Yechimi: nusxa kerak bo'lsa [...arr]; ataylab in-place bo'lsa hujjatlang.
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- Big-O 3.1-bob: bu naqshlar O(n²) ni O(n) ga tushiradi.
- Array/String/Map/Set (2.6, 2.7, 2.9): asosiy vositalar.
- Saralash/qidiruv (3.9, 3.10): ko'p naqsh saralangan massivga tayanadi.
- Masala yechish 3.13-bob: LeetCode masalalarining asosi shu naqshlar.
- Backend ma'lumot qayta ishlash (5, 6): DB natijalarini filtrlash/tahlil.
- DP 3.12-bob: prefiks yig'indi DP ning kirishi.
8. Eng yaxshi amaliyotlar (best practices)
- Naqshni taning: uzluksiz oraliq sliding window; saralangan/juftlik ikki ko'rsatkich; qidiruv/dublikat hash map.
- Avval Big-O ni o'ylang 3.1-bob — ichma-ich tsikldan qoching.
- Chegaraviy holatlarni boshida tekshiring 0.6-bob: bo'sh, bitta, teng emas.
- String uchun massivga aylantiring (
[...s]), tsiklda+=qilmang 2.6-bob. - In-place vs immutable ni ongli tanlang 2.7-bob.
- Qo'lda kichik misolda yuring 0.6-bob — off-by-one'ni topadi.
- Hash map — eng kuchli qurol ko'p massiv masalasida 2.9-bob.
9. Amaliy loyiha: "Algoritm Naqshlari To'plami"
Massiv/string naqshlarini amalda mustahkamlovchi masalalar to'plami.
Maqsad
Ikki ko'rsatkich, sliding window, prefiks yig'indi va hash map naqshlarini qo'llab, har masala uchun Big-O ni aniqlash 3.1-bob.
Talablar (requirements)
Har masala uchun: pseudokod 0.6-bob, yechim, Big-O tahlili, chegaraviy holatlar:
- Palindrom (ikki ko'rsatkich — 2.2): bo'sh/bitta belgi, bo'shliq/registr.
- Two Sum (hash map — 2.5): juftlik yo'q holat.
- Eng uzun takrorsiz substring (sliding window — Misol 2).
- Anagram (chastota — 2.7): uzunlik teng emas.
- Eng katta k-uzunlikdagi yig'indi (sliding window — 2.3).
- Eng katta yig'indili subarray (Kadane — 2.3): manfiy sonli holat.
- Oraliq yig'indisi (prefiks sum — 2.4): bir nechta so'rov.
- Nollarni surish (in-place — Misol 4).
- Eng ko'p uchragan element (Map — Misol 3).
- Har yechim uchun O(n²) "sodda" versiya bilan solishtiring (qaysi tezroq va nega).
Maslahatlar (hint)
- Avval pseudokod, keyin kod 0.6-bob.
- Ikki ko'rsatkich:
while (l < r); sliding window:Set/yig'indi yangilash. - Two Sum/anagram: Map/object chastota 2.9-bob.
- Chegaraviy holatlarni boshida 0.6-bob.
- Har masala uchun "n 10x vaqt necha barobar?" 3.1-bob.
"Tayyor" mezonlari (acceptance criteria)
- 9 masala ishlaydi, har biri pseudokod bilan.
- To'g'ri naqsh ishlatilgan (ikki ko'rsatkich/window/hash map).
- Har yechim O(n) yoki O(n log n) (sodda O(n²) emas).
- Chegaraviy holatlar tekshirilgan.
- Big-O tahlili har masala uchun yozilgan.
- In-place masala asl massivni to'g'ri o'zgartiradi.
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda massiv va string ustidagi klassik algoritm naqshlarini o'rgandik:
- Massiv — xotirada ketma-ket 0.1-bob; indeks O(1), o'rta qo'shish/o'chirish O(n).
- Ikki ko'rsatkich — qarama-qarshi yoki fast/slow; palindrom, juftlik (O(n), O(1) xotira).
- Sliding window — uzluksiz oraliq; surib yangilash (qayta hisoblamasdan, O(n)).
- Kadane — eng katta yig'indili subarray; manfiy sonli holatda ham O(n).
- Prefiks yig'indi — oraliq so'rovlari O(1).
- Hash map/set 2.9-bob — qidiruv/dublikat/chastotani O(n) ga tushiradi.
- Best: naqshni taning, Big-O ni o'ylang, chegaraviy holatlarni tekshiring.
Keyingi bob — 3.3-bob: Linked List. Massiv — ketma-ket xotira (indeks tez, o'rta o'zgartirish sekin). Linked List — boshqa yondashuv: elementlar xotirada tarqoq, lekin "ko'rsatkich" bilan bog'langan; bosh/o'rtaga qo'shish/o'chirish O(1). Bu — birinchi "ko'rsatkichli" ma'lumotlar tuzilmasi.
Foydalanilgan rasmiy/ishonchli manbalar
- bigocheatsheet.com — massiv operatsiyalari murakkabligi
- MDN Web Docs — Array, String metodlari (2.6, 2.7)
- Universal CS — two pointers, sliding window, prefix sum naqshlari
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!