290 likes | 513 Views
ساختمان داده ها. گراف ها. مهدی ایل بیگی دانشگاه آزاد اسلامی دماوند پاییز 89. 1. 3. 2. 4. 1. 2. 3. گراف (Graph). براي نمايش گراف G اینگونه می نويسيم: G = (V, E) . هر گراف G شامل دو مجموعه V و E است: V (Vertex) : مجموعه محدود و غيرتهي از رئوس است
E N D
ساختمان داده ها گراف ها مهدی ایل بیگی دانشگاه آزاد اسلامی دماوند پاییز 89
1 3 2 4 1 2 3 گراف (Graph) • براي نمايش گراف G اینگونه می نويسيم: G = (V, E) . هر گراف G شامل دو مجموعه V و E است: • V (Vertex): مجموعه محدود و غيرتهي از رئوس است • E (Edge): مجموعه اي محدود و احتمالا تهي از لبه ها مي باشد. • لبه های گراف می توانند جهت دار یا بدونه جهت باشند. • براي گراف بدونه جهت، لبه ها به صورت خطوط يا منحني نمايش داده مي شوند. و براي گراف هاي جهت دار لبه ها به صورت فلش هايي که از گره مبداء به گره مقصد رسم شده است، ارائه مي گردند. • مثال: گراف جهت دار V (G2) = {1,2,3} E(G2) = {<1,2>, <2,1>, <2,3>} گراف بدون جهت V (G1) = {1,2,3,4} E(G1) = {(1,2),(1,3),(1,4),(2,3),(2,4),(3,4)}
نکاتی در مورد گراف ها • محدوديت هاي زير بر روي گراف ها اعمال مي شود: • يک گراف فاقد لبه اي از يک راس مانند i، به خودش مي باشد. اين مطلب بدين معني است که لبه (vi, vi) غير معتبر مي باشد. • يک گراف فاقد رويداد چندگانه از يک لبه مي باشد. یعنی بین دو گره نباید دو یا چند یال مشابه وجود داشته باشد. • گراف کامل: گراف کامل گرافي است که داراي حداکثر تعداد لبه باشد. • براي يک گراف بدون جهت با n راس، حداکثر تعداد لبه ها برابر است با: n(n-1) / 2 • اگرگراف جهت داري با n گره داشته باشیم، بيشترين تعداد لبه هاي آن برابر است با: n(n-1)
1 3 2 4 نکاتی در مورد گراف ها • یک مسیر بین راس vp و vq مجموعه ای از رئوسvp,vi1,vi2, ...,vin,vq است بطوری که یال های (vp,vi1),(vi1,vi2), ...,(vin,vq) در مجموعه یال های گراف موجود باشد. • طول يک مسير تعداد لبه هاي موجود در آن است. • مسير ساده: مسيري است که همه رئوس آن احتمالا به جز اولي و آخري مجزا باشند. • حلقه يا سيکل: يک مسيرساده است که اولين و آخرين راس آن يکي باشد. • مثال: در گراف روبرو مجموعه 1, 2, 3, 4, 1 یک حلقه است:
نکاتی در مورد گراف ها • در گراف بدون جهت مانند G، دو راس vi و vj را متصل مي گويند، اگر مسيري در G از vi به vj وجود داشته باشد. • يک گراف بدون جهت را متصل (همبند) مي ناميم اگر براي هر زوج راس vi و vj در V(G)، مسيري از vi به vj در G وجود داشته باشد. • يک مولفه اتصال يا به طور ساده تر يک مولفه، در گراف بدون جهت، بزرگترين زيرگراف متصل آن است. • يک گراف جهت دار را کاملا متصل مي نامیم، اگر براي هر زوج از رئوس viو vjدر V(G)، مسيري جهت دار از vi بهvjو همچنين از vj به vi وجود داشته باشد. • يک مولفه کاملا متصل، بزرگترين زيرگرافي است که کاملا متصل باشد.
V0 V2 V1 V3 V4 V5 V6 V7 اجزاي دو اتصالي و نقاط اتصال • نقطه اتصال: نقطه اتصال يک راس مانند v از گراف G مي باشد به نحوي که حذف راس v همراه با تمام لبه هاي متلاقي با v، گرافي به نام G’ ايجاد مي کند که حداقل داراي دو جز متصل است. • گراف دو اتصالي: يک گراف متصل است اگر فاقد نقاط اتصالي باشد. 0 8 9 گراف دو اتصالي 1 7 گراف متصل 3 5 2 6 4 نقطه اتصال
نکاتی در مورد گراف ها • درجه يک راس تعداد لبه هاي متلاقي (متصل) با آن راس است. • اگر در گراف G با n راس، di درجه راس i و e تعداد لبه ها باشد، به آساني مي توان ديد که تعداد لبه ها برابر است با: • نکته: در گراف غیرجهتدار، تعداد راس های با درجه فرد، زوج می باشد. • برای نمايش گراف ها سه روش وجود دارد: • ماتريس مجاورتي • ليست هاي مجاورتي • ليست هاي چندگانه (لیست هایی مانند نمایش ماتریس اسپارس با لیست های پیوندی)
0 2 1 3 نمایش گراف با ماتریس مجاورتی • فرض کنيد G = (V, E) يک گراف با n راس باشد (n ≥ 1)، ماتريس مجاورتي گراف G يک آرايه دوبعدي n × n به نام adj_mat مي باشد. اگر لبه (vi, vj) (براي گراف جهت دار <vi, vj>) در E(G) باشد، آنگاه adj_mat[i] [j] = 1 خواهد بود. • نکته: ماتريس مجاورتي براي يک گراف بدون جهت، متقارن است، زيرا لبه (vi, vj) در E(G) خواهد بود، اگر و تنها اگر لبه (vi, vj) نيز در E(G) باشد. • براي گراف بدون جهت، درجه هر راس مانند i مجموع عناصر سطري آن است و براي يک گراف جهت دار، مجموع سطري، درجه خارجه و مجموع ستوني، درجه وارده خواهد بود. 0 12 3 0 01 0 0 0 1 1 1 1 0 1 0 1 1 0 0 1 2 3 ماتريس مجاورتي
0 0 1 1 3 3 2 1 0 3 0 2 2 NULL NULL . NULL NULL . . . . . . . 2 1 3 نمایش گراف با لیست های مجاورتی • در اين نمايش، n سطر ماتريس مجاورتي در n ليست پيوندي قرار مي گيرد. پس براي هر راس از گراف G، يک ليست وجود دارد و هر گره از لیست، حداقل دو فيلد دارد: راس و اتصال • در هر ليست مشخصي مانند i، گره هاي ليست حاوي رئوس مجاور از راس i مي باشند. • در يک گراف بدون جهت با n راس و e لبه، n گره head و 2e گره ليست نیاز است. • درجه هر راس در يک گراف بدون جهت را مي توان به سادگي با شمارش تعداد گره هاي آن در ليست مجاورتي تعيين کرد. • اگر تعداد رئوس گراف G برابر با n باشد، تعداد کل لبه ها، در زمان O( n + e ) تعيين مي شود. head nodes link vertex 0 1 2 3 . . . .
نمایش گراف با لیست های مجاورتی • در گراف جهت دار، تعداد عناصر هر لیست درجه خروجی گراف را مشخص می کند. • برای بدست آوردن درجه ورودی هر راس می توانیم از لیست های مجاورتی معکوس استفاده نماییم. • لیست مجاورتی معکوس، مانند لیست مجاورتی می باشد با این تفاوت که در لیست سطر iام، گره هایی قرار می گیرند که از آن ها به گره i، یک لینک مستقیم وصل شده باشد (یعنی از آن ها به گره i، یک مسیر به طول یک وجود داشته باشد). • نکته1: اگر گراف وزن دار باشد (یال ها دارای وزن باشند)، در نمایش لیست همجواری (مجاورتی) باید برای هر گره یک فیلد وزن نیز درنظر بگیریم. در نمایش ماتریس مجاورتی برای ذخیره سازی وزن یال ها باید بجای اینکه با عدد یک (1)، وجود یال را نشان دهیم، با وزن یال، وجود یال را نشان دهیم. پس یالی با وزن صفر یعنی اتصالی بین دو راس وجود ندارد. گرافي که لبه هايش داراي وزن باشد، شبکه ناميده مي شود. • نکته2: از ماتریس همجواری در گراف هایی با تعداد یال های زیاد و از لیست همجواری برای نمایش گراف هایی با تعداد یال های کم استفاده می شود.
پیمایش گراف • با توجه به گراف بدون جهت G(V, E) و راس v از V(G)، مي خواهيم رئوسي از G را که از v قابل دسترسي هستند، به دست آوريم، يعني می خواهیم همه رئوس متصل به v را بیابیم. براي اين کار دو روش وجود دارد: • جستجوي عمقي (Depth First Search (DFS)): روش عمقي تا حدودي شبيه پيمايش preorder يک درخت است. • جستجوي رديفي(Breadth First Search (BFS)) : اين روش تا حدودي پيمايش ترتيب سطحي را مجسم مي کند. • در پيمايش هاي عمقي و رديفي، فرض مي کنيم که براي نمايش گراف ها از ليست مجاورتي پيوندي استفاده شده است.
Depth First Search (DFS) • در آغاز راس v را ملاقات مي کنيم. بعد راسي مانند w را که قبلا ملاقات نشده و مجاور به v است را انتخاب کرده و روش جستجوي عمقي را با w دنبال مي کنيم. موقعيت جاري راس v در ليست مجاورتي با قرار دادن آن در يک پشته صورت مي گيرد. در نهايت، جستجو به راسي مانند u خواهد رسيد که فاقد هر گونه راس غيرملاقات شده در ليست مجاورتي باشد. در اين مرحله u از پشته انتخاب و حذف شده و فرآيند فوق به همين صورت ادامه پيدا مي کند. بر اساس اين روش، رئوس ملاقات شده، خارج شده و رئوس ملاقات نشده، داخل پشته قرار مي گيرند. جستجو زماني پايان مي پذيرد که پشته تهي باشد. • پس برای پیاده سازی DFS از پشته استفاده می نماییم و برای اینکه بدانیم کدام گره ها ملاقات شده اند از یک آرایه به نام visited با مقدار اولیه صفر استفاده می کنیم و هر راسی که ملاقات بشود خانۀ هم شماره با آن در آرایه visited برابر یک خواهد شد. • اگر گراف با لیست همجواری نمایش داده شود، رئوس مجاور راس v با پیمایش لیست مربوط به v مشخص می شود و بدلیل اینکه هر گره یک بار ملاقات می شود پس کل زمان جستجو O(e) خواهد بود. اگر G توسط ماتريس مجاورتي نمايش داده شود، زمان لازم براي تعيين همه رئوس مجاور به v، O(n) است. از آنجا که حداکثر n راس مشاهده مي شود، کل زمان O(n2) خواهد شد.
تابع جستجوی عمقی Void dfs (int V) { /* depth first search of a graph beginning with vertex V . */ node *w; visited[V] = TRUE; cout << V << “ “; for (w = graph[V]; w ; w = w-> link ) if ( ! Visited [w->vertex] ) dfs (w-> vertex ); }
V0 V2 V1 V3 V4 V5 V6 V7 مثالی برای جستجوی عمقی 0 1 2 3 4 5 6 7 . 1 . 2 NULL . • پیمایش DFS گراف فوق با شروع از راس V0 بصورت زیر خواهد بود: V0, V1, V3, V7, V4, V5, V2, V6 0 . 3 . 4 NULL . 0 . 5 . 6 NULL . 1 . 7 NULL . 1 . 7 NULL . 2 . 7 NULL . 2 . 7 NULL . 3 . 4 . 6 NULL 5 . نمايش ليست هاي مجاورتي
Breadth First Search (BFS) • پيمايش را با راس v شروع نموده، پس از ملاقات راس مزبور، آنرا علامت گذاري مي کنيم. سپس هر يک از رئوس مجاور به راس v را در ليست مجاورتي ملاقات مي کنيم. زماني که همه رئوس مجاور با راس v را ملاقات کرديم، تمام رئوس ملاقات نشده که مجاور با اولين راس مجاور با v در ليست مجاورتي است را ملاقات مي کنيم. براي انجام اين کار ماداميکه هر راس ملاقات مي شود، آنرا در يک صف قرار مي دهيم. هنگامي که ليست مجاورتي تمام شد، راسي را از صف حذف و با تست هر يک از رئوس در ليست مجاورتي اين فرآيند را ادامه مي دهيم. رئوس ملاقات نشده، ملاقات و سپس در صف قرار مي گيرند (رئوس ملاقات شده ناديده گرفته مي شوند). جستجو هنگامي که صف تهي گردد، خاتمه مي يابد. • به عنوان مثال جستجوی ردیفی گراف اسلاید قبل با شروع از گره V0 بصورت زیر خواهد بود: V0, V1, V2, V3, V4, V5, V6, V7 • اگر گراف با لیست همجواری نمایش داده شود، رئوس مجاور راس v با پیمایش لیست مربوط به v مشخص می شود و بدلیل اینکه هر گره یک بار ملاقات می شود پس کل زمان جستجو O(e) خواهد بود. اگر G توسط ماتريس مجاورتي نمايش داده شود، زمان لازم براي تعيين همه رئوس مجاور به v، O(n) است. از آنجا که حداکثر n راس مشاهده مي شود، کل زمان O(n2) خواهد شد.
درخت های پوشا (Spanning Trees) • چنانچه گراف G متصل باشد، پيمايش هاي جستجوي عمقي يا جستجوي رديفي، تمام رئوس گراف G را ملاقات مي کنند. • در اين حالت با اعمال هر يک از پيمايش ها، لبه هاي گراف G به دو قسمت تقسيم مي شوند: • T (براي لبه هاي درخت): مجموعه لبه هاي به کار رفته يا پيموده شده در جستجو مي باشد. (لبه هاي T، يک درخت که شامل تمام رئوس گراف G مي باشد را تشکيل مي دهند) • N (براي لبه هاي غير درخت): مجموعه لبه هاي باقي مانده مي باشد. • تعريف درخت پوشا: درختي که تعدادي از لبه ها و تمام رئوس G را در بر دارد، درخت پوشا ناميده مي شود. مثال: گراف سه درخت پوشاي آن
درخت های پوشا • درخت پوشايي که از فراخواني dfs به دست مي آيد را درخت پوشاي عمقي مي نامند. چنانچه از روش bfs استفاده شود، درخت پوشاي حاصل را درخت پوشاي رديفي مي نامند. • هر گراف متصل با n راس، بايستي حداقل n-1 لبه داشته باشد و همه گراف ها متصل با n-1 لبه، درخت هستند. درخت پوشا داراي n-1 لبه مي باشد. • هزينه يک درخت پوشاي که يک گراف جهت دار داراي وزن است، مجموع هزينه هاي (وزن هاي) لبه ها در درخت پوشا مي باشد. • درخت پوشاي حداقل هزينه، درخت پوشايي است که داراي کمترين هزينه باشد. • براي به دست آوردن درخت پوشاي حداقل هزينه يک گراف جهت دار متصل، مي توان از سه الگوريتم متفاوت استفاده نمود: الگوريتم راشال، الگوريتم پريم، الگوريتم سولين • هر سه روش فوق از يک مشی طراحي الگوريتمي به نام خط مشي greedy استفاده مي کنند.
الگوريتم راشال • الگوریتم ما براي بدست آوردن درخت هاي پوشا با حداقل هزینه، بايد داراي شرايط زير باشد: • بايد فقط از لبه هاي داخل گراف استفاده کنيم. • بايد دقيقا از n-1 لبه استفاده کنيم. • نبايد از لبه هايي که ايجاد يک حلقه مي کنند، استفاده کنيم. • الگوریتم راشال: در اين روش، درخت پوشاي با کمترين هزينه (T)، لبه به لبه ساخته مي شود. لبه هاي مورد استفاده در T، به ترتيب صعودي وزن ها مي باشد. يک لبه در Tخواهد بود، اگر با لبه هاي قبل که در T بوده اند، تشکيل يک حلقه ندهد. چون G متصل است و داراي n > 0 راس است، دقيقا n – 1 لبه براي T انتخاب مي شود. • قضیه: فرض کنيد G يک گراف متصل بدون جهت باشد، الگوريتم راشال يک درخت پوشاي حداقل را ايجاد مي کند.
10 28 10 10 14 16 24 12 25 18 12 22 0 0 10 10 10 10 1 1 14 14 14 14 16 16 16 5 6 5 6 2 2 25 12 12 12 12 4 4 3 3 22 22 0 1 2 5 6 4 3 0 0 0 0 0 1 1 1 1 1 5 6 5 5 5 5 6 6 6 6 2 2 2 2 2 4 4 4 4 4 3 3 3 3 3 مثال برای الگوریتم راشال
الگوريتم پریم • الگوريتم پريم مانند الگوريتم راشال، در هر زمان يک لبه از درخت پوشاي حداقل هزينه را مي سازد با این تفاوت که در هر مرحله از الگوريتم، مجموعه لبه ها انتخاب شده يک درخت را تشکيل مي دهند. در صورتیکه مجموعه لبه هاي انتخاب شده در الگوريتم راشال در هر مرحله يک جنگل را تشکيل مي دهند. • الگوريتم پريم با يک درخت مانند T، که تنها شامل يک راس است، شروع مي کند. اين راس مي تواند هر يک از رئوس در گراف اصلي باشد. سپس يک لبه با کمترين هزينه مانند (u, v) به T اضافه مي شود به نحوي که T ⋃ {(u, v)}نيز خود يک درخت شود. اين عمل را تا زماني که T شامل n – 1 لبه بشود، ادامه مي دهيم.
0 0 0 10 28 10 10 1 1 1 10 14 16 5 6 5 6 5 6 2 2 2 24 25 25 25 25 18 4 4 4 12 12 3 3 3 22 22 22 0 0 0 10 10 10 1 1 1 14 16 16 5 6 5 6 2 2 5 6 2 25 25 4 4 12 4 12 3 3 3 22 22 0 1 2 5 6 4 3 مثال برای الگوريتم پریم
الگوريتم سولین • بر خلاف الگوريتم پريم و راشال، الگوريتم سولين چندين لبه را براي اضافه نمودن در هر مرحله انتخاب مي کند. در انتهای الگوریتم، لبه هاي انتخاب شده، همراه با تمام n راس گراف، تشکيل يک درخت پوشا را مي دهند. • در ابتداي مرحله اول، برای هر گره یک یال از یال های متصل به آن که دارای کمترین هزینه است را انتخاب می کنیم. حال در گراف تعدادی درخت داریم که تشکیل یک جنگل را می دهند. • در مرحله بعدی يک لبه که هزینه مینیمم دارد براي هر درخت در جنگل انتخاب مي کنيم (در واقع هر درخت را مانند یک گره در نظر می گیریم)، و به همین ترتیب ادامه می دهیم. • اين الگوريتم هنگامي به پايان مي رسد که فقط يک درخت در انتهاي يک مرحله باقي بماند و يا هيچ لبه اي براي انتخاب باقي نمانده باشد.
28 10 0 14 0 16 10 1 1 10 24 14 14 16 25 18 12 5 6 2 5 6 2 22 25 4 12 4 12 3 3 22 22 0 1 2 5 6 4 3 مثال برای الگوريتم سولین