ساختمان داده در پایتون روشی برای سازماندهی دادهها «Data» هستند تا بتوان بر اساس نیاز، آنها را به شکل کارآمدتری مورد دسترسی قرار داد. این ساختمانها پایه و اساس هر زبان برنامهنویسی «Programming» را تشکیل میدهند و برنامهها بر اساس آنها ساخته میشوند.
مقدمه
یادگیری مفاهیم پایهای ساختمان داده در پایتون در مقایسه با سایر زبانهای برنامهنویسی، به شکل سادهتری امکانپذیر است. در این مقاله، به بررسی ساختمان داده در پایتون و ارتباط آنها با برخی از انواع دادهای خاص در پایتون میپردازیم و ساختمان داده داخلی مانند لیستها «Lists»، تاپلها «Tuples»، دیکشنریها «Dictionaries» و غیره را بررسی خواهیم کرد، همچنین به برخی از ساختمان داده پیشرفته مانند درختها «Tree» و گرافها «Graphs» نیز خواهیم پرداخت.
ساختمان داده لیست در پایتون
برای شروع بررسی ساختمان داده در پایتون از لیست List شروع میکنیم. ساختمان داده لیستها در پایتون شبیه به آرایهها «Arrays» در سایر زبانها هستند و مجموعهای مرتب از دادهها را نگه میدارند. این ساختار در پایتون بسیار انعطافپذیر است، زیرا آیتمهای یک لیست الزامی به داشتن نوع دادهای یکسان ندارند. پیادهسازی لیست در پایتون مشابه با Vectors در ++C یا ArrayList در Java است. انجام عملیات درج یا حذف یک عنصر از ابتدای لیست هزینهبر است، زیرا همه عناصر باید جابهجا شوند. همچنین، در مواردی که حافظهی از پیش تخصیصیافته پر شود، درج و حذف در انتهای لیست نیز میتواند پردازش سنگینی داشته باشد. میتوان یک لیست را در پایتون به شکل زیر ایجاد کرد.
مثال: ایجاد یک لیست در پایتون
List = [1, 2, 3, "PS", 2.3] print(List)
خروجی:
[1, 2, 3, 'PS', 2.3]
عناصر یک لیست را میتوان با استفاده از اندیس اختصاص دادهشده به آنها دسترسی داشت. در پایتون، اندیسگذاری لیست از ۰ شروع میشود و اندیس آخرین عنصر (در صورتی که لیست N عنصر داشته باشد) N-1 خواهد بود.
مثال: عملیات روی لیست در پایتون
# Creating a List with
# the use of multiple values
List = ["Program", "Store"]
print("\nList containing multiple values: ")
print(List)
# Creating a Multi-Dimensional List
# (By Nesting a list inside a List)
List2 = [['Program'], ['Store']]
print("\nMulti-Dimensional List: ")
print(List2)
# accessing a element from the
# list using index number
print("Accessing element from the list")
print(List[0])
print(List[2])
# accessing a element using
# negative indexing
print("Accessing element using negative indexing")
# print the last element of list
print(List[-1])
# print the third last element of list
print(List[-3])
خروجی:
List containing multiple values: ['Program', 'Store'] Multi-Dimensional List: [['Program'], ['Store']] Accessing element from the list Program Program Accessing element using negative indexing Program Program
ساختمان داده دیکشنری در پایتون
ساختمان داده دیکشنری در پایتون مشابه hash table در سایر زبانها است و دارای پیچیدگی زمانی O(1) میباشد. این ساختمان داده، مجموعهای نامرتب از دادهها است که برای ذخیرهسازی مقادیر بهصورت کلید مقدار استفاده میشود. برخلاف سایر انواع داده که تنها یک مقدار را بهعنوان عنصر نگه میدارند، دیکشنری از این روش برای بهینهتر شدن استفاده میکند.
ایندکسگذاری در دیکشنریهای پایتون با استفاده از کلیدها انجام میشود. کلیدها باید از نوع hashable باشند، یعنی اشیایی که تغییرپذیر نیستند، مانند رشتهها، اعداد، تاپلها و غیره. میتوان یک دیکشنری را با استفاده از آکولاد ({}) یا Dictionary Comprehension ایجاد کرد.
مثال: عملیات روی دیکشنری در پایتون
# Creating a Dictionary
Dict = {'Name': 'Program', 1: [1, 2, 3, 4]}
print("Creating Dictionary: ")
print(Dict)
# accessing a element using key
print("Accessing a element using key:")
print(Dict['Name'])
# accessing a element using get()
# method
print("Accessing a element using get:")
print(Dict.get(1))
# creation using Dictionary comprehension
myDict = {x: x**2 for x in [1,2,3,4,5]}
print(myDict)
خروجی:
Creating Dictionary:
{'Name': 'Program', 1: [1, 2, 3, 4]}
Accessing a element using key:
Geeks
Accessing a element using get:
[1, 2, 3, 4]
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
ساختمان داده تاپل در پایتون
ساختمان داده تاپل در پایتون مجموعهای از اشیا است که مشابه لیست عمل میکند، اما تغییرناپذیر است، یعنی پس از ایجاد، امکان افزودن یا حذف عناصر آن وجود ندارد. مانند لیست، تاپل نیز میتواند شامل عناصر با انواع مختلف باشد. در پایتون، تاپل با قرار دادن یک دنباله از مقادیر، که با ویرگول (،) از هم جدا شدهاند، ساخته میشود. این کار میتواند با یا بدون پرانتز انجام شود.
نکته: تاپلهای تکعضوی «Single-member tuple» هم امکانپذیر هستند، اما تعریف آنها کمی متفاوت است. داشتن یک مقدار درون پرانتز کافی نیست، بلکه باید یک ویرگول در انتها قرار داده شود تا پایتون آن را بهعنوان یک تاپل تشخیص دهد.
مثال: عملیات روی تاپل در پایتون
# Creating a Tuple with
# the use of Strings
Tuple = ('Program', 'For')
print("\nTuple with the use of String: ")
print(Tuple)
# Creating a Tuple with
# the use of list
list1 = [1, 2, 4, 5, 6]
print("\nTuple using List: ")
Tuple = tuple(list1)
# Accessing element using indexing
print("First element of tuple")
print(Tuple[0])
# Accessing element from last
# negative indexing
print("\nLast element of tuple")
print(Tuple[-1])
print("\nThird last element of tuple")
print(Tuple[-3])
خروجی:
Tuple with the use of String:
('Program', 'Store')
Tuple using List:
First element of tuple
1
Last element of tuple
6
Third last element of tuple
4
ساختمان داده مجموعه در پایتون
ساختمان داده مجموعه در پایتون یک ساختمان دادهی بدون ترتیب است که قابلیت تغییر دارد و از ورود عناصر تکراری جلوگیری میکند. مجموعهها بیشتر برای بررسی عضویت عناصر و حذف موارد تکراری استفاده میشوند. در این ساختمان داده از هشینگ استفاده میشود که یک روش کارآمد برای درج، حذف و پیمایش عناصر با میانگین زمان اجرای O(1) است.
اگر چند مقدار در یک موقعیت شاخص (ایندکس) یکسان قرار بگیرند، این مقادیر در همان شاخص به صورت لیست پیوندی «Linked List» ذخیره میشوند. در CPython، مجموعهها با استفاده از دیکشنری پیادهسازی میشوند که دارای متغیرهای کمکی هستند. در این روش، کلیدها اعضای مجموعه را تشکیل میدهند و با بهینهسازیهایی، پیچیدگی زمانی بهبود پیدا میکند.
پیادهسازی مجموعه:

# Creating a Set with
# a mixed type of values
# (Having numbers and strings)
Set = set([1, 2, 'Program', 4, 'Store', 6, 'Tabriz'])
print("\nSet with the use of Mixed Values")
print(Set)
# Accessing element using
# for loop
print("\nElements of set: ")
for i in Set:
print(i, end =" ")
print()
# Checking the element
# using in keyword
print("Program" in Set)
خروجی:
Set with the use of Mixed Values
{1, 2, 'Program', 4, 6, 'For'}
Elements of set:
1 2 Program 4 6 For
True
مجموعه های ثابت در پایتون
مجموعههای ثابت در پایتون غیرقابل تغییر «Immutable» هستند و فقط از روشها و عملگرهایی پشتیبانی میکنند که بدون تغییر مجموعه، نتیجهای تولید میکنند. در حالی که عناصر یک مجموعه معمولی در هر زمان قابل تغییر هستند، عناصر یک مجموعه ثابت پس از ایجاد شدن بدون تغییر باقی میمانند.
اگر هیچ پارامتری به آن داده نشود، یک مجموعه ثابت خالی برمیگرداند.
# Same as {"a", "b","c"}
normal_set = set(["a", "b","c"])
print("Normal Set")
print(normal_set)
# A frozen set
frozen_set = frozenset(["e", "f", "g"])
print("\nFrozen Set")
print(frozen_set)
# Uncommenting below line would cause error as
# we are trying to add element to a frozen set
# frozen_set.add("h")
خروجی:
Normal Set
{'a', 'c', 'b'}
Frozen Set
frozenset({'g', 'e', 'f'})
ساختمان داده رشته در پایتون
ساختمان داده رشته در پایتون آرایهای از بایتها هستند که کاراکترهای یونیکد را نمایش میدهند. به زبان ساده، یک رشته آرایهای غیرقابل تغییر «Immutable» از کاراکترها است. پایتون نوع دادهای مخصوص کاراکتر ندارد، بنابراین یک کاراکتر تنها یک رشته با طول ۱ محسوب میشود.
نکته: از آنجا که رشتهها تغییرناپذیر هستند، هر گونه تغییر در یک رشته باعث ایجاد یک نسخهی جدید از آن میشود.
String = "Welcome to ProgramStore"
print("Creating String: ")
print(String)
# Printing First character
print("\nFirst character of String is: ")
print(String[0])
# Printing Last character
print("\nLast character of String is: ")
print(String[-1])
خروجی:
Creating String: Welcome to ProgramStore First character of String is: W Last character of String is: s
ساختمان داده Bytearray در پایتون
ساختمان داده Bytearray در پایتون یک دنبالهی قابل تغییر «Mutable» از اعداد صحیح در بازهی ۰ تا ۲۵۵ است.
مثال: عملیات روی Bytearray در پایتون
# Creating bytearray
a = bytearray((12, 8, 25, 2))
print("Creating Bytearray:")
print(a)
# accessing elements
print("\nAccessing Elements:", a[1])
# modifying elements
a[1] = 3
print("\nAfter Modifying:")
print(a)
# Appending elements
a.append(30)
print("\nAfter Adding Elements:")
print(a)
خروجی:
Creating Bytearray: bytearray(b'\x0c\x08\x19\x02') Accessing Elements: 8 After Modifying: bytearray(b'\x0c\x03\x19\x02') After Adding Elements: bytearray(b'\x0c\x03\x19\x02\x1e')
ماژول Collections در پایتون
ماژول collections در پایتون برای بهبود عملکرد انواع دادهای داخلی معرفی شده است. این ماژول مجموعهای از ظرفیتهای دادهای را ارائه میدهد که در بسیاری از موارد کاربردی هستند و ویژگیهای بیشتری نسبت به توابع تعریفشده قبلی دارند.
کانترها در پایتون
کانتر «Counters» یک زیرکلاس از دیکشنری است که برای نگهداری تعداد عناصر در یک دنباله «iterable» استفاده میشود. این کانترها به صورت یک دیکشنری بدون ترتیب عمل میکنند که در آن کلیدها نمایانگر عناصر دنباله و مقادیر نشاندهنده تعداد آن عنصر در دنباله هستند. این مشابه مفهوم کیسه «bag» یا مجموعه چندتایی «multiset» در زبانهای دیگر است.
مثال: عملیات روی Counter در پایتون
from collections import Counter
# With sequence of items
print(Counter(['B','B','A','B','C','A','B','B','A','C']))
# with dictionary
count = Counter({'A':3, 'B':5, 'C':2})
print(count)
count.update(['A', 1])
print(count)
خروجی:
Counter({'B': 5, 'A': 3, 'C': 2})
Counter({'B': 5, 'A': 3, 'C': 2})
Counter({'B': 5, 'A': 4, 'C': 2, 1: 1})
OrderedDict در پایتون
یک OrderedDict نیز زیرکلاس دیکشنری است، اما برخلاف دیکشنری معمولی، ترتیب وارد شدن کلیدها را به خاطر میسپارد. به این معنا که زمانی که کلیدها وارد میشوند، ترتیب آنها در OrderedDict حفظ میشود.
مثال: عملیات روی OrderedDict در پایتون
from collections import OrderedDict
print("Before deleting:\n")
od = OrderedDict()
od['a'] = 1
od['b'] = 2
od['c'] = 3
od['d'] = 4
for key, value in od.items():
print(key, value)
print("\nAfter deleting:\n")
od.pop('c')
for key, value in od.items():
print(key, value)
print("\nAfter re-inserting:\n")
od['c'] = 3
for key, value in od.items():
print(key, value)
خروجی:
Before deleting: a 1 b 2 c 3 d 4 After deleting: a 1 b 2 d 4 After re-inserting: a 1 b 2 d 4 c 3
DefaultDict در پایتون
DefaultDict برای فراهم کردن مقادیر پیشفرض برای کلیدهایی که وجود ندارند استفاده میشود و هیچگاه KeyError ایجاد نمیکند. اشیای DefaultDict میتوانند با استفاده از متد ()DefaultDict و با ارسال نوع دادهای به عنوان آرگومان مقداردهی اولیه شوند.
نکته: default_factory یک تابع است که مقدار پیشفرض را برای دیکشنری ایجاد شده فراهم میکند. اگر این پارامتر وجود نداشته باشد، KeyError رخ خواهد داد.
مثال: عملیات روی DefaultDict در پایتون
from collections import defaultdict
# Defining the dict
d = defaultdict(int)
L = [1, 2, 3, 4, 2, 4, 1, 2]
# Iterate through the list
# for keeping the count
for i in L:
# The default value is 0
# so there is no need to
# enter the key first
d[i] += 1
print(d)
خروجی:
defaultdict(<class 'int'>, {1: 2, 2: 3, 3: 1, 4: 2})
ChainMap در پایتون
ChainMap مجموعهای از دیکشنریها را در یک واحد ترکیب میکند و یک لیست از دیکشنریها برمیگرداند. زمانی که نیاز به پیدا کردن یک کلید باشد، تمام دیکشنریها به صورت یک به یک جستجو میشوند تا زمانی که کلید پیدا شود. این روش برای مدیریت چندین دیکشنری به صورت همزمان و با ترتیب جستجو مفید است.
مثال: عملیات روی ChainMap در پایتون
from collections import ChainMap
d1 = {'a': 1, 'b': 2}
d2 = {'c': 3, 'd': 4}
d3 = {'e': 5, 'f': 6}
# Defining the chainmap
c = ChainMap(d1, d2, d3)
print(c)
print(c['a'])
print(c['g'])
خروجی:
ChainMap({'a': 1, 'b': 2}, {'c': 3, 'd': 4}, {'e': 5, 'f': 6})
1
KeyError: 'g'
NamedTuple در پایتون
NamedTuple یک شی تاپل «Tuple» است که برای هر موقعیت یک نام اختصاص میدهد، در حالی که تپلهای معمولی فاقد این ویژگی هستند. به عنوان مثال، اگر یک تپل به نام student داشته باشیم که اولین عنصر آن نمایانگر نام، دومین عنصر نمایانگر نام خانوادگی و سومین عنصر نمایانگر تاریخ تولد باشد، به جای یادآوری موقعیت ایندکسها، میتوانیم از نامهایی مثل fname برای دسترسی به عنصرها استفاده کنیم. این ویژگی به دسترسی آسانتر به عناصر تپل کمک میکند.
مثال: عملیات روی NamedTuple در پایتون
from collections import namedtuple
# Declaring namedtuple()
Student = namedtuple('Student',['name','age','DOB'])
# Adding values
S = Student('Nandini','19','2541997')
# Access using index
print ("The Student age using index is : ",end ="")
print (S[1])
# Access using name
print ("The Student name using keyname is : ",end ="")
print (S.name)
خروجی:
The Student age using index is : 19 The Student name using keyname is : Nandini
Deque در پایتون
Deque یا صف دوطرفه یک نوع لیست بهینهشده است که برای انجام عملیات اضافه کردن «append» و حذف «pop» از هر دو طرف لیست به طور سریعتر طراحی شده است. این نوع لیست برای عملیات اضافه کردن و حذف از ابتدا یا انتهای لیست دارای پیچیدگی زمانی O(1) است، در حالی که لیستهای معمولی پیچیدگی زمانی O(n) برای این عملیاتها دارند.
Deque در پایتون با استفاده از لیستهای پیوندی دوطرفه پیادهسازی شده است. بنابراین، عملکرد دسترسی تصادفی به عناصر در این ساختمان داده O(n) است.
مثال: عملیات روی Deque در پایتون
# importing "collections" for deque operations
import collections
# initializing deque
de = collections.deque([1,2,3])
# using append() to insert element at right end
# inserts 4 at the end of deque
de.append(4)
# printing modified deque
print("The deque after appending at right is : ")
print(de)
# using appendleft() to insert element at left end
# inserts 6 at the beginning of deque
de.appendleft(6)
# printing modified deque
print("The deque after appending at left is : ")
print(de)
# using pop() to delete element from right end
# deletes 4 from the right end of deque
de.pop()
# printing modified deque
print("The deque after deleting from right is : ")
print(de)
# using popleft() to delete element from left end
# deletes 6 from the left end of deque
de.popleft()
# printing modified deque
print("The deque after deleting from left is : ")
print(de)
خروجی:
The deque after appending at right is : deque([1, 2, 3, 4]) The deque after appending at left is : deque([6, 1, 2, 3, 4]) The deque after deleting from right is : deque([6, 1, 2, 3]) The deque after deleting from left is : deque([1, 2, 3])
UserDict در پایتون
UserDict یک نوع کانتینر مشابه دیکشنری است که به عنوان یک پوشش «wrapper» برای اشیا دیکشنری عمل میکند. این کانتینر زمانی استفاده میشود که بخواهید دیکشنری خاص خود را با برخی ویژگیها یا عملکردهای جدید یا اصلاحشده ایجاد کنید.
مثال: استفاده از UserDict در پایتون
from collections import UserDict
# Creating a Dictionary where
# deletion is not allowed
class MyDict(UserDict):
# Function to stop deletion
# from dictionary
def __del__(self):
raise RuntimeError("Deletion not allowed")
# Function to stop pop from
# dictionary
def pop(self, s = None):
raise RuntimeError("Deletion not allowed")
# Function to stop popitem
# from Dictionary
def popitem(self, s = None):
raise RuntimeError("Deletion not allowed")
# Driver's code
d = MyDict({'a':1,
'b': 2,
'c': 3})
print("Original Dictionary")
print(d)
d.pop(1)
خروجی:
Original Dictionary
{'a': 1, 'b': 2, 'c': 3}
RuntimeError: Deletion not allowed
UserList در پایتون
UserList یک کانتینر مشابه لیست است که به عنوان پوششی برای اشیا لیست عمل میکند. این کانتینر زمانی مفید است که کسی بخواهد لیست خاص خود را با برخی ویژگیها یا عملکردهای جدید یا اصلاحشده ایجاد کند.
مثالها: استفاده از UserList در پایتون
# Python program to demonstrate
# userlist
from collections import UserList
# Creating a List where
# deletion is not allowed
class MyList(UserList):
# Function to stop deletion
# from List
def remove(self, s = None):
raise RuntimeError("Deletion not allowed")
# Function to stop pop from
# List
def pop(self, s = None):
raise RuntimeError("Deletion not allowed")
# Driver's code
L = MyList([1, 2, 3, 4])
print("Original List")
print(L)
# Inserting to List"
L.append(5)
print("After Insertion")
print(L)
# Deleting From List
L.remove()
خروجی:
Original List [1, 2, 3, 4] After Insertion [1, 2, 3, 4, 5]
RuntimeError: Deletion not allowed
UserString در پایتون
UserString یک کانتینر مشابه رشته است که مانند UserDict و UserList به عنوان پوششی برای اشیا رشتهای عمل میکند. این کانتینر زمانی استفاده میشود که کسی بخواهد رشته خاص خود را با برخی ویژگیها یا عملکردهای جدید یا اصلاحشده ایجاد کند.
مثال: استفاده از UserString در پایتون
from collections import UserString
# Creating a Mutable String
class Mystring(UserString):
# Function to append to
# string
def append(self, s):
self.data += s
# Function to remove from
# string
def remove(self, s):
self.data = self.data.replace(s, "")
# Driver's code
s1 = Mystring("Program")
print("Original String:", s1.data)
# Appending to string
s1.append("s")
print("String After Appending:", s1.data)
# Removing from string
s1.remove("e")
print("String after Removing:", s1.data)
خروجی:
Original String: Program String After Appending: Programm String after Removing: Pgram
ساختمان داده لیست پیوندی در پایتون
ساختمان داده لیست پیوندی در پایتون، خطی است که در آن عناصر در مکانهای حافظه پیوسته ذخیره نمیشوند. به عبارت دیگر، هر عنصر در یک لیست پیوندی به وسیلهی اشارهگرها «pointers» به عنصر بعدی متصل میشود. این ویژگی باعث میشود که در لیست پیوندی بتوانیم به راحتی عناصری را اضافه یا حذف کنیم، بدون اینکه نیاز به جابجایی دیگر عناصر داشته باشیم.
در یک لیست پیوندی، هر عنصر معمولاً شامل داده و آدرس (یا اشارهگر) به عنصر بعدی است. این ساختمان داده به ما اجازه میدهد که به طور پویا به حافظه دسترسی داشته باشیم و اندازه لیست را به راحتی تغییر دهیم.
لیست پیوندی توسط یک اشارهگر به اولین گره «node» لیست پیوندی نمایش داده میشود. اولین گره به نام head شناخته میشود. اگر لیست پیوندی خالی باشد، مقدار head برابر NULL است. هر گره در لیست حداقل دو بخش دارد:
- داده «Data»
- اشارهگر یا مرجع به گره بعدی «Pointer or Reference to the next node»
در صورتی که بخواهید یک لیست پیوندی را در پایتون تعریف کنید، میتوانید یک کلاس برای گرهها و کلاس دیگری برای خود لیست ایجاد کنید که در آن روشهایی برای اضافه کردن، حذف کردن یا پیمایش لیست فراهم باشد.
# Node class
class Node:
# Function to initialize the node object
def __init__(self, data):
self.data = data # Assign data
self.next = None # Initialize
# next as null
# Linked List class
class LinkedList:
# Function to initialize the Linked
# List object
def __init__(self):
self.head = None
برای ایجاد یک لیست پیوندی ساده با ۳ گره، میتوانیم از یک کلاس برای گرهها و یک کلاس دیگر برای لیست پیوندی استفاده کنیم.
# A simple Python program to introduce a linked list
# Node class
class Node:
# Function to initialise the node object
def __init__(self, data):
self.data = data # Assign data
self.next = None # Initialize next as null
# Linked List class contains a Node object
class LinkedList:
# Function to initialize head
def __init__(self):
self.head = None
# Code execution starts here
if __name__=='__main__':
# Start with the empty list
list = LinkedList()
list.head = Node(1)
second = Node(2)
third = Node(3)
'''
Three nodes have been created.
We have references to these three blocks as head,
second and third
list.head second third
| | |
| | |
+----+------+ +----+------+ +----+------+
| 1 | None | | 2 | None | | 3 | None |
+----+------+ +----+------+ +----+------+
'''
list.head.next = second; # Link first node with second
'''
Now next of first Node refers to second. So they
both are linked.
list.head second third
| | |
| | |
+----+------+ +----+------+ +----+------+
| 1 | o-------->| 2 | null | | 3 | null |
+----+------+ +----+------+ +----+------+
'''
second.next = third; # Link second node with the third node
'''
Now next of second Node refers to third. So all three
nodes are linked.
list.head second third
| | |
| | |
+----+------+ +----+------+ +----+------+
| 1 | o-------->| 2 | o-------->| 3 | null |
+----+------+ +----+------+ +----+------+
'''
برای پیمایش لیست پیوندی، تابع ()printList به این شکل عمل میکند که داده هر گره را چاپ کرده و به گره بعدی میرود تا زمانی که به انتهای لیست برسد.
# A simple Python program for traversal of a linked list
# Node class
class Node:
# Function to initialise the node object
def __init__(self, data):
self.data = data # Assign data
self.next = None # Initialize next as null
# Linked List class contains a Node object
class LinkedList:
# Function to initialize head
def __init__(self):
self.head = None
# This function prints contents of linked list
# starting from head
def printList(self):
temp = self.head
while (temp):
print (temp.data)
temp = temp.next
# Code execution starts here
if __name__=='__main__':
# Start with the empty list
list = LinkedList()
list.head = Node(1)
second = Node(2)
third = Node(3)
list.head.next = second; # Link first node with second
second.next = third; # Link second node with the third node
list.printList()
خروجی:
1 2 3
ساختمان داده Stack در پایتون
ساختمان داده استک یک ساختمان داده خطی است که اقلام را به روش آخرین وارد، اولین خارج «LIFO» یا اولین ورود، آخرین خارج «FILO» ذخیره میکند. در استک، یک عنصر جدید در یک انتها اضافه میشود و تنها از همان انتها حذف میشود. عملیات افزودن و حذف معمولاً به نامهای push و pop شناخته میشوند.
عملکردهای مرتبط با استک عبارتند از:
- ()empty: بررسی میکند که آیا استک خالی است یا خیر – پیچیدگی زمانی: O(1)
- ()size: اندازه استک را برمیگرداند – پیچیدگی زمانی: O(1)
- ()top: ارجاعی به بالاترین عنصر استک میدهد – پیچیدگی زمانی: O(1)
- push(a): عنصر a را در بالای استک وارد میکند – پیچیدگی زمانی: O(1)
- ()pop: بالاترین عنصر استک را حذف میکند – پیچیدگی زمانی: O(1)
پیادهسازی استک در پایتون
استک در پایتون میتواند به روشهای مختلف پیادهسازی شود:
- استفاده از لیست
- استفاده از collections.deque
- استفاده از queue.LifoQueue
پیادهسازی با استفاده از لیست
ساختمان داده داخلی پایتون، لیست، میتواند به عنوان یک استک عمل کند. به جای استفاده از ()push، از ()append برای افزودن عناصر به بالای استک استفاده میشود، در حالی که ()pop برای حذف عنصر به ترتیب LIFO به کار میرود.
stack = []
# append() function to push
# element in the stack
stack.append('P')
stack.append('S')
stack.append('T')
print('Initial stack')
print(stack)
# pop() function to pop
# element from stack in
# LIFO order
print('\nElements popped from stack:')
print(stack.pop())
print(stack.pop())
print(stack.pop())
print('\nStack after elements are popped:')
print(stack)
# uncommenting print(stack.pop())
# will cause an IndexError
# as the stack is now empty
خروجی:
Initial stack ['P', 'S', 'T'] Elements popped from stack: P S T Stack after elements are popped: []
پیادهسازی با استفاده از collections.deque در پایتون
استک در پایتون میتواند با استفاده از کلاس deque از ماژول collections پیادهسازی شود. Deque به دلیل ارائه پیچیدگی زمانی O(1) برای عملیات افزودن و حذف از هر دو انتهای کانتینر، نسبت به لیست ترجیح داده میشود. در حالی که لیست دارای پیچیدگی زمانی O(n) برای این عملیاتها است.
from collections import deque
stack = deque()
# append() function to push
# element in the stack
stack.append('P')
stack.append('S')
stack.append('T')
print('Initial stack:')
print(stack)
# pop() function to pop
# element from stack in
# LIFO order
print('\nElements popped from stack:')
print(stack.pop())
print(stack.pop())
print(stack.pop())
print('\nStack after elements are popped:')
print(stack)
# uncommenting print(stack.pop())
# will cause an IndexError
# as the stack is now empty
خروجی:
Initial stack: deque(['P', 'S', 'T']) Elements popped from stack: P S T Stack after elements are popped: deque([])
پیادهسازی با استفاده از ماژول queue
ماژول queue همچنین یک LIFO Queue دارد که در واقع همان استک است. دادهها با استفاده از تابع ()put به صف اضافه میشوند و تابع ()get دادهها را از صف خارج میکند.
from queue import LifoQueue
# Initializing a stack
stack = LifoQueue(maxsize = 3)
# qsize() show the number of elements
# in the stack
print(stack.qsize())
# put() function to push
# element in the stack
stack.put('P')
stack.put('S')
stack.put('T')
print("Full: ", stack.full())
print("Size: ", stack.qsize())
# get() function to pop
# element from stack in
# LIFO order
print('\nElements popped from the stack')
print(stack.get())
print(stack.get())
print(stack.get())
print("\nEmpty: ", stack.empty())
خروجی:
0 Full: True Size: 3 Elements popped from the stack P S T Empty: True
ساختمان داده Queue در پایتون
ساختمان داده صف «Queue» یک ساختمان داده خطی است که اقلام را به روش اولین وارد، اولین خارج ذخیره میکند. در صف، اولین عنصری که وارد میشود، اولین عنصری است که حذف میشود. یک مثال خوب از صف، صف مصرفکنندگان یک منبع است که مصرفکنندهای که اول آمده، اول خدمت میگیرد.
عملیات مرتبط با صف عبارتند از:
- Enqueue: افزودن یک عنصر به صف. اگر صف پر باشد، به آن Overflow condition گفته میشود – پیچیدگی زمانی: O(1)
- Dequeue: حذف یک عنصر از صف. اقلام به همان ترتیبی که وارد شدهاند حذف میشوند. اگر صف خالی باشد، به آن Underflow condition گفته میشود – پیچیدگی زمانی: O(1)
- Front: گرفتن اولین عنصر از صف – پیچیدگی زمانی: O(1)
- Rear: گرفتن آخرین عنصر از صف – پیچیدگی زمانی: O(1)
پیادهسازی صف در پایتون
صف در پایتون میتواند به روشهای مختلف پیادهسازی شود:
- استفاده از لیست
- استفاده از collections.deque
- استفاده از queue.Queue
پیادهسازی با استفاده از لیست
به جای استفاده از ()enqueue و ()dequeue ، از توابع ()append و ()pop برای افزودن و حذف عناصر به صف استفاده میشود.
# Initializing a queue
queue = []
# Adding elements to the queue
queue.append('P')
queue.append('S')
queue.append('T')
print("Initial queue")
print(queue)
# Removing elements from the queue
print("\nElements dequeued from queue")
print(queue.pop(0))
print(queue.pop(0))
print(queue.pop(0))
print("\nQueue after removing elements")
print(queue)
# Uncommenting print(queue.pop(0))
# will raise and IndexError
# as the queue is now empty
خروجی:
Initial queue ['g', 'f', 'g'] Elements dequeued from queue P S T Queue after removing elements []
پیادهسازی با استفاده از collections.deque در پایتون
در مواردی که نیاز به عملیات سریعتر append و pop از هر دو انتهای کانتینر داریم، استفاده از deque نسبت به لیست ترجیح داده میشود. زیرا deque پیچیدگی زمانی O(1) برای عملیات اضافه کردن و حذف دارد، در حالی که لیست برای این عملیاتها پیچیدگی زمانی O(n) دارد.
from collections import deque
# Initializing a queue
q = deque()
# Adding elements to a queue
q.append('P')
q.append('S')
q.append('T')
print("Initial queue")
print(q)
# Removing elements from a queue
print("\nElements dequeued from the queue")
print(q.popleft())
print(q.popleft())
print(q.popleft())
print("\nQueue after removing elements")
print(q)
# Uncommenting q.popleft()
# will raise an IndexError
# as queue is now empty
خروجی:
Initial queue deque(['P', 'S', 'T']) Elements dequeued from the queue P R T Queue after removing elements deque([])
پیادهسازی با استفاده از queue.Queue در پایتون
queue.Queue(maxsize) یک متغیر صف با حداکثر اندازه maxsize را مقداردهی اولیه میکند. اگر مقدار maxsize برابر صفر باشد، به معنای صف نامحدود است. این صف از قانون FIFO پیروی میکند، به این معنی که اولین عنصر وارد شده، اولین عنصر خارج شده خواهد بود.
from queue import Queue
# Initializing a queue
q = Queue(maxsize = 3)
# qsize() give the maxsize
# of the Queue
print(q.qsize())
# Adding of element to queue
q.put('P')
q.put('S')
q.put('T')
# Return Boolean for Full
# Queue
print("\nFull: ", q.full())
# Removing element from queue
print("\nElements dequeued from the queue")
print(q.get())
print(q.get())
print(q.get())
# Return Boolean for Empty
# Queue
print("\nEmpty: ", q.empty())
q.put(1)
print("\nEmpty: ", q.empty())
print("Full: ", q.full())
# This would result into Infinite
# Loop as the Queue is empty.
# print(q.get())
خروجی:
0 Full: True Elements dequeued from the queue P S T Empty: True Empty: False Full: False
ساختمان داده Priority Queue در پایتون
ساختمان داده Priority Queue در پایتون یک ساختمان داده انتزاعی است که در آن هر داده یا مقداری در صف یک اولویت خاص دارد. برای مثال، در خطوط هوایی، چمدانهایی با عنوان Business یا First-class زودتر از بقیه چمدانها دریافت میشوند. Priority Queue یک گسترش از صف است که ویژگیهای زیر را دارد:
- عنصری با اولویت بالا قبل از عنصری با اولویت پایین از صف حذف میشود.
- اگر دو عنصر اولویت یکسانی داشته باشند، بر اساس ترتیب ورودشان به صف پردازش میشوند.
# A simple implementation of Priority Queue
# using Queue.
class PriorityQueue(object):
def __init__(self):
self.queue = []
def __str__(self):
return ' '.join([str(i) for i in self.queue])
# for checking if the queue is empty
def isEmpty(self):
return len(self.queue) == 0
# for inserting an element in the queue
def insert(self, data):
self.queue.append(data)
# for popping an element based on Priority
def delete(self):
try:
max = 0
for i in range(len(self.queue)):
if self.queue[i] > self.queue[max]:
max = i
item = self.queue[max]
del self.queue[max]
return item
except IndexError:
print()
exit()
if __name__ == '__main__':
myQueue = PriorityQueue()
myQueue.insert(12)
myQueue.insert(1)
myQueue.insert(14)
myQueue.insert(7)
print(myQueue)
while not myQueue.isEmpty():
print(myQueue.delete())
خروجی:
12 1 14 7 14 12 7 1
ساختمان داده Heap queue در پایتون
ساختمان داده Heap queue در پایتون ، ساختمان داده heap را فراهم میکند که عمدتا برای نمایش صف اولویت استفاده میشود. ویژگی ساختمان داده در پایتون این است که هر بار که کوچکترین عنصر هیپ حذف میشود (min-heap). هر زمان که عناصری اضافه یا حذف میشوند، ساختار هیپ حفظ میشود. عنصر heap[0] همچنین همیشه کوچکترین عنصر را باز میگرداند.
این ساختمان داده در پایتون از استخراج و وارد کردن کوچکترین عنصر با پیچیدگی زمانی O(log n) پشتیبانی میکند.
# importing "heapq" to implement heap queue
import heapq
# initializing list
li = [5, 7, 9, 1, 3]
# using heapify to convert list into heap
heapq.heapify(li)
# printing created heap
print ("The created heap is : ",end="")
print (list(li))
# using heappush() to push elements into heap
# pushes 4
heapq.heappush(li,4)
# printing modified heap
print ("The modified heap after push is : ",end="")
print (list(li))
# using heappop() to pop smallest element
print ("The popped and smallest element is : ",end="")
print (heapq.heappop(li))
خروجی:
The created heap is : [1, 3, 9, 7, 5] The modified heap after push is : [1, 3, 4, 7, 5, 9] The popped and smallest element is : 1
درخت یک ساختمان داده سلسلهمراتبی است.
tree
----
j <-- root
/ \
f k
/ \ \
a h z <-- leaves
گره بالاترین درخت به نام ریشه «Root» شناخته میشود، در حالی که گرههای پایینترین سطح یا گرههایی که فرزند ندارند به نام گرههای برگ «Leaf nodes» شناخته میشوند. گرههایی که مستقیما زیر یک گره قرار دارند، به نام فرزند «Children» آن گره شناخته میشوند و گرههایی که مستقیما بالای یک گره قرار دارند، به نام والد «Parent» آن گره شناخته میشوند.
درخت دودویی درختی است که در آن هر عنصر میتواند حداکثر دو فرزند داشته باشد. در درخت دودویی، معمولاً این دو فرزند به نامهای فرزند چپ و فرزند راست شناخته میشوند. هر گره در درخت دودویی شامل بخشهای زیر است:
- داده
- اشارهگر به فرزند چپ
- اشارهگر به فرزند راست
# A Python class that represents an individual node
# in a Binary Tree
class Node:
def __init__(self,key):
self.left = None
self.right = None
self.val = key
خروجی:
tree
----
1 <-- root
/ \
2 3
/
4
برای افزودن داده به درخت، ابتدا باید بررسی کنیم که آیا درخت خالی است یا خیر. اگر درخت خالی باشد، داده به ریشه اختصاص داده میشود. در غیر این صورت، داده باید به درستی در موقعیت مناسب در درخت قرار گیرد.
# Python program to introduce Binary Tree
# A class that represents an individual node in a
# Binary Tree
class Node:
def __init__(self,key):
self.left = None
self.right = None
self.val = key
# create root
root = Node(1)
''' following is the tree after above statement
1
/ \
None None'''
root.left = Node(2);
root.right = Node(3);
''' 2 and 3 become left and right children of 1
1
/ \
2 3
/ \ / \
None None None None'''
root.left.left = Node(4);
'''4 becomes left child of 2
1
/ \
2 3
/ \ / \
4 None None None
/ \
None None'''
عبور از درخت
درختها میتوانند به روشهای مختلفی پیمایش شوند. در زیر روشهای معمول برای پیمایش درختها آورده شده است. فرض کنید درخت زیر را در نظر بگیریم
tree
----
1 <-- root
/ \
2 3
/ \
4 5
عبور از درخت به روش عمقی:
- درونتر (چپ، ریشه، راست): ۴ ۲ ۵ ۱ ۳
- پریدرونتر (ریشه، چپ، راست): ۱ ۲ ۴ ۵ ۳
- پستدرونتر (چپ، راست، ریشه): ۴ ۵ ۲ ۳ ۱
الگوریتم درونتر(tree):
- زیر درخت چپ را پیمایش کن، یعنی فراخوانی درونتر برای زیر درخت چپ.
- ریشه را بازدید کن.
- زیر درخت راست را پیمایش کن، یعنی فراخوانی درونتر برای زیر درخت راست.
الگوریتم پریدرونتر(tree):
- ریشه را بازدید کن.
- زیر درخت چپ را پیمایش کن، یعنی فراخوانی پریدرونتر برای زیر درخت چپ.
- زیر درخت راست را پیمایش کن، یعنی فراخوانی پریدرونتر برای زیر درخت راست.
الگوریتم پستدرونتر(tree):
- زیر درخت چپ را پیمایش کن، یعنی فراخوانی پستدرونتر برای زیر درخت چپ.
- زیر درخت راست را پیمایش کن، یعنی فراخوانی پستدرونتر برای زیر درخت راست.
- ریشه را بازدید کن.
# Python program to for tree traversals
# A class that represents an individual node in a
# Binary Tree
class Node:
def __init__(self, key):
self.left = None
self.right = None
self.val = key
# A function to do inorder tree traversal
def printInorder(root):
if root:
# First recur on left child
printInorder(root.left)
# then print the data of node
print(root.val),
# now recur on right child
printInorder(root.right)
# A function to do postorder tree traversal
def printPostorder(root):
if root:
# First recur on left child
printPostorder(root.left)
# the recur on right child
printPostorder(root.right)
# now print the data of node
print(root.val),
# A function to do preorder tree traversal
def printPreorder(root):
if root:
# First print the data of node
print(root.val),
# Then recur on left child
printPreorder(root.left)
# Finally recur on right child
printPreorder(root.right)
# Driver code
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)
print("Preorder traversal of binary tree is")
printPreorder(root)
print("\nInorder traversal of binary tree is")
printInorder(root)
print("\nPostorder traversal of binary tree is")
printPostorder(root)
خروجی:
Preorder traversal of binary tree is 1 2 4 5 3 Inorder traversal of binary tree is 4 2 5 1 3 Postorder traversal of binary tree is 4 5 2 3 1
زمان پیچیدگی – O(n)
پیمایش به روش عرضی یا سطحی
پیمایش سطحی درخت همان پیمایش به روش عرضی است. پیمایش سطحی درخت بالا به این صورت است: ۱ ۲ ۳ ۴ ۵.
برای هر گره، ابتدا گره بازدید میشود و سپس فرزندان آن در یک صف FIFO قرار میگیرند. در زیر الگوریتم مربوطه آورده شده است:
- یک صف خالی q ایجاد کن.
- temp_node = root /* از ریشه شروع کن */
- حلقه را در حالی که temp_node برابر NULL نیست اجرا کن:
- داده temp_node را چاپ کن.
- فرزندان temp_node را (اول فرزند چپ و سپس فرزند راست) به صف اضافه کن.
- یک گره از صف خارج کن.
# Python program to print level
# order traversal using Queue
# A node structure
class Node:
# A utility function to create a new node
def __init__(self ,key):
self.data = key
self.left = None
self.right = None
# Iterative Method to print the
# height of a binary tree
def printLevelOrder(root):
# Base Case
if root is None:
return
# Create an empty queue
# for level order traversal
queue = []
# Enqueue Root and initialize height
queue.append(root)
while(len(queue) > 0):
# Print front of queue and
# remove it from queue
print (queue[0].data)
node = queue.pop(0)
# Enqueue left child
if node.left is not None:
queue.append(node.left)
# Enqueue right child
if node.right is not None:
queue.append(node.right)
# Driver Program to test above function
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)
print ("Level Order Traversal of binary tree is -")
printLevelOrder(root)
خروجی:
Level Order Traversal of binary tree is - 1 2 3 4 5
زمان پیچیدگی: O(n)
ساختمان داده گراف در پایتون
ساختمان داده گراف در پایتون گراف یک ساختمان داده غیرخطی است که از گرهها و یالها تشکیل شده است. گرهها گاهی اوقات به عنوان راس «vertice» نیز شناخته میشوند و یالها خطوط یا کمانهایی هستند که دو گره را در گراف به هم متصل میکنند. به طور رسمیتر، گراف به عنوان یک مجموعه شامل مجموعهای از راسها یا گرهها و مجموعهای از یالها تعریف میشود که یک جفت گره را به هم متصل میکنند.
در گراف بالا، مجموعه رئوس V = {۰، ۱، ۲، ۳، ۴} و مجموعه یالها E = {۰۱، ۱۲، ۲۳، ۳۴، ۰۴، ۱۴، ۱۳} است.
دو نمایش معمول گراف به شرح زیر هستند:
ماتریس مجاورت در پایتون
ماتریس مجاورت یک آرایه دوبعدی با اندازه V x V است که V تعداد راسها در گراف است. فرض کنید آرایه دوبعدی به نام [ ][ ]adj داشته باشیم، در این صورت اگر adj[i][j] = 1 باشد، به این معنی است که یک یال از راس i به راس j وجود دارد. ماتریس مجاورت برای گرافهای بدون جهت همیشه متقارن است. ماتریس مجاورت همچنین برای نمایش گرافهای وزنی استفاده میشود. اگر adj[i][j] = w باشد، به این معناست که یک یال از راس i به راس j با وزن w وجود دارد.
# A simple representation of graph using Adjacency Matrix
class Graph:
def __init__(self,numvertex):
self.adjMatrix = [[-1]*numvertex for x in range(numvertex)]
self.numvertex = numvertex
self.vertices = {}
self.verticeslist =[0]*numvertex
def set_vertex(self,vtx,id):
if 0<=vtx<=self.numvertex:
self.vertices[id] = vtx
self.verticeslist[vtx] = id
def set_edge(self,frm,to,cost=0):
frm = self.vertices[frm]
to = self.vertices[to]
self.adjMatrix[frm][to] = cost
# for directed graph do not add this
self.adjMatrix[to][frm] = cost
def get_vertex(self):
return self.verticeslist
def get_edges(self):
edges=[]
for i in range (self.numvertex):
for j in range (self.numvertex):
if (self.adjMatrix[i][j]!=-1):
edges.append((self.verticeslist[i],self.verticeslist[j],self.adjMatrix[i][j]))
return edges
def get_matrix(self):
return self.adjMatrix
G =Graph(6)
G.set_vertex(0,'a')
G.set_vertex(1,'b')
G.set_vertex(2,'c')
G.set_vertex(3,'d')
G.set_vertex(4,'e')
G.set_vertex(5,'f')
G.set_edge('a','e',10)
G.set_edge('a','c',20)
G.set_edge('c','b',30)
G.set_edge('b','e',40)
G.set_edge('e','d',50)
G.set_edge('f','e',60)
print("Vertices of Graph")
print(G.get_vertex())
print("Edges of Graph")
print(G.get_edges())
print("Adjacency Matrix of Graph")
print(G.get_matrix())
خروجی:
Vertices of Graph [‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’] Edges of Graph [(‘a’, ‘c’, 20), (‘a’, ‘e’, 10), (‘b’, ‘c’, 30), (‘b’, ‘e’, 40), (‘c’, ‘a’, 20), (‘c’, ‘b’, 30), (‘d’, ‘e’, 50), (‘e’, ‘a’, 10), (‘e’, ‘b’, 40), (‘e’, ‘d’, 50), (‘e’, ‘f’, 60), (‘f’, ‘e’, 60)] Adjacency Matrix of Graph [[-1, -1, 20, -1, 10, -1], [-1, -1, 30, -1, 40, -1], [20, 30, -1, -1, -1, -1], [-1, -1, -1, -1, 50, -1], [10, 40, -1, 50, -1, 60], [-1, -1, -1, -1, 60, -1]]
فهرست مجاورت در پایتون
از یک آرایه از لیستها استفاده میشود. اندازه آرایه برابر با تعداد راسها است. فرض کنید آرایه به نام [ ]array داشته باشیم. هر ورودی array[i] نمایانگر لیستی از راسها مجاور به راس i است. این نمایش میتواند برای گرافهای وزنی نیز استفاده شود. وزنهای یالها میتوانند به عنوان لیستهایی از جفتها (زوجهای راس و وزن) نمایش داده شوند.
نمایش فهرست مجاورت گراف بالا به شرح زیر است:
- array[0] = {1, 4}
- array[1] = {0, 2, 4, 3}
- array[2] = {1, 3}
- array[3] = {2, 4, 1}
- array[4] = {0, 1, 3}
# A class to represent the adjacency list of the node
class AdjNode:
def __init__(self, data):
self.vertex = data
self.next = None
# A class to represent a graph. A graph
# is the list of the adjacency lists.
# Size of the array will be the no. of the
# vertices "V"
class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = [None] * self.V
# Function to add an edge in an undirected graph
def add_edge(self, src, dest):
# Adding the node to the source node
node = AdjNode(dest)
node.next = self.graph[src]
self.graph[src] = node
# Adding the source node to the destination as
# it is the undirected graph
node = AdjNode(src)
node.next = self.graph[dest]
self.graph[dest] = node
# Function to print the graph
def print_graph(self):
for i in range(self.V):
print("Adjacency list of vertex {}\n head".format(i), end="")
temp = self.graph[i]
while temp:
print(" -> {}".format(temp.vertex), end="")
temp = temp.next
print(" \n")
# Driver program to the above graph class
if __name__ == "__main__":
V = 5
graph = Graph(V)
graph.add_edge(0, 1)
graph.add_edge(0, 4)
graph.add_edge(1, 2)
graph.add_edge(1, 3)
graph.add_edge(1, 4)
graph.add_edge(2, 3)
graph.add_edge(3, 4)
graph.print_graph()
خروجی:
Adjacency list of vertex 0 head -> 4 -> 1 Adjacency list of vertex 1 head -> 4 -> 3 -> 2 -> 0 Adjacency list of vertex 2 head -> 3 -> 1 Adjacency list of vertex 3 head -> 4 -> 2 -> 1 Adjacency list of vertex 4 head -> 3 -> 1 -> 0
پیمایش گراف
پیمایش به روش جستجوی اول عرضی (BFS)
پیمایش به روش عرضی برای یک گراف مشابه پیمایش عرضی درخت است. تنها تفاوت این است که برخلاف درختها، گرافها ممکن است شامل چرخهها باشند، بنابراین ممکن است دوباره به همان گره برسیم. برای جلوگیری از پردازش دوباره یک گره، از یک آرایه بولی visited استفاده میکنیم. برای سادگی، فرض میشود که همه راسها از راس شروع قابل دسترسی هستند.
برای مثال، در گراف زیر، پیمایش را از راس ۲ شروع میکنیم. وقتی به راس ۰ میرسیم، تمام راسها مجاور آن را بررسی میکنیم. راس ۲ نیز یک راس مجاور ۰ است. اگر راسهابازدید شده را علامتگذاری نکنیم، راس ۲ دوباره پردازش میشود و این باعث ایجاد یک فرآیند غیرپایانپذیر خواهد شد.
پیمایش به روش جستجوی اول عرضی (BFS) در گراف زیر به این صورت است: ۲، ۰، ۳، ۱.
# Python3 Program to print BFS traversal
# from a given source vertex. BFS(int s)
# traverses vertices reachable from s.
from collections import defaultdict
# This class represents a directed graph
# using adjacency list representation
class Graph:
# Constructor
def __init__(self):
# default dictionary to store graph
self.graph = defaultdict(list)
# function to add an edge to graph
def addEdge(self,u,v):
self.graph[u].append(v)
# Function to print a BFS of graph
def BFS(self, s):
# Mark all the vertices as not visited
visited = [False] * (max(self.graph) + 1)
# Create a queue for BFS
queue = []
# Mark the source node as
# visited and enqueue it
queue.append(s)
visited[s] = True
while queue:
# Dequeue a vertex from
# queue and print it
s = queue.pop(0)
print (s, end = " ")
# Get all adjacent vertices of the
# dequeued vertex s. If a adjacent
# has not been visited, then mark it
# visited and enqueue it
for i in self.graph[s]:
if visited[i] == False:
queue.append(i)
visited[i] = True
# Driver code
# Create a graph given in
# the above diagram
g = Graph()
g.addEdge(0, 1)
g.addEdge(0, 2)
g.addEdge(1, 2)
g.addEdge(2, 0)
g.addEdge(2, 3)
g.addEdge(3, 3)
print ("Following is Breadth First Traversal"
" (starting from vertex 2)")
g.BFS(2)
# This code is contributed by Neelam Yadav
خروجی:
Following is Breadth First Traversal (starting from vertex 2) 2 0 3 1
زمان پیچیدگی: O(V+E) که در آن V تعداد رئوس در گراف و E تعداد یالها در گراف است.
جستجوی عمقی یا DFS
پیمایش به روش جستجوی عمقی برای یک گراف مشابه پیمایش عمقی درخت است. تنها تفاوت این است که برخلاف درختها، گرافها ممکن است شامل چرخهها باشند و یک گره ممکن است دو بار بازدید شود. برای جلوگیری از پردازش دوباره یک گره، از یک آرایه بولی visited استفاده میکنیم.
الگوریتم:
- یک تابع بازگشتی ایجاد کن که شاخص گره و آرایه visited را دریافت کند.
- گره فعلی را به عنوان بازدید شده علامتگذاری کرده و آن را چاپ کن.
- تمام گرههای مجاور و علامتگذاری نشده را پیمایش کرده و تابع بازگشتی را برای گرههای مجاور فراخوانی کن.
# Python3 program to print DFS traversal
# from a given graph
from collections import defaultdict
# This class represents a directed graph using
# adjacency list representation
class Graph:
# Constructor
def __init__(self):
# default dictionary to store graph
self.graph = defaultdict(list)
# function to add an edge to graph
def addEdge(self, u, v):
self.graph[u].append(v)
# A function used by DFS
def DFSUtil(self, v, visited):
# Mark the current node as visited
# and print it
visited.add(v)
print(v, end=' ')
# Recur for all the vertices
# adjacent to this vertex
for neighbour in self.graph[v]:
if neighbour not in visited:
self.DFSUtil(neighbour, visited)
# The function to do DFS traversal. It uses
# recursive DFSUtil()
def DFS(self, v):
# Create a set to store visited vertices
visited = set()
# Call the recursive helper function
# to print DFS traversal
self.DFSUtil(v, visited)
# Driver code
# Create a graph given
# in the above diagram
g = Graph()
g.addEdge(0, 1)
g.addEdge(0, 2)
g.addEdge(1, 2)
g.addEdge(2, 0)
g.addEdge(2, 3)
g.addEdge(3, 3)
print("Following is DFS from (starting from vertex 2)")
g.DFS(2)
خروجی:
Following is DFS from (starting from vertex 2) 2 0 1 3
زمان پیچیدگی: O(V + E)، که در آن V تعداد رئوس و E تعداد یالها در گراف است.
نتیجه گیری
درک مفاهیم ساختمان داده در پایتون برای سازماندهی و مدیریت دادهها در برنامهها لازم و ضروری است. این ساختارها با ارائه راهحلهای بهینه برای ذخیرهسازی و دسترسی به دادهها، به توسعهدهندگان کمک میکنند تا برنامههای کارآمدتر و مقیاسپذیرتری ایجاد کنند. پایتون با ویژگیهای منحصر بهفرد خود، مانند سادگی در استفاده و پیادهسازی، امکان استفاده از ساختارهای داده مختلف مانند لیستها، دیکشنریها و تاپلها را به راحتی فراهم میآورد.
این زبان بهویژه برای یادگیری مفاهیم پایهای ساختمان داده ها مناسب است و به توسعهدهندگان امکان میدهد تا از این ساختمان داده در حل مسائل مختلف بهطور بهینه استفاده کنند. علاوه بر این، توانایی پایتون در پشتیبانی از ساختمان دادههای پیشرفتهتری مانند درختها و گرافها، انعطافپذیری این زبان را برای پروژههای پیچیدهتر بیشتر میکند. به طور کلی، پایتون با ارائه ساختمان داده قدرتمند و کاربردی، ابزارهایی مناسب برای هر سطح از توسعهدهندگان فراهم میکند تا بتوانند به بهترین نحو ممکن نیازهای برنامهنویسی خود را برطرف کنند.















