Barcha qo'llanmalar

Linked list'lar va Python list'lari: qachon qaysi birini ishlatish kerak

Python'da linked list'larni aniq kod misollari, keng tarqalgan implementatsiyalar va sizga haqiqatan ham beriladigan klassik intervyu savollari orqali puxta o'zlashtiring.

Muallif: Kimberly Fessel

List elementlaringiz sizga nafaqat qiymatni, balki keyin qayerga borish kerakligini ham aytib berishini xohlaganmisiz? Agar shunday bo'lsa, linked list'lar aynan siz izlagan narsa bo'lishi mumkin. Linked list'lar har bir node'da qiymat saqlashdan tashqari keyingi node'ga reference ham saqlaydi va sizni to'g'ri yo'nalishga yo'naltirib turadi.

Ushbu maqolada siz linked list'larning asosiy tarkibiy qismlari va keng tarqalgan amallari haqida ko'proq bilib olibgina qolmay, Python'da linked list'larning turli xillarini qanday amalga oshirishni ham ko'rasiz. Oddiy list'lar bilan qisqacha taqqoslashdan boshlab, sizni linked list bo'yicha klassik intervyu savollari bilan tanishtiramiz va Python'da allaqachon built-in bo'lgan muqobillarni ham ko'rsatamiz, shunda siz ishlarni bajarishning yagona usuliga bog'lanib qolmaysiz.

Linked list nima?

Linked list — computer science'ning asosini tashkil etuvchi chiziqli ma'lumotlar tuzilmasi. Bu node deb ataladigan elementlar ketma-ketligidan foydalanadigan ma'lumotlarni tashkil etish usullaridan biri. Har bir node quyidagilarni o'z ichiga oladi:

  • Muayyan ma'lumot qiymati

  • Zanjirdagi keyingi node'ga reference/pointer

Linked list bo'ylab uning head'idan, ya'ni birinchi node'idan tail'igacha, ya'ni oxirgi node'igacha bir node'dan keyingisiga o'tib borishingiz mumkin. Oddiy, singly linked list'da oxirgi node None'ga ishora qiladi.

Linked list massiv kabi xotiradan uzluksiz joy egallashi shart emas. Buning o'rniga har bir node keyingi node bilan bog'lanish uchun o'zining next reference'idan foydalanadi.

Python'da built-in linked list ma'lumotlar tuzilmasi yo'q. Biroq uni o'zingiz amalga oshirishingiz mumkin, odatda buning uchun o'zingiz yozgan sinflardan foydalaniladi.

Linked list va list'lar

Python'da linked list'larni yaratish usuliga o'tishdan oldin ularni an'anaviy list'lardan farqlab olish muhim.

Python'ning built-in list'i ham, linked list ham elementlarning tartiblangan to'plamlarini saqlash uchun ishlatiladi, lekin ichki tomondan ular juda boshqacha ishlaydi. Oddiy list'lar xotirada uzluksiz massivdan foydalanadi va index orqali elementlariga tez hamda oson murojaat qilish imkonini beradi. Biroq list'lar oldindan uzluksiz xotiraning qat'iy blokini ajratib olishi kerak, bu esa element qo'shilganda yoki o'chirilganda hajmni o'zgartirishga olib kelishi mumkin va bu unumdorlikka ta'sir qilishi mumkin. Shunga qaramay, bu faqat vaqti-vaqti bilan sodir bo'ladi, chunki Python'ning list'i ortiqcha sig'imni oldindan ajratib qo'yadi, shu sababli append() chaqiruvlarining ko'pchiligi hajm o'zgarishini keltirib chiqarmaydi

Linked list'lar esa, aksincha, pointer'lar bilan bog'langan node'lardan foydalanadi. Bu ularni massivlar kabi random access uchun yaroqsiz qiladi (ya'ni index'ini bilgan holda muayyan node'ga to'g'ridan-to'g'ri murojaat qila olmaysiz), ammo element qo'shish yoki elementlarni o'chirishda ular moslashuvchanroq va samaraliroq, chunki bu amallar faqat bir-ikkita pointer'ni qayta ulashni talab qiladi.

Quyidagi diagramma farqlarni ko'rsatib beradi:

List va linked list

Linked list yaratish uchun asosiy qismlar

Endi siz o'zingizning Python linked list'ingizni yaratishga tayyorsiz. Bu odatda ikkita asosiy sinf yordamida amalga oshiriladi:

  • Node — har bir alohida node uchun

  • LinkedList — umumiy ma'lumotlar tuzilmasi uchun

Bu sinflarni yaratish alohida node xatti-harakatini list darajasidagi xatti-harakatdan ajratishga yordam beradi, bu esa traverse qilish, yangi elementlar qo'shish, elementlarni o'chirish va qidirish uchun hal qiluvchi ahamiyatga ega.

Alohida node'lar

Node Python sinfi linked list'dagi bitta elementni ifodalaydi. Har bir node o'z ma'lumotini va ketma-ketlikdagi keyingi node'ga reference'ni saqlaydigan alohida obyektdir.

Mana Node'ning ta'rifi. def __init__(self, data) qatori constructor'ni aniqlaydi, u attribute'larni saqlashga yordam beradi:

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

next attribute'i zanjirdagi keyingi node'ga reference saqlaydi; biroq siz boshqa node bilan bog'lanish o'rnatmaguningizcha u None qiymati bilan initialize qilinadi.

Node obyektlarini yaratishni boshlash uchun initialize qilishda qiymat bering. So'ng node'lar o'rtasida bog'lanish hosil qilish uchun next property'sini o'rnating:

head = Node(100)
second_node = Node(200)

head.next = second_node

Bo'sh list'ni initialize qilish

Node tayyor bo'lgach, LinkedList sinfini yaratish vaqti keldi:

class LinkedList:
    def __init__(self):
        self.head = None

Shundan so'ng bo'sh linked list yaratishingiz mumkin:

linked_list = LinkedList()

print(linked_list.head)

# Expected result:
# None

Bu list'da hozircha birorta ham node yo'q, lekin ularni sinf method'i yordamida qo'shishingiz mumkin. Keyingi qadam — linked list'ning standart amallari doirasida node'larni qo'shish, o'chirish yoki ular orasidan qidirish uchun kod yozish.

Linked list'lar uchun asosiy method'lar

Sizda Python linked list'ining ikki sinfdan iborat skeleti allaqachon bor: alohida node'lar uchun Node va umumiy list tuzilmasi uchun LinkedList. Odatda siz method'lar, ya'ni sinf ichida joylashadigan va sinf bajara oladigan amallarni tavsiflaydigan maxsus funksiyalarni yozishni xohlaysiz.

Yangi node'lar qo'shish

Boshlash uchun Python linked list'ingiz oxiriga node'lar qo'shadigan append() nomli method yaratishingiz mumkin. Bu method yangi qiymatni qabul qilishi, yangi node yaratishi va uni list'ingiz oxiriga qo'shishi kerak. Agar list'ingiz bo'sh bo'lsa, yangi node head'ga aylanadi, agar list'ingizda allaqachon node'lar bo'lsa, bu method yangi node'ni joriy tail node'dan keyingi node sifatida o'rnatadi. E'tibor bering, bu funksiya LinkedList sinfining bir qismi bo'lishi kerak:

def append(self, value):
    new_node = Node(value)

    # Set as head if empty list
    if self.head is None:
        self.head = new_node
        return

    current = self.head

    # Traverse linked list looking for the tail node
    while current.next is not None:
        current = current.next

    # Append new_node at the end of the list
    current.next = new_node

Bu implementatsiya tail'ni alohida saqlamagani uchun append() oxirgi node'ni topish uchun head'dan boshlab butun list'ni traverse qilishi kerak. Tail'ga yetib borgach, uning next reference'ini yangi node'ga o'rnating.

Endi Python linked list'ingizga yangi node'larni quyidagicha qo'shishingiz mumkin:

linked_list.append(100)
linked_list.append(200)
linked_list.append(300)

Linked list'ni traverse qilish

Butun linked list'ni traverse qilish uchun maxsus method ham yaratishingiz mumkin. Bu traverse() method'i head'dan boshlab, list oxiriga yetguncha doimiy ravishda keyingi node'ga o'tib, list'ning har bir qiymatini chiqaradi:

def traverse(self):
    current = self.head

    while current is not None:    # Continue through the tail node
        print(current.data)
        current = current.next      # Move to the next node

linked_list.traverse()

# Expected result:
# 100
# 200
# 300

Node'ni qidirish

Oddiy Python list'laridan farqli o'laroq, linked list'lar index bo'yicha to'g'ridan-to'g'ri murojaatni qo'llab-quvvatlamaydi. Biroq linked list'da muayyan qiymatni qidiradigan method yaratishingiz mumkin. Bu search() method'i list'ning head'idan boshlaydi va har bir node qiymatini kiritilgan qiymat bilan solishtiradi. U ma'lumoti mos keladigan birinchi node'ni qaytaradi, agar qiymatni topa olmasa None qaytaradi:

def search(self, value):
    current = self.head

    while current is not None:
        if current.data == value:
            return current

        current = current.next

    return None

my_node = linked_list.search(200)

print(my_node.data)

# Expected result:
# 200

Node'larni o'chirish

Node'ni qiymati bo'yicha o'chirish uchun maqsadli node'ni chetlab o'tib, oldingi node'ni keyingisi bilan ulash kifoya. Bu remove() method'i kiritilgan qiymatga mos keladigan birinchi node'ni olib tashlashga yordam beradi. Agar birorta node mos kelmasa, u ValueError ko'taradi:

def remove(self, value):
    # Raise error if list is empty
    if self.head is None:
        raise ValueError(f"{value} not found in linked list")

    # Handle removing the head as a special case
    if self.head.data == value:
        self.head = self.head.next
        return

    current = self.head

    # Look for value, connect around it if found
    while current.next is not None:
        if current.next.data == value:
            current.next = current.next.next
            return

        current = current.next   # Move on if value doesn’t match

    # Raise error if value not found
    raise ValueError(f"{value} not found in linked list")

linked_list.remove(200)
linked_list.traverse()

# Expected result:
# 100
# 300

Yuqoridagi kodda joriy node'ning next attribute'ini yangilashni unutmang. Yangi node'lar qo'shishda yoki elementlarni o'chirishda node'laringiz pointer'larini o'zgartirishni unutib qo'yish juda keng tarqalgan xatodir.

Bu asosiy method'lar Python linked list'lari bilan ishlash uchun zarur vositalarni beradi; biroq ularning amalga oshirilishi siz tanlagan linked list turiga qarab farq qilishi mumkin.

Linked list'larni amalga oshirish: Python'da singly, doubly, circular

Hozircha oddiydek ko'rinsa-da, linked list'lar aslida bir nechta turda bo'ladi va ularning har biri o'z afzalliklari va kamchiliklariga ega. Ushbu maqola davomida siz singly linked list'lar ustida ishladingiz, lekin doubly yoki circular linked list'larga ham duch kelishingiz mumkin.

Singly linked list'lar bir node'dan keyingisiga chiziqli yo'l bo'ylab boradi. Har bir node zanjirdagi keyingi node'ga ishora qiluvchi aynan bitta reference'ga ega. Siz list'ni uning head'idan tail'igacha faqat bir yo'nalishda traverse qilasiz.

Oldingi bo'limlarda ko'rgan Python implementatsiyangiz singly linked list edi. Boshqa turdagi linked list'lar uchun kod ko'plab umumiy xususiyatlarga ega, lekin bir nechta muhim jihatlarda farq qiladi.

Doubly Linked Lists

Singly linked list va doubly linked list o'rtasidagi asosiy farq shundaki, doubly linked list'larda har bir node'da ikkita reference bo'ladi. Har bir node zanjirdagi ham oldingi, ham keyingi node'ga ishora qiladi. Bu shuni anglatadiki, faqat oldinga harakatlanish mumkin bo'lgan singly linked list'dan farqli o'laroq, list bo'ylab oldinga ham, orqaga ham harakatlanishingiz mumkin. List bo'ylab orqaga harakatlana olish ba'zi use case'lar uchun foydali bo'lishi mumkin bo'lsa-da, qo'shimcha reference'lar uchun ma'lumot saqlash xotira sarfini oshiradi.

Doubly linked list uchun Node Python sinfingiz quyidagicha o'zgaradi:

class Node:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None

Yangi prev attribute'iga e'tibor bering. Bu attribute linked list'dagi oldingi node'ga ishora qiladi va list bo'ylab orqaga harakatlanishga yordam beradi.

Ushbu sinf yordamida node yaratganingizda next'ni ham, prev'ni ham o'rnatishingiz mumkin:

head = Node(100)
second = Node(200)

head.next = second
second.prev = head

Doubly linked list yaratishda LinkedList sinfi ham odatda bitta muhim yangilanishni oladi. head'ni kuzatishdan tashqari, endi unda tail reference'i ham bo'ladi, u orqaga traverse qilish uchun boshlang'ich nuqta vazifasini bajaradi:

class LinkedList:
    def __init__(self):
        self.head = None
        self.tail = None

Singly linked list uchun yozgan method'laringiz esa bir nechta muhim jihatlarda o'zgaradi:

  • append(): Eski tail uchun next'ni va tail'dagi yangi node uchun prev'ni o'rnating

  • traverse(): List bo'ylab oldinga yoki orqaga yurish imkoniyatini qo'shing

  • search(): List'ning old yoki orqa tomonidan boshlab qidirish imkoniyatini qo'shing

  • remove(): Qo'shni node'larning next va prev reference'larini yangilang

Circular Linked Lists

Circular linked list yaratganingizda list'ingizning tail'i None'ga ishora qilish o'rniga qaytib head'ga ulanadi. Shu sababli circular linked list'ni halqa deb tasavvur qilishingiz mumkin. Circular linked list'lar singly yoki doubly linked bo'lishi mumkin va ularning implementatsiyalari asosiy tuzilishining katta qismi bir xil.

Circular linked list uchun Node sinfi singly yoki doubly linked list'dagi bilan bir xil tuzilishga ega, faqat oxirgi node'ingiz aylanib list'ingizning birinchi node'iga ishora qilishiga ishonch hosil qiling. Singly linked misolni quyidagicha yaratishingiz mumkin:

head = Node(100)
second = Node(200)
third = Node(300)

head.next = second
second.next = third
third.next = head    # Tail points to head

LinkedList sinfining constructor'i o'zgarishsiz qoladi, lekin circular linked list'ni traverse qilish method'i o'zgaradi, chunki bu turdagi list'da tabiiy None tugash nuqtasi yo'q. Xususan, circular linked list bo'ylab yurish uchun sizga aniq to'xtash sharti kerak bo'ladi:

def traverse(self):
    # Detect empty list
    if self.head is None:
        return

    current = self.head

    while True:
        print(current.data)
        current = current.next

        # Stop when reaching head once more
        if current == self.head:
            break

Shundan so'ng o'zingizning circular linked list'ingizni yaratib, uni traverse qilishingiz mumkin:

circular_linked_list = LinkedList()
circular_linked_list.head = head

circular_linked_list.traverse()

# Expected result:
# 100
# 200
# 300

E'tibor bering, boshqa method'lar ham kichik o'zgarishlarni talab qilishi mumkin. Masalan, append() None'ga ishora qiladigan node'ni emas, balki qaytib head'ga ishora qiladigan node'ni qidirishi kerak. So'ngra yangi node'ning reference'ini head'ga o'rnatib, yangi node'ni tail'ga aylantirishingiz kerak.

Pirovardida singly linked list'lar eng oddiy tuzilmani taqdim etadi, doubly va circular linked list'lar esa qo'shimcha reference'lar yoki murakkabroq mantiq evaziga qo'shimcha moslashuvchanlik beradi.

Kod misollari bilan klassik intervyu savollari

Linked list'lar haqidagi savollar coding intervyularida tez-tez uchraydi. Ko'pincha traversal masalalari yoki list reference'larini o'zgartiradigan masalalar ustida bosh qotirishingizga to'g'ri keladi. Avval qaysi turdagi linked list bilan ishlayotganingizni aniqlab oling, so'ng javobingiz edge case'larni to'g'ri qamrab olishiga ishonch hosil qilish uchun ular haqida o'ylab ko'ring. Boshqa savollar ham ko'p bo'lsa-da, bu yerda linked list bo'yicha eng keng tarqalgan intervyu mavzularidan ba'zilarini ko'rib chiqamiz.

Linked list'ni qanday teskari aylantirasiz?

Linked list bo'yicha barcha intervyu savollari ichida bu, ehtimol, eng mashhuri. Intervyu oluvchilar bu savolni berganda odatda prev reference'lari bo'lmagan singly linked list'ni nazarda tutishadi, lekin boshlashdan oldin buni albatta aniqlashtirib oling.

Singly linked list'ni teskari aylantirish uchun list'ingizni traverse qiling va uchta elementni kuzatib boring: oldingi, joriy va keyingi node'lar. current.next'ni keyingi node o'rniga oldingi node'ga ishora qiladigan qilib o'zgartirishingiz kerak bo'ladi, lekin zanjirni uzib, list'ning qolgan qismiga murojaatni yo'qotib qo'ymaslik uchun current.next'ni qayta yozishdan oldin list'da qaysi node keyin kelishini vaqtincha saqlab qo'yishni unutmang.

Mana singly linked list uchun LinkedList sinfingizga qo'shishingiz mumkin bo'lgan method:

def reverse(self):
    previous = None
    current = self.head

    while current is not None:		# Continue through tail node
        next_node = current.next		# Save next node 
        current.next = previous		# Switch reference to previous
        previous = current			# Get ready for next iteration
        current = next_node

    self.head = previous			# Update head to be old tail

Barcha reference'larni teskari aylantirganingizdan so'ng eski tail yangi head'ga aylanadi, shuning uchun self.head'ni previous'ga o'rnating. Aytgancha, big-O notation bo'yicha bu yondashuv O(n) vaqt va O(1) qo'shimcha xotira oladi, chunki u list'ni bir marta traverse qiladi va faqat bir nechta reference'ni saqlab turadi.

Linked list'dagi cycle'ni qanday aniqlaysiz?

Agar linked list'ni teskari aylantirish 1-o'rinda bo'lsa, cycle'ni aniqlash, ehtimol, linked list'lar haqidagi 2-eng keng tarqalgan intervyu savolidir. Bu savolning kaliti — Floyd's cycle detection algorithm, u tortoise and hare algorithm deb ham ataladi.

Singly linked list deb faraz qilsak, cycle node oxir-oqibat None'ga yetib borish o'rniga qaytib oldingi node'lardan biriga ishora qilganda yuzaga keladi. Bu oddiy traversal uchun muammo tug'diradi, chunki u cheksiz davom etishi mumkin.

Cycle'ni ikkita pointer yordamida aniqlashingiz mumkin:

  • slow — har bir iteratsiyada faqat bitta node oldinga siljiydi

  • fast — har bir iteratsiyada ikkita node oldinga siljiydi

Agar fast list'ingiz oxiriga yetib borsa, linked list'ingizda cycle yo'q, lekin slow va fast oxir-oqibat bir xil node'ga reference qilsa, sizda cycle bor.

Ushbu method'ni LinkedList sinfingizga qo'shish cycle'ni aniqlash vazifasini bajaradi:

def has_cycle(self):
    slow = self.head
    fast = self.head

    # Continue while fast can advance
    while fast is not None and fast.next is not None:
        slow = slow.next		# Advance one node
        fast = fast.next.next	# Advance two nodes

        # Cycle detected if slow and fast meet
        if slow == fast:
            return True

    return False  # No cycle if fast reaches the end of the list

Yuqoridagi kodda avval tashrif buyurilgan har bir node uchun elementlarni saqlashingiz shart emas, chunki siz faqat cycle mavjudligini aniqlamoqchisiz. Complexity nuqtai nazaridan bu yechim node'lar bo'ylab yurish uchun O(n) vaqt va O(1) oraliq xotira oladi.

Linked list'ning o'rtasini qanday topasiz?

Yana singly linked list deb faraz qilsak, bu masala dastlab oddiy tuyulishi mumkin. List'ingizni traverse qilib, node'lar sonini sanab, so'ngra o'rtadagi nuqtagacha yana bir marta traverse qilishingiz mumkin. Bu yechim ishlasa-da, u eng samarali implementatsiya emas.

Buning o'rniga slow va fast pointer'laridan foydalanib, o'rtani bitta traversal bilan topishingiz mumkin. Cycle detection algoritmidagi asosiy usulning o'zidan foydalaning:

  • slow har bir iteratsiyada bitta node oldinga siljiydi

  • fast har bir iteratsiyada ikkita node oldinga siljiydi

Lekin eng zo'r jihati mana bu: fast list oxiriga yetganda slow o'rtada bo'ladi.

Ushbu LinkedList method'i sizga o'rtadagi node'ni beradi; e'tibor bering, bo'sh list'lar kabi edge case uchun u shunchaki None qaytaradi:

def find_middle(self):
    slow = self.head
    fast = self.head

    while fast is not None and fast.next is not None:
        slow = slow.next
        fast = fast.next.next

    # slow gives you the midpoint when fast reaches the end
    return slow  

Node'lar soni toq bo'lsa, bitta aniq o'rta nuqta bo'ladi; biroq node'lar soni juft bo'lsa, ikkita mumkin bo'lgan "o'rta" bor. Bu implementatsiya ikkalasidan ikkinchisini qaytaradi, lekin intervyu oluvchidan qaysi birini afzal ko'rishini so'rab bilishingiz kerak. Bu usul ham O(n) time complexity'ga ega va O(1) yordamchi xotiradan foydalanadi.

Boshqa keng tarqalgan savollar

Linked list bo'yicha bilimni tekshiradigan intervyuda yuqoridagi uchta savoldan kamida bittasiga duch kelishingiz ehtimoli katta, lekin boshqa mashhur savollar qatoriga quyidagilar kiradi:

  • Ikkita sort qilingan linked list'ni qanday merge qilasiz?

  • Linked list oxiridan k-node'ni qanday topish mumkin?

  • Linked list'dan takrorlanuvchi qiymatlarni qanday olib tashlaysiz?

  • Linked list palindrom ekanligini yoki yo'qligini aniqlay olasizmi?

  • Linked list'dan muayyan pozitsiyadagi node'ni qanday o'chirasiz?

  • Linked list'larni sort qilishda merge sort quick sort'dan samaraliroqmi?

  • Linked list'ning turli amallari time complexity va space complexity'sini taqqoslang.

Real hayotdagi qo'llanilishi

Linked list'lar elementlar bo'ylab oldinga va orqaga traverse qilish uchun juda qulay, ayniqsa doubly linked list bo'lsa. Brauzeringizning sahifalar tarixini doubly linked list sifatida tashkil qilishingiz mumkin. Oldingi sahifalarga o'tish yangiroq sahifalarga oldinga o'tish kabi oson bo'lardi. Musiqa playlist'i uchun ham xuddi shunday. Tinglovchi oldingi qo'shiqni qayta tinglash uchun orqaga qaytishi yoki qo'shiq unga yoqmasa, list'da oldinga o'tib ketishi mumkin. Hatto playlist'ni uzluksiz takrorlash uchun circular linked list ham yaratishingiz mumkin.

Linked list'lar, shuningdek, tez-tez yangi node'lar qo'shish yoki elementlarni o'chirish kerak bo'lganda va tegishli node'larga allaqachon murojaatingiz bo'lganda foydali. Foydalanuvchining tahrirlarini linked list yordamida kuzatib borishni tasavvur qiling. U tarixga yangi amallar qo'shib borar ekan, oldingi tahrirlarni bekor qilish uchun bemalol orqaga yoki ularni qayta bajarish uchun oldinga o'tishi mumkin. Linked list'lar graph'lardagi adjacency list'larni ifodalashda yoki hash table'larni amalga oshirishda separate chaining deb ataladigan usul orqali collision'larni hal qilishda ham asqotadi. Linked list'lardan queue va deque'lar uchun foydalanishingiz mumkin, ammo Python'da bu use case uchun odatda standart kutubxonadagi collections.deque'dan foydalanasiz.

Linked list'ning built-in muqobillari

Python'da built-in linked list ma'lumotlar tuzilmasi yo'q, lekin shunga o'xshash asosiy xatti-harakatlarga erishish uchun bir nechta tayyor variantlar mavjud. Bu muqobillardan foydalansangiz, o'zingizning linked list'ingizni amalga oshirishingizga kamdan-kam ehtiyoj tug'iladi.

list

Python'ning umumiy list obyekti ma'lumotlar ketma-ketligini saqlashda sizga kerak bo'ladigan ishlarning ko'pini bajara oladi. list linked list emas, balki dinamik massivdir. Linked list'dan farqli o'laroq, elementlarni index bo'yicha O(1), ya'ni konstant vaqtda topishingiz mumkin. Oddiy list oxiriga element qo'shish yoki oxiridan element olib tashlashda ancha samarali: qo'shish amortized O(1) vaqt oladi, oxirgi elementni olib tashlash esa O(1) time complexity'ga ega. Biroq list'ning boshi yoki o'rta qismlariga element qo'shish yoki ulardan element olib tashlash O(n) vaqt oladi, chunki ketma-ketlikni buzmaslik uchun elementlarni list bo'ylab siljitishingizga to'g'ri keladi.

collections.deque

Standart kutubxonadagi collections moduli deque deb ataladigan yana bir ma'lumotlar tuzilmasini taklif qiladi. deque — double-ended queue bo'lib, boshidan yoki oxiridan elementlarni oson qo'shish yoki olib tashlash imkonini beradi. Har ikki uchidan qo'shish yoki o'chirish atigi O(1) time complexity'ga ega, shuning uchun deque queue va stack'larni amalga oshirish uchun ajoyib variant. Biroq u index bo'yicha elementni tez topish uchun mo'ljallanmagan, shuning uchun agar sizga aynan shu funksionallik kerak bo'lsa, oddiy list'da qolishni tanlashingiz mumkin.

Ma'lumotlar tuzilmalarining time complexity'si va unumdorligini taqqoslash

Standart amallar uchun linked list'larning time complexity'si list va deque bilan qanday taqqoslanishi quyida keltirilgan:

Amal Linked list list deque
Index bo'yicha murojaat O(n) O(1) O(n)*
Qiymat bo'yicha qidirish O(n) O(n) O(n)
Boshiga qo'shish/boshidan o'chirish O(1) O(n) O(1)
Oxiriga qo'shish O(1)** O(1) amortized O(1)
Ma'lum node'da qo'shish/o'chirish O(1) – –

*deque uchun index bo'yicha murojaat har ikki uchida O(1), o'rtaga tomon esa O(n)gacha sekinlashadi.
**Agar tail reference'ini saqlamasangiz, oxiriga qo'shish linked list'ni traverse qilish uchun O(n) talab qiladi.

Linked list'dan qachon foydalanmaslik kerak

Ba'zi vaziyatlarda linked list'dan foydalanish shunchaki ma'noga ega emas. Agar elementlarni index bo'yicha muntazam olish uchun random access kerak bo'lsa, standart Python list'idan foydalaning. Xuddi shunday, agar asosan oxiriga element qo'shib, to'plamingiz bo'ylab iteratsiya qiladigan bo'lsangiz ham list'dan foydalaning.

Queue va stack masalalari uchun, ehtimol, deque'ni tanlaysiz, chunki ular bunday holatlar uchun samarali va deque Python'da allaqachon amalga oshirilgan.

Linked list'lar xotira bilan bog'liq muammolarga ega, chunki ular node reference'lari uchun ma'lumot saqlashi kerak. Agar qaysi node'ni o'chirish yoki qaysi node yoniga qo'shish kerakligini oldindan bilmasangiz, ularning O(1) qayta ulash afzalligi O(n) traversal time complexity'si ortida yo'qolib ketadi.

Umuman olganda, Python linked list'ini shunchaki qila olganingiz uchungina amalga oshirmang. Ularni list bo'ylab tez-tez oldinga va orqaga yurish yoki ma'lum node'larda qo'shish/o'chirish kerak bo'ladigan maxsus holatlar uchun saqlab qo'ying. Python dasturchilari kundalik vazifalar uchun odatda list va deque'ni afzal ko'radi.

Xulosa

Python'da built-in linked list yo'q, lekin Node va LinkedList sinflari yordamida linked list'ni o'zingiz yaratishingiz mumkin. Ehtimol, LinkedList sinfida yangi node'lar qo'shish, list'ingizni traverse qilish, node'larni qidirish va eski node'larni o'chirish uchun method'lar yozishni xohlaysiz. Shuningdek, linked list'ni teskari aylantirish, cycle'ni aniqlash yoki o'rta nuqtani topish uchun method'lar ham qo'shishingiz mumkin, ayniqsa linked list bo'yicha eng keng tarqalgan intervyu savollarining ba'zilarini yechayotganda.

Singly linked list'lar sizga klassik, bir yo'nalishli node'lar zanjirini beradi, lekin keyingi va oldingi elementlarga reference'lari bo'lgan doubly linked list'larga ham duch kelishingiz mumkin. Tail node head'ga ishora qiladigan circular linked list ham bo'lishi mumkin. Faqat avvalo vaziyatingiz haqiqatan ham linked list'ni talab qilishiga ishonch hosil qiling. Python'da implementatsiyaga ketadigan vaqtingizni tejaydigan va use case'ingizga, ehtimol, yaxshiroq mos keladigan tayyor list va deque ma'lumotlar tuzilmalari mavjud.

Linked list bo'yicha mahoratingizni ko'rsatishga tayyormisiz? AI Tutor bilan bellashib ko'ring. Bilimlar zanjir reaksiyasini boshlash uchun ushbu prompt'dan foydalaning:

Yo'l xaritalariQo'llanmalarFAQYouTube

yo'lxaritaroadmap.shasosida

Hamjamiyat tomonidan yaratilgan yo'l xaritalari, eng yaxshi amaliyotlar, loyihalar, maqolalar, manbalar va yo'nalishlar — o'z yo'lingizni tanlash va kasbingizda o'sish uchun.

© yo'lxarita·Shartlar·Maxfiylik·

roadmap.sh

Barcha kontent roadmap.sh loyihasidan olingan (© Kamran Ahmed) va shaxsiy o'rganish uchun o'zbek tiliga tarjima qilinmoqda.

Qidiruv

Yo'l xaritalari, loyihalar va savollarni qidiring