Հաջորդական տվյալների հետ աշխատելիս սովորաբար օգտագործում ենք զանգվածներ կամ ցանկեր, որպեսզի պահենք տվյալներն ու հասանելիություն ունենանք դրանց։ Այնուամենայնիվ, որոշ պարագաներում ավելի արդյունավետ է օգտվել կապակցված ցուցակից։ Մասնավորապես, եթե ծրագիրը բազմիցս պետք է ցուցակի սկզբում նոր տարր ավելացնի կամ ջնջի, կապակցված ցուցակը կարող է լինել ավելի արագ։
Կապակցված ցուցակն ամենահիմնական տվյալների կառուցվածքներից մեկն է։ Այն ունի տարրերի ցանկ, կարող է այդ ցանկը թարմացնել նոր տարրեր ավելացնելով և հասանելիություն ունենալ փուլայինի պես։ Կապակցված ցուցակ օգտագործելիս, ամեն տարրը սովորաբար անվանում ենք Node (հանգույց), որն իր մեջ պարունակում է տվյալ և հղում դեպի հաջորդ Node-ը։ Միակողմանի կապակցված ցուցակում (singly linked list) յուրաքանչյուր տարր (Node) պարունակում է հղում դեպի հաջորդ հանգույցը, իսկ վերջին հանգույցի հղումը ուղղված է ոչնչի (null)։ Գրականության մեջ, կապակցված ցուցակի առաջին տարրը կոչվում է head, իսկ վերջինը՝ tail։ Մեկիկողմանի կապակցված ցուցակի մի օրինակ իրագործվում է ներքևում:
class Node:
def __init__(self, data):
self.data = data # Պահպանում է հանգույցի արժեքը
self.after = None # Պահպանում է հղում դեպի հաջորդ հանգույցը
class LinkedList:
def __init__(self):
self.head = None # Սկզբում ցուցակում տարրեր չկան
def append(self, data): # Ցուցակին նոր հանգույց է ավելացնում վերջում
new_node = Node(data) # Ստեղծում է հանգույց, որը պահում է data
if self.head is None: # Եթե ցուցակը դեռ դատարկ է
self.head = new_node # head-ը ուղղում ենք նոր հանգույցին
return
current = self.head # Սկսում ենք անցնել ցուցակով head-ից
while current.after: # Քանի դեռ չենք հասել վերջին հանգույցին
current = current.after # Շարունակում ենք դեպի հաջորդ հանգույց
current.after = new_node # Վերջին հանգույցի հղումը կապում ենք նոր հանգույցի հետ
def print(self): # Տպում է ցուցակի բոլոր տարրերը
current = self.head # Սկսում ենք head-ից
while current: # Քանի դեռ չենք հասել ցուցակի վերջ
print(current.data, end='\n' if current.after is None else ' --> ')
current = current.after # Շարունակում ենք դեպի հաջորդ հանգույց
names = LinkedList() # Ստեղծում է դատարկ LinkedList
names.append('Anna') # Ավելացնում է Anna-ն ցուցակում
names.append('Bob') # Ավելացնում է Bob-ը ցուցակում
names.append('Sona') # Ավելացնում է Sona-ն ցուցակում
names.print() # Anna --> Bob --> Sona
Վերոնշյալ իրագործման մեջ մենք ունենք երկու դասեր՝ Node և LinkedList։ Node-ն ունի երկու հատկություն՝ data և after։ data-ն պահում է հանգույցի արժեքը, իսկ after-ը պահում է հղումը դեպի հաջորդ հանգույցը։ LinkedList-ը ունի մեկ հատկություն՝ head, որը պահում է կապակցված ցուցակի առաջին հանգույցի հղումը։ Նոր հանգույց ավելացնելու համար օգտագործվում է append() մեթոդը, իսկ print() մեթոդը տպում է բոլոր տարրերը։
Երկկողմանի կապակցված ցուցակ (Doubly Linked List)
Բացի այդ, կարելի է օգտագործել նաև երկկողմանի կապակցված ցուցակ, որտեղ յուրաքանչյուր տարր ունի հղում և՛ դեպի հաջորդ, և՛ դեպի նախորդ հանգույցը। Սա հնարավորություն է տալիս հեշտությամբ անցնել ցուցակով երկու ուղղություններով։
Կապակցված ցուցակի վերլուծություն
Կապակցված ցուցակների հետ աշխատելիս կարևոր է հիշել.
Ցուցակային որևէ տարր գտնելը տևում է ժամանակ, քանի որ հարկավոր է սկսել head-ից ու հերթով անցնել մինչև գտնել անհրաժեշտ տարրը։
Եթե մենք գիտենք այն Node-ը, որի հետևում պետք է տեղադրել նոր հանգույց, ապա տեղադրումը տևում է ժամանակ, քանի որ բավարար է ուղղակի թարմացնել հարակից հանգույցների հղումները։
Նույն ձևով, եթե մենք գիտենք, թե որ Node-ը պետք է ջնջենք, ապա ջնջումը նույնպես տևում է , քանի որ բավարար է թարմացնել հարակից հանգույցների հղումները։
Կապակցված ցուցակներում ամեն հանգույց պահում է նաև լրացուցիչ հղում, ինչը ընդհանուր առմամբ ավելի շատ հիշողություն է պահանջում, քան զանգվածները։
Առաջադրանք
Տրված է n հարցում։ Անրաժեշտ է դրանցից յուրաքանչյուրն իրականացնել։ Հարցումների 2 տեսակ կա.
Տեղադրել թիվը ցուցակի սկզբում։
Տպել ամբողջ ցուցակը։
Մուտք
Մուտքի առաջին տողում տրված է մեկ ամբողջ թիվ n (1 ≤ n ≤ 1000)՝ հարցումների քանակը։
Հաջորդ n տողերը պարունակում են հարցումները՝ print, եթե պետք է տպել ամբողջ ցուցակը, և insert x, եթե անհրաժեշտ է թիվ x ավելացնել ցուցակի սկզբում ( ≤ x ≤ )։
Ելք
Ծրագիրը պետք է պատշաճ ձևով իրականացնի բոլոր հարցումները և տպի ցուցակը՝ տարրերը բաժանելով բացատներով։