Professional Documents
Culture Documents
هياكل البيانات باستخدام c
هياكل البيانات باستخدام c
ناليف
ل ا خ ن ــــ
الأسياذ الد ور ال ر اذي
ي ع يض ض ي ك
1
هياكل البيانات باستخدام C++
هياكل البيانات باستخدام C++
3
هياكل البيانات باستخدام C++
العبادي ،نضال
هياكل البيانات باستخدام C++
رقم اإليداع في دار الكتب والوثائق -بغداد)2018/609( :
رقم التصنيف0053.13 :
الواصفات / : :لغات البرمجة / /الحاسبات الكترونية / /لغةC++
تم إعداد بيانات الفهرسة والتصنيف األولية من قبل دار الذاكرة للنشر والتوزيع
الطبعة األولى
2018
حقوق الطبع محفوظة للناشر
5
هياكل البيانات باستخدام C++
هياكل البيانات باستخدام C++
دول المحتويات
المقدمة19 ......................................................................................
المعلومات والبيانات 20 .....................................................................
هياكل البيانات20 .............................................................................
انواع هياكل البيانات 21 .....................................................................
صفات هياكل البيانات 23 ...................................................................
لماذا هياكل البيانات 24 ......................................................................
ماهي الخوارزمية 26 ........................................................................
المقدمة33 ......................................................................................
المصفوفات 33 ................................................................................
المصفوفات االحادية 34 .....................................................................
انشاء المصفوفة36 ...........................................................................
الوصول الى عناصر المصفوفة 40 .......................................................
7
هياكل البيانات باستخدام C++
المقدمة53 ......................................................................................
الترتيب المستقر وغير المستقر 54 ........................................................
خوارزميات الترتيب المالئمة وغير المالئمة55 ........................................
مصطلحات مهمة 55 .........................................................................
ترتيب الفقاعة 55 .............................................................................
ترتيب الحشر59 ..............................................................................
ترتيب االختيار63 ............................................................................
الترتيب بالدمج 67 ............................................................................
الترتيب بطريقة شل73 ......................................................................
الترتيب السريع 76 ...........................................................................
البحث89 .......................................................................................
جداول التجزئة 102 ..........................................................................
المقدمة113 ....................................................................................
تعريف المكدس 113 .........................................................................
العمليات التي تجرى على المكدس 115 ..................................................
هياكل البيانات باستخدام C++
المقدمة167 ....................................................................................
ما هو الطابور168 ...........................................................................
بعض تطبيقات الطابور169 ................................................................
الطوابير الخطية 170 ........................................................................
تنفيذ قوائم االنتظار170 .....................................................................
خزن البيانات في هياكل بيانات مستقرة 172 ............................................
خزن الطابور في هياكل بيانات ديناميكية 173 ..........................................
خوارزمية اضافة عنصر الى طابور 174 ...............................................
حذف عنصر من الطابور 175 .............................................................
الطابور الدائري 177 ........................................................................
المقدمة 187.................................................................................
ابتداء القوائم الموصولة 188..............................................................
ماهي القوائم الموصولة189 ................................................................
العقدة190 ......................................................................................
لماذا القوائم الموصولة192 .................................................................
المؤشرات192 ................................................................................
هيكلية القوائم الموصولة198 ...............................................................
المرور على عناصر القائمة الموصولة 200 ............................................
القوائم الموصولة االحادية 202 ............................................................
القوائم الموصولة الثنائية 238 ..............................................................
القوائم الموصولة الدائرية 268 .............................................................
القوائم الموصولة الدائرية الثنائية 294 ....................................................
المقدمة 325.................................................................................
االعادة326 ....................................................................................
كيف يستخدم االستدعاء الذاتي 328 .......................................................
االستدعاء الذاتي باستخدام مثال329 ......................................................
كتابة دوال االستدعاء الذاتي 338 ..........................................................
هياكل البيانات باستخدام C++
المقدمة343 ....................................................................................
تعريف االشجار الثنائية 344 ...............................................................
العقدة345 ......................................................................................
مصطلحات االشجار 346 ...................................................................
فوائد االشجار 346 ...........................................................................
الشجرة الثنائية 352 ..........................................................................
اشجار البحث الثنائي359 ...................................................................
العمليات التي تجرى على شجرة البحث الثنائي 362 ...................................
اشجار التعابير الرياضية386 ..............................................................
تمثيل االشجار397 ...........................................................................
الفص التاسع ..تكديس هياك البيانات 407 ........... Heap Data Structure
المقدمة407 ....................................................................................
تعريف التكديس407 .........................................................................
خوارزمية انشاء التكديس 409 .............................................................
الحذف من شجرة التكديس 415 ............................................................
تمثيل التكديس 417 ...........................................................................
11
هياكل البيانات باستخدام C++
المقدمة423 ....................................................................................
ما هو المخطط 424 ..........................................................................
تطبيقات نظرية المخططات424 ...........................................................
مصطلحات المخططات 425 ...............................................................
اولر وجسور كونزبيرج441 ...............................................................
انواع المخططات 443 .......................................................................
طرق المرور على المخطط 447 ..........................................................
العمليات االولية االساسية التي تجرى على المخططات 470 .........................
تمثيل المخطط472 ...........................................................................
خوارزمية ديكسترا481 .....................................................................
المقدمة
الحمد هلل حمدًا يلير بج ل و هه وعظيم سلبانه ،وأشهد أل ال إله إال هللا وحةد ال
شريو له ،وأشهد أل محمدًا عبد هللا ورسوله ،صلوات هللا وس مه عليه وعلل آله
وأصحابه البيبين الباهرين ،و َمن سار علل نهجه إلل يةو الةدين .الحمةد هلل الةذي
لنا من ال لم نورا نهدي به.
عند الحديث عن اي فرع من فرروع الحاسربات الرقميرة فرال برد ان نسرتذكر ان الغايرة
من اختراع الحاسبات الرقمية الحديثة هو لغرض تسهيل وتسريع العمليات الحسابية
المعقدة والتي تستهلك وقت .في غالبيرة التطبيقرات فران قردرة الحاسربات علرى الخرزن
والوصول الى كمية المعلومات الكبيرة ،فضال عن سرعة اجراء العمليرات الحسرابية
تلعب دور رئيس وتعتبر الميزة االولى لها.
دائما ً ما نجد أن األشياء تمتاز بمجموعرة صرفات ،وهرذل الصرفات تعتبرر الخصرائص
المميزة والمحددة لألشياء ،وتنتظم هذل الصفات طبيعيا ً بشكل بنائي منظم.
نحن نرغب لتجميع العناصر ذات العالقرة معرا ،ونرغرب ايضرا لتنظريم هرذل البيانرات
على شكل تكتالت بطريقة مناسبة للبرمجة وكفوءة في التنفيذ.
تاريخيررا فرران هياكررل البيانررات تعتبررر الدعامررة الرئيسررية ألقسررام علرروم الحاسرربات .اننررا
ندرس هياكل البيانات لنتعلم كيفية كتابة برامج حاسوب أكثرر كفراءة .علرى مردى 20
سنة االخيرة ازداد التركيز على هذا الموضوع وأصبح واسعا بشكل ملحوظ.
واذ نحن نضع بين ايديكم هذا الكتاب الذي نرجرو ان يكرون فري المسرتوى المطلروب،
ونأمل اننا على االقل لم نقصر ولم نهمل تبيان جواهر عناصر الكتاب.
لقد كانت رحلرة جاهردل لالرتقراء بردرجات العقرل ومعرراج االفكرار ،فمرا هرذا اال جهرد
مقل وال ندعي فيه الكمال ولكن عذرنا انا بذلنا فيه قصارى جهدنا ،فان أصربنا فرذا
مرادنا وان أخطأنا فلنا شرف المحاولة والتعلم.
اننررا نعتقررد ان هياكررل البيانررات مررن المواضرريع المعقرردة نسرربيا لررذلك تحترراج الررى جهررد
اضافي لتبسيطها وجعلها أسهل للفهم.
13
هياكل البيانات باستخدام C++
الكتاب تم كتابته بطريقة مبسطة لمساعدة طلبة علوم الحاسبات في الدراسة االوليرة،
وكذلك من الممكن ان يكون مساعد لمحترفي البرمجيات ممن يرغب ان يفهم هياكل
البيانات بخطوات مبسطة .تم مراعاة عدم االسرهاب غيرر المبررر ممرا يجعرل الكتراب
يمررنح القررار فهمررا واسررعا حررول مفرراهيم هياكررل البيانررات .أتمنررى أن أكررون موفقررا فرري
سررردي لعناصررر الكترراب سررردا ال ملررل فيرره وال تقصررير موضررحا ابيررار ا يجابيررة
والسلبية لهذا الموضوع الشائك الممتع.
ان الراغبين برتعلم هياكرل البيانرات يجرب ان تكرون لهرم المعرفرة بلغرة البرمجرة Cاو
C++والتي تستخدم كأساس برمجي لكل فقرات وفصول الكتاب.
ان المبرررر وراء كتابررة هررذا الكترراب هررو افتقررار المكتبررة العربيررة الررى كتررب عربيررة
احترافية في مجال علوم الحاسبات .لذلك فران الهردف االولري لكتابرة هرذا الكتراب هرو
تغطية التفاصيل والمواضيع الخاصة بموضوع هياكل البيانات بطريقة تسراعد علرى
فهررم واسررتنباط المفرراهيم االساسررية لهياكررل البيانررات فضررال عررن انهررا تراعرري جميررع
المسررتويات .كررذلك فرران الكترراب يرروفر امثلررة واشرركال توضرريحية تسرراعد علررى متابعررة
الموضوع خطوة خطوة ..ناهيك عن تعزيزها بمجموعة من البرامج التي كتبت بلغة
البرمجة .C++
يختلررف هررذا الكترراب عررن كتررب علرروم الحاسرربات بشرركل عررام والترري تكررون مملرروءة
بالنظريررات والعالقررات الرياضررية ،وامثلررة ربمررا تكررون مررن الصررعب حلهررا ،حيررث تررم
التركيز في هذا الكتاب على الشرح السلس للتقنيرات التري تطبرل علرى مشراكل العرالم
الحقيقي .تجنبنا البراهين المعقدة والرياضيات الثقيلة .واضريفت الكثيرر مرن االشركال
لتوضيح المواضيع المعقدة.
يتكون الكتاب من عدد من الفصول اشير لها هنا باختصار.
الفص ة االول :تررم التركيررز فيرره علررى بعررض المصررطلحات العامررة وتعريررف هياكررل
البيانررات وعالقتهررا مررع الخوارزميررات .وتررم توضرريح الخوارزميررات بشرركل مختصررر
لبيان اهميتها في البرمجة.
هياكل البيانات باستخدام C++
الفصة الثةةاني :ركررز علررى المصررفوفات االحاديررة والثنائيررة علررى اعتبررار انهررا البدايررة
لمواضيع هياكل البيانات ،الفصل يحتوي على عدد من االمثلة للتوضيح.
الفصة الثالةةث :تررم التطرررق الررى خوارزميررات الترتيررب وقررد حاولنررا فرري هررذا الفصررل
تحليررل ودراسررة عرردد مررن خوارزميررات الترتيررب بشرركل مبسررط مررع امثلررة توضرريحية
ورسوم توضيحية لغرض المتابعة.
الفصة الرابةةع :يتنراول المكرردس بالتفصريل والررذي هرو مررن مواضريع هياكررل البيانررات
كثيرة االستخدام .تم توضريح اسرتخدام المكردس مرع التعرابير الجبريرة وعرزز الفصرل
بكثير من األمثلة التوضيحية.
الفص الالامس :نستمر في الفصل الخامس مع الطابور (الخطي والدائري) وكل ما
يتعلل بهذا الموضروع مرن عمليرات اضرافة وحرذف .ترم توضريح تطبيقرات ومميرزات
الطابور وعزز الفصل بعدد من االمثلة والبرامج.
الفصة السةةا س :تناولنررا فيرره القرروائم الموصررولة وانواعهررا االربررع ،ونظرررا الن هرذا
الموضوع فيه شيء من التعقيد لذلك حاولنرا االسرهاب بره قلريال .خوارزميرات البحرث
والحشر والحذف كلها تم مناقشتها مع امثلة ورسوم توضيحية.
الفص السابع :ناقش موضوع االستدعاء الذاتي نظررا السرتخداماته الواسرعة ،حيرث
ركزنررا علررى اعطرراء القررار فرصررة لفهررم المفرراهيم االساسررية لالسررتدعاء الررذاتي وتررم
توضيح كيف من الممكن استخدامه فري حرل المشراكل البرمجيرة .الخطروط العريضرة
لكتابة دوال استدعاء ذاتي تم توضيحها باستخدام االمثلة.
الفص الثامن :تطرقنا فيه الى االشجار بشكل عرام واشرجار البحرث الثنرائي كطريقرة
لتنظيم البيانات ،مع بيان عدد من العمليات االساسية التي تجرى على اشجار البحرث
الثنائي .الفصل ايضا تضمن بعض طرق البحث عطاء القار الفرصة للمقارنة.
الفص التاسع :تم التطرق الى موضوع التكديس والعمليات التي تجرى عليره نظررا
ألهمية الموضوع ،تم توضيح العمليات باستخدام امثلة توضيحية معززة بالرسوم.
15
هياكل البيانات باستخدام C++
الفص ال اشةر :الفصرل العشرر هرو خترام الكتراب مرع موضروع المخططرات وكرل مرا
يتعلل بها ،تم توضيح المفاهيم والمصطلحات االساسرية المسرتخدمة مرع المخططرات
وكذلك كيفية تنفيذها وعمليات البحث والحشر والحذف..
في ختام هذل المقدمة فاني ال ازيد على ما قاله عماد االصفهاني "رأيت انره ال يكترب
انسان كتابا فري يومره ال قرال فري غردل لرو غيرر هرذا لكران أحسرن ولرو زيرد كرذا لكران
يستحسن ولو قدم هذا لكان أفضل ولرو ترر هرذا لكران أجمرل وهرذا مرن أعظرم العبرر
وهو دليل على استيالء النقص على جملة البشر".
وأخيرا َ بعرد أن تقردمنا باليسرير فري هرذا المجرال الواسرع نملرين أن ينرال القبرول ويلقرى
االستحسان ..وصل اللهم وسلم على سيدنا وحبيبنا محمد وعلى نل بيته اجمعين..
نضال ال با ي
النجف األشرذ /ال راق 2018
comp_dep_educ@yahoo.com
هياكل البيانات باستخدام C++
المؤلف باوتصار:
17
هياكل البيانات باستخدام C++
هياكل البيانات باستخدام C++
الفص االول
هياك البيانات والالوارزميات
DATA STRUCTURES AND
ALGORITHMS
1.1المقدمة
تستخدم كلمة هيكل وهيكلية فري مجراالت عديردة لتوصريف أغرراض كبيررة ترم بنائهرا
مررن وحرردات بنائيررة صررغيرة بأسررلوب تكررراري ،وهياكررل البيانررات هرري كتلررة منطقيررة
نجمت عن تكراريرة عناصرر البيانرات وفرل ترتيرب محردد وعالقرات ،وتعتبرر هياكرل
البيانات مرحلة وسيطة بين الملفات على وسائط التخزين وبين برامج التطبيقات.
فمثالً أن أسماء أفراد العائلة تنتظم تحت بناء يشبه الشجرة " ,"treeحيث يعبر الجرد
عن الجذر فيها واألبناء عن الفروع وكذلك األحفاد وهكذا.
وكمثال نخر ينتظم الناس في طابور لشراء حاجة ما ،فيكون الواقف أوالً يمثل رأس
الطابور واألخير يمثل ذيل الطابور.
أن دليل التلفون يضم في كل صفحة من صفحاته عمرودين أحردهما يردل علرى أسرماء
أشررخاص معينررين ،مؤسسررات أو هي ررات محررددة ،والعمررود األخررر يشررتمل علررى أرقررام
الهواتف التي يملكها األفراد أو المؤسسات أو الهي ات الموجودة في العمود األول.
ويكتمل هذا التشكيل المنظم بترتيب العمود األول أبجديا ً وذلك لتسهيل عملية البحرث
عن فرد أو مؤسسة أو هي ة معينة.
كررل هررذل األمثلررة تبررين لنررا حقيقررة واحرردة ،هرري أن المعلومررات تمترراز بالترتيررب ،ه رذا
الترتيب يعرف بالهيكل أو هياكل البيانات "."data structure
19
هياكل البيانات باستخدام C++
1.3هياك بيانات
هياكل البيانات هي تمث يرل لعالقرات منطقيرة موجرودة برين عناصرر البيانرات المفرردة.
بكالم اخر ،فان هياكل البيانات تحدد طريقة تنظيم كامل عناصر البيانات والتي تأخذ
باالعتبررار لرريس البيانررات الترري تخررزن فقررط لكررن ايضررا عالقتهررا مررع بعررض .مصررطلح
هياكل البيانات يستخدم لوصف طريقة خزن البيانات.
اذن هياكل البيانات هي طريقة خاصة لخزن وتنظيم البيانرات فري الحاسروب ،بحيرث
من الممكن ان تستخدم بشكل كفوء.
لتطوير برنامج لخوارزمية معينرة يجرب علينرا اختيرار هيكرل البيانرات المناسرب لهرذل
الخوارزمية .لذلك ،فان البرنامج عبارة عن:
هياكل البيانات باستخدام C++
21
هياكل البيانات باستخدام C++
Integers
)Boolean (true, false
)Floating (Decimal numbers
Character and Strings
List
Array
Stack
Queue
هياكل البيانات باستخدام C++
23
هياكل البيانات باستخدام C++
لها مجموعة من العمليات التي من الممكن ان تنجز علرى عناصرر البيانرات.
مثل البحث ،الحشر...الا.
تصف القواعد لكيفية عالقة عناصر البيانات مع بعضها.
وقت التشغيل او التنفيذ لعمليرات هياكرل البيانرات يجرب ان يكرون بأقرل وقرت
ممكن.
الذاكرة المستخدمة لعملية هياكل البيانات يجب ان تكون بأقل ما يمكن.
)1بحةةث البيانةةات -نفرررض مطلرروب جرررد مليرون عنصررر ضررمن مخرزن .لررذا فرران
التطبيل يبحث عن كل عنصر .للبحث عن عنصر ضمن مليون عنصرر مرثال،
فرران كررل عنصررر يسررتغرق وقتررا يجررادل ومعالجترره ،وبالتررالي فرران كررل عنصررر
سرريجعل عمليررة البحررث أكثررر بطررأ .لررو فرضررنا ان هررذل البيانررات نمررت (أي كبررر
حجمها وازداد عدد عناصرها) فان البحث بالتأكيد يصبح ابطأ.
)2سرعة الم ةال -سررعة المعرالج برالرغم مرن انهرا تبردو سرريعة جردا ،لكرن هرذل
السرعة ستكون أكثر تحديدا ذا البيانات نمت الى بليون قيد مثال.
)3البلبةةات المت ةةد ة -ذا كرران هنررا مليررون مسررتخدم يبحررث عررن بيانررات بشرركل
متزامن على خادم الويب ،حتى وان كان الخرادم سرريع جردا فانره ربمرا سيفشرل
عندما يبحث البيانات.
هررذل بعررض المشرراكل الترري تسررتوجب ايجرراد حررل لهررا ،هياكررل البيانررات تررأتي ل نقرراذ.
البيانررات مررن الممكررن ان تررنظم بهيكررل بيانررات بطريقررة بحيررث انهررا تسرراعد علررى ان ال
تكون هنا حاجة لبحرث كرل البيانرات المطلوبرة ،مرن الممكرن ان تبحرث غالبرا بشركل
هياكل البيانات باستخدام C++
لحظي عند استخدام هيكل البيانات المناسب (بغض النظر عن سرعة المعرالج وعردد
طلبات البحث).
واحدة من الطرق التي ننظر بها الرى هياكرل البيانرات هرو ان نركرز علرى نقراط القروة
والضعف لكل نوع منها .في هذا الفصل سنوفر نظرة عامرة علرى هرذل النقراط والتري
نوضحها في الجدول .1.1
25
هياكل البيانات باستخدام C++
الجدول 1.1يمثل خرواص هياكرل البيانرات التري سنناقشرها فري هرذا الكتراب ،لرذلك ال
تندهش ان كانت غامضة بعض الشيء فسناتي عليها بالتفصيل في الفصول الالحقة.
1.7ماهي الالوارزمية
الخوارزمية هري مجموعرة منتهيرة مرن االيعرازات او المنطرل ،تكترب لغررض انجراز
مهمه معرفة مسبقا .الخوارزمية هي ليست شفرة كاملة او برنامج ،هي فقط المنطرل
االسرراس (الحررل) لمشرركلة ،والترري مررن الممكررن توضرريحها امررا علررى شرركل وصررف ذا
مستوى عال (باستخدام اللغات الحية مثال) او باستخدام المخططات ).(flowchart
الخوارزمية يقال عنها انها كفوءة وسريعة ذا كان وقت تنفيذها قليل و ذا لم تسرتهلك
مساحة كبيرة من الذاكرة .ان كفاءة اداء أي خوارزمية تقاس بموجب ما يلي:
.1تعقيد الوقت Time Complexity
.2تعقيد الفضاء Space Complexity
حيث ان Cهي الجزء الثابت و ) SP(Iهي الجزء المتغير للخوارزمية التري تعتمرد
على الخاصية االنية ) .(Iفيما يلي مثال نحاول من خالله توضيح هذا المفهوم
Step 1 - START
Step 2 - C ← A + B + 10
Step 3 - Stop
هنا لدينا يالث متغيرات ) (A, B, and Cويابرت واحرد ،لرذلك فران .S(P) = 1+3
االن الفضراء يعتمررد علرى نرروع البيانرات للمتغيرررات التري تعطررى ونروع الثابررت والترري
ستضرب بها وفقا لحجمها في الذاكرة.
27
هياكل البيانات باستخدام C++
تعقيد الوقت غالبا يقدر بحساب عدد الدوال االولية التي تنجز بالبرنامج .ونظررا الن
كفرراءة الخوارزميررة ربمررا تتغيررر مررع اخررتالف انررواع البيانررات المدخلررة ،عليرره فأننررا
سنستخدم لكل خوارزمية (اسوأ تعقيد وقت )worst-case Time complexity
وذلك الن هنا اقصى وقت يحسب ألي حجم مدخالت.
; Statement
اعالل لدينا عبارة واحدة .تعقيد الوقت لها سيكون يابت .وقت التشغيل للعبرارة سروف
ال يتغير نسبة الى .N
)for (i=0 ; i < N ; i++
{
هياكل البيانات باستخدام C++
;statement
}
تعقيد الوقت للخوارزمية اعالل سيكون خطي .وقت التشغيل او التنفيذ لحلقة التكررار
تتناسررب بشرركل مباشررر مررع قيمررة ( Nحيررث ان Nتمثررل عرردد مرررات تنفيررذ العبررارة
.)statementعندما نضاعف Nفان وقت التنفيذ سيضاعف.
)for (i=0; i < N; i++
{
{
;statement
}
}
هنا ،تعقيد الوقرت للخوارزميرة اعرالل سريكون تربيعري .وقرت التنفيرذ لحلقتري التكررار
تتناسب مع مربع .Nفعندما تضاعف ,Nفان وقت التنفيذ يزداد بمقدار .N * N
)while (low <= high
{
;else break
}
29
هياكل البيانات باستخدام C++
الخوارزميرررة اعرررالل هررري لتجزئرررة مجموعرررة مرررن االرقرررام الرررى النصرررف .االن ،هرررذل
الخوارزمية سيكون لها لوغاريثم وقت تعقيد .بالتاكيرد فران وقرت التنفيرذ للخوارزميرة
يتناسب مع عدد مرات Nالتي من الممكن التقسيم علرى (( )2حيرث ان Nهنرا تمثرل
االعلى – االدنى ) .)(high-lowهذا بسبب ان الخوارزمية تقسم مساحة العمل الرى
النصف في كل دورة.
)void quicksort (int list [ ], int left, int right
{
;)int pivot = partition (list, left, right
}
لنأخررذ المثررال السررابل ونناقشرره ،الخوارزميررة اعررالل هرري جررزء منطقرري صررغير مررن
خوارزميررة ترتيررب االعررداد (سندرسررها الحقررا) .االن فرري هررذل الخوارزميررة سننصررف
القائمة كل مرة (يعني وقت التعقيد هو لوغاريثم) ،ولكن نعيد التكرار Nمن المرات
(حيررث ان Nيمثررل حجررم القائمررة) (بمعنررى اعررادة التعقيررد اللوغرراريثمي بعرردد Nمررن
المرات) .لذا فان وقت التعقيد سيكون ).N*log (N
وقرررت التنفيرررذ لعررردد Nمرررن التكررررارات والتررري هررري لوغاريثميرررة ،وهرررذا يعنررري ان
الخوارزمية هي لوغاريثمية وخطية بنفس الوقت.
م حظة :بشركل عرام ،عمرل شريء مرع كرل عنصرر فري مصرفوفة احاديرة هرو خطري،
عمررل شرريء مررع كررل عنصررر فرري مصررفوفة ينائيررة هررو تربيررع ،تقسرريم مسرراحة العمررل
للنصف هو لوغاريثمي .في الجدول 1.2بعض الحاالت القياسية لتعقيد الوقت.
هياكل البيانات باستخدام C++
31
هياكل البيانات باستخدام C++
هياكل البيانات باستخدام C++
الفص الثاني
المصفوفات ARRAYS
المقدمة 2.1
القوائم هي واحدة من أكثر هياكل البيانات التي يتكرر استخدامها .بالرغم مرن
ان كل البرامج تشرتر برنفس التعريرف للقروائم وهري (متواليرة مرن عناصرر البيانرات
المتجانسررة) ،لكررن نرروع البيانررات الترري تخررزن فرري القرروائم تختلررف مررن برنررامج ألخررر.
بعضها يستخدم قوائم من االعداد الصحيحة ،اخرى تسرتخدم قروائم الرمروز ،االرقرام
الكسرية ...وهكذا .في هذا الفصل سيتم التركيز على اول انواع هياكل البيانات وهي
المصفوفات بنوعيها المصفوفات ذات البعد االحرادي والمصرفوفات المتعرددة األبعراد
مع امثلة توضيحية والعمليات التي من الممكن ان تجرى عليها.
2.2المصفوفات
المصفوفات هي هيكل بيانات تخزن مجموعة من المتغيرات لها نفرس النروع،
فهي تجميع لكيانات البيانات المتشابهة والتي تخزن في مواقع ذاكرة متجراورة تحرت
اسم محدد .بكالم اخر ،فان المصفوفة هي مجموعة من الكيانات (تسمى العناصرر)،
جميررع هررذل العناصررر مررن نرروع واحررد ويررتم خزنهررا فرري الررذاكرة فرري مواقررع متجرراورة،
وتعرف المصفوفة من خالل االسم الذي يسرند لهرا ويرتم اختيرار اسرم المصرفوفة وفقرا
لقواعد اختيار اسماء المتغيرات ،ويستخدم هذا األسم ل شرارة الرى المصرفوفة ولريس
الررى عناصررر المصررفوفة اذ ان عناصررر المصررفوفة يررتم االشررارة الررى كررل واحررد منهررا
باستخدام أسم المصفوفة متبوعا برقم الدليل (الفهرس) الذي يشير الى موقع العنصر
في المصفوفة.
تسررتخدم المصررفوفة كبررديل عررن االعررالن عررن عرردد مررن المتغيرررات المتشررابهة
النوع ،فمثال برنامج يحتاج الى عشرة متغيررات مرن نروع األعرداد الصرحيحة ،فبردال
من األعالن عن عشرة متغيرات وبأسماء مختلفة يمكرن ان تعلرن عرن هرذل العناصرر
كمصفوفة من نوع األعداد الصحيحة ،حجمها عشرة عناصرر ولهرا اسرم واحرد ،وان
33
هياكل البيانات باستخدام C++
كررل عنصررر مررن الممكررن ان يعامررل كمتغيررر مفرررد لرريس لرره عالقررة ببرراقي عناصررر
المصفوفة األخرى ويرتم االشرارة لره مرن خرالل أسرم المصرفوفة وموقرع العنصرر فري
المصفوفة.
الح هنا ان حجرم المصرفوفة يوضرع برين قوسرين مرربعين .هرذا االعرالن يعلرن عرن
ترتيرررب خطررري لمواقرررع المصرررفوفة (مواقرررع متجررراورة) ,ومرررن الممكرررن ان نصرررور
المصررفوفة بعررد ان يررتم االعررالن عنهررا كمررا فرري الشرركل ( 2.1الحر ان االعررالن عررن
المصفوفة يعني تحديد مواقع في الذاكرة خالية من القيم).
كل عنصر في المصفوفة من الممكن الوصول اليه مباشررة وذلرك مرن خرالل الموقرع
النسبي له في المصفوفة.
هياكل البيانات باستخدام C++
35
هياكل البيانات باستخدام C++
;]Char s [$
م حظةةة :قبررل اسررتخدام أي مصررفوفة احاديررة او متعررددة االبعرراد فرري البرنررامج ،يجررب
توفير المعلومات التالية الى المترجم او المفسر
-1نوع المصفوفة مثال )….(int, float, char
-2اسرررم المصرررفوفة (ويرررتم اختيرررارل مرررن المبررررمج ويفضرررل ان يررردل علرررى عمرررل
المصفوفة).
-3عرردد األبعرراد ( )subscriptفرري المصررفوفة (هررل المصررفوفة احاديررة او متعررددة
االبعاد).
-4العدد الكلي لمواقع الذاكرة المخصصة او بتحديرد أكثرر ،عردد العناصرر لكرل بعرد
من أبعاد المصفوفة.
وضع قيم بين قوسين متوسطين تفصل بين قيمة واخرى فارزة (بنفس طريقرة كتابرة
المجاميع) ومساواتها الى المصفوفة (تسند لها قيم ايناء كتابة البرنامج) كما يأتي:
; } ABC [ 5 ] = { 5, -77, 34, 60, 2
ويجب ان تنتبه الرى ان عردد القريم برين القوسرين المتوسرطين يجرب ان ال تزيرد
على عدد عناصر المصفوفة التي تم االعالن عنها في اعالن المصرفوفة (فراذا أعلرن
عن مصفوفة من خمسة عناصر ووضع بين القوسين المتوسطين سرتة قريم ،فري هرذل
الحالة سيصدر المترجم رسالة خطأ .القيم ستسند الى عناصر المصفوفة بالتتالي من
اليسررار الررى اليمررين (اي ان القيمررة فرري اقصررى اليسررار ( )5ستسررند الررى العنصررر فرري
الموقع ( ،)0والقيمة التي على يمينهرا ( )-77ستسرند الرى العنصرر فري الموقرع (..)1
وهكذا).
م حظةةة :عنرردما يررتم ابتررداء القرريم سرروف تسررند لعناصررر المصررفوفة C++ ،يسررمح
بإمكانيرة ترر االقرواس المربعرة فارغرة ][ .فري هرذل الحالرة ،فران المتررجم سرريفرض
حجم للمصفوفة يطابل عدد القيم الموجودة بين االقواس المتوسطة .مثال
; }ABC [ ] = {2, 5, 8
من الممكن انشاء المصفوفة وذلك بأسناد قيم لعناصرر المصرفوفة مرن لوحرة المفراتيح
عند تنفيذ البرنامج ،وهذل هي الطريقة االفضل ألنها تعني ان البرنرامج سريكون عرام
ألي قيم يمكن ادخالها وال يحدد برالقيم التري ترم كتابتهرا اينراء كتابرة البرنرامج كمرا فري
37
هياكل البيانات باستخدام C++
الطريقة السابقة .هذه الطريقة وهي االكثر شيوعا ستعتمد على حلقة التكررار إلسرناد
قيم لعناصر المصفوفة كما في المثال ادناه.
>#include<iostream
; using namespace std
{ )void main (void
; ]int A [7
; int i
) for ( i= 0 ; i <= 6 ; i++
; ]cin >> A [i
}
الح ر اننررا اسررتخدمنا حلقررة تكرررار بعرردد عناصررر المصررفوفة وذلررك لكرري يررتم
المرور على جميع مواقع المصفوفة ويسرند لهرا قريم .امرا طباعرة عناصرر المصرفوفة
فتتم بنفس الطريقة التي استخدمنا فيها حلقرة التكررار ألسرناد قريم لعناصرر المصرفوفة
مع تغيير ايعاز االدخال بإيعاز االخراج ..على ان تنتبه الى انه ال يمكنك طباعرة اي
عنصر من عناصر المصفوفة ذا لم تسبقه بعملية اسناد قيم لعناصر المصفوفة.
م حظة :المصفوفة االحادية عادة ودائما تبدأ بالموقع صفر.
مثال :برنامج يوضح طريقة اسناد وطباعة عناصر مصفوفة.
># include<iostream
;using namespace std
{ )void main (void
;}int A [7] ={11, 12, 13, 14, 15, 16, 17
; int i
; " cout<< " contents of the array: \n
)for (i=0 ; i <= 6; i++
; 'cout << a[i] << '\t }
هياكل البيانات باستخدام C++
ان من أكثر االخطاء التي تحدث هري عنردما تحراول خرزن عنصرر فري مواقرع
ماوراء حجم المصفوفة ،فمثال لو عرفت مصفوفة كما يأتي:
; ]int A [6
عندما تستخدم هرذل المصرفوفة فران دليرل المصرفوفة يترراوح برين ( ،)5-0فراذا
حددت الدليل بغير ذلك فان خطأ سيحدث .في معظم الحواسيب ال يوجرد تحرذير عنرد
39
هياكل البيانات باستخدام C++
اسررتخدام دليررل خررارج حجررم المصررفوفة ،كمثررال افرررض أنررك حررددت قيمررة الررى الرردليل
الموضح ادنال:
; A [7] = 255
هنررا الحاسرروب سرريعامل هررذا االمررر علررى انرره صررحيح ويضررع القيمررة 225فرري
العنرروان المناسررب فرري الررذاكرة ،ولكررن عنررد حسرراب موقررع او عنرروان هررذ القيمررة فأنهرا
ستكون في العنوان الذي يحوي المتغير ( )more_stuffكما في الشكل ,2.2ولذلك
فان هذل القيمة الخاصة بالمتغير ( )more_stuffسوف تتغير بشكل غير مقصود.
م حظة :ان حساب موقع اي عنصر من عناصر المصفوفة في الرذاكرة يتبرع
العالقة التالية:
عةةد بايتةةات نةو الموقةةع فةةي الةةذاكرة و ال نةةوال االبتةةدائي ي رقةةم الةةدلي
المصفوفة
العنرروان االبترردائي هررو عنرروان اول عنصررر بالمصررفوفة (فرري الشرركل 2.2هررو
1023للمصفوفة .)A
رقم الدليل هو موقع العنصر في المصفوفة مثال العنصر في الموقع .5
عدد بايتات النوع هو عدد البايتات التي تحدد لكل نوع في الذاكرة فمثال يحردد
بايتين لألعداد الصحيحة (من الممكن اربعة) وبايت للحروف وهكذا.
فمثال لو أردنا استخراج موقع الذاكرة ل يعاز ] A [7سيكون
1023 + 7 * 2 = 1037 العنوان =
موقع هذا العنصر في المصفوفة ،ويكون الرقم محدد برين قوسرين مرربعين ،كمرا فري
ادنال:
; ] Name [ index
هذل الصيغة تمثل قيمرة العنصرر ،وهري تكراف اسرم المتغيرر االعتيرادي وعلرى
هررذل الصرريغة با مكرران اجررراء كررل العمليررات الترري با مكرران اجراءهررا علررى المتغيررر
االعتيادي من ذلك النوع .فمثال ذا كنت ترغب بأسناد القيمة 45الى العنصر الثاني
في المصفوفة ( ،)ABCفسيتم ذلك كما يأتي:
; ABC [ 2 ] = 45
كما يمكنك ان تمرر هذل القيمة الى متغير اخر اعتيادي مثال ( ،)xوكما يأتي:
; ] x = ABC [ 2
41
هياكل البيانات باستخدام C++
; Myarray [ 3 ] = 0
; Myarray [ 4 ] = 0
م حظة :في C++ال يسمح بالعمليات البسيطة التي تتضمن كامل المصفوفة .حيرث
ان اسم المصفوفة يعامل كمتغيرر منفصرل للعمليرات مثرل عمليرة المسراواة (االسرناد)،
عمليات المقارنة ...وهكذا فمثالً لو كانت ) (A, Bمصفوفتان مرن نفرس النروع وذات
الحجم فأن عمليات االسناد والمقارنة يجب ان تجري فقط لعنصر مع عنصر اخر.
}int A[4]={2, 3, 4, 5
}int B[4]={1, 3, 5, 7
فالعمليات التالية مقبولة:
) ] * if (A [ 2 ] > B [ 2
;" cout<<" array are different \n
) ] * while ( A [ 1 ] == B [ 3
;" cout<<" AAAAAAAAAA \n
العمليات التالية غير مقبولة:
)* if (A == B
;" cout<<" array elements are equal \n
)* while (A > B
;" cout<<" array processing \n
البعدين سيكون لها رقما دليل بعد اسم المصفوفة ،والمصرفوفة ذات الثاليرة ابعراد لهرا
يالية ارقام دليل بعد اسم المصفوفة وهكذا .المصفوفات من الممكن ان يكون لهرا اي
عدد من االبعاد ،ولكننا سنكتفي في هذا القسرم بشررح المصرفوفة ذات البعردين فضرال
عن المصفوفة ذات البعد الواحرد النهمرا االكثرر اسرتخداما ،وجميرع المصرفوفات ذات
االبعاد االكثر تطابل المصفوفة ذات البعدين بالعمل .من االمثلرة الجيردة للمصرفوفات
الثنائيرة هرري رقعررة الشررطرنج ،حيرث تتكررون مررن يمانيررة صرفوف ويمرراني اعمرردة (كررل
صف يمثل مصفوفة احادية وكل عمرود يمثرل مصرفوفة احاديرة ايضرا) ،المصرفوفات
الثنائيررة تتكررون مررن صررفوف واعمرردة ترررقم الصررفوف ابتررداءا مررن الرررقم ( )0وترررقم
االعمدة ايضا ابتداءا من الرقم ( .)0وكل خلية في المصفوفة الثنائية تمثرل موقرع فري
الررذاكرة وبالتررالي سررتحمل قيمررة ،وكمررا فرري المصررفوفات االحاديررة فرران لكررل مصررفوفة
ينائية اسم وحيد تعرف به وهو اي اسم يتم اختيارل من المبرمج على ان يتبرع قواعرد
تسمية المتغيرات ،وبالتأكيد فران لكرل مصرفوفة ينائيرة نروع وهرو يمثرل نروع البيانرات
المخزنررة فرري المصررفوفة وبا مكرران اسررتخدام اي نرروع مررن االنررواع المقبولررة فرري لغررة
.C++
المصفوفات الثنائية لها اسرتخدامات كثيررة وهري تسراعد علرى تسرهيل التعامرل
مع بعض المسرائل المعقردة ..فمرثال لردينا عردد مرن المعامرل (ياليرة معامرل ..معمرل،1
معمل ،2معمل )3التي تنتج مواد كهربائية متشابهة مثرل (تلفزيرون ،يالجرة ،غسرالة،
مجمدة ،مكيف) فمن الممكن تمثيلها بمصفوفة ينائية والتعامرل معهرا علرى هرذا المبردأ
كما يلي:
43
هياكل البيانات باستخدام C++
االن لو سألنا كم غسالة انتجت فري المعمرل ..1بالتأكيرد سريكون الجرواب ،56
و ذا كان السؤال كرم مكيرف أنرتج فري المعمرل 3فسريكون الجرواب 12وهكرذا (عليرك
االن ان تستنتج الطريقة التي تتعامرل بهرا مرع عناصرر المصرفوفة) .حجرم المصرفوفة
هو (( )3×5اسماء االعمدة والصفوف في الشكل 2.3هي للتوضيح).
االعالن اعالل يمثل اعالن عن مصفوفة ينائيرة (عردد االقرواس المربعرة اينران
وهذا يعني انها ينائية) من نوع االعداد الصحيحة (اي ان جميع عناصرها مرن نروع
االعررداد الصررحيحة) ،تحررت اسررم ( )TestArrayوهرري تحترروي علررى ياليررة صررفوف
وخمسة اعمدة (اي ان عدد عناصرها الكلري يسراوي حاصرل ضررب عردد الصرفوف
في عدد االعمدة وسيكون مساوي .)3x5 = 15
م حظة :ال يجوز اطالقا تخصيص القوس المربع االول لألعمدة والثاني للصفوف،
الن المترررجم دائمررا ينظررر الررى القيمررة الترري فرري القرروس المربررع االول علررى انهررا عرردد
الصفوف ونفس الشيء للقوس المربع الثراني حيرث يعتبرر القيمرة التري فيره علرى انهرا
عدد االعمدة.
يتواجد به العنصر المطلوب ،اما القوس المربع الثاني فيشرير الرى رقرم العمرود الرذي
يوجررد فيرره العنصررر المطلرروب (انظررر الشرركل ،)2.4اذ ان العنصررر المطلرروب هرررو
العنصر المضلل وهرو موجرود بالصرف الثالرث والعمرود الثراني ،لرذلك فران الوصرول
ألي عنصر من عناصر المصفوفات الثنائية يكون بداللرة رقرم الصرف ورقرم العمرود
(ودائمررا القرروس المربررع االول يسررتخدم لرررقم الصررف والقرروس المربررع الثرراني لرررقم
العمود).
عند الوصول ألي عنصر من عناصر المصفوفة الثنائية فيمكنك التعامل معره
واجراء كافة العمليات التي تتناسب مع نوعه كأي متغير اعتيادي ،مثال
لغرض أسناد القيمة ) (56لعنصرر مرن عناصرر مصرفوفة ينائيرة (نفررض ان
العنصر هو في الموقع )3×5فيتم ذلك كما يأتي:
; TestArray [3][5] = 56
الح ر عنررد العمررل علررى عناصررر المصررفوفة ال تحترراج لتحديررد النرروع ألنرره تررم
تحديدل عند االعالن عن المصفوفة.
االن لو اردت طباعة قيمة هذا العنصر على الشاشة فسيكون كما يأتي:
45
هياكل البيانات باستخدام C++
في هذل الحالة فران اول يرالث قريم يرتم اسرنادها الرى المواقرع الرثالث فري الصرف )(1
وياني يالث عناصر تسند الى المواقع الثالث في الصف الثاني وهكذا.
ويمكرررن ان تكرررون مجررراميع يالييرررة ضرررمن المجموعرررة الرئيسرررة وتسرررندها
للمصفوفة وكما يأتي:
int theArray [5][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}, {13, 14,
; } }15
المترجم سيهمل االقواس الداخلية ،هي توضع للمسراعدة فري فهرم توزيرع القريم
بشكل سهل.
با مكان اسناد قيم الى عناصر المصفوفة باستخدام لوحة المفاتيح ايناء تنفيذ
البرنررامج وذلررك باسررتخدام حلقترري تكرررار متررداخلتين ،الحلقررة الخارجيررة تعمررل كعررداد
هياكل البيانات باستخدام C++
للصفوف (تضع مؤشر على الصرفوف) بينمرا الحلقرة الداخليرة تعمرل كعرداد لألعمردة
(تضع مؤشر على االعمدة)( ،بكالم اخر فان حلقتي التكررار سرتعمالن علرى وضرع
قيم للصفوف بالتتابع اي يتم وضع قيم لعناصر الصرف ( )1ابترداءا مرن العمرود ()1
الى العمود االخير يم ينتقل الى الصف الثاني وهكذا.
مثال :برنامج لقراءة مصفوفة ينائية بإدخال قيم عناصر من لوحة المفاتيح.
>#include <iostream
;using namespace std
)(int main
{
; ]int SomeArray[5][4
)for (int i = 0; i<5 ; i++
)for (int j =0; j<4 ; j++
;return 0
}
2.6.4طباعة المصفوفة
يستخدم نفس البرنامج السابل لغرض طباعة عناصر المصرفوفة علرى ان يرتم
ابدال امر االدخال بامر االخراج وكما يأتي:
• مثال برنامج لقراءة وطباعة عناصر مصفوفة ينائية.
>#include <iostream
;using namespace std
47
هياكل البيانات باستخدام C++
)(int main
{
; ]int SomeArray[5][4
)for (int i = 0; i<5; i++
)for (int j = 0; j<4; j++
}
الح في المثال السرابل اليمكرن اسرتخدام اوامرر االخرراج مرالم يرتم اسرناد قريم
لعناصر المصفوفة باحدى طرق اسناد القيم المبينة اعالل.
مثال :برنامج لقراءة مصفوفة اعداد صحيحة احادية حجمها ( 100عنصرر)،
وايجاد العدد االكبر في المصفوفة.
>#include<iostream
;using namespace std
Larg = B[0];
for (i=0 ; i <=99 ; ++i)
if (Larg < b[i])
Larg = b[i] ;
cout<<" largest value in the array = "<< Larg ;
main( ){
int D[row][col] ;
for ( int i=0 ; i< 5 ; i++ )
for ( int j =0 ; j< 5 ; j++ )
cin>>D [i][j] ;
for ( i =0 ; i < 5 ; i++ )
cout << D[i][i] << endl ;
return 0;
49
C++ هياكل البيانات باستخدام
51
هياكل البيانات باستخدام C++
هياكل البيانات باستخدام C++
الفص الثالث
الترتيب SORTING
المقدمة 3.1
الترتيب يشير الرى تنظريم البيانرات وفقرا لصريغة او نظرام معرين .خوارزميرة الترتيرب
تحدد الطريقة لتنظيم البيانات بترتيب معين .غالبيرة عمليرات الترتيرب المعروفرة هري
تعتمد على ترتيب االعداد او المعاجم (الحروف).
ان اهمية عملية الترتيب تأتي من حقيقرة ان بحرث البيانرات يكرون بمسرتوى عرال مرن
االمثلية ذا كانت البيانات مرتبرة بطريقرة معينرة .كرذلك فران الترتيرب يسرتخدم لتمثيرل
البيانات بصيغ أكثر قابلية للقراءة .بعض االمثلة للترتيب في حياتنا هي:
دليل الهاتف ..حيث ان دليل الهراتف يحفر ارقرام الهواترف لألشرخاص مرتبرة حسرب
اسمائهم .وبهذا فمن الممكن ان نبحث عن االسماء واالرقام بسهولة.
القاموس ..والقاموس هرو لحفر الكلمرات مرع معانيهرا ،مرتبرة بترتيرب ابجردي ولرذلك
فان البحث عن اي كلمة يكون سهل.
خوارزميات الترتيب ربما تتطلب بعض مساحات الخزن االضافية للمقارنة والخزن
المؤقررت لرربعض العناصررر القليلررة مررن البيانررات .هررذل الخوارزميررات الترري ال تتطل رب
مساحة خزن اضافية ،وعملية الترتيرب تحردث فري المكران او علرى سربيل المثرال فري
المصفوفة نفسها ،هذل تسرمى ترتيرب فري المكران ) .(in-place sortingمثرال علرى
ذلك ترتيب الفقاعات .Bubble sorting
لكن في خوارزميات ترتيب اخرى ،فران البرنرامج يحتراج الرى مسراحة ربمرا تزيرد او
مساوية الى العناصر التي يراد ترتيبها .الترتيب الذي يحتاج الى مسراحة مسراوية او
أكثررر يسررمى ترتيررب لرريس فرري المكرران .not-in-place sortingمثررال علررى ذلررك
طريقة الترتيب بالدمج Merge-sort
53
هياكل البيانات باستخدام C++
ان خوارزمية الترتيب التي لم تغير ترتيب العناصر المتشرابهة حسرب ظهورهرا بعرد
اجراء ترتيب المحتويات ،تسمى خوارزمية الترتيب المستقر كما في الشكل كمرا فري
الشكل .3.1
امررا ذا كانررت خوارزميررة الترتيررب ،بعررد ترتيررب المحتويررات ،تغيررر ترتيررب العناص رر
المتشابهة حسب ظهورها ،فهذل تسمى ترتيب غير مستقر كما في الشكل .3.2
ووارزميةات الترتيةب الم ئمةة وغيةر الم ئمةة Adaptive and non- 3.3
Adaptive Sorting Algorithms
خوارزميررة الترتيررب يقررال عنهررا مناسرربة او مالئمررة ذا اسررتفادت مررن ميررزة العناصررر
المرتبررة اصررال فرري القائمررة المررراد ترتيبهررا .عليرره ،عنررد الترتيررب ذا كانررت القائمررة
االصررلية لهررا عناصررر مرتبررة مسرربقا ،فرران الخوارزميررة المناسرربة سرروف تأخررذ بنظررر
االعتبار هذل العناصر وال تعيد ترتيبها يانية.
بينمررا الخوارزميررة غيررر المناسرربة هرري الخوارزميررة الترري ال تأخررذ بنظررر االعتبررار
العناصر التي هي مرتبة مسبقا .فهي تحاول اجبار كل عنصرر عرادة ترتيبره للتأكرد
من عملية الترتيب.
3.4مصبلحات مهمة Important Terms
بعض المصطلحات التي صيغت بشكل عام عند مناقشة تقنيات الترتيب:
ترتيب تصاعدي Increasing order
متواليررة مررن القرريم يقررال عنهررا مرتبررة تصرراعديا ذا كرران العنصررر الالحررل أكبررر مررن
العنصر السابل .مثال 1,2,3,4,5هي مرتبة تصاعديا ،حيث كرل عنصرر هرو أكبرر
من سابقه.
ترتيب تنازلي Decreasing order
متوالية من القيم يقال عنها مرتبة ترتيب تنازلي ذا كان العنصر الالحل أصرغر مرن
العنصر الحالي .كمثال 9, 8, 7, 6, 4, 1هذل بترتيب تنازلي ،حيث كل عنصر فري
المتوالية أصغر من العنصر الذي يسبقه.
55
هياكل البيانات باستخدام C++
طبيعة الترتيب ان كانتا غير مرتبة (بمعنى ذا كان المطلروب ترتيرب تصراعدي فران
العنصر االصغر يجب ان يكون في البداية) .هذل الخوارزمية غير مناسبة لمجموعة
بيانات كبيرة.
لتوضيح هذل الخوارزمية سنلجأ لمثال بحيث نقتررح مجموعرة مرن القريم غيرر مرتبرة
ويتم خزنها بمصفوفة (عادة يفضرل لترتيرب ارقرام ان تخرزن هرذل االرقرام بمصرفوفة
ألنها تسهل العمل) ونحتاج الى ترتيبها ترتيب تصاعدي
قائمة االرقام هي )(20, 3, 17, 19, 25, 35, 9, 42, 16, 27
فسيكون عمل خوارزمية الفقاعة كما يلي:
عمليررة الترتيررب سررتتم بعرردة مراحررل ،عرردد هررذل المراحررل غيررر محرردد ويختلررف حسررب
طبيعرررة القررريم المرررراد ترتيبهرررا ،الخوارزميرررة كمرررا سررربل وقلنرررا سرررتقارن كرررل عرررددين
متجاورين وتبدل بينهما ذا كان التبديل ضروري ،وتستمر بهذل العملية لحين اكمرال
جميع القيم في المصفوفة عندها تكون المرحلة االولى قد انتهت وتبدأ مرحلة جديردة.
تنتهي خوارزمية الترتيب عندما نصل الى مرحلة ال يوجد فيها تبديل بين قيمتين.
االن لننرراقش المرحلررة االولررى لهررذل القرريم ونسررتعين بالشرركل ,3.3بدايررة سرريتم مقارنررة
العررددين ) (20, 3ولمررا كانررت 3أصررغر مررن 20لررذلك تررتم عمليررة التبررديل لتكررون 3
بالموقع االول و 20بالموقع الثاني .بعدها تتم مقارنة العرددين ) (20, 17وحيرث ان
17أصغر من 20تتم عملية التبديل.
م حظة :عملية المقارنة تبدأ بالعنصرر الرذي فري الموقرع ) (0مرن المصرفوفة ليقرارن
مررع العنصررر الررذي فرري الموقررع ) .(1يررم ينتقررل المؤشررر الررى العنصررر فرري الموقررع )(1
ليقارن مع العنصر في الموقع ) .. (2وهكذا.
االن تقررارن ) (20, 19وحيررث ان 19أصررغر مررن 20فكررل واحرردة تبرردل الررى موقررع
االخرى.
) (20, 25هنا 20اصغر من 25لرذلك ال يوجرد تبرديل .وكرذلك ) (25, 35ال يوجرد
تبديل.
هياكل البيانات باستخدام C++
العددين المتجاورين ) (35, 9يتم بينهم التبديل الن 9أصرغر مرن (35, 42) ,35ال
يوجد بينهم تبديل.
) (42, 16تجرى بينهم التبديل الن 16اصغر من .42
واخر التبديالت في المرحلة االولى ) (42, 27يتم بينهم التبديل الن 27أصرغر مرن
.42
ونظرررا للوصررول الررى العنصررر االخيررر فرري المصررفوفة فرران هررذل المرحلررة (المرحلررة
االولى) قد انتهت وسنبدأ مرحلة يانية تتم فيهرا المقارنرة مرن اول عنصرر ونتبرع ذات
الخطرروات اعررالل .تسررتمر هررذل العمليررات لحررين ان نصررل مرحلررة ال يوجررد فيهررا تبررديل
بذلك تكون جميع العناصر قد تم ترتيبها .الشكل 3.3يوضح الخطروات الكاملرة لهرذل
الخوارزمية.
{
{
{
{
57
هياكل البيانات باستخدام C++
}}}}
59
هياكل البيانات باستخدام C++
3.6.2كيف ت م هذ الالوارزمية
لكي نفهم طريقة الحشر بشكل جيد سنستعين بالشكل ,3.4وهو بداية يمثرل مصرفوفة
مررن القرريم الموزعررة بشرركل عشرروائي ،والمطلرروب ان يررتم ترتيررب هررذل القرريم ترتيررب
تصرراعدي او تزايرردي .هنررا فرري اي موقررع ) (iمررن المصررفوفة (الحر ان المتغيررر ),(i
الررذي يظهررر بجانررب المصررفوفة فرري الشرركل ) (3.4يمثررل موقررع العنصررر الررذي سرريتم
مقارنته واضافته الى القائمة الجزئية المرتبة .كذلك فان القائمة الجزئية المرتبرة هري
تبدأ بالنمو من بدايرة المصرفوفة كمرا فري الشركل .)3.4مرثال فري الموقرع صرفر هنرا
القيمة 3وهري اول عنصرر فري القائمرة الجزئيرة .عنردما ننتقرل الرى الموقرع (i=1) 1
حيث سيكون دور العنصر في الموقع 1لالضافة الى القائمة الجزئية ،هنا الموقرع 1
في المصفوفة هو ذا قيمة تساوي 1وتتم مقارنتها مع عناصر القائمة الجزئية يجاد
المكرران المناسررب لهررا فرري القائمررة الجزئيررة ،وحيررث ان القائمررة الجزئيررة تحترروي علررى
هياكل البيانات باستخدام C++
القيمة 3فقط ،ونظرا الن القيمة 1أصغر من 3لذا تتم عملية تزحيرف القيمرة 3الرى
اليمين بمقدار موقع واحرد (المقصرود تزحيفره الرى الموقرع المجراور فري المصرفوفة).
وتصبح القائمة الجزئية المرتبة تحتوي على القيمتين ).(1, 3
االن جاء دور ) (i=2وفي هذا الموقع القيمة ,4ونظررا الرى ان القيمرة 4هري ضرمن
الترتيب لذلك ال تنفذ عملية التزحيف وتضاف هذل القيمرة الرى نهايرة القائمرة الجزئيرة
المرتبة.
عندما تكون ) (i=3فان القيمة في هذا الموقع هي 1لذلك ستقارن مع القيم التي فري
ذيرل المصررفوفة الجزئيررة المرتبررة ابتررداءا وكررل قيمررة اكبررر منهررا تزحررف لليمررين موقررع
واحد ,ستقارن مع القيمة 4ونظرا الن 1اصغر من 4لذا تتم عمليرة تزحيرف القيمرة
4الررى الموقررع االيم رن المجرراور لموقعهررا الحررالي وهررو الموقررع (( )3الح ر هنررا ان
موقعها الحالي ( )2اصبح خاليا) ,وكما سبل واشرنا فان هذل القيمة تقارن مع جميرع
قرريم المصررفوفة الجزئيررة لحررين ان يررتم وضرعها او حشرررها فرري موقعهررا المناسررب ,لررذا
ستقارن مع القيمة 3التري هري فري الموقرع ,1وايضرا تجررى عمليرة التزحيرف حيرث
تزحف القيمة 3الى الموقع الخالي على يمينها ( )2ويصبح موقعهرا ( )1هرو الفرار
او الخررالي ,وعنررد مقارن رة القيمررة 1مررع القيمررة 1فسررتكون القيمترران متسرراويتان لررذلك
توضع القيمة 1في الموقع الخالي (.)1
هذل العملية تستمر لحين الوصول الى نهاية القائمة وبالتالي سنحص على قائمرة مرن
القيم مرتبة ترتيب تصاعدي.
61
هياكل البيانات باستخدام C++
{
{
{
;j--
}
;AB[ j ] = index
}}
63
هياكل البيانات باستخدام C++
3.7.2كيف ت م الالوارزمية
في هذل الخوارزمية فان عملية الترتيرب ترتم بعردد مرن المراحرل تصرل الرى )(n – 1
حيث ان ) (nتمثل عدد العناصر في المصفوفة.
هياكل البيانات باستخدام C++
سنوضررح عمررل هررذل الخوارزميررة باسررتخدام الشرركل ,3.5فرري المرحلررة االولررى سرريتم
اختيار العنصر االول ويفرض علرى انره االصرغر ولرذلك يوضرع بمتغيرر لنقرل اسرمه
) ،(Minهذا العنصر الذي قيمته 20ستتم مقارنته مع جميرع العناصرر االخررى فري
المصفوفة ،واول عنصر هو العنصر الذي قيمته ,12ولما كانت 12أصغر من 20
لذلك ستتم عملية التبديل ويوضع كل عنصر بموقع االخر (بمعنى ان التبديل يتم بين
موقع المتغير )( (Minوهو الموقع صفر) وموقع العنصر الرذي قيمتره أصرغر وهرو
الموقررع االول .بهررذا ،ان المتغيررر Minسرريحمل القيمررة 12برردل مررن 20بعررد اجررراء
التبديل) .تستمر عملية المقارنة وسيكون العنصرر االخرر الرذي يقرارن مرع Minهرو
,10ولما كانرت 10أصرغر مرن 12لرذا ترتم عمليرة التبرديل بحيرث كرل عنصرر يحترل
موقع العنصر االخر ،والمتغير Minستكون قيمته هي .10
تستمر عملية المقارنة وهذل المرة تكون المقارنة مرع القيمرة 15وحيرث ان 15أكبرر
من 10تبقى كل قيمة على وضعها وال تجرى عملية تبديل .واخيرا جاء دور القيمرة
االخيرة والتي قيمتها 2وتقارن مرع ,Minونظررا الن 2أصرغر مرن 10ترتم عمليرة
التبديل ولتكن كل قيمة بموقرع االخررى كمرا فري الشركل ,3.5ويصربح المتغيرر Min
يحمل القيمة .2
بهذا انتهت المرحلة االولى ،وحان دور المرحلرة الثانيرة والتري فري هرذل الحالرة سريتم
وضع القيمة في الموقع الالحل وهو موقع 1في المصفوفة في المتغير ,Minوهري
القيمرررة .20وكمرررا فررري المرحلرررة االولرررى ،تعررراد نفرررس الخطررروات لعمليرررات المقارنرررة
والتبديل ،وهكذا لكل المراحل الالحقة وكما واضح في الشكل .3.5
65
C++ هياكل البيانات باستخدام
int min = i;
min = j ;
AB [ i ] = AB [min];
AB [min] = temp;
}}
هياكل البيانات باستخدام C++
ترتيب الدمج هري تقنيرة تعتمرد علرى تقنيرة ( ،)divide and conquerوهري واحردة
من الخوارزميات المعتبرة.
ترتيب الدمج يقسم المصفوفة الى نصفين متساويين وبعدها تتم عملية دمجهم بطريقة
مرتبة.
الخوارزمية ببساطة تقسم المصفوفة بشكل متكرر الرى نصرفين ،يرم تقسرم النراتج الرى
نصفين وهكذا لحين الحصول على عناصرر صرغيرة ال يمكرن تقسريمها أكثرر .عمليرة
التقسيم هذل تحاف على ترتيب هذل العناصر في المصفوفة االصلية.
بعررد عمليررة التقسرريم تررتم عمليررة الرردمج بررنفس طريقررة التقسرريم اي ان كررل عنصرررين
متجاورين ناتجة من التقسيم تدمج يانية لكن وفل شرط وهو ان تكون هرذل العناصرر
مرتبررة ،بمعنررى ان تررتم مقارنررة قرريم هررذين العنصرررين وترتررب ترتيررب تصرراعدي او
تنازلي (اجراء التبديل بينهم ذا استوجب) ومن يم تدمج.
في عملية الدمج الثانية تدمج كل قائمتين متجاورتين مع بعضهما (هنا كرل قائمرة لهرا
عنصرررين) ،مررع االخررذ بنظررر االعتبررار بترتيررب قرريمهم .وهكررذا تسررتمر العمليررة لحررين
اعادة الدمج لكامل القائمة.
67
هياكل البيانات باستخدام C++
3.8.2كيف ت م الالوارزمية
في الشكل 3.6مصفوفة من االعداد غير المرتبة .لكي ننفذ عليها طريقرة الردمج نبردأ
بتقسيم المصفوفة والتي تتكون من 8عناصر الى نصرفين ،بحيرث يكرون كرل نصرف
يحترروي علررى 4عناصررر كمررا واضررح فرري المرحلررة الثانيررة مررن الشرركل .3.6وفرري
المرحلة الثالثة ايضا يتم التقسيم لنحصرل علرى أربرع مجراميع كرل منهرا يحتروي علرى
عنصررين فقرط .وفرري المرحلرة الرابعررة نقسرم جميررع المجراميع وسنحصررل علرى يمرران
مجرراميع كررل منهررا يحترروي عنصررر واحررد ،ونظرررا الررى ان هررذل المجرراميع غيررر قابلررة
لالنقسام اكثر لذلك يتوقف التقسيم.
االن حرران دور الرردمج وبررنفس طريقررة التقسرريم وبشرركل عكسرري تررتم عمليررة دمررج كررل
عنصرين متجاورين مع ضرورة ان تكون العناصرر فري مرحلرة الردمج مرتبرة (هنرا
ترتيب تصاعدي).
الح سيتم دمج ) (56, 29ونظرا الن هذين العنصرين غير مرتبة لذلك ترتم عمليرة
التبديل يم الدمج وسنحصل على ) ،(29, 56نفس الشيء للعناصرر التري بعردها وهرم
) (35, 42يتم دمجهم دون الحاجة للتبديل نظرا ألنها مرتبة .العنصرين االخرين هم
) (15, 41تدمج دون تبديل ألنها مرتبة .العناصر ) (75, 21يتم ترتيبهم يرم دمجهرم
لتكون هذل المجموعة ) .(21, 75بهذا تكون المرحلة الرابعة قد انجزت.
في المرحلة الخامسة يرتم دمرج المجراميع الناتجرة والتري عرددها أربرع مجراميع لنكرون
منهم مجموعتين ،في هذل المرحلة ايضا من المفروض ان يتم التبرديل برين العناصرر
االربع ذا استوجب ذلك .واخيرا في المرحلة السادسة يتم دمج المجموعتين لنحصل
على قائمة مرتبة.
هياكل البيانات باستخدام C++
م حظةةة :عنرردما يكررون عرردد عناصررر المصررفوفة لرريس زوجرري وبالتررالي فرران عمليررة
التقسيم ال تتم بتقسيم المصفوفة الى قسمين متساويين كمرا فري الشركل ,3.6لرذلك فري
هذل الحالة سيتم التقسيم الى قسمين غير متساوين وتنجز كل االعمال بشكل اعتيادي
كما سبل ووضحنا ،الشكل 3.7يوضح هذل الحالة.
69
هياكل البيانات باستخدام C++
شك :3.7مالبط توضيحي لتنفيذ ووارزمية الدم عندما يكول عد عناصر المصفوفة فر ي.
{
;int i, j, k
; int n1 = m
;int n2 = r - m
;i = 0 ابتداء الدليل (الفهرس) الى المصفوفة الثانوية االولى //
;j = 0 ابتداء الدليل (الفهرس) الى المصفوفة الثانوية الثانية //
;k = l ابتداء الدليل (الفهرس) الى مصفوفة الدمج الثانوية //
{
{
;]arr[k] = L[i
;i++
}
else
{
;]arr[k] = R[j
;j++
}
71
هياكل البيانات باستخدام C++
;k++
}
*/استنساخ العناصر المتبقية للمصفوفة ][ ,Lاذا كان هناك عناصر */
{
;]arr[k] = L[i
;i++
;k++
}
{
;]arr[k] = R[j
;j++
;k++
}
}
* l /هو الفهرس االيسر r ,هو الفهرس االيمن للمصفوفة الثانوية المطلوب ترتيبها */ arr
{
{
;int m = l+(r-l)/2
;)mergeSort(arr, l, m
;)merge(arr, l, m, r
}
}
ان قيمة dدائما هي عدد صحيح ,وان Nتمثل عدد عناصر المصفوفة.
73
هياكل البيانات باستخدام C++
هذل الخوارزمية كفروءة لمجراميع البيانرات متوسرطة الحجرم وهري فري اسروأ الحراالت
تكون درجة التعقيد لها )) (complexity is O(nحيث ان nتمثل عدد العناصر.
3.9.2كيفية عم الالوارزمية
عملية الترتيب بطريقة شل تعتمد اساسا على حساب قيمة ) (dوالتي يتم حسابها فري
كل مرحلة ،ان هذل المسافة سيتم االعتماد عليها لتحديرد العناصرر التري يرتم مقارنتهرا
مررع بعررض .لتبسرريط الموضرروع دعنررا نرررى الشرركل 3.8والررذي يمثررل مصررفوفة مررن 6
عناصر موزعرة بشركل عشروائي .بدايرة يرتم حسراب قيمرة المسرافة ) .(dفري المرحلرة
االولى تحسب المسافة وفقا للعالقة ادنال ،اما في المراحل الالحقة فيتم تنصريف قيمرة
المسررافة فرري كررل مرحلررة (كررل مرحلررة لهررا قيمررة مسررافة تسرراوي نصررف قيمررة مسررافة
المرحلة السابقة).
d = (N + 1 ) / 2
d = (6 + 1 ) / 2 = 3
هذا يعني ان العنصر االول سيتم مقارنته مع العنصر الرابع ،والعنصر الثاني يقارن
مع العنصر الخامس ،والعنصر الثالث يقارن مع العنصر السادس.
عند مقارنة العنصر االول مع العنصر الرابرع ) (86, 84نالحر ان 84أصرغر مرن
86لررذلك يررتم التبررديل بحيررث كررل عنصررر يبرردل الررى موقررع العنصررر االخررر (ترتيررب
تصاعدي).
هياكل البيانات باستخدام C++
مقارنة العنصر الثاني مع العنصر الخامس ) (94, 69ايضا ترتم عمليرة تبرديل وذلرك
الن 69أصغر من .94
الحالررة االخرررى مقارنررة العنصررر الثالررث مررع العنصررر السررادس ) (76, 91هنررا كررل
عنصر يبقى بمكانه دون تبديل الن العنصرين مرتبين.
75
هياكل البيانات باستخدام C++
{
//perform the insertion sort for this section
)]if (temp < array[j-increment
{
;]array[j] = array[j-increment
}
else
{
;break
}
}
;array[j] = temp
}
}
هررذل الخوارزميررة كفرروءة جرردا لمجموعررة البيانررات الكبيرررة ،علمررا برران اسرروأ حالررة لهررا
لتعقيرد الوقرت هرو )) (Time complexity is O (n log nحيرث ان nهرو عردد
العناصر في المصفوفة.
77
هياكل البيانات باستخدام C++
سيكون باالتجال المعاكس اي يبدأ من اخر موقع في المصفوفة ويتجه باتجرال اليسرار
اي نررزوال باتجررال بدايررة المصررفوفة سرريحتوي علررى القرريم الترري هرري أكبررر مررن قيمررة
المحرور .لتنفيررذ ذلررك سنضررع مؤشرررين أحرردهما فري بدايررة القائمررة واالخررر علررى اخررر
عنصر في القائمة .كما في الشكل .3.9
المؤشر االيمن سيتحر الى اليسار كما سبل وأشرنا ليمر على عناصر المصفوفة
واحد بعد االخر ،فاذا كان العنصر الذي يمر عليه أكبر من المحرور يتركره ويسرتمر
بالحركررة ،و ذا كرران أصررغر مررن قيمررة المحررور يقررف عليرره ،بالمقابررل المؤشررر االيسرر
يتحر باتجال اليمين ليتوقف عند اول عنصر يصادفه أكبر من قيمة المحور.
في مثالنا هذا ،يبدأ المؤشرين بالحركة فيتحر المؤشر االيمن ويقف عنرد اول قيمرة
تصادفه وهي القيمة ) (49والتي هي أصغر من قيمة المحور .بالمقابل فان المؤشرر
االيسر ايضرا يتحرر ويقرف عنرد القيمرة ) (70وذلرك ألنهرا أكبرر مرن قيمرة المحرور.
عليه سيتم ابدال هاتين القيميتين واحدة بمكان االخرى.
بعررد اجررراء التبررديل تسررتمر حركررة المؤشرررات ويتحررر المؤشررر االيمررن ليقررف عنررد
القيمة ) (16ألنها أصغر من قيمرة المحرور ،بينمرا المؤشرر االيسرر يقرف عنرد القيمرة
) (97ألنهررا أكبررر مررن قيمررة المحررور .تجرررى عمليررة التبررادل بيررنهم .وتسررتمر حركررة
المؤشرات فيقف المؤشر االيمرن عنرد القيمرة ) (55ألنهرا أصرغر مرن قيمرة المحرور،
والمؤشر االيسر سيقف عند القيمرة ) (63كونهرا أكبرر مرن قيمرة المحرور وايضرا ترتم
عملية التبديل .تستمر هذل العملية كما واضح في الشكل 3.9لحين ان يصل المؤشر
االيمررن الررى القيمررة ) (9والمؤشررر االيسررر يصررل الررى القيمررة ) ،(76وحيررث ان قيمررة
المؤشرر االيمرن (اي رقررم الموقرع الررذي يقرف عليره فرري المصرفوفة) أصررغر مرن قيمررة
المؤشر االيسر لذلك تتوقف الخوارزمية.
وبهررذا فأنن را قسررمنا المصررفوفة الررى قسررمين تكررون قرريم القسررم االول الررذي فرري بدايررة
المصفوفة هي أصغر من قيم عناصر القسم الثاني الذي هو في نهاية المصفوفة كما
واضح من الشكل .3.9
79
هياكل البيانات باستخدام C++
خطرروة واحرردة اضررافية وهرري ان القيمررة الكبيرررة الترري يؤشررر عليهررا المؤشررر االيسررر
ستوضع في الموقرع االخيرر الفرار ،ليصربح موقعهرا خرالي حيرث ستوضرع بره قيمرة
المحور والذي سيكون في موقعه الصحيح.
بعررد هررذل المرحلررة سرريتم اسررتدعاء خوارزميررة الترتيررب السررريع لترتيررب قرريم النصررف
االول من المصفوفة والتي قيمها اقل من )( (57طبعرا هرذا االسرتدعاء هرو اسرتدعاء
ذاتي ويتم تكرارل لحين اكمال ترتيب كل العناصر).
ايضرررا سررريتم اختيرررار يرررالث قررريم لتحديرررد قيمرررة المحرررور ) (pivotلهرررذا الجرررزء مرررن
المصفوفة ،هنا القيم الثالث هي ) (24, 38, 9وبذلك فستكون قيمة المحرور تسراوي
.24
ونظررا الن القرريم 9أصرغر مررن قيمرة المحررور فسريتم نقلهررا الرى الموقررع صرفر ويبقرى
موقعها فار لحين انتهاء عملية ترتيب هذا الجزء من المصفوفة.
ايضا يستخدم مؤشرين ايمن وأيسر فيتحر المؤشر االيمن ليقف عند القيمرة ),(21
بينمرررا يقرررف المؤشرررر االيسرررر بعرررد ان يتحرررر علرررى القيمرررة ) ,(49يرررتم ابررردال القررريم
) (21, 49واحد بدل االخر .يستمر المؤشران بالحركرة ليقرف المؤشرر االيمرن علرى
القيمة ) ،(16ويقف المؤشر االيسر على القيمة ) ،(38وتجرى عملية التبديل.
الح هنا ان المؤشرين أصبحا بموقعين متعاكسين بمعنرى ان قيمرة المؤشرر االيسرر
أكبر من قيمة المؤشر االيمن ،لذلك نتوقف.
فرري هررذل الحالررة ونظرررا الن القيمررة الترري يؤشررر عليهررا المؤشررر االيسررر كبيرررة فسريتم
تحريكها ونقلها الى الموقع الخالي في نهايرة هرذا القسرم مرن المصرفوفة ،ويرتم وضرع
قيمة المحور في مكانها الذي أصبح خاليا.
االن سيتم استدعاء دالة الترتيب السريع على النصرفين المتولردين اخيررا مرن نصرف
المصفوفة التي قيمها أصغر من ,57وتعاد نفس الخطوات ألجل ترتيبها.
هياكل البيانات باستخدام C++
نفس الشيء يتم بالنسبة لعناصر القسرم الثراني مرن المصرفوفة والتري قيمهرا أكبرر مرن
57لغررررض ترتيرررب عناصررررل .متابعرررة الشررركل 3.9يسررراعد علرررى فهرررم أفضرررل
للخوارزمية.
81
هياكل البيانات باستخدام C++
هياكل البيانات باستخدام C++
83
هياكل البيانات باستخدام C++
هياكل البيانات باستخدام C++
85
هياكل البيانات باستخدام C++
هياكل البيانات باستخدام C++
87
C++ هياكل البيانات باستخدام
/* * التقسيم/
while (i <= j) {
while (arr[i] < pivot)
i++;
while (arr[j] > pivot)
j--;
if (i <= j) {
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
}
/* * االستدعاء الذاتي/
if (left < j)
quickSort(arr, left, j);
if (i < right)
quickSort(arr, i, right);
}
هياكل البيانات باستخدام C++
3.11البحث Searching
اسررتعادة المعلومررات هرري واحرردة مررن اهررم تطبيقررات الحاسرربات .عررادة تتضررمن اعطرراء
جررزء مررن المعلومررات تسررمى المفترراح ,ويررتم السررؤال عررن ايجرراد القيررد الررذي يحترروي
معلومات مشتركة اخرى .هذل تنجز بالذهاب اوال الى القائمة لمعرفة ذا كان المفتاح
موجود او ال ،هذل العملية تسمى بحث .searchingانظمة الحاسوب تستخدم لخزن
كميات كبيررة مرن البيانرات ،وتبررز دائمرا الحاجرة السرتعادة قيرود مفرردة وفقرا لربعض
صفات البحث .ان عملية البحث عن عنصر معين في هياكل البيانات من الممكرن ان
يكون بسيط او معقد جدا.
البحررث مررن الممكررن ان يكررون علررى شرركل هياكررل بيانررات داخليررة او هياكررل بيانررات
خارجيررة .اسررتعادة المعلومررات حسررب الصرريغة المطلوبررة هرري الفعاليررة المركزيررة لكرل
تطبيقات الحاسبات.
هنررا طرررق بحررث مختلفررة وهرري بشرركل عررام مررن الممكررن ان نجملهررا بنرروعين ،وهمررا
البحث الخطي والبحث غير الخطي.
89
هياكل البيانات باستخدام C++
م حظة :خوارزمية البحث المثالية هي التي تصل الى البيانات المرغوبة بأقرل عردد
من المقارنات الممكنة.
م حظة :عملية البحث تنتهي بواحدة مرن نتيجترين ،واحردة للبحرث النراجح واالخررى
للبحث غير الناجح.
مثةال :فري الشركل 3.10فران البحرث عرن القيمرة ) (45تعيرد (غيرر موجرود) ..وهرذل
عملية بحث عير ناجح.
امررا البحررث عررن القيمررة ) (12سررتعيد الرررقم ) (4وهررو يمثررل موقررع القيمررة الترري تررم
ايجادها ..وهذل عملية بحث ناجحة.
91
هياكل البيانات باستخدام C++
الالبةةوة الثانيةةة :ايضررا نحرراول ايجرراد المنتصررف للنصررف االعلررى مررن المصررفوفة،
ونقارن.
موقرع الوسرط = ( 6 = (8 + 5) / 2دائمرا الكسرور تقررب الرى اقررب عردد صرحيح
باستخدام عملية تدوير فمثال 6.5تقرب الى 6وهكذا).
االن ،القيمة في موقع الوسط هي 77كما فري الشركل ,3.13ونظررا الن 44اصرغر
من 77لذلك فانا الخطوة الالحقة ستكون البحرث فري النصرف االدنرى مرن المصرفوفة
الجديرردة ،وسرريكون الموقررع االعلررى هررو (( )5الح ر هنررا ان الموقررع االعلررى سرريكون
الوسط ،)1-بينما الموقع االدنى سيكون (.)5
م حظة :عندما نختار النصف االدنى مرن المصرفوفة فران الموقرع االدنرى للمصرفوفة
هو ذات الموقع االدنى لهذا النصف من المصفوفة ،بينما الموقع االعلرى لهرا يسراوي
(موقع الوسط .)1 -
اما ذا اخترنا النصف العلوي من المصفوفة فان الموقع االعلرى للمصرفوفة هرو ذات
الموقع االعلى لهذا النصف من المصفوفة ،بينما الموقع االدنى لهذا النصرف يسراوي
(موقع الوسط .)1 +
الالبوة الثالثة :في هذل الخطة فان موقع الوسط هرو 5وهرو الموقرع الوحيرد المتبقري
93
هياكل البيانات باستخدام C++
في المصفوفة والقيمة التي فيره هري ( ,)44وهري ذات القيمرة التري نبحرث عنهرا لرذلك
سيتم اعادة موقع القيمة وبيان ان البحث تم بنجاح .الح الشكل .3.14
لنعيد العمل بالمثال السابل ولكن لنبحث (بحث غير ناجح) عن القيمة (.)45
الالبوة االولل :موقع الوسط يحتوي على القيمة 38كما فري الشركل ,3.12ونظررا
الن ) (45 > 38فسيتم البحث في نصف المصفوفة االعلى.
الالبةوة الثانيةة :نسرتعين بالشركل ,3.13ونالحر ان قيمرة الوسرط ) (77>45لرذلك
سيتم البحث في الخطوة الالحقة في النصف االسفل من المصفوفة.
الالبةةوة الثالثةةة :يالحر هنررا وحسررب الشرركل 3,14ان القيمررة فرري الوسررط هرري ,44
ونظرا الى ان ) (44<45لذلك فان البحث في المرحلة الالحقرة سريكون فري النصرف
االعلى من المصفوفة.
هنا الموقع االعلى للمصفوفة الجديدة سيكون ذات الموقع االعلى لها وهو ( ،)5بينما
الموقررع االدنررى سرريكون ( .. )6=1+5مررن ذلررك نالح ر ان الموقررع االدنررى أكبررر مررن
الموقع االعلى وهذا داللة علرى انتهراء البحرث دون ان نجرد القيمرة التري نبحرث عنهرا
لذلك تتوقف عملية البحث.
95
C++ هياكل البيانات باستخدام
:من الممكن استدعاء الدالة مع تمرير قائمة القيم والمفتاح كما يلي
يمكن اعادة كتابة العالقة اعالل لغرض تفسير الرموز الواردة فيها كما يلي:
الموقةةع الوسةةط و الموقةةع اال نةةلي ((الموقةةع االعلةةل – الموقةةع اال نةةل) × (قيمةةة
المفتاح – القيمة في الموقةع اال نةل)) ÷ (القيمةة فةي الموقةع االعلةل – القيمةة فةي
الموقع اال نل)
97
C++ هياكل البيانات باستخدام
int mid;
while (bottom <= top) {
mid = bottom + (top - bottom) * ((item - a[bottom]) / (a[top] -
a[bottom])) ;
if (item == a[mid])
return mid + 1;
if (item < a[mid])
top = mid - 1;
else
bottom = mid + 1;
}
return -1;
}
int main() {
int arr[MAX];
C++ هياكل البيانات باستخدام
int i, num;
int item, pos;
return 0;
}
99
هياكل البيانات باستخدام C++
مثال :في المصفوفة التالية مطلوب البحث عن القيمة ( )20باستخدام البحث الثنائي،
البحث االستيفائي ,والبحث بالقفز.
}int[] a = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30
البحث االستيفائي
القيمة التي في الموقع ( )10تساوي ( )20وهي تساوي قيمة المفتاح .بهرذا يكرون قرد
تم ايجاد قيمة المفتاح.
101
هياكل البيانات باستخدام C++
البحث بالقفز
في C++يتم تنفيذ جردول التجزئرة عرادة علرى شركل مصرفوفة قروائم موصرولة .فهري
تخررز ن علررى شرركل مصررفوفة ينائيررة االبعرراد .بالنسرربة لمصررفوفة ينائيررة االبعرراد ،فرران
العناصر تتكون من صفوف يابتة االبعاد .لكن ،في جدول التجزئة فان العناصر مرن
الممكن ان تتمدد وتتقلص لتستوعب (افتراضيا) ما النهاية من مدخالت الجدول.
مررن حيررث الكفرراءة فرران جرردول التجزئررة يعتبررر حررل وسررط بررين المصررفوفات والقرروائم
الموصولة .فهو يسرتخدم كرل مرن الفهرسرة والمررور علرى القائمرة لخرزن واسرترجاع
البيانات.
البحث عن العناصر باسرتخدام الفهرسرة يجعرل المصرفوفة كفروءة جردا .بغرض النظرر
عررن موقررع خررزن العنصررر فرري المصررفوفة ،هرري دائمررا تأخررذ نفررس الوقررت السررتعادة
البيانررات .حسررب المصررطلحات الفنيررة ،الحصرررول علررى عنصررر مررن مصررفوفة هرررو
)) (O(1من العمليات او (وقت يابت).
البحث عن عناصر في القروائم الموصرولة هري اقرل كفراءة بكثيرر .ألنرك ال يمكنرك ان
تصررل بشرركل مباشررر الررى اي عقرردة فرري القائمررة .فبرردل الوصررول المباشررر فأنررك مررن
المفررروض ان تمررر علررى القائمررة مررن البدايررة لحررين الوصررول الررى العنصررر المطلرروب
بطريقة البحث التسلسلي .ذا وجدت العنصر المطلوب في بداية القائمة فان استعادته
تتطلب )) (O (1من العمليات حيث أنك مررت على عنصر واحد فقرط فري القائمرة.
اما ذا كان العنصر الذي تبحث عنره فري ذيرل القائمرة فأنرك تحتراج الرى )) (O(nمرن
العمليات ،حيث ان nتمثل العدد الكلي للعقد في القائمة.
بشكل عام فان جدول التجزئة مشابه للمصفوفات فهو يبحث بوقت يابت كمعدل وهو
)) ,(O(1بغض النظر عن عدد العناصر في الجدول.
103
هياكل البيانات باستخدام C++
.1ينررائي القيمررة والمفترراح :ان الثنررائي (القيمررة ،المفترراح) هرري جرروهر هيكررل بيانررات
جدول التجزئة .للحاالت المناسبة ،فأنها تجعل البحث حقيقة كفوء ومثالي.
في بعض الحاالت ليس بالضرورة استخدام مفتاح مع القيمة وانما تسرتخدم ذات
القيمة على انها قيمة ومفتاح بذات الوقت.
.2دالة التجزئة :دالة التجزئة تقرر اين يتم خزن او من ايرن يرتم اسرتعادة العناصرر
في جدول التجزئة .هي تأخذ مفتاح العنصر على انره معامرل ادخرال وتعيرد رقرم
الفهرس او الموقع لهرذا العنصرر .بشركل عرام ،دالرة التجزئرة تسرتخدم رياضريات
باقي القسمة .عمليا ،قيمة المفتاح تقسم على طول الجدول لتوليد رقم فهرسة في
الجدول .رقم الفهرسة هذا يشير الى الموقع في جدول التجزئة.
.3تصةةا عناصةةر ةةدول التجزئةةة :التصررادم يعنرري ان اينرران او ربمررا اكثررر مررن
المفاتيح لها نفس رقم الفهرس .لغرض تعظيم كفاءة خرزن واسرترجاع العناصرر
في جدول التجزئة ،فاننرا نحتراج الرى تقليرل عردد التصرادم الرى الحرد االدنرى .كرل
تصادم يعني عنصر اخر في القائمة ،او عقدة في القائمة الموصرولة .نحرن نعلرم
بان خزن واستعادة العناصرر فري القروائم الموصرولة تنمرو أكثرر فري مجرال عردم
الكفررراءة عنررردما تكبرررر القائمرررة .لرررذلك مرررن المحبرررذ ان نحررراف علرررى هرررذل القررروائم
الموصولة لتكون أقصر ما يمكن.
هياكل البيانات باستخدام C++
هنا عدة طرق لكتابة دالة تجزئة كفوءة بحيث تعالج حاالت التصادم بكفاءة ،منها:
.Aجعل حجم الجدول رقم اولري (اي رقرم ال يقبرل القسرمة اال علرى واحرد او نفسره)
هي طريقة جيدة لتقليل التصادم الى الحرد االدنرى .بشركل عرام ،ان دالرة التجزئرة
الجيدة هي التي تمأل جدول التجزئة بشكل متجانس وعادل.
ان السربب باسرتخدام العرردد االولري ،هرو ان القيمررة الناتجرة مرن دالررة التجزئرة فرري
حالة عدم استخدام عدد اولي لحجم المصفوفة ستكون صفر (باقي القسرمة) لكرل
مفترراح يقبررل القسررمة علررى الحجررم .لكررن ،ذا اسررتخدمنا عرردد اولرري للحجررم فأننررا
سنقلص فرص ان يكون المفتاح يقبل القسمة على الحجم.
.Bواحدة من الحلول المسرتخدمة هري عنردما نسرتخدم دالرة التجزئرة ويكرون الموقرع
الناتج محتل من قبل عنصر اخر فيتم البحث عن أقرب موقع فار لهرذا الموقرع
وتحشررر القيمررة الجديرردة فيرره .هررذل الطريقررة ال تعتبررر طريقررة جيرردة ،مررن الممك رن
استخدامها عندما يكون حجم الجدول يابت.
في الشكل 3.16توضيح لكيفية تنفيذ هذل الطريقة على القيم التالية عندما يكرون
حجم الجدول مساوي الى :20
.Cحل اخر هو تقنية سلسلة جدول التجزئة البسيط ،فكل موقع في المصفوفة يشرير
الى قائمة موصولة بحيث تتكون عناصر هذل القائمة من العناصر التي تتصرادم
في ذلك الموقع .ان عملية الحشر تتطلب ايجاد الموقع الصرحيح وربرط العنصرر
بنهاية القائمة في ذلك الموقع .اما عملية الحذف فهري تتطلرب البحرث فري القائمرة
ضمن الموقع المحدد زالة العنصر ،الح الشكل .3.17
105
هياكل البيانات باستخدام C++
مثال :لو فرضنا ان القيم التالية مطلوب خزنها في جدول تجزئة حجمه .20
شك :3.18توضيح لحساب مواقع القيم في دول التجزئة مع م الجة التصا ببريقة
مصفوفة القوائم.
{ struct datar
public:
107
هياكل البيانات باستخدام C++
;}
{ class hasher
public:
;)(hasher
;)int hash(int &id
;)(void output
;}
من المفضل استخدام رقم اولي لذا استخدم الرقم 11فهو افضل لتقليل التصادم
*/
;)return (id%51
}
return ((id+1)%51);
hasher::hasher() {
// خلق مصفوفة
int i;
for (i=0;i<=50;i++) {
numel = 0;
dt[hashed].id = d.id;
dt[hashed].data = d.data;
return 0;
} else {
int i=0;
109
C++ هياكل البيانات باستخدام
while(i<=50) {
hashed = rehash(hashed);
if(dt[hashed].id == -1) {
dt[hashed].id = d.id;
dt[hashed].data = d.data;
return 0;
} else if (i==50) {
} else {
//الجدول مملوء
return (-1);
if(dt[hashed].id == d.id) {
dt[hashed].id = -1;
numel -= 1;
return 0;
} else {
int i=0;
while (i<=50) {
if (dt[hashed].id == d.id) {
return 0;
//success
} else if (i==50) {
return -1;
// انهاء الدالة
void hasher::output() {
int i;
111
هياكل البيانات باستخدام C++
{ )for (i=0;i<51;i++
} }
{ )(int main
;int ret
;ret = 0
;datar d
;hasher h1
;d.id=id
}
h1.output(); // الحررظ المخرجررات سرريكون قيررد واحررد فررارغ فرري id = 271861
الفهرس او الموقع 33وهو القيد الذي له
;return 0
}
هياكل البيانات باستخدام C++
الفص الرابع
المكدس STACK
4.1المقدمة
المكدس هرو هيكرل مشرابه للقائمرة بحيرث ان عمليرة االضرافة والحرذف ترتم مرن نهايرة
واحدة .هذل القيود تجعل المكدس اقل مرونة من القوائم ،لكنهرا ايضرا تجعرل المكردس
اكثررر كفرراءة (بالنسرربة للعمليررات الترري يقرروم بهررا) وكررذلك سررهل بالتنفيررذ .العديررد مررن
التطبيقررات تتطلررب فقررط نمرروذج تحديرردات عمليررات االضررافة والحررذف الترري يوفرهررا
المكدس .بالرغم من هذل التحديدات فان المكدس له الكثير من التطبيقات.
113
هياكل البيانات باستخدام C++
يستخدم عادة المكردس هياكرل البيانرات عنرد كتابرة الشرفرة .مبردأ المكردس فري الحقيقرة
بسرريط ,والررذي يجعل ره حتررى ابسررط لكتابررة شررفرتة .افرررض هررذل الحالررة ,لررديك اربعررة
قمصرران موضرروعة علررى طاولررة فرروق بعضررها الرربعض (شرركل )4.1فرراذا رغبررت ان
تضرريف قمرريص خررامس لهررا ,فمرراذا تعمررل بكررل تاكيررد ستضررع القمرريص الخررامس فرروق
القمصرران االربررع ,االن مرراذا لررو اردت ان تسررحب القمرريص الثالررث (االبرريض) مررن
القمصان االربع التي لديك ,هنا عليك ان تزيل القميص األعلى يم الرذي بعردل وهكرذا
لحين ان يصبح القميص الثالث في األعلل ليتسنى لك الحصول عليره ومرن يرم اعرادة
القمصان التي ازلتهرا الرى مكانهرا وحسرب ترتيبهرا اي ان تضرعها قمريص بعرد االخرر
(فوق او أعلل القمصان االخرى).
الح هنا ,اني اشرت الى كلمة األعلى بحروف غامقة ,نعرم الن أعلرى المكردس هرو
اكثررر كلمررة مهمررة فرري التعامررل مررع المكرردس .البيانررات تخررزن فرري المكرردس بحيررث ان
اضافة البيانات اليسمح بها اال من األعلى فقرط .ان اضرافة وحرذف البيانرات اليسرمح
به اال من أعلى المكدس فقط (االعلى كلمة افتراضية وليس حقيقية).
م حظة :لكي اليحدث سوء فهم عندما نقول اعلى المكردس فهرو اليعنري ان المكردس
وعاء وله اعلى واسفل ,المقصود هنا اعلى منطقيا فمثال لو مثلنا المكدس على شركل
هياكل البيانات باستخدام C++
مصفوفة فانك اليمكنك العمل على بداية المصفوفة او الوصرول الرى أي عنصرر فري
المصفوفة وانما فقط الى اخر عنصر في المصفوفة.
115
هياكل البيانات باستخدام C++
4.4.1عند تنفيذ اي برنامج فان المعرالج سريكون لره الردور االسراس بعمليرة التنفيرذ,
لذلك اذا حدث وان تم اسرتدعاء دالرة معينرة فري البرنرامج فران المعرالج سريتوقف عرن
اكمررال تنفيررذ البرنررامج حسررب تسلسررل االيعررازات او االوامررر فرري البرنررامج ويتفرررع
الكمال تنفيذ الدالة يم العودة الى ذات الموقرع الرذي تفررع منره الكمرال التنفيرذ حسرب
تسلسل االيعازات ,في هذل الحالة فان المعالج يجب ان تكون له االمكانية للعودة الى
البرنامج في الموقع الذي تفرع منه لتنفيذ الدالة التي تم استدعائها مع المحافظة على
جميع القيم الوسطية ,لذلك فان العنوان الحرالي للبرنرامج (اي االيعراز الرذي كران مرن
المفروض ان ينفذ قبل ان يتفرع المعرالج الرى تنفيرذ الدالرة) سريدفع الرى المكردس ليرتم
حفظة ,وعندما ينتهي المعالج من تنفيذ الدالة ,فان العنوان الذي تم خزنة سيتم سحبة
من المكدس ليوجه المعالج اليه الكمال التنفيذ ,نفس الشيء بالنسربة للنترائج الوسرطية
سيتم حفظها ودفعها الى المكدس لكي يتم العمل عليها عند العودة من تنفيذ الدالة.
117
هياكل البيانات باستخدام C++
4.4.4تطبيل اخر للمكدس ,نفرض انك تقوم بعمليرة جمرع أعرداد كبيررة جردا ,علرى
سبيل المثال افرض انك ترغب بجمع العددين
o 353,120,457,764,910,452,008,700,786
o 234,765,000,129,654,080,277,543
اوال الح التالي انه من الصعب تمثيل اي من العددين كمتغير من نروع االعرداد
الصحيحة ) (long intالن هرذا المتغيرر اليمكنرة مرن اسرتيعاب العردد الكبيرر اعرالل.
هذل المشكلة باالمكان حلهرا وذلرك بمعاملرة العردد كسلسرلة رمزيرة مرن االرقرام ,ويرتم
خرررزنهم بمكدسرررين ,بعررردها تجررررى عمليرررة االضرررافة او الجمرررع بسرررحب ارقرررام مرررن
المكدسين.
4.4.5ايجاد نتيجة تعبير حسابي :المكدس مفيد اليجاد نتيجة تعبير حسابي ,افرض
لديك التعبير التالي
5 * 3 +2 + 6 * 4
من ال ممكن ايجاد نتيجة التعبير وذلك بايجاد ,اوال نتيجرة حاصرل ضررب 5فري
, 3بعد ذلك تخزن النتيجة في متغير مثال ,Aيم قم باضافة الرقم 2الى المتغير A
واخزن النتيجة برالمتغير .Aاالن اجرري عمليرة الضررب للررقمين 6فري 4واخرزن
النرراتج بررالمتغير .Bعنررد ذلررك سررتنتهي العمليررة باضررافة Aالررى Bواتررر النتيج رة
النهائية بالمتغير . A
A = 15 )(5×3
A = 15 + 2 )(15 + 2
= 17
B=6×4 )(6 × 4
=24
A = 17 + 24 )( 17 + 24
هياكل البيانات باستخدام C++
= 41
هذل الفكرة االولية من الممكن انجازها بسهولة وكفاءة باستخدام المكدس بعرد اجرراء
بعض التحويرات على التعبير الرياضي.
x+y*z
المعامالت :هي كميات (وحدة بيانات ) والتي تجرى عليها العمليات الرياضية.
والمعامالت ممكرن ان تكرون متغيررات مثرل x, y, zاو يوابرت مثرل ,2, -1
.. 12 ,34الا
العوامررل الرياضرررية :هررري رمرروز تررردل علرررى عمليررات رياضرررية او منطقيرررة برررين
المعامالت ,مثال على بعض العوامل الشائعة =< +, ‐, *, /, ^,
واقعا هنا يالث أنواع من التعابير الرياضية .وهي:
4.5.1ت بير ال مة القياسية :infixوهرذا هرو النروع الرذي اعتردنا علرى التعامرل
معررره فررري الرياضررريات منرررذ االيرررام االولرررى للمدرسرررة ,حيرررث يكرررون موضرررع العامرررل
الرياضي (العالمة الرياضية) بين المعاملين الذين مرن المفرروض ان تجررى علريهم
العملية الرياضية وفقا لهذا العامل ,مثل
A+B
4.5.2ت بيةر ال مةة ال حقةة :Postfixفري هررذا التعبيرر يكرون العامرل الرياضري
تابع او الحل الى المعاملين اي بعد المعاملين الذين من المفروض ان تجررى علريهم
العملية الرياضية وفقا لهذا العامل ,مثل
AB+
119
هياكل البيانات باستخدام C++
4.5.3ت بيةةر ال مةةة السةةابقة :Prefixهنررا يكررون مكرران العامررل الرياضرري قبررل
المعاملين الذين من المفروض ان تجرى عليهم العملية الرياضرية وفقرا لهرذا العامرل,
مثل
+AB
التعبيرررر ) (3+5*4مرررن الممكرررن ايجررراد نتيجتررره علرررى انررره يسررراوي 32وهرررو يعنررري
) ,((3+5)*4او مرررن الممكرررن لشرررخص اخرررر يجرررد نتيجتررره علرررى انهرررا 23بمعنرررى
)) .(3+(5*4السؤال هو اي من هاتين النتيجتين هي الصحيحة.
ولحل هذل المشكلة فان استخدام العوامل الرياضية المرافقة للتعبير الرياضري سروف
اليكون اختياريا او عشوائيا وانما يخضع لضوابط وقواعد تمثل اسبقية استخدام كرل
عامل رياضي في التعبير .ان اسبقية العوامرل ترتحكم بترتيرب وتنظريم عمليرة حسراب
النتائج للعمليات الحسابية المختلفة ضمن التعبير الحسابي الواحد.
القاعدة ال امةة تشرير الرى ان العامرل ذو االسربقية االعلرى يرتم حسرابة او تطبيقرة قبرل
العامل ذو االسبقية االوطأ.
ان استخدام هذل االسربقيات فري الحاسروب سريولد نروع مرن التعقيرد ولرذلك فران تعبيرر
العالمررة القياسرري مررن الصررعب تحليلررة ,النرره يحترراج الررى تحديررد افضررلية العوامررل,
المحددات ,وكسر التعادل (اي عنردما يكرون عراملين برنفس االفضرلية) ,وهرذا يجعرل
تقييم الحاسوب للتعبير صعب اكثر من الالزم.
هذا التعقيد من الممكن تجاوزل عنرد اسرتخدام تعبيرر العالمرة السرابقة وتعبيرر العالمرة
الالحقة النهما اليعتمدان على افضرلية العوامرل ,المحرددات ,وكسرر التعرادل .لرذا فران
تقييم التعابير بهذل الصيغ يكون اسهل.
^ تعني الرفع الى اس معين ,اما العوامل االحادية فهي كررل العوامررل االحاديررة6 ,
تعني العوامل الرياضية التي تسبل معامل واحد فقط مثل ^
121
هياكل البيانات باستخدام C++
(.)-7
( )%تشير الى باقي القسمة 5 % , * , /
الثنائيررة تعنرري انهررا تسررتخدم معرراملين واحررد قبررل العامررل 4 - , +الثنائية
الرياضي واالخر بعدل مثل (.)12 + 4
المساواة التي تستخدم باسناد قيمة معينة الى متغير 0 =
يعامل تعبير العالمة القياسري علرى انره سلسرلة مرن الرمروز (معرامالت وعوامرل .1
رياضية) ويتم فحصها او قرراءة هرذل الرمروز واحرد بعرد االخرر مرن اليسرار الرى
اليمرررررين (يعتبرررررر تعبيرررررر العالمرررررة القياسرررررية هرررررو المررررردخل ويخرررررزن بسلسرررررلة
رمزية ).) (string
يررتم اسررتخدام المكرردس كخررزان وسررطي (يكررون المكرردس ابتررداءا فررار ) ,وسلسررلة .2
رمزية للمخرجات تكون بداية خالية من اي رمز.
افحص العنصر الالحل بالمدخالت (اي ضمن تعبير العالمة القياسية). .3
اذا كان هذا العنصر معامل (متغير او يابت) يتم وضرعة فري سلسرلة المخرجرات .4
مباشرة.
اذا كان قوس مفتوح يتم دفعة الى المكدس. .5
اذا كان عامل رياضي قم بمايلي: .6
هياكل البيانات باستخدام C++
)aاذا كان المكدس فار ادفع العامل الى المكدس .اذهب الى الخطوة 7
)bاذا كان ماموجود باعلى المكدس هو قوس مفتوح ادفع العامل الرى المكردس.
اذهب الى الخطوة .7
)cاذا كان هذا العامل له اسبقية اعلرى مرن العامرل فري اعلرى المكردس ادفرع هرذا
العامل الى المكدس .اذهب الى الخطوة .7
)dاذا كان هذا العامل له اسبقية اوطأ مرن العامرل الموجرود فري اعلرى المكردس,
في هذل الحالة اسحب العامل مرن اعلرى المكردس واخرجرة ليرتم وضرعة فري
سلسلة المخرجات ,اذهب الى الخطوة aفي بداية الفقرة 6
.7اذا كان قوس مغلل اسحب العوامرل مرن المكردس واحرد بعرد االخرر ,وكرل عامرل
يتم سحبه يوضع في سلسلة المخرجات واحد بعد االخر ,لحين تصل الى القوس
المفترروح .عمليررة السررحب سررتهمل القرروس المفترروح( .عمليررة االضررافة الررى سلسررلة
المخرجات هي عملية جمع )) (concatenation
.8اذا كان هنا مزيد من المدخالت اذهب الى الخطوة .3
.9اذا لررم يكررن هنررا مزيررد مررن المرردخالت ,اسررحب العوامررل المتبقيررة فرري المكرردس
واضفهم الى سلسلة المخرجات (واحد بعد االخر).
4.6.3امث لرررة لتحويرررل تعبيرررر العالمرررة القياسررري الرررى تعبيرررر العالمرررة الالحقرررة (هنرررا
باستخدام االسبقيات وليس المكدس)
123
هياكل البيانات باستخدام C++
م حظة :التوجد حاجة الى االقواس في تعبير العالمة الالحقة ,أي ان تعبير العالمة
الالحقة اليحتوي اقواس.
مثةةال :4مثررال توضرريحي لكيفيررة تنفيررذ خوارزميررة تحويررل تعبيررر العالمررة القياسررية
باستخدام المكدس:
هياكل البيانات باستخدام C++
ابتداءا فان المكدس يكون فار وسلسلة التعبير الالحل ليس فيهرا اي رمروز كمرا فري
الشكل .4.4
االن ,الرمز االول الذي يتم فحصه هرو ’ ‘aوالرذي سرتتم اضرافتة الرى سلسرلة تعبيرر
العالمة الالحقة (النه ليس عالمة رياضية) الح الشكل .4.5
الرمز الثراني الرذي يرتم فحصرة هرو عالمرة الجمرع ’ ‘+وهرذا برالطبع عامرل رياضري
والعوامل الرياضية تضاف الى المكدس ,الشكل 4.6يوضح النتيجة.
125
هياكل البيانات باستخدام C++
الرمررز التررالي الررذي سرريتم قرائتررة هررو الرمررز ’ ‘bوالررذي سيوضررع فرري سلسررلة تعبيررر
العالمة الالحقة .بعدها يكون الدور للرمز الذي سيتم قرائته هو عالمة الضررب ’*‘
والذي هو عامل رياضي .االن هذا يجب ان يودع في المكدس ويجرب ان نالحر ان
اعلررى المكرردس يحترروي العامررل الرياضرري ’ ‘+والررذي هررو ذو اسرربقية اقررل مررن عامررل
الضرب ,لذلك فان عامل الضرب سيدفع الرى اعلرى المكردس ,الحر وضرع المكردس
في الشكل .4.7
الرمز االخر الذي سيتم قرائته هو عالمة الطرح ‘ ‘-وهذا عامل رياضي المفروض
ان يوضع في المكدس ,الح ان اعلى المكدس يحتروي عالمرة الضررب ’*‘ والرذي
هو ذو اسبقية اعلى من عامل الطرح ونظرا لعدم امكانية ان يوضرع عامرل رياضري
ذو اسرربقية دنيررا فرروق عامررل رياضرري ذو اسرربقية اعلررى فرري المكردس ,لررذا سرريتم سررحب
العامل ’*‘ من المكردس ويضراف الرى سلسرلة تعبيرر العالمرة الالحقرة (المخرجرات).
حتى االن فان المكدس ليس فار .االن بعد سحب عالمة الضرب فان عالمة الجمع
’ ‘+هي في اعلى المكدس وهذا العامرل مسراوي باالسربقية لعامرل الطررح ,ولرذا فران
عاملين متساويين باالسبقية اليمكن ان يكونا احردهما فروق االخرر مباشررة بالمكردس,
لررذا فرران العامررل ’ ‘+يسررحب ايضررا مررن المكرردس ويوضررع فرري سلسررلة تعبيررر العالمررة
الالحقة ,عند ذا يتم دفع العامل ‘ ‘-الى المكدس ,الح الشكل .4.9
الرمز الذي سيقرا االن هو الرمز ’ ‘dوالرذي سيضراف الرى تعبيرر العالمرة الالحقرة,
وبعد قراءة الرمز dنكون قد قرأنا جميع الرمروز فري تعبيرر العالمرة القياسرية وبهرذا
وصررلنا الررى حالررة هرري عرردم وجررود اي رمررز اخررر لررم يررتم قرائتررة وعليررة فرران جميررع
العناصرر الموجررودة فرري المكرردس سرريتم سررحبها واحرد بعررد االخررر ,وتررتم اضررافتها الررى
127
هياكل البيانات باستخدام C++
سلسلة تعبير العالمة الالحقة بالتعاقرب .فيرتم هنرا سرحب عامرل الطررح ويضراف الرى
سلسررلة تعبيررر العالمررة الالحقررة .لررذا بعررد ان يررتم قررراءة جميررع الرمرروز فرران المكرردس
وسلسلة تعبير العالمة الالحقة سيكونان كما في الشكل .4.10
تمرين :1حول تعبير العالمة القياسية في االمثلة التالية الرى تعبيرر العالمرة الالحقرة
باستخدام االسبقيات واستخدام المكدس ( .االجوبة في نهاية الفصل)
1. )(a + b) * (c – d
2. )a – b / (c + d * e
3. )((a + b) * c – (d – e))/(f + g
2*3/(2-1)+5*3
هياكل البيانات باستخدام C++
129
هياكل البيانات باستخدام C++
تعبير العالمرة الالحقرة يتكرون عرادة مرن معرامالت وعوامرل رياضرية ,باالمكران .2
اعتبارهررا كسلسررلة مررن الرمرروز ,يررتم قرائتهررا او فحصررها مررن اليسررار الررى اليمرين
تباعا.
يتم قراءة رمز واحد من السلسلة في كل مرة. .3
اذا كان الرمز هو معامل يتم دفعة الى داخل المكدس. .4
اذا كان الرمز عامل رياضي ,عندها يتم مايلي: .5
)aسررحب اينررين مررن المعررامالت مررن اعلررى المكرردس (اي المعامررل االعلررى فرري
المكدس والمعامل الذي يليه).
)bاجررراء العمليررة الحسررابية علررى المعرراملين الررذين تررم سررحبهما ووفقررا للعام رل
الرياضي الذي تم قرائتة.
)cيتم دفع النتيجة المستحصلة من الخطوة bالى داخل المكردس (توضرع عرادة
في اعلى المكدس)
اذا لم تكن السلسلة فارغة االنتقال الى الخطوة .3 .6
ليكون الناتج 5هذا الناتج يعاد دفعة الرى المكردس وطبعرا سريكون فري اعلرى المكردس
(الخطوة ,)5تستمر عملية قراءة رموز تعبيرر العالمرة الالحقرة فنقررأ الررقم 8وهرذا
رقررم فيرردفع الررى المكرردس كمررا فرري الخطرروة ( )6فرري الشرركل ,4.11بعرردها نقرررأ عالمررة
الضرب وبهذا يتم سحب رقمين من اعلى المكدس وهما الرقم 8والذي يلية الررقم 5
لتتم عليهم عملية الضرب ويكون الناتج 40الذي يدفع الى اعلى المكدس (خطوة )7
فرري الشرركل 4.11تسررتمر عمليررة قررراءة رمرروز تعبيررر العالمررة الالحقررة حيررث عالمررة
الجمع عندها يسحب اعلى رقمين وهرم 40والررقم 5وتجررى عمليرة الجمرع والنراتج
هو 45يدفع الى اعلى المكدس ,بعدها يتم قراءة الرقم 3ويدفع الى اعلى المكدس يم
نقرأ عالمرة الجمرع ونسرحب الررقم 45والررقم 3مرن اعلرى المكردس وتجررى علريهم
عمليررة الجمررع ليكررون النرراتج 48يرردفع الررى اعلررى المكرردس (الخطرروة )10فرري الشرركل
,4.11واخيرررا تررتم عمليررة قررراءة عامررل الضرررب ويررتم سررحب الرررقم 48والرررقم 6
الجراء عملية الضرب وينتج الرقم 288الذي يدفع الى المكدس وبهذا تنتهي العملية
الرياضية نظرا النتهاء جميع رموز تعبير العالمة الالحقة ,ولم يبقى بالمكدس سوى
الناتج الذي هو .288
العملية تتوقف عندما تنتهي جميع العوامل الرياضية والمعامالت في السلسلة (تعبير
العالمة الالحقة) ,نتيجة حساب التعبير المتحصلة يرتم سرحبها مرن المكردس وسرتكون
هي الوحيدة في المكدس اي معامل واحد يبقى بالمكدس.
131
هياكل البيانات باستخدام C++
مثال : 7مثال اخر توضيحي اليجاد نتيجة تعبير العالمة الالحقة باسرتخدام المكردس
للتعبير:
623+-382/+*2^3+
623+-382/+*2^3+
عامررل رياضرري ,يررتم سررحب اعلررى معرراملين مررن المكرردس ,وتجرررى 6, 5 +
علرريهم العمليررة الرياضررية وفقررا للعامررل الرياضرري هنررا عمليررة جمررع
) (2+3يم تعاد النتيجة ( )5الى اعلى المكدس
عامل ,يسحب اعلى معاملين ويجرى عليهم عملية الطرح 1 -
عامل القسمة ,يسحب اعلى معاملين بالمكدس وتجرى عليهم القسمة 1, 3, 4 /
) (8/2وتدفع النتيجة ( )4الى اعلى المكدس.
م حظةةة :الم امةة االول الةةذي يسةةحب مةةن المكةةدس يوضةةع ب ةةد
ال ام الرياضي بينما الم ام الثاني يوضع قب ال ام الرياضي.
جمع المعاملين ) (3+4ودفع النتيجة ( )7الى اعلى المكدس 1, 7 +
عمليررة ضرررب العلررى معرراملين بالمكرردس ) (7*1ودفررع النتيجررة ()7 7 *
الى اعلى المكدس
عامل الرفرع (اس) العلرى معراملين بالمكردس ) (7^2وتردفع النتيجرة 49 ^
( )49الى اعلى المكدس
اجرراء عمليررة الجمرع علررى اعلرى معرراملين بالمكردس ( )3+49ودفررع 52 +
النتيجة ( )52الرى اعلرى المكردس ,وهري اخرر عمليرة وتمثرل النتيجرة
النهائية.
133
هياكل البيانات باستخدام C++
1+2*3^4/5-6
1 1
+*/ ^1234
+/ *^1234
+- 1234^*5/
- 1234^*5/+
هياكل البيانات باستخدام C++
1-(2+3)*4
1 1
- 123+
- *123+4
135
هياكل البيانات باستخدام C++
))(4+8)*(6-5)/((3-2)*(2+2
( (
) 48+
/ *48+65-
االن لو اردنا ان نحول تعبير العالمة الالحقة الى تعبير العالمة القياسي .ترتم بعمليرة
بسيطة وهي مشابهة جدا الى عملية ايجاد ناتج تعبير العالمة الالحقة ,الفارق هو في
ايجرراد النرراتج عنرردما نقرررا عالمررة رياضررية كنررا نسررحب اعلررى عنصرررين فرري المكرردس
وتجرى عليهم العملية الرياضية يرم يردفع النراتج الرى اعلرى المكردس ,فري هرذل الحالرة
لغرض التحويل الى العالمة القياسة نعمل نفرس الشريء عنردما نقررا عالمرة رياضرية
نسحب اعلى عنصرين في المكدس ونضع العالمرة بينهمرا يرم نردفع هرذل النتيجرة الرى
اعلى المكدس ..هذا الموضوع سيكون اكثر وضوحا عند متابعة المثال .11
137
هياكل البيانات باستخدام C++
*–ab–c/de+f
تعتبر النتيجة عنصر واحد عند دفعها الى المكدس لذا )(a-b
توضررع بررين قوسررين للتعامررل معهررا علررى انهررا عنصررر
واحد
(a-b)/c,(d+e)-f
(a-b)/c*(d+e)-f
تمرين :2حول كل تعبير من تعابير العالمة القياسية ادناة الى تعبير العالمة الالحقة
باستخدام المكدس ( االجابة في نهاية الفصل)
1. A+B*C–D*E/F
2. A+B^C/D*E+F–A
3. A^B^C/2*B+4
4. A/B+C/D–E*F^2/B
5. (A + B) * (C – B) / 2 ^ 4
139
هياكل البيانات باستخدام C++
ان تحويل تعبير العالمة القياسية الى تعبير العالمة السابقة يتبع الخطوات التالية بعد
ان يتم فحص تعبير العالمة القياسية من اليمين الل اليسار ويتم تكرار الخطوات
التالية على كل عنصر من عناصر التعبير لحين ان يفر المكدس.
.1قراءة عنصر جديد من تعبير العالمة القياسية.
.2اذا كان العنصر هو معامل فيتم اضافتة الى سلسلة المخرجات.
.3اذا كان العنصر هو عامل رياضي فيتم اجراء مايلي:
3.1اذا كان العامل قوس ايمن " ( " يتم وضعة في المكدس.
3.2اذا كان العامل قوس ايسر " ) " في هرذل الحالرة يرتم سرحب جميرع العوامرل
الموجودة في المكدس لحين الوصول الى القوس االيمن .جميع هذة العوامل
التي سريتم سرحبها مرن المكردس تكترب فري سلسرلة المخرجرات بالتعاقرب (اي
واحد بعد االخر) .االقواس تهمل.
3.3اذا كان العنصر عامل رياضي فيتم اجراء مايلي:
3.3.1اذا كان المكدس فار فيتم اضافة العامل الرياضي الى المكدس.
3.3.2اما اذا كان المكدس غير فار ,عليك بالقيام بمايلي:
)aايجاد االفضلية للعامل الرياضي.
)bايجاد االفضلية للعامل الرياضي الموجود في اعلى المكدس.
مقارنررة االفضررليات للعرراملين ,فرراذا كانررت افضررلية العامررل الرياضرري المررراد )c
اضررافتة اعلررى مررن افضررلية العامررل الرياضرري الموجررود فرري اعلررى المكرردس فرري ه رذل
هياكل البيانات باستخدام C++
الحالررة يررتم دفررع هررذا العامررل الررى المكرردس ليكررون فرري اعلررى المكرردس .امررا اذا كانررت
افضلية العامل المراد اضافتة اقل من افضرلية العامرل الرياضري الموجرود فري اعلرى
المكدس عندها يتم اخراج العامل الرياضي في اعلرى المكردس ويضراف الرى سلسرلة
المخرجات ..تستمر عملية المقارنة مع العامل الذي سريكون فري اعلرى المكردس بعرد
سحب العامل السابل وتطبل نفس الطريقة ,وهكذا لحين الوصول الرى مكردس فرار
او عامل رياضي في اعلى المكدس افضليتة اقرل مرن العامرل المرراد اضرافتة عنردها
يضاف العامل الى اعلى المكدس.
.4اذا كان هنا عناصر اخرى في تعبير العالمة القياسية ,الذهاب الى الخطروة .1
في خالف ذلك الذهاب الى الخطوة .5
.5افرا جميع محتويات المكدس (ازالة كل العوامل من اعلى المكدس) واضافتها
الى سلسلة المخرجات.
.6بعد االنتهاء من افرا المكدس يتم عكس رمروز سلسرلة المخرجرات ( اي يكرون
الرمز االيسر باليمين والرمز الذي باليمين باليسار) حيرث سرتكون هرذل السلسرلة
الناتجة تمثل تعبير العالمة السابقة.
م حظةةةة :باالمكررران عكرررس رمررروز تعبيرررر العالمرررة القياسرررية ابترررداءا وتطبيرررل
الخوارزميررة اعررالل عليهررا ,دون الحاجررة الررى عكررس النتيجررة النهائيررة ,لكررن هررذا
اليتبع في السالسل الحرفية ايناء البرمجة.
م حظة :اهم الفروق بين خوارزمية التحويل الى العالمة الالحقة والتحويل الى
العالمة السابقة هو:
فرري حالررة التحويررل الررى العالمررة الالحقررة فرران السلسررلة تقرررا مررن اليسررار الررى
اليمين بينمرا فري حالرة التحويرل الرى العالمرة السرابقة فران السلسرلة تقررا مرن
اليمين الى اليسار.
في حالرة العالمرة الالحقرة اليجروز ان تكرون اسربقيتين متسراويتين احرداهما فروق
االخرى في المكدس ,بينما هذا الشرط غير متوفر فري التحويرل للعالمرة السرابقة
141
هياكل البيانات باستخدام C++
)(a–b)/c *(d+e–f/g
رمةةةوز ت بيةةةر ت بيةةةةةةر ال مةةةةةةة المكدس الم حظات
ال مةةةةةةةةةةةةةةةةة السابقة
القياسةةةةةةةةةةةةةية (المالر ات)
(المدو ت)
) ) قوس ايمن يدفع للمكدس
143
هياكل البيانات باستخدام C++
)2*3/(2-1)+5*(4-1
رموز ت بير ال مة القياسية المكدس سلسلة المالر ات
(المدو ت) ت بير ال مة السابقة
) )
1 ) 1
- )- 1
4 )- 14
( فار 14-
* * 14-
5 * 14-5
+ + *14-5
) )+ *14-5
1 )+ 14-5*1
هياكل البيانات باستخدام C++
145
هياكل البيانات باستخدام C++
*/–abc–+def
عناصةةر ت بيةةر ال مةةة المكدس الم حظات
السابقة (المدو ت)
f f معامل يدفع الى المكدس
e f,e معامل يدفع الى المكدس
d f,e,d معامل يدفع الى المكدس
+ عامل رياضي ,يتم سرحب اعلرى معراملين f
بالمكرررردس وتوضررررع العالمررررة الرياضررررية
بينهما ,وهما ..d, eويتم دفع النتيجة الرى
اعلى المكدس
م حظة :الم ام االول الذي يسحب مةن )f,(d+e
اعلةةةةل المكةةةةدس يوضةةةةع قبةةةة ال مةةةةة
الرياضةةية ب بينمةةا الم امةة الثةةاني الةةذي
يسحب مةن المكةدس يوضةع ب ةد ال مةة
الرياضية.
- عامل رياضي
(d+e)-f
c (d+e)-f,c معامل يدفع الى المكدس
b (d+e)- معامل يدفع الى المكدس
هياكل البيانات باستخدام C++
f,c,b
a (d+e)- معامل يدفع الى المكدس
f,c,b,a
- عامل رياضي ,يتم سرحب اعلرى معراملين (d+e)-f,c
بالمكدس
وهمررا ..a, bوضررع العالمررة الرياضررية
بينهما ودفع النتيجة الى اعلى المكدس
(d+e)-
)f,c,(a-b
/ (d+e)-f
(d+e)-f,(a-
b)/c
*
* (a-b)/c تعبير العالمة القياسية
(d+e)-f
147
هياكل البيانات باستخدام C++
(a-b)/c * (d+e)-f
رمروز تعبيرر العالمرة القياسرية (مرن اليمررين سلسرررررلة المخرجرررررات المكدس
الى اليسار) (المدخالت) تعبير العالمة السابقة
f f
- - f
) )-
e )- fe
+ -)+ fe
d -)+ fed
( - fed+
* *- fed+
c *- fed+c
/ -*/ fed+c
) )-*/
b )-*/ fed+cb
- -*/)- fed+cb
a -*/)- fed+cba
( -*/ fed+cba-
*- fed+cba-/
- *fed+cba-/
فار fed+cba-/*-
يتم عكس النات للحصول علل ت بير ال مة السابقة -*/-abc+def
هياكل البيانات باستخدام C++
تمرين :3حول تعابير العالمة الالحقة التالية الى تعرابير العالمرة القياسرية ,موضرحا
محتويات المكدس بعد كل عملية ( االجوبة في نهاية الفصل) (تم وضةع فةوارز بةين
القيم للتمييز).
1. 5, 3, + , 2 , * , 6 , 9 , 7 , - , / , -
2. 3, 5, + , 6 , 4 , - , * , 4 , 1 , - , 2 , ^ , +
3. 3, 1, + , 2, ^ 7 , 4, - , 2 , * , + , 5 , -
4. * 20, 45, + 20 , 10 , - , 15 , + ,
{
البد من التاكد من المكدس ليس مملوء قبل االضافة if ( Top = = size -1 ) //
{
; )exit(0 }
else
{
; Top + +
149
C++ هياكل البيانات باستخدام
int R ;
exit(0) ; }
else
R = Stack [ Top ] ;
Top - - ;
class Stack
int Top ;
public:
};
int Val ;
cin>> Val ;
if ( Top = = 9 )
exit ( 0) ; }
else
{ Top + + ;
int R ;
151
C++ هياكل البيانات باستخدام
if ( Top = = -1 )
{ cout<<”Stack Underflow”;
exit (0) ;
else
{ R = Data [Top ] ;
Top - - ;
}
برنامج يستخدم لتحويل تعبير العالمة القياسية الى تعبير العالمة الالحقة4.9.3
#include<iostream>
#include<stdio>
#include<conio>
#include<string>
#include<stdlib>
using namespace std ;
int main( )
{
char ele, elem, st[2];
strcpy (postfix,” “) ;
gets(infix);
for (int i=0 ; infix[i] !=0 ; i++)
{
if (infix [i] != ’(‘ && infix[i] != ’)’ && infix[i] != ’^’ && infix[i] != ’*’
&& infix[i] != ’/’ && infix[i] != ’+’ && infix[i] != ’-‘ )
postfix [j++] = infix[i] ;
else
if (infix[i] = = ’(‘ )
{
elem = infix [i] ;
push(elem);
}
else
if (infix[i] = = ’)’ )
{
while (popped = pop( ) != ‘(‘ )
153
C++ هياكل البيانات باستخدام
}
else
{
elem=infix[i];
pre = precedence (elem) ; //خزن افضلية العامل الذي ياتي من تعبير العالمة القياسية
ele = topelement ( ) ;
prep = precedence (ele) ; //خزن االفضلية للعامل الموجود في اعلى المكدس
push(elem);
else
{
while(prep >= pre)
{
if(ele = = ’#’ )
break;
popped=pop();
ele=topelement();
postfix[j++] = popped ;
prep=precedence(ele);
}
push(elem);
}
C++ هياكل البيانات باستخدام
}
}
postfix[j++] = popped ;
postfix[j]=’\0’;
system(“pause”);
return 0;
{
switch(ch)
{
case ‘^’ : return 5;
default : return 0;
155
هياكل البيانات باستخدام C++
}
}
{
;char ret
)if(top!=-1
;]{ ret =stack[top
;top--
;return ret
}
else
;’return ‘#
}
دالة العادة العنصر االعلى في المكدس دون ان تسحبهة من المكدس char topelement() //
{
;char ch
C++ هياكل البيانات باستخدام
if(top!=-1)
ch=stack[top];
else
ch=’#’;
return ch;
{
if(top!=size-1)
{
top++;
stack[top]= ch;
}
}
#include <iostream>
#include <stdlib>
#include <math>
#include <ctype>
using namespace std ;
157
C++ هياكل البيانات باستخدام
class postfix
private :
int stack[MAX] ;
int top, nn ;
char *s ;
public :
postfix( ) ;
int pop( ) ;
void calculate( ) ;
void show( ) ;
};
postfix :: postfix( )
top = -1 ;
s = str ;
if ( top == MAX – 1 )
else
top++ ;
stack[top] = item ;
if ( top == -1 )
return NULL ;
top-- ;
return data ;
159
C++ هياكل البيانات باستخدام
while ( *s )
if ( *s == ‘ ‘ || *s == ‘\t’ )
{
s++ ;
continue ;
if ( isdigit ( *s ) )
nn = *s – ‘0’ ;
push ( nn ) ;
else
n1 = pop( ) ;
n2 = pop( ) ;
switch ( *s )
{
C++ هياكل البيانات باستخدام
case ‘+’ :
n3 = n2 + n1 ;
break ;
case ‘-‘ :
n3 = n2 – n1 ;
break ;
case ‘/’ :
n3 = n2 / n1 ;
break ;
case ‘*’ :
n3 = n2 * n1 ;
break ;
case ‘%’ :
n3 = n2 % n1 ;
break ;
case ‘$’ :
n3 = pow ( n2 , n1 ) ;
break ;
default :
exit ( 1 ) ;
161
C++ هياكل البيانات باستخدام
push ( n3 ) ;
s++ ;
{
nn = pop ( ) ;
void main( )
char expr[MAX] ;
postfix q ;
q.setexpr ( expr ) ;
q.calculate( ) ;
q.show( ) ;
}
هياكل البيانات باستخدام C++
تمةةرين :4حررول تعررابير العالمررة القياسررية التاليررة الررى مايكاف هررا مررن تعررابير العالمررة
الالحقة موضحها حالة المكدس:
) 1. ((A – B ) * ( D / E )) / ( F * G * H
2. ( A + B ^ D ) / ( E – F ) + G
) 3. A * ( B + D ) / E – F – ( G + H / K
4. A * ( B + ( C + D ) * ( E + F ) / G ) * H
5. A + ( ( B + C ) + ( D + E ) * F ) / G
6. NOT A OR NOT B AND NOT C
7. NOT ( A OR B ) AND C
تمرين :5اكتب تعبير العالمة القياسية المكافيء لتعابير العالمة الالحقة التالية:
1. 10 , 3 , * , 7 , 1 , - , * , 23 , +
2. 12, 7, 3, - , / , 2 , 1 , 5 + , * , +
3. ABCD^*E/+F-
تمرين :1
1. *ab+cd-
2. abcde*+/-
3. ab+c*de--fg+/
163
C++ هياكل البيانات باستخدام
:2 تمرين
1. ABC*+DE*F/–
2. ABC^D/E*+F+A–
3. A B^^C 2/B*4+
4. AB/CD/+EF2^*B/–
5. AB+CB-*24^/
6. AB+CD–*
7. ABCDE*+/–
8. AB+C*DE––FG+/
:3 تمرين
1. 13
2. 25
3. 17
4. 1625
:4 تمرين
1. AB – DE / * FG * H * /
2. ABD ^ + E F - / G +
3. ABD+*E/F–GHK/+-
4. ABCD+EF+*G/+*H*
5. ABC+DE+F*+G/+
6. A NOT B NOT C NOT AND OR
هياكل البيانات باستخدام C++
تمرين :5
1. 10 * 3 * ( 7 – 1 ) + 23
2. 12 / ( 7 – 3 ) + ( 1 + 5 ) * 2
3. A+B*C^D/E-F
165
هياكل البيانات باستخدام C++
هياكل البيانات باستخدام C++
الفص الالامس
البابور QUEUE
5.1المقدمة
في حياتنا اليومية هنا الكثير من الحاالت التري نررى فيهرا النراس يقفرون علرى هي رة
طوابير لشراء سلعة او انجاز عمل ,هي موجودة غالبا الي خدمة ..من دفع الفرواتير
في السوبرماركت الى صعود الباص وليس انتهراءا فري البنرو والمصرارف .فعنردما
يكون مجهز الخدمة غير قادر على انجاز خدمة عميل قبل ان يصرل العميرل الالحرل
فعنررد ذا نحترراج الررى الطررابور .تخيررل مايحرردث عنرردما يخرردم مقرردم الخدمررة (مررثال
المحاسب في البنك) شخص ما بشكل عشوائي قبل االخرين الذين ربما وصرلوا قبلره
دون مراعاة لزمن وصول االشخاص للحصول على هذل الخدمة.
تشبه قوائم االنتظار في الحاسبات طوابير االنتظرار فري الحيراة اليوميرة عنردما يقرف
الناس على هي ة طرابور لشرراء سرلعة أو نجراز عمرل ،ويطبرل فري كالهمرا مبردأ مرن
يأتي أوالً يخدم أوالً ،وفي الحاسوب يطبل نفس المبدأ.
167
هياكل البيانات باستخدام C++
5.2ماهو البابور
الطابور مشابهة للمكدس ماعدا امكانية اضافة البيانرات مرن خلرف الطرابور والحرذف
من االمام .كتابة برنامج الطابور هرو اكثرر صرعوبة مرن كتابرة برنرامج المكردس ,فري
الطابور نحاف علرى اينرين مرن المؤشررات مرن نروع االعرداد الصرحيحة واحرد يحردد
النهاية االمامية والثاني يحدد النهاية الخلفية للطابور.
الطابور هو مثال لهياكل البيانات الخطية ,هو نوع خاص لتجميع البيانرات بحيرث ان
البيانررات فرري التجميررع يررتم المحافظررة عليهررا بترتيررب يتناسررب مررع العمليررات الترري مررن
الممكررن ان تجرررى علررى هررذا الترتيررب ,مررن هررذل العمليررات اضررافة بيانررات الررى نهاي رة
الطابور وحذف البيانات من بداية الطابور .وهذا يجعرل الطرابور هيكرل بيراني يعتمرد
على مبدأ الداخل اوال يخررج اوال )) (First In First Out (FIFOان هرذا النظرام
FIFOيعني ان العنصرر االول الرذي يضراف الرى الطرابور يخررج اوال ,ولرذلك فران
اضافة عنصر جديد يعني ان جميع العناصر التي اضيفت قبلره يجرب ان تخررج قبرل
ان يخرج هذا العنصر الجديد.
كمثال على الطابور السريارات التري تقرف واحردة خلرف االخررى فري محطرة البنرزين
لغرض التزود بالوقود ,االنتظار بالدور على دفع فاتورة في محرل ,االنتظرار بالردور
لقطع تذكرة ,االنتظار في الطابور في نقاط التفتيش االمني...الا.
وفرري مجررال الحاسرربات فرران الطررابور يعتبررر مررن الهياكررل المعروف رة جرردا فرري انظمررة
التشغيل وشبكات الحاسوب.
والطررابور مررن الممكررن ان ينمررو كبرررا ليصرربح طويررل ,ولكررن فقررط عنصررر واحررد مررن
الممكن ان يدخل في الوقت الواحد وكرذلك عنصرر واحرد يخررج او يخردم فري الوقرت
الواحد ,ويتبع الطابور هيكلية الداخل اوال خارج اوال بشكل عام .وهنا نروعين مرن
الطوابير هما الطابور الخطي والطابور الدائري.
هياكل البيانات باستخدام C++
5.4البوابير الالبية
169
هياكل البيانات باستخدام C++
من الممكن تمثيل الطوابير ماديا في الذاكرة كمصفوفة وتدعى حينها هياكل البيانرات
المستقرة static data structureوعندها سترث جميع صفات المصرفوفات ,او
مررن الممكرررن ان تمثرررل كقائمرررة موصرررولة وبهررذا تررردعى هياكرررل البيانرررات الديناميكيرررة
dynamic data structureوستمتلك جميع خواص وصفات القروائم الموصرولة,
وبغررض النظررر عررن طريقررة تمثيررل الطررابور فرران عمليررات االضررافة تحرردث فرري ذيررل
الطابور بينما الحذف يحدث في راس الطابور.
ويحتوي الطابور على مؤشرين احدهما مؤشر البداية واالخر مؤشر النهاية.
يتطلب تنفيذ قوائم االنتظار على الحاسوب حجز حيز في الرذاكرة يسرتوعب البيانرات
المراد وضعها في الطابور ووفقا لحجم الطابور ,مع وجود مؤشرين يترولى المؤشرر
األول ا شارة لى رأس او بدايرة موقرع االنتظرار (مؤشرر الرراس) Head Pointer
بينما يشير ابخرر لرى نهايرة موقرع االنتظرار (مؤشرر الرذيل) Tail Pointerويشرير
مؤشر رأس القائمة لى عنوان أول موقع مشغول بينما يشير مؤشر ذيل القائمرة لرى
عنوان اخرر موقرع مشرغول فري كتلرة المواقرع المعنونرة المخصصرة للقائمرة .يوضرح
الشكل 5.3طريقة التعامل مع قوائم االنتظار.
هياكل البيانات باستخدام C++
171
هياكل البيانات باستخدام C++
استخدام مؤشرين واحد يؤشر على العنصرر االول بالطرابور والثراني يؤشرر
على العنصر االخير بالطابور.
تعتبر طريقة جيدة لترشيد استهال الذاكرة.
173
هياكل البيانات باستخدام C++
عمليررة الوصررول الررى هررذل العقررد تررتم مررن خررالل اسررتخدام المؤشرررات ,والترري سرريتم
تخصيص فصل لشرح كيفية عمل القوائم.
else
;rear = rear+1
}
175
C++ هياكل البيانات باستخدام
void deletion()
{
if (front = = 0 )
{
cout<< “\n Queue Underflow\n\n” ;
item=q[front];
if ( front = = rear )
{
front = 0 ;
rear = 0 ;
}
else if ( front = n )
front=1;
else
front = front + 1 ;
cout<< “\n%d is deleted\n\n” << item ;
}
هياكل البيانات باستخدام C++
5.10البابور الدائري
دعنررا نسررتخدم نفررس خطرروات خوارزميررة االضررافة والحررذف الترري اسررتخدمت فرري
الطابور الخطي خالل هذا الفصل .بداية نبتدأ كال النهرايتين بالقيمرة ( )-1للداللرة
على ان الطابور فار .فعندما يتم اضافة بيانات الى الطابور فران كرال النهرايتين
ستحصل على قيم جديدة .وعندما تضاف بيانات جديدة ,فان النهاية الخلفية ترزاد
بمقدار واحد ,وعندما تحذف بيانات فان النهاية االمامية تنقص بمقدار واحد .هذا
العمل جيد ولكن يملرك عيرب اساسري .االن مراذا لرو كانرت القيمرة العظمرى لطاقرة
الطابور هي 5عناصر .ونفرض ان المستخدم اضاف اربع عناصرر ابترداءا ,يرم
حذف يالث عناصر واضاف اينان اخران مرة اخررى .الطرابور سروف اليسرمح
له باكمال االضافة ,حيث سيضيف عنصر واحد فقط ويقرر ان الطابور اليوجرد
بره فرررا .السرربب اننررا نزيرد ونقلررل مررن نهررايتين مختلفترين اعتمررادا علررى االضررافة
والحذف بشكل اعمى ,والندر ان كال النهايتين متعلقتان ببعض .هنرا سرنخلص
الى سؤال مهم هو لماذا تظهر رسالة بامتالء الطابور في الوقت الذي يكرون فيره
الطابور في الحقيقة شبه فار .هذل المشركلة تقودنرا الرى ان نفكربطريقرة جديردة
تركز على البيانات اكثر من تركيزها على نهاية االضافة او نهاية الحذف.
السةةؤال المهةةم هنةةا :المصررفوفة لهررا حجررم محرردد ,فرراذا كرران مؤشررر الخلررف فرري نهايررة
المصفوفة وكان هنا عنصر جديد يراد اضافته ,فما هو الحل؟
هنا اينان من الحلول:
تزحيف كل العناصر الى االمام في المصفوفة ( وهذل مكلفة جدا).
االلتفاف حول المصفوفة وهذا يولد الطابور الدائري.
177
هياكل البيانات باستخدام C++
يتم تغيير قيم المؤشرات عند االضافة والحذف باضافة واحد لهما.
rear = (rear + 1) mod n
.1فحررص مؤشررر النهايررة فيمررا اذا كرران فرري موقررع ( ,)-1اي ان الطررابور فررار ,
عندها سيكون االجراء هو:
.Aتغير قيمة المؤشرين وذلك باضافة واحد اليهما.
front = rear = 0
179
هياكل البيانات باستخدام C++
اضافة العنصر في بداية الطابور (اول عنصر في الطابور) arr [front] = data; //
}
else {
;return
}
{ else
}
} }
]element = Q[front
يتم تغيير قيمة المؤشر وذلك بانقاصها بواحد ..الح هنا حالتين .B
)aامررا ان يكررون مؤشررري البدايررة والنهايررة متسرراويين (هررذا يعنرري ان
الطابور يحتوي على عنصر واحد فقط) عندها يتم مساواتهم للصفر
او ).(-1
front = rear = -1
)bاما اذا كان مؤشري البداية والنهاية غير متساويين ) اي ان الطابور
يحت روي علررى اكثررر مررن عنصررر واحررد) ,عنرردها يضرراف واحررد الررى
مؤشر البداية كما في ادنال
front = (front + 1) % max
م حظةةة :فرري الخطرروة ( )2تررم حجررز العنصررر المررراد حذفررة بمتغيررر وذلررك لغرررض
طباعته الحقا او اجراء اي عملية عليه ,ويمكن اهمال هرذل الخطروة اذا لرم تكرن قيمرة
العنصر المحذوف مهمة.
م حظة :ربما يسأل القاريء لماذا استخدام باقي القسرمة فري الخطروات التري تتطلرب
االضافة الى المؤشرات كما ورد في خروارزميتي االضرافة والحرذف .والجرواب هنرا
ان ) (maxتعنرري حجررم الطررابور الرردائري وبمررا اننررا نسررتخدم طررابور دائررري فهنررا
احتمررال امررتالء الطررابور وحررذف عناصررر منرره وبالتررالي يتغيررر موقررع مؤشررر النهايررة
والبداية ,فاذا امتأل الطابور يكون مؤشر النهاية على اخر موقع في الطابور (لنتخيل
الطابور ممتأل ولكن عدد من العناصر تم حذفها هنا سيكون فرا في بداية الطابور)
فاذا اردنا اضافة جديدة فان المؤشر ستزاد قيمته بواحد وبالترالي سرتكون قيمتره اكبرر
من القيمة العليا لحجم الطابور لذلك نعمل باقي القسمة لتكون قيمة مؤشر النهاية ()1
او اي قيمررة اخرررى ضررمن مرردى حجررم الطررابور ,ونفررس الشرريء ينطبررل علررى مؤشررر
البداية.
م حظة :مؤشر البداية يستخدم للحذف ومؤشر النهاية يستخدم لالضافة.
181
C++ هياكل البيانات باستخدام
return;
else {
if (front == rear) { // فقط عنصر واحد موجود في الطابور
else {
} }
int i;
cout << "front : " << front << " rear : " << rear << endl;
cout << "Current circular Queue Elements ( front to rear ) : " <<
endl;
if (front = = -1) {
هياكل البيانات باستخدام C++
;return
}
{ else
;i = front
{ do
; i = (i + 1) % SIZE
}
}
183
هياكل البيانات باستخدام C++
شك :5.7يوضح البابور الفارغب ثم وضع البابور ب د اضافة ث ث قيم بالت اقب.
االيعاز الثاني هو اضافة القريم ) (50, 10بالتعاقرب الرى الطرابور ,هرذا سريتم
وحسب ماموضح في الشكل .5.8
هنا الطابور ليس فار لذلك باالمكان الحذف وذلك بتغيير قيمة مؤشر البداية.
front = (front + 1) % size = (0 + 1) % 5 = 1 % 5 = 1
لنحذف عنصر اخر ,نتبع نفس الطريقرة اعرالل ونتاكرد مرن ان الطرابور لريس
فررار ,بعررد ان تاكرردنا مررن ان الطررابور لرريس فررار نقرروم بعمليررة الحررذف بتغييررر قيمررة
مؤشر البداية.
front = (front + 1) % size = (1 +1 ) % 5 = 2 % 5 = 2
185
هياكل البيانات باستخدام C++
الفص السا س
القوائم الموصولة LINKED LISTS
هياكل البيانات باستخدام C++
6.1المقدمة
اذا قمت بخلل قائمة (مجموعة من القيم التي لها نفس النروع مثرل قائمرة او مجموعرة
مررن القرريم مررن نرروع االعررداد الصررحيحة لغرررض تمثيررل ارقررام هواتررف مررثال ,قائمررة او
مجموعررة مررن نرروع السالسررل الحرفيررة لحف ر اسررماء الطلبررة وهكررذا ) اعتمررادا علررى
المصررفوفات فانررك مررن الممكررن ان تبرردأ االعررالن عررن المتغيرررات اعضرراء المصررفوفة
والتي ستحمل العناصر (البيانات) ,وكل عنصر من الممكرن الوصرول اليرة بواسرطة
رقم الفهرس المقابل له .عندما تعمل ذلك ,وغالبا اينراء العمرل مرع المصرفوفات فانرك
يجب ان تحدد عدد العناصر االكبر التي تتوقع انها ستمثل بواسرطة هرذل المصرفوفة.
بدون تخطيط جيد فان البعد او االبعاد المحددة للمصفوفة من الممكن ان تكون كبيرة
جدا او صغيرة جدا وهذا مرن الممكرن ان يرؤدي امرا الرى ضرياع مسراحة مرن الرذاكرة
تحجز للمصفوفة دون االستفادة منها او ان يكون هنا حاجة الرى مزيرد مرن مسراحة
الذاكرة لم يتم حجزها مسبقا وبالتالي فران كلترا الحرالتين سرتؤديان الرى قلرة فري كفراءة
البرامج التي تستخدم المصفوفات ,علما بان C++ال تسمح با عالن عن مصفوفة
دون تحديد االبعاد قبل ان تبدا المصفوفة .هذا يعني ,عنرد خلرل مصرفوفة فانرك غالبرا
يجررب عليررك ان تحرردد العرردد االكبررر للعناصررر الترري ستتضررمنها المصررفوفة .مالحظررة
اخرى بهذا الشأن هرو ان المصرفوفة تخرزن بمواقرع متجراورة وعنردما يكرون حجمهرا
كبير احيانا تبرز مشكلة عدم توفر المساحة الخزنية الكافية للمصفوفة.
187
هياكل البيانات باستخدام C++
(برنامج) المستخدم .نوع البيانرات لريس مهرم وذلرك الن نفرس التركيرب يعمرل لخرزن
عناصرررر ألي نررروع مرررن انرررواع البيانرررات .واحررردة مرررن طررررق التفكيرررر حرررول القررروائم
الموصولة هو النظر الى كيفية عمل المصفوفات والتفكير بطرق مختلفة.
ان الفرق الرئيس بين استخدام المصفوفات والقوائم الموصرولة يتمثرل بميرزة القروائم
الموصولة على اضافة عناصر جديدة وحذف عناصرر مرن القائمرة بسرهولة وسررعة
وهذل الميزة تعتبر صعبة في المصفوفات ,كذلك فان القوائم تمتاز بانها مرنه بحجم
الذاكرة التي تستخدمها حيث يمكن التمدد والتقلص بحجم الذاكرة حسب الحاجة ايناء
التنفيذ في الوقت الذي ال تتوفر هذل الميزة في المصفوفات التي تمتراز بثبرات الحجرم
وصررعوبة تغييررررل اينررراء التنفيرررذ ,ولكرررن المصرررفوفات تمتررراز بسررررعة الوصرررول الرررى
عناصرها (وصول مباشر) على عكس القروائم التري ال تصرل الرى عناصررها بشركل
مباشر ,وبالتالي بطأ الوصول الى عناصر القائمة مقارنة بالمصفوفات.
كمررا وضررحنا سررابقا فرران كررل عنصررر مررن عناصررر المصررفوفة يمكررن الوصررول لرره
باستخدام الفهرسة (رقم الموقع) وهذا يضراف الرى اسرم المصرفوفة التري عرادة تحمرل
عنوان العنصرر االول بالمصرفوفة .ونظررا الرى ان عناصرر القائمرة الموصرولة غيرر
مفهرسة وال تحتوي ارقام مواقرع لرذلك فعلينرا ان نجرد اليرة اخررى لغررض الوصرول
الى عناصر القائمة الموصولة .ان اسهل طريقرة لعمرل ذلرك هرو اوال ايجراد العنصرر
االول فرري القائمررة وبعرردها يررتم اسررتخراج عنرروان العنصررر الالحررل لرره .امررا اذا كانررت
القائمة فارغة فران هرذل المعلومرات سرتوفر لنرا ( nullوهري تعنري الشريء) .اذا كران
العنصر االول الذي وصلت لره هرو اخرر عنصرر فري القائمرة ,فانره ايضرا سريوفر لنرا
الشرريء .nullاي عنصررر اخررر فرري الوسررط مررن الممكررن ان يرروفر لررك وصررول الررى
العنصررر الالحررل فرري القائمررة .لرردعم هررذل النظريررة فعليررك ايجرراد مؤشررر الررى العنصرر
الالحل في القائمرة كمتغيرر عضرو الرى الصرنف الرذي يحمرل العناصرر .هرذا المتغيرر
العضو يمكن ان يدعى nextولكن يمكن تسميته باي اسم اخر.
والقو ائم الموصولة من الممكن ان تكون من اي نوع من االنواع التاليرة وفقرا لعالقرة
العقد او عناصر القائمة فيما بينها:
189
هياكل البيانات باستخدام C++
191
هياكل البيانات باستخدام C++
المطلوبرة .هرذا هررو السربب الن تكررون القروائم الموصررولة لهرا اشرركال مختلفرة لتسررهيل
الوصول ,مثل القوائم الموصولة الثنائية والدائرية.
القوائم الدائرية هي القوائم التي تكون فيها اخر عقدة دائما تؤشر الى العقردة االولرى.
بينمرا الثنائيررة تحترروي علررى مؤشررين احرردهما يشررير الررى العقردة الالحقررة بينمررا الثرراني
يؤشر الى العقدة السابقة.
6.6المؤشرات
سررنتطرق بمراجعررة سررريعة لرربعض المصررطلحات والقواعررد للمؤشرررات pointers
والتي تعتبر االساس في كتابة وتنفيرذ بررامج القروائم الموصرولة .ان فهرم المؤشررات
يساعد بدرجة كبيرة على فهم القوائم الموصولة والعمليات التي تجرى عليها.
المؤشر هو متغير يخزن عنوان موقع ذاكرة وبالتالي فان هذا المتغير الرذي هرو مرن
نوع مؤشر سيتعامل مرع عنراوين الرذاكرة بردل ان يتعامرل مرع القريم او البيانرات التري
تحتويها مواقع الذاكرة كما في المتغيرات االعتيادية .الموقرع او العنروان الرذي يشرير
له المتغير من نروع المؤشرر بعرض االحيران يعررف باسرم المؤشرر عليره .احيانرا فران
المؤشررات ربمرا تكرون مجموعرة مرن القريم الخاليرة nullوالتري تفسرر علرى انهرا ال
تؤشرر حاليررا الررى مؤشرر عليرره (فرري لغرة C++فرران القيمررة الخاليرة يمكررن ان تسررتخدم
القيمة المطلقة .( false
لقد تعلمنا من خالل البررامج التري نكتبهرا بران المتغيررات ينظرر لهرا علرى انهرا خاليرا
متجاورة في الذاكرة والتي من الممكن الوصول اليها من خالل المعررف الرذي يمثرل
المتغير ,وفي هذل الطريقة فان المستخدم ال يكترث او ال يهرتم حرول المواقرع الماديرة
للبيانات في الذاكرة ,وببساطة فانه يسرتخدم هرذل المعرفرات فري أي وقرت يرغرب فيره
االشارة الى متغيراته والعمل عليها.
ان ذاكررة الحاسربة مرن الممكرن ان نتخيلهرا كمجموعرة متعاقبرة مرن خاليرا الرذاكرة او
مواقع الخرزن المتجراورة ,وحجرم كرل خليرة تخصرص للمتغيرر هرو بقردر حجرم النروع
هياكل البيانات باستخدام C++
المعلن عنه لهذا المتغير ,وترقم خاليا الرذاكرة بشركل تسلسرلي واضرح .هرذل الطريقرة
تسررراعد علرررى الوصرررول الرررى أي موقرررع فررري الرررذاكرة وذلرررك باسرررتخدام هرررذل االرقرررام
التسلسلية ,وهي ارقام وحيدة لكل خلية في الذاكرة ,بمعنى انها ال تتكرر (تسمى هذل
االرقام عناوين ) .) (addressesفي كل مرة يتم االعالن عرن متغيرر مرا فران حجرم
الذاكرة التي يحتاجها هذا المتغير ستسند او تخصص له في موقع محدد مرن الرذاكرة
(هذا الموقع لره رقرم يمثرل عنوانره فري الرذاكرة) وبشركل عرام فران المسرتخدم ال يحردد
الموقع للمتغير ضمن مستوى خاليا الذاكرة .ولحسن الح فان هذل العملية تنجز اليا
بواسطة نظام التشغيل وخرالل وقرت التشرغيل ,وعلرى كرل حرال ففري بعرض الحراالت
ربما يكون المستخدم راغبا بمعرفرة العنروان الرذي ترم خرزن المتغيرر بره خرالل وقرت
التشررغيل وذلررك لكرري يعمررل علررى /او ينفررذ اوامررر نسرربة لموقعهررا فرري الررذاكرة ولكررن ال
يكون ذلك متاحا عادة.
بعد ان يرتم االعرالن عرن المؤشرر المحلري وقبرل ان يرتم اسرناد قيمرة لره ,فران المؤشرر
يحتوي على قيمة غير معروفرة .المؤشررات العامرة تبردأ اليرا بالقيمرة )( (nullوالتري
تعنرري الفرررا او ال شرريء او ربمررا تفسررر علررى انهررا صررفر) .اتفاقيررة مهمررة هرري :ان
المؤشر الذي ال يؤشر حاليا الى موقرع ذاكررة محردد فيجرب اسرناد القيمرة ) (nullلره,
حيث ان أي مؤشر يحتوي على القيمة ) (nullفهذا يعني ان المؤشرر يؤشرر علرى ال
شيء .وعلى كل حرال ,وبسربب ان المؤشرر لره القيمرة ) (nullفران ذلرك كرافي لجعلره
غيررر امررن .ان لغررة C++ال ترررغم المؤشررر الن تكررون لرره القيمررة .nullالمؤشرررات
الخالية من الممكن استخدامها لتحديد نهاية القائمة الموصولة او العقدة التي ليس لهرا
عقدة الحقة.
6.6.1عوام المؤشرات
اكثر معاملين مهمين يستخدمان مع المؤشرات هما:
عامررل العنرروان )&( (اذا وضررع امررام متغيررر فانرره سرريعيد عنرروان هررذا المتغيررر فرري
الذاكرة).
193
هياكل البيانات باستخدام C++
عامل التأشرير )*( (اذا وضرع امرام متغيرر فران هرذا المتغيرر سيشرير الرى موقرع فري
الذاكرة يخزن به عنوان ذاكرة).
يجب مالحظة ان متغيرات المؤشرر يجرب االعرالن عرن نوعهرا مسربقا فري البرنرامج
ونقصد با عالن هو تحديد عدد المواقع التي تؤشر عليها هذل المتغيرات وهري ذات
الطريقة التي كنا نتعامل بها مع المتغيررات االعتياديرة ,وسربب ان نحردد نروع متغيرر
المؤشر هو لكي يتم تحديد حجم خاليا الذاكرة التي سيتم تحديردها لعمرل هرذا المتغيرر
وبالتالي سهولة اسناد واسترجاع القيم منها وفقا لنوعها.
مثال
اذا كان لدينا االعالن التالي
;int i, *pi
في هذا االعالن ,فان iهو متغير من نوع االعداد الصحيحة ( اي انه يشير
الى بيانات في الذاكرة يحتوي على بيانات من نوع االعداد الصحيحة).
بينما piهرو مؤشرر الرى موقرع يحتروي علرى االعرداد الصرحيحة (اي ان pi
يشير الى موقع في الذاكرة (عنوان في الذاكرة) هذا العنوان اذا وصلنا له من خرالل
البرنامج سنجدل يحتوي على بيانات من نوع االعداد الصحيحة) .اما اذا قلنا
;pi = &i
هنا ان &iتعيد عنوان المتغير iوتسند قيمته الى المتغير . piاما اذا اردنرا
ان نسند قيمة الى المتغير iفيمكننا القول:
;i = 10 or ;*pi = 10
من ذلك نستنتج ان العالمة )&( اذا وضعت امام اي متغير ايناء تنفيذ البرنامج فانها
ستعيد عنوان هذا المتغير في الذاكرة وليس قيمته .اما المتغير من نوع المؤشر الذي
يعلن عنره كمؤشرر بوضرع العالمرة )*( امرام المتغيرر فانره سريحمل دائمرا عنروان فري
ال ذاكرة وال يمكن ان يحمل بيانات .بينما المتغيرات االعتيادية التي ال يوضع امامها
اي من العالمتين اعالل فأنها ستشير الى قيم او بيانات فري موقرع محردد فري الرذاكرة.
الح عندما نضع )*( امام متغير من نوع مؤشر فان التعامرل معره سريكون مشرابه
هياكل البيانات باستخدام C++
وللتوضيح ,نفرض ان اي موقع فري الرذاكرة مرن الممكرن تمثيلره مثرل الردار الرذي لره
عنوان ضمن المنطقة او الحي ,وهذا الدار يحتوي على غرفرة او اكثرر (وهري تمثرل
النوع (افتراضا) ,لنفرض الغرفة هي بايت واحد وبالتالي غرفة واحردة يعنري بايرت
واحد وهي من نوع ,characterوغرفتين كما لو نقول من نوع االعداد الصرحيحة
وهكذا) .لذلك فان الوصول الى عنوان الدار يكون من خالل اسم الدار مثال ) ,(aاما
الشررخص او االشررخاص الررذين يسرركنون هررذا الرردار فهررو/هم يمثررل (محترروى الرردار او
محترروى موقررع الررذاكرة) ,فرري الشرركل ( )6.3القيمررة هرري ) (50ورقررم الرردار (حسررب
تسلسله في المنطقة) هو موقع الذاكرة ) .(4010اذا اردنا تغيير قيمة المتغيرر ( اسرم
الشخص) في هذا الدار ,فان الطريقة المناسبة هو ان نغير الشخص الذي يسركن هرذا
الدار وبالتالي نقول ان الدار يحتوي الشخص الرذي سركن حرديثا فري الردار ..هرذا مرن
الممكن برمجيا باستخدام االيعاز التالي:
; a = 100
195
هياكل البيانات باستخدام C++
وهررذا يعنرري اننررا اخرجنررا 50مررن الرردار ووضررعنا برردال عنرره ,100دائمررا ان عمليررة
المساواة تنسا القيمة القديمة وتبدلها بالقيمة الجديدة.
لكررن الح ر ان اسررتخدام المؤشررر يسرراعد علررى الررذهاب مباشرررة الررى موقررع الررذاكرة
للعنصرررر )( (aاي الرررى رقرررم الررردار او حسرررب المصرررطلحات البرمجيرررة موقعررره فررري
الررذاكرة) ,ومررن الممكررن تغييررر قيمررة المتغيررر فرري ذلررك الموقررع مررن دون الحاجررة الررى
العنصر ) . (aمثال
; int * b
هنا تم االعالن عن مؤشر باسم bمن نوع االعداد الصحيحة
;b = & a
االن من الممكن تغييرر قيمرة aدون الوصرول الرى ( aالحر هنرا ان المتغيرر bهرو
موقع في الذاكرة لكنه بدل ان يخزن بيانات فانره يخرزن عنروان ذاكررة ,وهرو عنروان
المتغير .) a
; *b = 100
عندما تصدر ايعاز الى الحاسوب للوصول الى * bفانره يقررا المحتويرات داخرل b
والتي هي بالحقيقة عنوان aوبعدها يتم اتباع العنوان للوصرول الرى ( ) aاو عنروان
aوتتم تغيير محتويات aلتكون القيمة .100
السررؤال االن هررل مررن الممكررن قررراءة وتغييررر محتويررات aدون الوصررول الررى b؟
الجواب نعم ممكن اذا عملنا مؤشر الى مؤشر .الح المثال التالي:
) (int main
{
;*b = 100
;c = &b
;**c = 200 تغيير قيمة ' 'aباستخدام مؤشر الى مؤشر //
;return 0
}
م حظةةة :فرري كثيررر مررن البرررامج نرررى االيعرراز q=q->linkداخ رل البرنررامج .هررذل
العبارة فقط تزحف المؤشر من عقدة الى عقدة اخرى (أي من موقع في الذاكرة الرى
موقع اخر).
197
هياكل البيانات باستخدام C++
{
int ;data
;node * next
;}
الح هنا ان المؤشر اعلن عنه من نوع nodeاي من نروع التركيرب وهرذا
يعني بانه سيؤشر الى التركيب وليس الى حقل محدد من التركيب.
م حظة :عند العمل على انشاء قوائم موصرولة فأننرا سرنحتاج الرى خلرل عقرد جديردة
باستمرار وكلما دعت الحاجة ,هذا من الممكن ان يتم من خالل االيعاز ).(new
مثال
هياكل البيانات باستخدام C++
; node *Q
هنا تمت اضافة بيانات وهي القيمة 100الى حقل البيانات في العقدة )(Q
اضافة عنوان الى حقل العنوان في العقدة التي يتم خلقها ,يتم مرن خرالل االيعراز
التالي مع مالحظة ان الجانب االيمن من المساواة اما يكون عنوان عقدة اخررى
او يكون ) (nullفي حالة كرون هرذل العقردة التري خلقرت هري العقردة االخيررة فري
القائمة.
; Q -> next = null
199
هياكل البيانات باستخدام C++
الح عنوان العقدة الثاني موجرود فري حقرل العنروان للعقردة االولرى ,وعنروان العقردة
الثالثة موجود في حقل العنوان للعقدة الثانية ..وهكذا لبقية العقرد حيرث يكرون عنروان
كل عقدة موجود في حقل عنوان العقدة السابقة.
هياكل البيانات باستخدام C++
الوصررول الررى العقرردة الالحقررة يكررون مررن خررالل قررراءة عنوانهررا الموجررود فرري حقررل
العنوان للعقدة التي تسبقها.
فمثال للوصول الى العقدة الثانيرة ,يجرب اوال الوصرول الرى العقردة االولرى مرن خرالل
عنوانها المحفوظ بالمتغير startيم قرراءة عنروان العقردة الثانيرة المحفروظ فري حقرل
العنوان للعقدة االولى باستخدام االيعاز التالي:
; start - > next
لو اردنا ان نصل الى العقدة الثالثرة مرثال نضرع مؤشرر علرى العقردة االولرى )(temp
ونحركرره للثالثررة .ايضررا مررن الممكررن الوصررول للعقرردة الثالثررة باسررتخدام العنرراوين فرري
العقدة االولى والثانية ,وذلك الن عنوان العقدة الثانية موجود باألولى وعنوان الثالثة
بالثانية فيكون االيعاز التالي مفيد
; Start -> next -> next
الح هنا اوال سنصل من خالل االيعاز ) (start -> nextالى عنوان العقدة الثانية
وهذا االيعاز يكاف العنوان (1684ألن حقل عنوان العقدة الثانيرة موجرود فري حقرل
العنروان nextللعقردة االولرى ) startولرذلك فران االيعراز >(start -> next -
) nextمن الممكرن ان نتخيلره ليصربح ) (1684-> nextوهرذا يعنري انره يشرير الرى
عنوان العقدة الالحقة في العقدة ( 1684سيتم قراءة العنروان 1688وبهرذا نكرون قرد
وصلنا الى العقدة الثالثة) .االن حاول تفسير االيعاز >(start -> next -> next -
) .next
لغررض قررراءة جميررع بيانرات القائمررة الموصررولة (وهرري نفرس فكرررة الوصررول لجميررع
العقد في القائمة) نالح الكود التالي:
; temp = start
) while ( temp!=null
{
; " "<< cout << temp -> data عرض بيانات العقدة المؤشر عليها بالمؤشر // temp
201
هياكل البيانات باستخدام C++
}
بعض العمليات االساسية التي من الممكن انجازها على القروائم الموصرولة االحاديرة
هي:
بعض االحيان تبررز الحاجرة الرى اضرافة عقردة جديردة فري نهايرة القائمرة الموصرولة.
ولغرض االضافة في نهاية القائمة فالبد من الوصول الى العقدة االخيرة فري القائمرة
ومن يم االضافة بعدها (العقدة االخيرة فري القروائم االحاديرة يرتم معرفتهرا وذلرك بران
يكون حقل العنوان في العقدة يحمرل القيمرة ) .((nullوحيرث اننرا ال نملرك معلومرات
عن القائمة سوى معلومات عن راس القائمة او عنوان العقدة االولى ,لذلك علينا ان
نبرردأ مررن العقرردة االولررى ويررتم المرررور علررى جميررع العقررد واحرردة تلررو االخرررى لحررين
الوصررول الررى العقرردة االخيرررة ومررن بعررد ذلررك تررتم االضررافة بعررد هررذل العقرردة (العقرردة
االخيرة) .في هذل الحالة فان من المهم ان نتبع الخطوات التالية:
203
هياكل البيانات باستخدام C++
دائمررا يررتم فحررص القائمررة للتأكررد مررن انهررا ليسررت فارغررة .نخلررل عقرردة جديرردة .1
(لنسررميها ) tempيررم نضرريف المعلومررات المطلوبررة الررى هررذل العقرردة ,وهرري قسررمين
االول قسم البيانات اي نضيف بيانات الرى حقرل البيانرات ,ويانيرا حقرل العنروان وهنرا
يضاف عنوان العقدة الالحقة في حقرل العنروان ,وبمرا اننرا سنضريف هرذل العقردة الرى
نهاية القائمة الموصولة ,وهذا يعني ان هذل العقدة ستكون اخر عقدة فري القائمرة ,اي
ان العنوان الذي سيوضع في حقل العنوان هو ال شيء ).(null
يتم خلل او االعالن عن متغير من نوع المؤشر ويوضع عنوان عقدة البداية .2
في هذا المؤشر (نفرض مؤشر باسم )qهذا المؤشر سيتم تحريكه من عقدة الخرى
لحين الوصول الى العقدة االخيرة .ان السبب باستخدام مؤشر يان يؤشر على العقردة
االولى الن العقدة االولرى دائمرا يؤشرر عليهرا مؤشرر يسرمى مؤشرر البدايرة او مؤشرر
الراس ) )head or startويجرب ان نحراف عليره لكري نرتمكن مرن الوصرول الرى
القائمة متى ما رغبنا اما اذا تم استخدام مؤشر البداية وتم تحريكه الرى العقردة الثانيرة
فأننا سنفقد عنوان العقدة االولى والقائمة.
هياكل البيانات باستخدام C++
;q->next = temp
شك :6.10توضيح كيفية وصول المؤشر الثاني الل ال قدة االويرة مع الحفاظ علل مؤشر
البداية
بهذا نكون قد ربطنا عقدة جديدة بنهاية القائمة الموصولة. .4
205
هياكل البيانات باستخدام C++
دعنا نحاول اضافة عقدة جديدة الى بداية القائمة او بكالم اخر وضع عقدة جديدة في
بدايرة القائمررة .ان ربررط عقردة جديرردة فرري بدايرة القائمررة هررو عمرل بسرريط وذلررك ألنره ال
يتطلب المرور على عناصر القائمة وايجاد الموقع المطلوب لحشر العقدة به .عملية
االضافة تتم بربط العقردة الجديردة بالعقردة االولرى يرم تغييرر اسرم العقردة الجديردة لرتكن
عقدة البداية.
اذا لرم تكررن القائمررة فارغررة ,يررتم خلرل عقرردة جديرردة ) (tempيررم نبرردأ بإضررافة )a
البيانات الى حقل البيانات للعقدة الجديدة المراد اضافتها.
اضافة عنوان العقدة االولى الى حقل العنوان في العقدة المراد اضرافتها الرى )b
القائمة .وحسب االيعاز التالي:
وضررع عنررروان العقررردة المضرررافة فررري متغيررر عنررروان العقررردة االولرررى للقائمرررة )c
) (startاي ان المتغير الذي يحمل اسم العقدة الجديردة tempسريتم تغييرر تسرميته
بحيث يكون اسمه startلكي يدل على انه العقدة االولى او عقدة البداية.
نفرررض وجررود الحاجررة الررى حشررر عقرردة جديرردة فرري القائمررة (فرري أي موقررع محرردد
بالقائمة) .هنا سيكون من المهم المرور على عناصر القائمة يجاد الموقع المطلوب
لحشر العقدة فيه .في عمليرة المررور ,يجرب ان نحراف علرى مؤشررين احردهما يشرير
الررى العقرردة السررابقة واالخررر يشررير الررى العقرردة الالحقررة فرري القائمررة (بمعنررى ان يكررون
المؤشررران علررى عقرردتين متجرراورتين) .وذلررك الن العقرردة عنرردما يررراد حشرررها بررين
207
هياكل البيانات باستخدام C++
عقدتين ,فالبد ان تكون هنا عقدة سابقة لها واخرى الحقة لهرا (أي اننرا نحتراج الرى
عناوين عقدتين لنربط العقدة الجديدة بهما).
شك :6.15شك توضيحي لقائمة تضاذ لها عقدة ديدة في موقع محد .
دائما نفحرص ان تكرون القائمرة غيرر فارغرة .فران كانرت غيرر فارغرة نسرتمر )a
بعملنا.
يتم خلل عقدة جديدة ,ومن يم نضيف البيانات الى العقدة المراد اضافتها. )b
في هذل المسالة توجد حالتين وهما ..االولى اضرافة العقردة قبرل عقردة محرددة )c
في القائمة ,والثانية اضافة العقدة بعرد عقردة محرددة فري القائمرة ..ان هراتين الحرالتين
تختلفان بشكل بسيط في طريقة المعالجة.
في كلتا الحالتين سنبحث عرن العقردة المحرددة المرراد الوصرول لهرا ,اي كرأن )d
تكون عقدة تحتوي على بيانات معينة ,او بموقع معين مثال العقردة الثالثرة او الرابعرة
وهكذا ,في هذل الحالة نتبع الخطوات التالية:
اذا كان المطلوب اضافة العقدة قبل عقدة محددة (مرثال قبرل العقردة الثالثرة ,او
قبل العقدة التي تحتوي القيمة ,)40عندها سنعمل ما يلي:
هياكل البيانات باستخدام C++
استحداث اينان مرن المتغيررات مرن نروع مؤشرر يسرند عنروان العقردة االولرى .I
الحرردهم ,بينمررا يسررند لألخررر عنرروان العقرردة الثانيررة (يكررون المؤشررران علررى
عقدتين متجاورتين).
;q = start
209
هياكل البيانات باستخدام C++
على عقدتين متجاورتين ,بحيرث المؤشرر الثراني يؤشرر علرى العقردة السرابقة
للمؤشر االول).
التأكد بشكل مسرتمر ان المؤشرر االول ) (Sلرم يصرل الرى نهايرة القائمرة قبرل .V
تحريكه .فان وصل الى نهاية القائمة هذا يعني عدم وجود العقدة التي نبحث
عنها لغرض ان نضيف العقدة بجوارها.
في حالة الوصرول الرى العقردة المطلوبرة سريكون المؤشرر االول ) (Sيؤشرر .VI
عليها بينما المؤشر الثاني ) (qيؤشر على العقدة السابقة لها.
اضافة عنوان العقدة المحددة (المرراد االضرافة قبلهرا) الرى حقرل العنروان فري .VII
العقدة الجديدة المراد اضافتها (هذا العنوان موجود فري حقرل العنروان للعقردة
التي يؤشر عليها المؤشر االول )S
اضرافة عنرروان العقرردة المرراد اضررافتها الررى حقررل العنروان للعقرردة الترري يؤشررر .VIII
عليها المؤشر الثاني .q
هياكل البيانات باستخدام C++
شك :6.18يوضح كيفية ربط ال قدة الجديدة بالقائمة وطريقة وضع ال ناوين.
211
هياكل البيانات باستخدام C++
يضاف عنوان العقردة الالحقرة (الالحقرة للعقردة المرراد االضرافة بعردها وهري .II
الترري يؤشررر عليهررا المؤشررر ,Sهررذا العنرروان موجررود فرري حقررل عنرروان العقرردة
المراد االضافة بعدها) الى حقل العنوان في العقدة المرراد اضرافتها( .ارجرو
االنتبال الى ان عنوان العقدة الالحقة للعقدة التي يؤشر عليها المؤشرر Sهرو
موجود في الحقل nextفي الشكل .)6.20
يضاف عنوان العقدة المراد اضافتها الى حقل عنوان العقدة المراد االضرافة .III
بعدها.
شك :6.20توضيح كيفية اضافة ال قدة ب د عقدة محد ة وطريقة اضافة ال ناوين.
ان حذف عقدة من بداية القائمة عملية التحتاج الى جهد كبير ,وهري تتمركرز بتغييرر
موقع مؤشر العقدة االولى ليوضع على العقردة الثانيرة (بحيرث تكرون هرذل العقردة هري
العقدة االولى) ,هذل العملية من الممكن انجازها وذلك بان نضع عنوان العقدة الثانية
في المتغير .start
يتم تحرير مساحة العقدة االولى (هذا يتم بوضرع مؤشرر علرى العقردة االولرى قبرل ان
يتم تسمية العقدة الثانية باسم العقدة االولى).
213
هياكل البيانات باستخدام C++
; Q = start
;start = start->next
; )free (Q
حذف عقدة من نهاية القائمة الموصولة يتطلب الوصول الى العقردة النهائيرة لغررض
حذفها ,الح هنا اذا وصلنا الى العقدة النهائية وتم حذفها فان العقدة السابقة لهرا هري
التي ستكون العقدة النهائية وهذا يعني من المفروض اضافة nullالى حقرل عنروان
العقدة التي تسبل العقدة النهائية الحالية.
من هذا فأننا نحتاج الى مؤشر يصل الى العقدة النهائيرة ,وكرذلك مؤشرر اخرر يؤشرر
على العقدة السابقة للعقدة النهائية لكي نحاف عليها عند حذف العقدة النهائية.
اذن سنخلل مؤشرين احدهم يوضع على العقدة االولى ) (Sوالثاني ) (qيوضع على
العقدة الثانية ,ويتم تحريك المؤشرين بمقدار عقدة واحدة في كل مررة لحرين وصرول
المؤشر االول ) (Sالذي وضع ابتداءا على العقدة الثانية الى العقدة النهائية.
عنرد الوصرول الرى العقرردة النهائيرة يرتم وضرع nullفرري حقرل العنروان للعقردة السررابقة
للعقدة النهائية والمؤشر عليها بالمؤشر qوبالتالي فان العقدة النهائية سرتكون خرارج
القائمة.
يتم تحرير مساحة العقدة االخيرة والمؤشر عليها بواسطة المؤشر .S
; q->next = null
215
هياكل البيانات باستخدام C++
; )free (S
الحالةةة الثالثةةة هةةي حةةذذ عقةةدة مةةن او ة القائمةةة اي ال هةةذ ال قةةدة تو ةةد بةةين
عقدتين.
في هذل الحالة ايضا سنحتاج الى مؤشررين مؤشرر سيصرل الرى العقردة المرراد حرذفها
واالخر يكون على العقدة السابقة لهرا ,ألننرا بالنتيجرة نحتراج ان نرربط العقردة السرابقة
للعقدة المراد حذفها بالعقدة التي تلي العقدة المرراد حرذفها بحيرث تكرون العقردة المرراد
حذفها خارج القائمة.
نخلل متغيرين من نوع مؤشر ونضعهما على العقدة االولى والثانية ,ونبردأ بتحريرك
المؤشرين بمقدار عقدة واحدة في كل مرة لحين وصول المؤشر االول Sالى العقردة
المطلرروب حررذفها (دائمررا عنررد تحريررك المؤشرررات يجررب ان يكررون المؤشرررين علررى
عقدتين متجاورتين).
هياكل البيانات باستخدام C++
شك :6.28قائمة موصولة مع المؤشرات التي وصلت الل ال قدة المرا حذفها.
تررتم عمليررة الحررذف وذل رك بوضررع عنرروان العقرردة الالحقررة للعقرردة الترري يؤشررر عليهررا
المؤشرر االول ( Sوهرري العقرردة المررراد حررذفها) فرري حقررل عنرروان العقرردة الترري يؤشررر
عليها المؤشر الثاني ( qالح هنا ان عنروان العقردة الالحقرة دائمرا موجرود فري حقرل
العنوان للعقدة التي يؤشر عليها المؤشر) .يتم تحرير مساحة العقدة المحذوفة.
شك :6.29كيفية انتقال ال ناوين عند حذذ عقدة بين عقدتين في قائمة موصولة احا ية.
217
هياكل البيانات باستخدام C++
ترتيب القائمة الموصولة سهل جدا الى حد ما .بدايرة نخلرل مؤشررين يوضرع احردهم
علررى العقرردة االولررى والثرراني يوضررع علررى العقرردة الثانيررة .هنررا يجررب ان نأخررذ بيانررات
العقدة االولى ونقارنها مع بيانات جميع العقرد الالحقرة ومترى مرا وجردنا بيانرات اكبرر
يتم اسرتبدالها (فري حالرة الترتيرب التنرازلي) .بعردها ننتقرل الرى العقردة الثانيرة ونقرارن
بياناتهررا مررع جميررع بيانررات العقررد الالحقررة لهررا ويررتم ابرردال القرريم الكبيرررة مررع الصررغيرة
وهكذا لحين اكمال جميع العقد.
{ ) ( void ListSort
; int x = 0
{ ) for (temp1 = start ; temp1 - > next != null ; temp1 = temp1 - > next
) for (temp2 = temp1 - > next; temp2 != null ; temp2 = temp2 - > next
البرنامج الكامل لجميع العمليات التي تجرى على القوائم الموصولة االحادية
*/
*/
>#include<iostream
>#include<cstdio
>#include<cstdlib
*/
*/
struct node
{
;int info
219
C++ هياكل البيانات باستخدام
} *start ;
/*
* االعالن عن الصنف
*/
class single_llist
public:
node* create_node(int);
void insert_begin();
void insert_pos();
void insert_last();
void delete_pos();
void sort();
void search();
void update();
void reverse();
void display();
single_llist()
start = NULL;
};
C++ هياكل البيانات باستخدام
main()
single_llist sl ;
start = NULL;
while (1)
cout<<endl<<"---------------------------------"<<endl;
cout<<endl<<"---------------------------------"<<endl;
cout<<"7.Search Element"<<endl;
cout<<"10.Exit "<<endl;
cin>>choice;
221
C++ هياكل البيانات باستخدام
switch(choice)
case 1:
sl.insert_begin();
cout<<endl;
break;
case 2:
sl.insert_last();
cout<<endl;
break;
case 3:
sl.insert_pos();
cout<<endl;
break;
case 4:
sl.sort();
cout<<endl;
break;
C++ هياكل البيانات باستخدام
case 5:
sl.delete_pos();
break;
case 6:
sl.update();
cout<<endl;
break;
case 7:
sl.search();
cout<<endl;
break;
case 8:
sl.display();
cout<<endl;
break;
case 9:
sl.reverse();
cout<<endl;
223
C++ هياكل البيانات باستخدام
break;
case 10:
cout<<"Exiting..."<<endl;
exit(1);
break;
default:
cout<<"Wrong choice"<<endl;
}
/*
* خلق عقدة
*/
if (temp == NULL)
return 0;
C++ هياكل البيانات باستخدام
else
temp->info = value;
temp->next = NULL;
return temp;
/*
*/
void single_llist::insert_begin()
int value;
cin>>value;
temp = create_node(value);
if (start == NULL)
start = temp;
start->next = NULL;
225
C++ هياكل البيانات باستخدام
else
p = start;
start = temp;
start->next = p;
}
cout<<"Element Inserted at beginning"<<endl;
/*
*/
void single_llist::insert_last()
int value;
cin>>value;
temp = create_node(value);
s = start;
s = s->next;
temp->next = NULL;
s->next = temp;
/*
*/
void single_llist::insert_pos()
cin>>value;
temp = create_node(value);
cin>>pos;
int i;
s = start;
while (s != NULL)
227
C++ هياكل البيانات باستخدام
s = s->next;
counter++;
if (pos == 1)
if (start == NULL)
{
start = temp;
start->next = NULL;
else
ptr = start;
start = temp;
start->next = ptr;
s = start;
{
C++ هياكل البيانات باستخدام
ptr = s;
s = s->next;
ptr->next = temp;
temp->next = s;
else
/*
*/
void single_llist::sort()
int value;
if (start == NULL)
return;
229
C++ هياكل البيانات باستخدام
ptr = start;
{
value = ptr->info;
ptr->info = s->info;
s->info = value;
ptr = ptr->next;
/*
*/
void single_llist::delete_pos()
if (start == NULL)
C++ هياكل البيانات باستخدام
cout<<"List is empty"<<endl;
return;
cin>>pos;
s = start;
if (pos == 1)
start = s->next;
else
while (s != NULL)
s = s->next;
counter++;
s = start;
231
C++ هياكل البيانات باستخدام
ptr = s;
s = s->next;
ptr->next = s->next;
else
{
free(s);
cout<<"Element Deleted"<<endl;
/*
*/
void single_llist::update()
if (start == NULL)
{
C++ هياكل البيانات باستخدام
cout<<"List is empty"<<endl;
return;
cin>>pos;
cin>>value;
s = start;
if (pos == 1)
start->info = value;
else
if (s == NULL)
return;
s = s->next;
233
C++ هياكل البيانات باستخدام
s->info = value;
cout<<"Node Updated"<<endl;
/*
* البحث عن عنصر
*/
void single_llist::search()
if (start == NULL)
cout<<"List is empty"<<endl;
return;
cin>>value;
s = start;
C++ هياكل البيانات باستخدام
while (s != NULL)
pos++;
if (s->info == value)
flag = true;
s = s->next;
if (!flag)
/*
*/
void single_llist::reverse()
if (start == NULL)
cout<<"List is empty"<<endl;
235
C++ هياكل البيانات باستخدام
return;
if (start->next == NULL)
return;
ptr1 = start;
ptr2 = ptr1->next;
ptr3 = ptr2->next;
ptr1->next = NULL;
ptr2->next = ptr1;
ptr1 = ptr2;
ptr2 = ptr3;
ptr3 = ptr3->next;
ptr2->next = ptr1;
start = ptr2;
/*
C++ هياكل البيانات باستخدام
*/
void single_llist::display()
if (start == NULL)
return;
temp = start;
cout<<temp->info<<"->";
temp = temp->next;
cout<<"NULL"<<endl;
237
هياكل البيانات باستخدام C++
القوائم الموصولة الثنائية هي مجموعة من العقرد (كمرا فري القروائم االحاديرة) تررتبط
فيما بينها بواسطة مؤشرات مع مالحظة ان كل عقدة في القوائم الثنائية تحتوي على
يالث حقرول وهري حقرل البيانرات وحقلرين اخررين احردهما يشرير الرى العقردة الالحقرة
ويسمى المؤشر االيمن ,ومؤشر اخر يشير الى العقدة السابقة وهرو المؤشرر االيسرر.
ان الفرق بين القوائم الموصولة االحادية والقروائم الموصرولة الثنائيرة هرو ان القروائم
الموصولة الثنائية تحتوي على مؤشر اضافي في كل عقدة يؤشر الى العقدة السابقة.
الح عندما ال توجد عقدة يؤشر عليها المؤشر فانه سيؤشرر الرى nullوهرذل الحالرة
سررتحدث فرري العقرردة االخيرررة حيررث ال توجررد عقرردة الحقررة لهررا وبالتررالي فرران المؤش رر
االيمن سيؤشر الى nullوكذلك العقدة االولى فان المؤشر االيسرر ال يؤشرر الرى اي
عقدة سابقة للعقدة االولى وبالتالي سيؤشر المؤشر االيسر للعقدة االولى الى .null
{
; int data
; node *next
; node *prev
}
بعد ان تتم عملية خلل العقدة (والتي سنسميها ) tempتتم اضافة البيانات الى حقل
البيانات ,يم نقوم بإضافة العناوين الى حقول العناوين االيمن وااليسر .الح هنا بما
اننررا نخلررل عقرردة جديرردة لقائمررة غيررر موجررودة اصررال (اي ان هررذل العقرردة هرري العقرردة
االولى او النواة لخلرل قائمرة جديردة) فران حقرول العنراوين االيمرن وااليسرر سروف ال
239
هياكل البيانات باستخدام C++
تؤشر الى اي عقدة سابقة او الحقة لعدم وجود عقد اخرى وبالتالي فسنضع فري هرذل
الحقول .null
; new temp
{
; temp -> prev = null
; start = temp
}
الح هنا اننا فحصنا عنوان العقدة االولى startلنتأكد من انها تساوي nullللداللة
على عدم وجود قائمة ,حيث انه اذا كانت هنا قائمة فان العنوان startسيكون فيره
قيمة عنوان وليس .null
نعمل حلقة تكرار تبدا من العقدة االولى وتنتهي عنردما يكرون عنروان العقردة الالحقرة
(العنرروان االيمررن) يسرراوي ,nullبحيررث فرري كررل دورة يررتم نقررل المؤشررر الررى العقرردة
الالحقة طالما مؤشر العنوان االيمن اليساوي .null
عندما يصل المؤشر الى العقدة االخيرة نقوم بإضافة العقدة الجديدة بعدها وكما يلي:
يوضع عنروان العقردة الجديردة فري حقرل العنروان االيمرن للعقردة االخيررة ,وبرذلك فران
العقدة االخيرة في القائمة ستؤشر على العقدة الجديدة بحيث تكون العقدة الجديدة هي
االخيرة) .يوضع عنوان العقدة االخيرة في حقل العنوان االيسر للعقدة الجديردة ,كمرا
في الشكل .6.32
; s = start
241
هياكل البيانات باستخدام C++
; start = temp
243
هياكل البيانات باستخدام C++
عند الوصل الى العقدة المراد االضافة بعدها او قبلها ,نتبع الخطوات التاليرة لغررض
اتمام عملية االضافة ,في هذل الحالة لدينا احتماالن..
)aاالول ان تكون االضافة بعد العقدة المحددة وهنا لدينا احتماالن:
االحتمال االول ..ان تكون العقدة المراد االضافة بعردها هري العقردة االخيررة
عندها تتم االضافة كما سبل وان وضحنا عند االضافة في نهاية القائمة.
االحتمال االخر ان تكون االضرافة فري وسرط القائمرة عنردها نتبرع الخطروات
التالية (الشكل :)6.35
نضع عنوان العقدة الالحقة للعقدة المراد االضافة بعردها (عنوانهرا موجرود فري حقرل
العنوان االيمن للعقدة )qفي حقل العنوان االيمن للعقدة الجديدة.
نضع عنوان العقدة التي سنضيف بعدها في حقل العنوان االيسر للعقدة المضافة.
نضع عنوان العقدة الجديدة فري حقرل العنروان االيسرر للعقردة التري تلري العقردة المرراد
االضافة بعدها.
هياكل البيانات باستخدام C++
كذلك نضع عنوان العقدة الجديدة في حقل العنوان االيمن للعقدة التي سنضيف بعدها
; temp -> next = q -> next
)bاالحتمال الثاني هو ان تكون االضافة قبل العقدة المحددة فري القائمرة ..وهنرا
لدينا احتماالن ايضا:
االحتمررال االول ان تكررون االضررافة قبررل العقرردة االولررى وبهررذا سررنتبع ذات
الخطوات ل ضافة قبل العقدة االولى.
االحتمال الثاني تكون االضافة وسرط القائمرة ,وسروف ال يختلرف كثيررا عرن
االضررافة بعررد عقرردة محررددة ,بدايررة نحررر مؤشررر ليصررل الررى العقرردة المررراد
االضافة قبلها ..يم تتم اضافة عنوان العقدة الجديدة في حقل العنروان االيسرر
للعقدة المحددة المراد االضافة قبلها ,وفي حقل العنوان االيمن للعقدة السابقة
للعقردة المحررددة (وعنوانهررا موجررود فرري حقررل العنرروان االيسررر للعقرردة المحردد
االضافة قبلها).
يررتم اضررافة عنرروان العقرردة المحرردد االضررافة قبلهررا فرري حقررل العنرروان االيم رن
للعقدة الجديدة ,ويضاف حقل عنروان العقردة السرابقة للعقردة المحردد االضرافة
قبلها في حقل العنوان االيسر للعقدة الجديدة ,الشكل .6.36
245
هياكل البيانات باستخدام C++
بداية نحف عنوان العقدة االولى بمتغير نسميه مثال ( tempنستفيد منه لحف عنوان
العقدة المحذوفة وامكانية تحرير مساحتها الحقا باستخدام هذا االسم).
نسمي العقدة الثانية باسم عقدة البداية اي نسميها .start
نضع nullفي حقل العنوان االيسر للعقدة الثانية ( مرن الخطروة السرابقة فران العقردة
الثانيرة اصرربحت هرري عقردة البدايررة) .عنرروان العقردة الثانيررة موجررود فري حقررل العنرروان
االيمن للعقدة االولى.
يتم تحرير مساحة العقدة المحذوفة.
; temp = start
; )free (temp
247
هياكل البيانات باستخدام C++
; q = start
{
; )free (q
} } ; break
249
هياكل البيانات باستخدام C++
كما سبل ووضحنا ان المؤشر الذي وضعنال على العقدة االولى وتم تحريكه سيصل
الررى العقرردة المطلرروب حررذفها ,وفرري هررذل الحالررة سررتكون العقرردة المطلرروب حررذفها هري
العقدة االخيرة (العقردة االخيررة يرتم التعررف عليهرا مرن فحرص حقرل العنروان االيمرن
حيث سنجد به ,)nullشكل .6.43
لكرري نحررذف هررذل العقرردة فرران العقرردة السررابقة لهررا سررتكون هرري االخيرررة لررذلك مررن
المفروض ان يكون حقل العنوان االيمن للعقدة السابقة للعقدة االخيررة المرراد حرذفها
يحمل القيمة .null
عليه ضع nullفي حقل العنوان االيمن للعقدة السابقة للعقدة االخيرة (الح هنا ان
عنوان العقدة السابقة للعقردة االخيررة يكرون موجرود فري حقرل العنروان االيسرر للعقردة
االخيرة).
تحرير مساحة العقدة االخيرة المحذوفة.
; q -> prev -> next = null
; )free (q
هياكل البيانات باستخدام C++
)while ( q != null
{
; q = q -> next
}
; q = start
)while (q != null
251
هياكل البيانات باستخدام C++
{
; q = q -> next
; count ++
}
; cout<< “ Number of nodes in linked list =” << count
عمليررة عكررس القائمررة الموصررولة الثنائيررة (اي ان تكررون العقرردة االولررى هرري االخيرررة
والعقدة االخيرة هي االولى) ال تعتبرر عمليرة صرعبة او معقردة وانمرا هري عمليرة فري
غايررة السررهولة ,كررل مرراهو مطلرروب ان تكررون العقرردة االولررى هرري االخيرررة والعقرردة
االخيرررة هرري االولررى ,وجميررع العقررد يررتم عكسررها بعمليررة ابرردال العنرراوين فرري حقررول
العناوين االيمن وااليسر للعقدة (بمعنى ان يكون حقل العنوان االيمرن ..ايسرر وحقرل
العنوان االيسر ..ايمن) .الح هنا ان العقدة الثانيرة سرتكون العقردة قبرل االخيررة الن
العقدة االولى اصبحت االخيرة وهكذا.
نخلل مؤشرين ونضع احدهما على العقدة االولى ) (p1والمؤشر الثاني على العقردة
الثانية ).(p2
االن نجعل في حقل العنوان االيمن للمؤشر االول القيمة .null
هياكل البيانات باستخدام C++
يوضررع فرري حقررل العنرروان االيسررر للعقرردة الترري يؤشررر عليهررا المؤشررر االول (العقرردة
االولى في هذل الحالة) عنوان العقدة التي يؤشر عليها المؤشر الثاني (اي هذل العقدة
ستؤشر على المؤشر الثاني .) p2
خلل حلقة تكرار يكون فيها شرط التوقف هو ان المؤشر الثاني p2يساوي .null
داخل حلقة التكرار سيكون ما يلي
سننقل العقد الى مواقع سرابقة للعقردة االولرى وحسرب ترتيبهرا كمرا فري الشركل ,6.45
هذا يتم من خالل تغيير قيم العناوين في حقول العناوين للعقد ,كما يلي:
في العقردة التري يؤشرر عليهرا المؤشرر الثراني ,p2وضرع العنروان الموجرود فري حقرل
العنوان االيمن في حقل العنوان االيسر.
وضررع عنرروان العقرردة الترري يؤشررر عليهررا المؤشررر p1فرري حقررل العنرروان االيمررن (اي
وضع p1في حقل العنوان االيمن).
نقل المؤشرين عقدة واحدة (دائما يبقى المؤشران متجاوران) .الح هنا ان المؤشر
االول تم نقله الى العقدة التي يؤشرر عليهرا المؤشرر الثراني وال مشركلة برذلك = (p1
) , p2بينمررا المؤشررر الثرراني فرري الخطرروة الثانيررة يررتم نقلرره الررى العقرردة الالحقررة لهررا
باستخدام ايعاز يبدو ظاهريا انه ينقله الى العقدة السابقة وليس الالحقة حسب االيعاز
) (p2 = p2 -> prevوسربب ذلرك ألننرا اساسرا فري خطروة سرابقة غيرنرا قيمرة هرذا
الحقررل (حقررل عنرروان العقرردة السررابقة) بحررث وضررعنا فيرره عنرروان العقرردة الالحقررة لررذل
اقتضى التنويه).
بعد انتهاء او توقف التكرار يتم وضع عنوان المؤشر االول p1في المتغير .start
253
C++ هياكل البيانات باستخدام
p1 = start ;
p2 = p1 -> next ;
p1 ->next = null ;
p1 -> prev = p2 ;
p1 = p2 ;
p2 = p2 ->prev ;
البرنامج الكامل لجميع العمليات التي تجرى على القوائم الموصولة الثنائية
/*
#include<iostream>
#include<cstdio>
#include<cstdlib>
/*
* االعالن عن العقدة
C++ هياكل البيانات باستخدام
*/
struct node
int info;
}* start;
/*
االعالن عن الصنف
*/
class double_llist
public:
void display_dlist( );
void count( );
void reverse( );
255
C++ هياكل البيانات باستخدام
double_llist( ) ;
start = NULL ;
};
int main( )
{
double_llist dl;
while (1)
cout<<endl<<"----------------------------"<<endl;
cout<<endl<<"----------------------------"<<endl ;
cout<<"1.Create Node"<<endl;
cout<<"2.Add at begining"<<endl;
cout<<"4.Delete"<<endl;
cout<<"5.Display"<<endl;
cout<<"6.Count"<<endl;
cout<<"7.Reverse"<<endl;
C++ هياكل البيانات باستخدام
cout<<"8.Quit"<<endl;
cin>>choice;
switch ( choice )
case 1:
cin>>element;
dl.create_list(element);
cout<<endl;
break;
case 2:
cin>>element;
dl.add_begin(element);
cout<<endl;
break;
case 3:
cin>>element;
cin>>position ;
dl.add_after(element, position) ;
257
C++ هياكل البيانات باستخدام
cout<<endl ;
break;
case 4:
if (start == NULL)
break;
}
cin>>element;
dl.delete_element(element) ;
cout<<endl;
break;
case 5:
dl.display_dlist( ) ;
cout<<endl;
break;
case 6:
dl.count( ) ;
break ;
case 7:
if (start == NULL)
C++ هياكل البيانات باستخدام
break;
dl.reverse( ) ;
cout<<endl;
break;
case 8:
exit(1) ;
default:
cout<<"Wrong choice"<<endl;
return 0;
/*
259
C++ هياكل البيانات باستخدام
temp->info = value;
temp->next = NULL;
if (start == NULL)
temp->prev = NULL;
start = temp;
}
else
s = start;
s = s->next;
s->next = temp;
temp->prev = s;
/*
{
C++ هياكل البيانات باستخدام
if (start == NULL)
return;
temp->prev = NULL;
temp->info = value;
temp->next = start;
start->prev = temp;
start = temp;
cout<<"Element Inserted"<<endl;
/*
if (start == NULL)
261
C++ هياكل البيانات باستخدام
return;
int i;
q = start;
{
q = q->next;
if (q == NULL)
cout<<pos<<" elements."<<endl;
return;
tmp->info = value;
if (q->next == NULL)
q->next = tmp;
tmp->next = NULL;
tmp->prev = q ;
C++ هياكل البيانات باستخدام
else
tmp->next = q->next;
tmp->next->prev = tmp;
q->next = tmp;
tmp->prev = q;
cout<<"Element Inserted"<<endl;
/*
if (start->info == value)
tmp = start;
start = start->next ;
start->prev = NULL;
cout<<"Element Deleted"<<endl;
263
C++ هياكل البيانات باستخدام
free(tmp) ;
return;
q = start;
tmp = q->next;
q->next = tmp->next;
tmp->next->prev = q;
cout<<"Element Deleted"<<endl;
free(tmp) ;
return;
q = q->next;
if (q->next->info == value)
tmp = q->next;
C++ هياكل البيانات باستخدام
free (tmp) ;
q->next = NULL;
cout<<"Element Deleted"<<endl;
return;
/*
void double_llist::display_dlist ( )
if (start == NULL)
return;
q = start;
while (q != NULL)
265
C++ هياكل البيانات باستخدام
q = q->next;
cout<<"NULL"<<endl;
/*
* حساب عدد العناصر في القائمة الموصولة الثنائية
*/
void double_llist::count ( )
int cnt = 0;
while (q != NULL)
q = q->next;
cnt++ ;
/*
*/
) (void double_llist::reverse
{
;p1 = start
;p2 = p1->next
;p1->next = NULL
;p1->prev = p2
{
;p2->prev = p2->next
;p2->next = p1
;p1 = p2
; p2 = p2->prev
}
;start = p1
; cout<<"List Reversed"<<endl
}
مالحظ ة :عمليررة تحرردث بيانررات عقرردة فرري القرروائم الموصررولة الثنائيررة التختلررف ع رن
عملية تحدث بيانات عقدة في القوائم الموصولة االحادية.
267
هياكل البيانات باستخدام C++
يجب عليك ان تتسائل ..لمراذا يريرد اي شرخص لعمرل شريء مثرل هرذا ؟ حسرنا ..هرل
تعلم ان القوائم الموصولة الدائريرة تسرتخدم غالبرا فري حراالت متعرددة ولهرا تطبيقرات
عديدة ,على سبيل المثال هي من الممكن ان تستخدم في االعالنات االلكترونية حيث
ان كل اعالن يضاف الى القائمرة ويرتم عرضره بعرد اخرر اعرالن ترم عرضره ,وبرذلك
فان القائمة ستعرض بشكل الي اول اعالن في القائمة .في العقد الدائرية من الممكرن
ان يكررون هنررا مؤشرررين يررتم حفظهمررا وهمررا مؤشررر العقرردة االولررى ومؤشررر العقرردة
االخيرة.
هنا عدد من العمليرات التري مرن الممكرن اجرائهرا علرى القروائم الموصرولة الدائريرة
كما في حالة القوائم الموصولة االحادية (في كل العمليات التري نرروم انجازهرا علرى
القرروائم الموصررولة الثنائيررة علينررا اوال ان نتأكررد ان القائمررة ليسررت فارغررة وهررذا ينجررز
هياكل البيانات باستخدام C++
بفحص المؤشر lastفاذا كان يؤشرر الرى nullفران القائمرة فارغرة وبخالفرة القائمرة
ليست فارغة).
ال مليات التي تنجز علل القوائم الدائرية 6.11.1
التختلف القوائم الدائرية عن باقي انواع القوائم الموصولة مرن حيرث العمليرات التري
تنجز على القائمة ,فقط هنا اختالف بسريط بعمليرة التنفيرذ وشرفرة البرنرامج ,بشركل
عام فان العمليات التي تنفذ على القوائم الدائرية هي:
.Aخلل عقدة.
.Bاضافة عقدة في نهاية القائمة.
.Cاضافة عقدة في بداية القائمة.
.Dاضافة عقدة بين عقدتين.
.Eحذف عقدة.
.Fعرض القائمة.
.Gتحديث القائمة.
.Hترتيب عناصر القائمة.
.Aولر عقدة
يتم ابتداء خلل عقدة جديدة ويوضع لهرا اسرم او مؤشرر باسرم معرين يؤشرر علرى هرذل
العقدة وهو يمثل اسم هذل العقدة (نفرض ان اسم المؤشر هو .) temp
تضاف البيانات المحددة لهذل العقدة ,تضاف الى حقل البيانات.
يتم فحص المؤشر ( lastالذي يمثل عنوان العقدة االخيررة المسرماة ) lastفراذا كران
يؤشر على ( nullهذا يعني عدم وجود عقد في القائمة اي ان القائمة فارغة) عنردها
نجعل اسم العقدة التي تم خلقها في حقل عنروان العقردة الالحقرة للعقردة التري ترم خلقهرا
(اي انها ستؤشر على نفسها) ,ويعاد تسمية lastبرنفس اسرم العقردة االولرى (اي ان
start & lastسيشيران الى نفس العنوان).
269
هياكل البيانات باستخدام C++
في هذل الحالرة يرتم خلرل عقردة جديردة كمرا اسرلفنا وتسرند البيانرات فري حقرل البيانرات
للعقدة الجديدة.
بعد وضع البيانات يتم تغيير عنوان العقدة الالحقة للعقدة التي ترم خلقهرا بحيرث يسرند
له ذات العنوان الموجود في حقل عنوان العقدة االخيرة في القائمة (طالما هذل العقدة
الجديرردة سررتكون االخيرررة لررذلك البررد ان تؤشررر علررى العقرردة االولررى كمررا كانررت العقرردة
االخيرة في القائمة تؤشر).
الخطوة التالية ان يتم ربط العقدة التي هي االن اخيرة مع العقردة الجديردة لكري تكرون
الجديدة هي االخيرة.
شك :6.47اضافة عقدة في نهاية القائمة مع توضيح لكيفية وضع ال ناوين وربط ال قدة.
;temp->next = last->next
هياكل البيانات باستخدام C++
االن سررنجعل حقررل العنرروان فرري العقرردة الترري هرري االخيرررة قبررل االضررافة يؤشررر علررى
العقدة التي تم خلقها وهي العقدة temp
; last -> next = temp
اخيرا يتم تغير اسم العقدة التي تم خلقها لتكون باسم lastبدال من temp
;last = temp
م حظة :من الممكن عدم استخدام المؤشر ,lastفي هرذل الحالرة نضرع مؤشرر علرى
العقدة االولى ونحركه ليصل العقدة االخيرة (العقردة االخيررة هري العقردة التري يكرون
فيها مؤشر العقردة الالحقرة يسراوي عنروان العقردة االولرى ولريس nullكمرا سربل فري
االنواع االخرى).
271
هياكل البيانات باستخدام C++
يتم خلل عقدة جديدة ,واسناد قيمة الى حقل البيانرات فري العقردة الجديردة .ربرط العقردة
التي تم خلقها الى بداية القائمة.
تتم عملية الربط بوضع عنوان العقدة االولى في حقل العنروان للعقردة التري ترم خلقهرا
(الن العقدة االولى فري القائمرة سرتكون العقردة الثانيرة بعرد االضرافة) ,وبرذلك سرتكون
هررذل العقرردة الترري تررم خلقهررا سررابقة للعقرردة االولررى فرري القائمررة اي سررتكون هرري العقرردة
االولى وكما يلي
; temp->next = last->next
OR
الح هنا (شكل )6.49ان عنوان العقدة االولى هو موجود في حقل العنروان للعقردة
االخيرة ,lastلذلك من الممكن الحصول عليه من هنا واسنادل الى العقردة التري ترم
خلقهررا كمررا وضررحنا اعررالل واعررادة تسررمية العقرردة المضررافة بحيررث تكررون ه ري العقرردة
االولررى ) ,(startومررن الممكررن ايضررا اسررتخدامه بشرركل مباشررر مررن العقرردة االولررى
).(start
الخطوة االخيرة هي جعل مؤشر العقردة االخيررة يؤشرر علرى العقردة التري وضرعناها
في البداية ,واعادة تسمية العقدة الجديدة باسم عقدة البداية ,وكما يلي
; last->next = temp
; start = temp
وهي ال تختلف عن طريقة االضافة في القوائم االحادية ,لرذلك سروف ال نتطررق لهرا
ومن الممكن الرجوع الى اضافة عقدة بين عقدتين في القوائم االحادية.
اذا لم تكن القائمة فارغة فران هنرا اكثرر مرن احتمرال .طبعرا البرد مرن وضرع مؤشرر
على العقدة االولى ,ليكن اسمه مثال ) (Sلكل الحاالت:
273
هياكل البيانات باستخدام C++
)aالحالة االولل :هو ان تكون القائمة متكونة مرن عقردة واحردة فقرط وهرذا مرن
الممكن فحصة وذلك بان يكون عنوان العقدة الالحقة هو ذاته عنروان العقردة
ولها قيمة في حقل القيمة مساوي للقيمة المطلوبة اذا تحقرل هرذين الشررطين
فهذا يعني وجود عقدة واحدة وسيتم حذفها لنحصل على قائمة فارغة.
حررذف هرررذل العقررردة يكررون بحفررر عنررروان العقررردة بمتغيررر ,يرررم حرررذف العقررردة
بمساواتها الى nullومن بعدها تحريرر (المسراحة) التري كانرت تشرغلها هرذل
العقدة وكما يلي
;temp = last
;last = null
; )free (temp
)bالحالةةة الثانيةةة :هررو ان القيمررة المطلوبررة موجررودة فرري العقرردة االولررى لقائمررة
تحتوي على اكثر من عقدة ,عملية الحذف تتم كما يلي:
يررتم وضررع عنرروان العقرردة الثانيررة وهررو العنرروان الررذي يوجررد فرري حقررل العنرروان للعقردة
االولى في حقل العنوان للعقدة االخير (شكل .)6.52
تسمية العقدة الثانية باسم عقدة البداية .start
يتم تحرير المساحة الخاصة بالعقدة االولى tempوكما يلي
الحالة الثالثة :هو حذف عقدة تكرون وسرط القائمرة وهرذا يرتم برنفس الطريقرة )c
التي استخدمت بحذف عقدة في وسط قائمة موصولة احادية.
275
هياكل البيانات باستخدام C++
الحالةةة الراب ةة :هررو ان تكررون العقرردة المطلرروب حررذفها هرري العقرردة االخيرررة )d
عندها سيتم وضع مؤشرين االول على العقدة االولى ) (Sوالثاني على العقدة الثانية
) (tempويتم تحريكهم بواقع عقدة واحدة في كرل مررة علرى ان يرتم المحافظرة علرى
تموضع المؤشرين على عقدتين متجاورتين دائما ,يتم التوقف عنرد وصرول المؤشرر
الثاني ) (tempالى العقدة النهائية.
يررتم تغييررر العنرراوين بمررا يضررمن حررذف العقرردة االخيرررة وذلررك بوضررع عنرروان العقرردة
االولى في حقرل العنروان للعقردة السرابقة للعقردة االخيررة والتري يؤشرر عليهرا المؤشرر
االول ).(S
يعاد تسمية العقدة التي يؤشر عليها المؤشر االول باسم last
تحرير مساحة العقدة المحذوفة
;s->next = last->next
;last = s
;)free (temp
هياكل البيانات باستخدام C++
يوضررع مؤشررر علررى العقرردة االولررى يررم يررتم نقلرره الررى العقرردة المجرراورة وهكررذا لحررين
الوصول الى نهاية القائمة.
هذل العملية تتم باستخدام حلقة تكرار تبدا من اول عقدة وتنتهي باخر عقدة.
في كل عقدة يتم المرور عليها يرتم عررض القيمرة الموجرودة فري حقرل البيانرات لتلرك
العقدة.
عملية التحديث هري عمليرة تغييرر القيمرة التري فري حقرل البيانرات لعقردة مرا فري موقرع
معين داخل القائمة.
نجاز ذلك نضع مؤشر على اول عقدة.
نعمل حلقة تكرار من اول عقدة الى الموقع المحدد.
ننقل المؤشر في كل مرة الى العقدة الالحقة.
عند الوصول الى الموقع المطلوب يتم تغيير القيمة في حقل البيانات لتلك العقدة.
اذا وصل المؤشر الى العقدة االخيررة قبرل ان يرتم ايجراد العقردة المطلوبرة ,عنردها يرتم
عرض رسالة توضح ان الموقع المطلوب خارج حجم القائمة.
277
C++ هياكل البيانات باستخدام
برنامج كامل لجميع العمليات التي تجرى على القوائم الموصولة الدائرية
/*
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
/*
* االعالن عن العقدة
*/
struct node
int info;
}*last;
/*
* االعالن عن الصنف
*/
class circular_llist
C++ هياكل البيانات باستخدام
public:
void display_list( );
void update();
void sort();
circular_llist( )
last = NULL;
};
int main()
circular_llist cl;
while (1)
cout<<endl<<"---------------------------"<<endl;
279
C++ هياكل البيانات باستخدام
cout<<endl<<"---------------------------"<<endl;
cout<<"1.Create Node"<<endl;
cout<<"2.Add at beginning"<<endl;
cout<<"3.Add after"<<endl;
cout<<"4.Delete"<<endl;
cout<<"5.Search"<<endl;
cout<<"6.Display"<<endl;
cout<<"7.Update"<<endl;
cout<<"8.Sort"<<endl;
cout<<"9.Quit"<<endl;
cin>>choice;
switch(choice)
case 1:
cin>>element;
cl.create_node(element);
cout<<endl;
break;
case 2:
C++ هياكل البيانات باستخدام
cin>>element;
cl.add_begin(element);
cout<<endl;
break;
case 3:
cin>>element;
cin>>position;
cl.add_after(element, position);
cout<<endl;
break;
case 4:
if (last == NULL)
break;
cin>>element;
cl.delete_element(element);
cout<<endl;
281
C++ هياكل البيانات باستخدام
break;
case 5:
if (last == NULL)
break;
}
cout<<"Enter the element to be searched: ";
cin>>element;
cl.search_element(element);
cout<<endl;
break;
case 6:
cl.display_list();
break;
case 7:
cl.update();
break;
case 8:
cl.sort();
break;
case 9:
C++ هياكل البيانات باستخدام
exit(1);
break;
default:
cout<<"Wrong choice"<<endl;
return 0;
/*
*/
temp->info = value;
if (last == NULL)
last = temp;
temp->next = last;
else
283
C++ هياكل البيانات باستخدام
temp->next = last->next;
last->next = temp;
last = temp;
/*
*/
if (last == NULL)
return;
temp->info = value;
temp->next = last->next;
last->next = temp;
C++ هياكل البيانات باستخدام
/*
*/
if (last == NULL)
return;
s = last->next;
s = s->next;
if (s == last->next)
return;
285
C++ هياكل البيانات باستخدام
temp->next = s->next;
temp->info = value;
s->next = temp;
if (s == last)
{
last=temp;
/*
*/
s = last->next;
{
C++ هياكل البيانات باستخدام
temp = last;
last = NULL;
free(temp);
return;
temp = s;
last->next = s->next;
free(temp);
return;
if (s->next->info == value)
temp = s->next;
s->next = temp->next;
free(temp);
cout<<"Element "<<value;
return;
287
C++ هياكل البيانات باستخدام
s = s->next;
if (s->next->info == value)
temp = s->next;
s->next = last->next;
free(temp);
last = s;
return;
/*
*/
int counter = 0;
s = last->next;
C++ هياكل البيانات باستخدام
while (s != last)
counter++;
if (s->info == value)
cout<<"Element "<<value;
return;
s = s->next;
if (s->info == value)
counter++;
cout<<"Element "<<value;
return;
/*
289
C++ هياكل البيانات باستخدام
*/
void circular_llist::display_list()
if (last == NULL)
s = last->next;
while (s != last)
cout<<s->info<<"->";
s = s->next;
cout<<s->info<<endl;
/*
*/
C++ هياكل البيانات باستخدام
void circular_llist::update()
if (last == NULL)
return;
cin>>pos;
cin>>value;
s = last->next;
if (s == last)
cout<<endl;
return;
s = s->next;
291
C++ هياكل البيانات باستخدام
s->info = value;
cout<<"Node Updated"<<endl;
/*
void circular_llist::sort()
int temp;
if (last == NULL)
return;
s = last->next;
while (s != last)
ptr = s->next;
if (ptr != last->next)
temp = s->info;
s->info = ptr->info;
ptr->info = temp;
else
break;
ptr = ptr->next;
s = s->next;
293
هياكل البيانات باستخدام C++
تختلف القروائم الموصرولة الدائريرة الثنائيرة عرن االنرواع االخررى التري سربل وان ترم
التطرق لها ..حيث ان كل عقدة هنا تحتوي على حقلين للعنوان بدل من حقرل واحرد,
احدهما يوضع به عنوان العقدة السابقة (وهو حقل العنوان االيسرر) ,امرا الثراني فهرو
حقل عنوان العقدة الالحقة والذي هو (حقل العنوان االيمن) ..وبالتالي فان كل عقردة
من الممكن ان نعرف من خاللها العقدة السابقة لها والعقدة الالحقة لها ,مرع مالحظرة
ان العقدة االولى تشير الى العقدة االخيررة والعقردة االخيررة تشرير الرى العقردة االولرى
اذا كانت القائمة تحتوي على اكثر من عقدة.
ال مليات التي تجرى علل القوائم الموصولة الثنائية الدائرية 6.12.1
هنا عدد من العمليرات التري مرن الممكرن اجرائهرا علرى القروائم الموصرولة الدائريرة
الثنائية وهي:
.Aاضافة عقدة في بداية القائمة الموصولة.
.Bاضافة عقدة في نهاية القائمة.
.Cاضافة عقدة في موقع محدد ضمن القائمة.
.Dحذف عقدة من القائمة.
.Eتحديث القائمة.
.Fالبحث عن عقدة داخل القائمة.
.Gالترتيب.
هياكل البيانات باستخدام C++
م حظة :في كل العمليات اعالل والتي سيتم توضيحها الحقا سيتم فررض اسرم عقردة
البداية startواسم عقدة النهاية lastوالعقدة التي يتم خلقها نسميها .temp
كررذلك فرري كررل العمليررات علينررا ابتررداء ان نفحررص القائمررة فيمررا اذا كانررت فارغررة او ال
وتكون القائمة فارغة اذا كانت عقدة البداية تساوي عقدة النهاية (اي لهم نفس االسم)
وعقدة البداية تساوي .null
المتغيرات هنا والتي ندعوها اسماء للعقد ) (start, last, tempهي في حقيقتها من
نوع مؤشر اي تحمل عنوان.
295
هياكل البيانات باستخدام C++
في حقل العنوان االيسرر للعقردة االولرى التري فري القائمرة سنضرع عنروان العقردة التري
سيتم اضافتها )( (tempالن العقدة المضافة ستضاف قبل العقدة االولرى فري القائمرة
وستكون الحقا هي العقدة االولى).
االن بعررد ان اصرربحت العقرردة االولررى تؤشررر علررى العقرردة المضررافة والعقرردة المضررافة
تؤشر على العقدة االولى اصبح واضحا ان العقدة المضافة اصبحت هي قبرل البدايرة
(العقدة االولى ) لذلك من المناسب ان نسميها العقدة االولى وذلرك بران نضرع عنروان
العقرردة الترري سنضرريفها فرري المؤشررر ( startأي ان يررتم تسررمية العقرردة المضررافة باسررم
,) startشكل .6.57
االن بقى لدينا حقل العقدة السابقة في العقدة المضافة (حقل العنوان االيسر) ليس فيه
عنوان ,ولما كانت القائمة دائريرة فسنضرع عنروان العقردة االخيررة فري حقرل العنروان
االيسر للعقدة المضافة.
كررذلك فرران العقرردة االخيرررة كانررت تشررير فرري حقررل عنرروان العقرردة الالحقررة الررى العقرردة
االولررى الترري اصرربحت االن يانيررة بعررد االضررافة ,لررذلك يجررب تحديثرره وذلررك بإضررافة
عنوان العقدة المضافة التي هي االن اولى وغيرنا اسمها الى العقدة االولى في حقرل
العنوان االيمن للعقدة االخيرة (الن القائمة دائرية).
;temp->next = start
;start->prev = temp
;start = temp
;start->prev = last
;last->next = start
297
هياكل البيانات باستخدام C++
ان العمررل االسرراس هررو اضررافة عنرراوين فرري حقررل العنرراوين للعقرردة المضررافة وحقررل
العنوان االيمن للعقدة االخيرة في القائمة وكذلك حقل العنوان االيسر للعقردة االولرى,
مع مالحظة ان العقدة االخيرة ستكون العقدة ما قبل االخيرة عند اضافة عقدة بعردها
(الن العقرردة المضررافة سررتكون هرري االخيرررة) لررذلك فرران هررذل العقرردة الترري كانررت ه ري
االخيرة ستشير في حقل العنوان االيمن الى العقدة المضافة وليس الى العقدة االولرى
كما كانت قبل االضافة.
بالنسرربة للعقرردة المضررافة سنضرريف عنرروان العقرردة الالحقررة فرري حقررل العنرروان االيم رن
والتي هي العقدة االولى نظرا الن هذل العقدة ستصبح اخر عقدة في القائمة الدائرية.
هياكل البيانات باستخدام C++
شك :6.61توضيح كيفية ربط ال قدة المضافة مع ال قدة االولل وال قدة االولل م ها.
في حقل العنوان االيسر للعقدة المضافة سنضيف عنوان العقدة التي كانرت هري اخرر
عقدة النها ستكون سابقة للعقدة المضافة.
يتم ابدال اسم العقدة المضرافة الرى العقردة االخيررة ) (lastالنهرا اصربحت هري العقردة
االخيرة.
بالنسبة للعقدة االولى فانها كانت تشير الى عنروان العقردة االخيررة فري حقرل العنروان
االيسر ,ولما كانت العقردة االخيررة قرد ترم تغييرهرا لرذلك يجرب تحرديث حقرل العنروان
االيسر للعقدة االولى ليشير الى العقدة المضافة التي هي اصبحت العقدة االخيرة.
;last->next = temp
;temp->prev = last
;last = temp
;start->prev = last
299
هياكل البيانات باستخدام C++
;last->next = start
بداية يتم حف عنوان العقدة االولى بمتغير من نوع مؤشر ليكن .S
هياكل البيانات باستخدام C++
نغير عنوان العقدة الالحقة في حقل العنوان االيمن للعقدة االخيررة ليشرير الرى العقردة
الثانية في القائمة بدال من االولى التي سيتم حذفها (عنوان العقدة الثانيرة موجرود فري
حقل العنوان االيمن للعقدة االولى).
نضع عنوان العقدة االخيررة فري حقرل العنروان االيسرر للعقردة الثانيرة (وبهرذا سرتكون
العقدة االولى خارج القائمة) .يجب ان نغير تسمية العقدة الثانية لتكون العقدة االولى
.start
نحرر مساحة العقدة االولى التي تم ازالتها والتي تم حف عنوانها بالمتغير.S
;S = start
;last->next = S->next
;start = S->next
;)free (S
301
هياكل البيانات باستخدام C++
اذا كانت العقدة المراد حذفها هي ليست العقدة االولى وانمرا عقردة فري موقرع )b
معررين داخررل القائمررة نتبررع ذات الخطرروات الترري تررم توضرريحها فرري حررذف عقرردة بررين
عقدتين في القوائم الثنائية.
اذا كانت العقدة المراد حذفها هي العقدة االخيرة عندها نتبع الخطوات التالية )c
نحر مؤشر واحد من عقدة البداية لحين الوصول الى العقدة االخيرة (نفرض اسمه
.)S
يررتم ربررط العقرردة الترري تسرربل العقرردة االخيرررة بالعقرردة االولررى وبررذلك ستصرربح العقرردة
االخيرة التي يؤشر عليها المؤشر Sخارج القائمة (في القوائم الثنائية عنروان العقردة
السابقة ألي عقدة يكون موجود في حقل العنوان االيسر).
يعاد تسمية العقدة التي تسبل العقدة االخيرة باسم .last
يتم تحرير المساحة للعقدة المحذوفة.
م حظة :اذا كنا نعلم مسبقا ان المطلوب هو حذف العقدة االخيرة فمن الممكن انجاز
ذلك دون الحاجة لتمرير مؤشرين وذلك باالعتماد على عنوان العقدة االخيرة . last
هياكل البيانات باستخدام C++
شك :6.67شك توضيحي لكيفية ربط ال قدة السابقة لل قدة االويرة مع ال قدة االولل لغر
حذذ ال قدة االويرة.
من الممكن وضع عنوان العقدة االولى مباشرة في حقرل العنروان االيسرر للعقردة قبرل
االخيرة كما واضح من الخط المنقط اعلى الشكل .6.67
; S -> prev -> next = start
ومررن الممكررن ايضررا وضررع العنرروان االيمررن فرري العقرردة االخيرررة (وهررو يحمررل عنرروان
العقدة االولرى) فري حقرل العنروان االيمرن للعقردة قبرل االخيررة كمرا واضرح مرن الخرط
المتصل اسفل الشكل .6.67
; S -> prev -> next = S -> next
303
هياكل البيانات باستخدام C++
التحديث .C
نحدد الموقع المراد تحديث بياناته.
نحدد قيمة البيانات المراد ادخالها في حقل البيانات للعقدة المطلوب تحديث بياناتها.
اذا كان الموقع هو الموقع االول (بمعنى العقدة االولى) يتم تحديثها ,بخالف ذلك
نضع مؤشر على العقدة االولى ونحركه الى نهاية القائمرة وفري كرل مررة مرن البدايرة
الى الن هاية نفحص البيانات للعقردة التري يؤشرر عليهرا المؤشرر فراذا كانرت هري العقردة
المطلوبة يتم تحديث بياناتها والخروج من حلقة التكرار.
;S = start
do
{
;cout<<"Node Updated"<<endl
; break
}
; S = S -> next
نضرع مؤشررر علررى العقرردة االولررى .نعمررل حلقررة تكرررار مررن العقرردة االولررى الررى العقردة
االخيرة.
في كل دورة يتم اظهر او طباعة البيانات في حقل البيانات للعقدة التري يؤشرر عليهرا
المؤشر .وفي كل دورة يتم نقل المؤشر الى العقدة الالحقة.
;S = start
)while ( S != last
{
}
البرنامج الكامل لجميع العمليات التي تجرى على القوائم الموصولة الثنائية
*/
*/
>#include<iostream
>#include<cstdio
>#include<cstdlib
*/
305
C++ هياكل البيانات باستخدام
* االعالن عن العقدة
*/
struct node
int info;
int counter = 0;
/*
* االعالن عن الصنف
*/
class double_clist
public:
void insert_begin();
void insert_last();
void insert_pos();
void delete_pos();
void search();
void update();
C++ هياكل البيانات باستخدام
void display();
void reverse();
void sort();
double_clist( )
start = NULL;
last = NULL;
};
int main()
int choice;
double_clist cdl;
while (1)
cout<<"\n-------------------------------"<<endl;
cout<<"\n-------------------------------"<<endl;
cout<<"1.Insert at Beginning"<<endl;
cout<<"2.Insert at Last"<<endl;
cout<<"3.Insert at Position"<<endl;
cout<<"4.Delete at Position"<<endl;
307
C++ هياكل البيانات باستخدام
cout<<"5.Update Node"<<endl;
cout<<"6.Search Element"<<endl;
cout<<"7.Sort"<<endl;
cout<<"8.Display List"<<endl;
cout<<"9.Reverse List"<<endl;
cout<<"10.Exit"<<endl;
switch(choice)
case 1:
cdl.insert_begin();
break;
case 2:
cdl.insert_last();
break;
case 3:
cdl.insert_pos();
break;
case 4:
cdl.delete_pos();
break;
C++ هياكل البيانات باستخدام
case 5:
cdl.update();
break;
case 6:
cdl.search();
break;
case 7:
cdl.sort();
break;
case 8:
cdl.display();
break;
case 9:
cdl.reverse();
break;
case 10:
exit(1);
default:
cout<<"Wrong choice"<<endl;
return 0;
309
C++ هياكل البيانات باستخدام
/*
*/
counter++;
struct node *temp;
temp->info = value;
temp->next = NULL;
temp->prev = NULL;
return temp;
/*
*/
void double_clist::insert_begin()
int value;
cin>>value;
C++ هياكل البيانات باستخدام
else
temp->next = start;
start->prev = temp;
start = temp;
start->prev = last;
last->next = start;
cout<<"Element inserted"<<endl;
/*
*/
311
C++ هياكل البيانات باستخدام
void double_clist::insert_last()
int value;
cin>>value;
temp = create_node(value);
if (start == last && start == NULL)
else
last->next = temp;
temp->prev = last;
last = temp;
start->prev = last;
last->next = start;
}
C++ هياكل البيانات باستخدام
/*
*/
void double_clist::insert_pos()
cin>>value;
cin>>pos;
temp = create_node(value);
if (pos == 1)
else
313
C++ هياكل البيانات باستخدام
counter--;
return;
else
{
if (counter < pos)
counter--;
return;
s = start;
ptr = s;
s = s->next;
if (i == pos - 1)
ptr->next = temp;
temp->prev = ptr;
C++ هياكل البيانات باستخدام
temp->next = s;
s->prev = temp;
cout<<"Element inserted"<<endl;
break;
/*
*/
void double_clist::delete_pos()
int pos, i;
return;
cin>>pos;
315
C++ هياكل البيانات باستخدام
return;
s = start;
if(pos == 1)
{
counter--;
last->next = s->next;
s->next->prev = last;
start = s->next;
free(s);
cout<<"Element Deleted"<<endl;
return;
s = s->next;
ptr = s->prev;
ptr->next = s->next;
s->next->prev = ptr;
C++ هياكل البيانات باستخدام
if (pos == counter)
last = ptr;
counter--;
free(s);
cout<<"Element Deleted"<<endl;
/*
*/
void double_clist::update()
return;
cin>>pos;
cin>>value;
317
C++ هياكل البيانات باستخدام
return;
s = start;
if (pos == 1)
s->info = value;
cout<<"Node Updated"<<endl;
return;
s = s->next;
s->info = value;
cout<<"Node Updated"<<endl;
/*
*/
void double_clist::search()
return;
cin>>value;
s = start;
pos++;
if (s->info == value)
flag = true;
s = s->next;
319
C++ هياكل البيانات باستخدام
if (!flag)
/*
*/
void double_clist::sort()
int value, i;
return;
s = start;
temp = s->next;
{
C++ هياكل البيانات باستخدام
value = s->info;
s->info = temp->info;
temp->info = value;
temp = temp->next;
s = s->next;
/*
*/
void double_clist::display()
int i;
return;
321
C++ هياكل البيانات باستخدام
s = start;
cout<<s->info<<"<->";
s = s->next;
cout<<s->info<<endl;
}
/*
*/
void double_clist::reverse()
return;
p1 = start;
p2 = p1->next;
p1->next = NULL;
C++ هياكل البيانات باستخدام
p1->prev = p2;
p2->prev = p2->next;
p2->next = p1;
p1 = p2;
p2 = p2->prev;
last = start;
start = p1;
cout<<"List Reversed"<<endl;
323
هياكل البيانات باستخدام C++
الفص السابع
االستدعاء الذاتي RECURSION
7.1المقدمة
هياكل البيانات باستخدام C++
فكرة االعادة ) (recursiveليست معروفة بشكل عام في العالم الحقيقي ,ولذلك فهي
ربما تبدو مربكة للمبرمج المبتديء .عليه ,فراني اتوقرع بامكانيرة اسرتخدامهم مبراديء
االعادة بشكل تدريجي.
احيانا تكون المشكلة صعبة جدا او معقدة جدا لكي يمكن حلها وذلك النها كبيرة جدا.
فاذا كان من الممكن تجزأة المشكلة الى نسا صغيرة مشابه لالصل ,فربما يكون من
الممكن ايجاد طريقة لحل واحدة من هذل النسا الصغيرة وبهذا سنكون قرادرين علرى
بناء حل لكامل المشكلة .هذل هي الفكرة ما وراء االعرادة ) ,(recursiveخوارزميرة
االعادة تجزأ المشكلة الى اجزاء صغيرة والتي ربمرا تكرون اجابتهرا معروفرة مسربقا,
او من الممكن حلها بتطبيل نفس الخوارزمية على كل جزء صغير ,وبعدها يتم دمج
الحلول.
االسررتدعاء الررذاتي هرري عمليررة اعررادة العناصررر نفسررها وبررنفس الطريقررة .فرري لغررات
البرمجررة ,اذا البرنررامج سررمح لررك السررتدعاء الدالررة مررن داخررل نفررس الدالررة ,فعنررد ذلررك
تدعى االستدعاء الذاتي للدالة.
لغة C++تدعم االستدعاء الذاتي ,بمعنرى دالرة تسرتدعي نفسرها .ولكرن عنرد اسرتخدام
االستدعاء الذاتي ,المبرمجين بحاجة الن يكونوا حذرين بتعريف شرط الخروج مرن
الدالة ,في خالف ذلك فانها ستذهب (الدالة) بحلقة تكرار ال نهائية.
7.2االعا ة Recursion
االعادة في علم الحاسروب هري طريقرة تسرتند الرى مفهروم حرل المشركلة اعتمرادا علرى
حلرول الحرراالت الصررغيرة لررنفس المشرركلة (عكرس التكرررار) .الطريقررة مررن الممكررن ان
تطبررل علررى العديررد مررن المشرراكل ,واالعررادة هرري واحررد مررن االفكررار الرئيسررية لعلرروم
الحاسوب.
325
هياكل البيانات باستخدام C++
دوال االستدعاء الذاتي مفيدة جردا لحرل العديرد مرن مشراكل الرياضريات ,مثرل حسراب
مضروب رقم معين ,توليد متوالية Fibonacciوغيرها.
ان قوة االستدعاء الذاتي يكمن في امكانية تعريف مجموعة غير محددة من الكيانات
بواسطة عبارات محددة .بنفس الطريقة ,عدد غير محردد مرن الحسرابات مرن الممكرن
وصفها بواسطة برنامج استدعاء ذاتي ,حتى وان كان هذا البرنرامج ال يحتروي علرى
تكرارات خارجية .خوارزميات االسرتدعاء الرذاتي ,هري مناسربة مبردئيا عنردما تكرون
المشرركلة قابلررة للحررل ,او الدالررة الترري نبرمجهررا او هيكررل البيانررات الترري تعررالج هرري
باالساس معرفة بداللة االستدعاء الذاتي.
ولكرري نبسررط الموضرروع سررنتطرق الررى الرردمى الروسررية ,فهررل سرربل لررك أن رأيررت
مجموعة من الدمى الروسية؟ في البداية ،ترى مجرد تمثال واحد من الخشب ويرسم
عليه نقوش لتبدو كما في الشكل .7.1
االن لو رفعت النصف العلوي من الدمية االولى ,فماذا سترى في داخلها؟ سترى ان
هنا دمية روسية اصغر منها قليال .كما في الشكل .7.2
هياكل البيانات باستخدام C++
من الممكن ان تزيل هذل الدمية االصرغر وتفصرل النصرف العلروي والنصرف السرفلي
للدمية الثانية .سترى دمية اخرى اصغر منها قليال شكل .7.3
ان االسررتمرار بهررذا العمررل (فررتح كررل دميررة اليجرراد دميررة اخرررى داخلهررا) سرريؤدي الررى
الحصول على مجموعة من الدمى وكل واحردة هري اصرغر مرن سرابقتها قلريال (علمرا
بانها جميعا كانت ضمن دمية واحدة رئيسية) وكما في الشكل .7.4
327
هياكل البيانات باستخدام C++
الح هنا اننا بدأنا بدمية كبيرة وتم التجزئة الى مجموعة من الردمى اصرغر فاصرغر
لحين الوصول الى دمية صغير جدا بحيث التحتوي على دمية اصغر منها.
ما عملنال للدمى الروسية سنعمله للخوارزميات ,فان المشكلة الكبيرة مرن الممكرن ان
تجزأ الى مشاكل اصرغر واصرغر مشرابهة للمشركلة الكبيررة االصرلية لحرين الوصرول
الى جزء صغير اليمكن تجزأته ومن الممكن حلها بشركل مباشرر .هرذل التقنيرة تردعى
االعادة (او االستدعاء الذاتي).
ان كلمة االستدعاء الذاتي تعني " ان لها صفات الن تعود مرة يانية ,او تتكررر" .ان
عملية استدعاء الدالة لنفسها تسرمى اسرتدعاء مباشرر وجميرع االمثلرة فري هرذا الفصرل
هري مرن نروع االسرتدعاء المباشرر .امرا االسرتدعاء غيرر المباشرر فيرتم عنردما دالرة A
تستدعي دالة Bودالة Bتستدعي دالة .A
}
}
{ )(int main
; cin>> i
;return 0
}
لتوضيح عمل االستدعاء الذاتي وحسرب البرنرامج اعرالل سرنفرض ان المطلروب هرو
ايجراد مضررروب الرررقم .4بدايررة لنشرررح البرنرامج يررم نوضررح كيفيررة ايجرراد مضرروب
329
هياكل البيانات باستخدام C++
الرقم .4الح ان البداية سيتم ادخال القيمة المطلوب ايجاد المضرروب لهرا ومرن يرم
تستدعى دالة ايجاد المضروب (سيتم ادخال الرقم .)4الدالة التي سيتم استدعائها في
هذا البرنامج هي باسم ) (factorialوالتي تجد المضرروب ,المردخل لهرا قيمرة الررقم
الذي نحاول ايجاد المضروب له ,هنا سيكون الرقم .4
الحر فرري بدايررة الدالررة هنررا شرررط ,هررذا الشرررط يسررتخدم اليقرراف عمليررة االسررتدعاء
الذاتي وعدم السماح بدخول الدالرة الرى حلقرة تكررار النهائيرة (حالرة االسراس) .طبعرا
شرط التوقف هو عندما نصل الى واحد فان الدالة ستصل نهايتهرا .الفكررة هرو ان لرم
نصل واحد فان الدالة ستستدعي نفسها .واحدة من الطرق الرياضية لوصف )!:(n
نفرررض كمررا فرري المثررال ان المطلرروب هررو )! ,(4وبسرربب ان ) (n>0فاننررا سنسررتخدم
الجزء الثاني من التعريف اعالل.
4! = 4 * 3 * 2 * 1 = 24
من الممكن ايضرا ان نوضرح ان )! (nمرن الممكرن كتابتهرا الي قيمرة) (nليسرت
سالبة.
هررذا التعريررف هررو تعريررف االسررتدعاء الررذاتي ,الننررا وضررحنا دالررة المضررروب بداللرة
نفسها .ووفقا للتعريف في العالقة اعالل فان
!4! = 4 * (4-1)! = 4 * 3
هياكل البيانات باستخدام C++
بالطبع فران عمليرة الضررب اليمكرن انجازهرا االن ,وذلرك الننرا النعررف قيمرة )!.(3
لذلك ,لنرى كيف سيتم انجاز هذا العمل عن طريل االستدعاء الذاتي (هنا ترم تجزئرة
المشكلة الى اجزاء صغيرة مشابهة للمشكلة االصلية) .
نفرض ان حسين هو احد الطلبة في قسرم الحاسروب وطلرب منره ايجراد )! ,(4حسرين
فهم ان
!4! = 4 * (4-1)! = 4 * 3
ونظرا الن حسين ال يعرف قيمة )! ,(3لذلك قرر االتصال بزميلته جنى وسالها عن
قيمررة )! ,(3جنررى فكرررت بالمس رألة وهرري تملررك نفررس الصرريغة (تعريررف المضررروب)
وتوصلت الى ان
!3! = 3 * (3 – 1)! = 3 * 2
331
هياكل البيانات باستخدام C++
جنى لم تسرتطع ايجراد النتيجرة النهائيرة حيرث ال يمكنهرا اكمرال عمليرة الضررب نظررا
لعدم معرفتها بقيمة )! ,(2لذلك قررت جنى ان تجعل حسين ينتظر وتتصرل بزميلهرا
محمد لمعرفة قيمة )!.(2
محمررد يملررك نفررس العالقررة الرياضررية الترري يملكهررا كررل مررن حسررين وجنررى ,لررذلك فقررد
توصل الى ان
!2! = 2 * (2 - 1)! = 2 * 1
لكن محمد لم يستطع اكمال عملية الضرب لعدم معرفته نتيجة )! ,(1لذلك طلرب مرن
جنى االنتظار لحين ايجاد النتيجة (االن جنى وحسين باالنتظار).
محمد قرر االتصال بوالدته والتي هي مدرسة فيزياء ليسألها عن نتيجة )!.(1
هياكل البيانات باستخدام C++
والدة محمد تعرف ذات العالقة ,لذلك فهي بسرعة توصلت الى ان
!1! = 1 * (1 – 1)! = 1 * 0
طبعا هي لم تتمكن من انجاز علمية الضرب وذلك النها لم تعرف نتيجة )! ,(0لذلك
فان والدة محمد قررت ان تجعل محمد ينتظر واتصلت بزوجها (والد محمد) مدرس
الرياضيات لتطلب منه معرفة نتيجرة )! ,(0والرد محمرد لرم يحتراج الرى وقرت للتفكيرر
فهو يعرف ذات العالقة والتعريف ,ويعلم ان الجزء االول من التعريف يشير الى ان
n! = 1, if n=0
والدة محمد اسرعت والفرح يغمرها باعالم محمد النتيجة وهي ).(1! = 1
333
هياكل البيانات باستخدام C++
محمد بمجرد سماع النتيجة عوض في العالقة التي لدية واستخرج النتيجة كما يلي
بعد حصول محمد على النتيجة عاد الى جنى ليعلمها ان ).(2! = 2
جنى والتي طال انتظارهرا اكملرت عمليرة الضررب لتسرتخرج قيمرة )!( (3هري كانرت
بانتظار نتيجة !.)2
3! = 3 * (3 – 1)! = 3 * 2! = 3 * 2 = 6
بنشوة النصر اخبرت جنى زميلها حسين بان نتيجة ) (3! = 6معتذرة عرن الترأخير
النشغالها قليال بالمطبا!.
واخيرررا فرران حسررين اسررتخدم هررذل القيمررة الترري حصررل عنهررا مررن جنررى ليكمررل عمليررة
الضرب كما في ادنال وهو في داخله يزداد اعجابا بذكاء جنى.
من المؤكد ,ان استخدام العودة هي ليست حكرا على مختصي الرياضريات باسرتخدام
الهواتررف .ان لغررات البرمجررة مثررل C++ترردعم العررودة وترروفر للمبرررمجين اداة قوي رة
وفاعلة لحل نوع محدد من المشاكل بتقليل درجرة التعقيرد او اخفراء تفاصريل المشركلة
كما في شفرة البرنامج اعالل.
لنتابع البرنامج الخاص بمضروب الرقم ( )4باستخدام الجدول وذلك لغررض تبسريط
الحل.
335
هياكل البيانات باستخدام C++
) (1-1هو صفر ,لذلك يتم التحول الى االيعاز 2 1 4
للتاكد من ان حل العودة (االستدعاء الذاتي) يعمل ,يجب ان تكون قادر على االجابرة
بنعم لالس لة الثالث التالية:
سررؤال حررول الحالررة االسرراس :هررل هنررا طريقررة غيررر االسررتدعاء الررذاتي .1
وبدون الدالة اليجاد النتيجة لجزء صغير من المشكلة ,وهرل الدالرة تعمرل بشركل
صحيح عند القيمة االساس.
سؤال االستدعاء االصغر :هرل ان االسرتدعاء الرذاتي للدالرة يتضرمن حالرة .2
تصغير للمشكلة االصلية ,بحيث يقود الى الحالة االساس بشكل ال مفر منه.
هياكل البيانات باستخدام C++
سرؤال الحالررة العامررة :نفررض ان االسررتدعاء الررذاتي يعمرل بشرركل صررحيح, .3
فهل ان كامل الدالة تعمل بشكل صحيح.
لنجرب هذل الشروط او االس لة على دالة المضروب التي تطرقنا لها:
ان الحالة االساس تحدث عندما تكون قيمة ) ,(n=0حيث ان الدالة ستسند .1
القيمة .1والتي هي القيمة الصحيحة لمضروب الصفر )! ,(0وعندها ال توجد
استدعاءات عودة اضافية .الجواب هو نعم.
الجابررة هررذا السررؤال ,يجررب ان ننظررر الررى المعررامالت الترري تمرررر الررى .2
استدعاء العودة .في الدالة ) (factorialفان استدعاء العودة يمرر ).(n-1
في كل مرة يتم فيها االستدعاء الذاتي للدالة ترسل قيمة معامالت اصرغر للدالرة,
لحين ان تكون القيمرة المرسرلة تسراوي صرفر .فري هرذل الحالرة فاننرا وصرلنا الرى
اصغر حالة ,وال توجد استدعاءات اضافية اخرى .الجواب هو نعم.
في حالرة دالرة مثرل الدالرة ) (factorialنحتراج ان نتحقرل مرن ان الصريغة .3
التررري نسرررتخدمها واقعرررا نتيجرررة حرررل صرررحيح .نفررررض بررران االسرررتدعاء الرررذاتي
)) (factorial (n-1تعطينا قيمة صحيحة للمضرروب !) ,(n-1عبرارة االعرادة
ستحسب !) n*(n-1وهرذا هرو تعريرف المضرروب ,حيرث اننرا نعلرم بران الدالرة
تعمل لجميع القيم الموجبة .لذلك فان االجابة نعم.
337
هياكل البيانات باستخدام C++
تحديررد وتعريررف الحالررة االسرراس ,والترري توضررح المشرركلة برردون عمليررات .3
العودة .و يتم التاكد منها عند االجابة بنعم لسؤال الحالة االساس.
تعريررف وحررل الحالررة العامررة بشرركل صررحيح بداللررة حالررة اصررغر لررنفس .4
المشرركلة (اسررتدعاء ذاترري) .هررذل تؤكررد االجابررة نعررم لسررؤالي االسررتدعاء االصررغر
والحالة العامة.
لو حاولنا تطبيل هذل الفقرات على برنامج المضروب.
تعريف المشكلة من الممكن ان نختصرل بتعريف دالة المضروب .حجم المشكلة هرو
عدد القيم التي من المفروض ان تضرب وهي تساوي .N
الحالررة االسرراس تحرردث عنرردما تكررون ) ,(N = 0فرري حالررة اننررا سررلكنا طريررل عرردم
االستدعاء الذاتي.
اخيررررا ,الحالرررة العامرررة تحررردث عنررردما تكرررون ) (N>0يرررؤدي الرررى اسرررتدعاء ذاتررري
للدالة factorialلحالة اصغر).factorial (N-1
fib(1) = 1
>#include <iostream
;using namespace std
main()
{
int n;
# include<iostream>
using namespace std;
int sum (int) ; // اعالن عن دالة
void main (void) {
int n ، temp ;
cout<<"enter any integer number "<<endl ;
cin>>n ;
temp = sum (n) ;
cout<<"value = "<<n<<"and its sum="<<temp ;
}
339
هياكل البيانات باستخدام C++
}
التوضيح التالي من الممكن ان يساعد على ادرا وفهم دالة االستدعاء الذاتي.
فلو فرضرنا انره ترم ادخرال قيمرة للمتغيرر ( )n=6لنررى كيرف سريتم ايجراد المجمروع،
اعتمادا على قيمة العبارة
; )value = n + sum (n-1
هنا العبارة () )sum (n-1هي استدعاء للدالة () ،)sum(intونظرا الن هذا
االسررتدعاء مررن داخررل الدالررة نفسررها اذن هررو يمثررل اسررتدعاء ذاترري اي اسررتدعاء لنفسرره
وسيكون عملها كما يأتي:
هياكل البيانات باستخدام C++
مع متوالية شك :7.9يبين كيفية تنفيذ الة االستدعاء الذاتي لبرنام
341
هياكل البيانات باستخدام C++
هياكل البيانات باستخدام C++
الفص الثامن
األشجار الثنائية BINARY TREES
8.1المقدمة
هنرا عرردد مررن اساسرريات هياكرل البيانررات الترري مررن الممكرن اسررتخدامها لحررل مشرراكل
التطبيقررات .المصررفوفات تعتبررر جيرردة لهياكررل البيانررات المسررتقرة والترري مررن الممكررن
الوصول اليهرا عشروائيا وهري تنفرذ بسرهولة .القروائم الموصرولة مرن جانرب اخرر هري
ليست مستقرة وهري مثاليرة للتطبيقرات التري تتطلرب عمليرات متكرررة مثرل االضرافة,
الحرذف ,والتحرديث .واحردة مرن مشراكل القروائم الموصرولة هري الوصرول المتسلسرل
للبيانات .هنا هياكل بيانات خاصرة اخررى مثرل المكردس والطرابور التري تسرمح لنرا
بحل المشاكل المع قدة باستخدام هياكل البيانات المحرددة هرذل .هيكرل بيانرات اخرر هرو
جدول التجزئة ) (Hash tableالتي تسمح للمستخدم لبرمجة التطبيقات التي تحتراج
الى بحث وتحديث متكرر.
احد مساو استخدام المصفوفات والقوائم الموصولة لخزن البيانات هو الوقت االزم
للبحررث عررن عنصررر معررين ,وحيررث ان كليهمررا المصررفوفات والقرروائم الموصررولة هرري
هياكل خطية ,فان الوقت المطلوب لبحث قائمة خطية يتناسب مرع حجرم البيانرات فري
المجموعررة .مثررال ,اذا كرران حجررم البيانررات فرري المجموعررة هررو ,nعليرره فرران عرردد
المقارنات التي تحتاجها اليجاد (او عدم ايجاد) عنصر معين ربمرا تكرون مرن السروء
لضرب nعدد من المررات .لرذلك تصرور انرك تريرد البحرث فري قائمرة موصرولة (او
مصرفوفة) وتحتروي مرثال n = 106عقردة .حترى فري الماكنرة التري بإمكانهرا ان تقروم
بمليون عملية مقارنة بالثانية ,فان البحث عن mعنصر ستحتاج الى mمن الثرواني
تقريبا .هذا غير مقبول في عالم اليوم حيث ان السررعة فري اكمرال عمليرة معينرة هرو
مهم جدا .وهذا يترجم كمبالغ مالية ,عليه فان هنا حاجة لهياكل بيانات افضل (اكثر
كفاءة) لخزن البيانات والبحث عنها.
343
هياكل البيانات باستخدام C++
شك :8.2هذ ليست اشجار وكلو .Aالنها ائرية .B .B-C-E-D-Bائرية غير مباشرة
;int data
;}
345
هياكل البيانات باستخدام C++
شك :8.4توضيح للتماث بين الشجرة الببي ية ) (Aوشجرة هيك البيانات ).(B
الجةذر : Rootاعلرى عقردة فرري الشرجرة .العقردة فري اعلررى الشرجرة دائمرا ترردعى
الجذر .هنا جذر واحد فقط فري الشرجرة .عنرد وجرود تجمرع للعقرد وروابرط برين
العقد والتي تعرف كشجرة ,فانره يجرب ان يكرون هنرا مسرار واحرد وواحرد فقرط
من الجرذر الرى اي عقردة اخررى .هنرا نتحردث عرن الجرذر لكامرل الشرجرة .احيانرا
هياكل البيانات باستخدام C++
اعلى عقدة في الشجرة الفرعية تردعى جرذر لتلرك الشرجرة الفرعيرة( ,هنرا شرجرة
هياكل البيانات هي شجرة مقلوبة بمعنى الجذر لالعلى واالوراق لالسفل).
االبن : Childعقدة او اكثر التي تلي مباشرة عقردة اخررى .اي عقردة ربمرا لهرا
رابط او اكثر ينطلل الى االسفل الى عقرد اخررى .هرذل العقردة اسرفل عقردة معينرة
مباشرة تدعى ابن.
االب : Parentالفكرة المعاكسة لالبن وهي العقدة التي اعلى االبن وتتصل بره
مباشرة .اي عقدة (باستثناء الجذر) لها بالضبط رابط واحرد ينطلرل الرى االعلرى,
الى اي عقدة اخرى .لكل عقدة هنا عقدة واحدة فقط اعلرى منهرا مباشررة تردعى
االب لهذل العقدة.
النسيب :Siblingوهي العقد التي لها نفس االب.
الورقةةة :Leafالعقرردة الترري لرريس لهررا ابنرراء ترردعى العقرردة الورقيررة او ببسرراطة
اوراق .في الشجرة هنا جذر واحد لكن هنا عدد من االوراق.
السلي :Descendantوهي العقد التي نصل اليها بعمليات تكرارية مرن االب
الى االبناء.
السلف : Ancestorوهي العقد التي نصرل اليهرا بعمليرات تكراريرة مرن االبنراء
الى االب.
ال قدة الداولية :Internal nodeهي العقدة التي لها على االقل ابن واحد.
ال قدة الالار ية :External nodeالعقدة التي ليس لها ابناء.
االشةةةجار الفرعيةةةة :Subtreeاي عقررردة مرررن الممكرررن ان تعتبرررر جرررذر لشرررجرة
فرعيرررة ,ان ابنررراء هرررذل العقررردة وابنررراء ابنائهرررا تمثرررل شرررجرة فرعيرررة .اذا فكررررت
بمصررطلحات العوائررل فرران الشررجرة الفرعيررة تتكررون مررن كررل االحفرراد .الشررجرة
الفرعية من الممكن ان تكون مجموعة من العقد او عقدة واحدة.
الدر ة :Degreeعدد االشجار الفرعية للعقدة.
الرابط (الحافة) :Edgeهو الرابط بين عقدة واخرى.
347
هياكل البيانات باستخدام C++
المسةةتوى : Levelالمسررتوى لعقرردة معين رة يشررير الررى عرردد االجيررال للعقرردة مررن
الجررذر .اذا فرضررنا الجررذر فرري المسررتوى صررفر ,عليرره فرران االبررن سرريكون فرري
المستوى ,1االبن الحفيد سيكون في المستوى 2وهكذا.
المفاتيح :Keysفي حقول البيانات في كيان معين توجد قيمة تسمى هذل القيمرة
مفترراح (اي القيمررة الترري فرري حقررل البيانررات للعقرردة) .هررذل القيمررة مررن الممكررن ان
تستخدم للبحث عرن عنصرر معرين ,او انجراز عمليرات محرددة عليهرا .عنرد تمثيرل
هياكل البيانات باستخدام C++
الشررجرة علررى شرركل مخطررط للشررجرة ,سررنرى دوائررر تمثررل العقررد ,وهررذل الرردوائر
تحمل بيانات هذل البيانات تمثل المفتاح.
ارتفا ال قدة :Node Heightهو طول الطريرل (عردد الحرواف او الرروابط)
ألطول طريل بين العقدة والورقة.
ارتفا الشجرة :Tree Heightان اطرول مسرار (يتضرمن الجرذر واالوراق)
يمثررل ارتفرراع الشررجرة .بالنسرربة لالشرجار غيررر الخاليررة ,فرران االرتفرراع هررو اطرول
مسار في الشجرة .1 +
349
هياكل البيانات باستخدام C++
حجةم ال قةدة : Size of Nodeهرو عردد العقرد التري تمثرل ابنراء واحفراد العقردة
(والعقرردة نفسررها تحسررب معهررم) اي ان حجررم الجررذر يمثررل عرردد العقررد الكليررة فرري
الشجرة من ضمنها الجذر.
رتبة الشجرة :Order of the Treeهي اكبر عدد من االبناء تحتويها عقردة
من عقد الشجرة.
ر ة الدوول :In Degreeللعقدة هي عدد الحواف التي تصرل العقردة .الجرذر
هي العقدة الوحيدة التي لها (.)in degree = 0
ر ة الالروج :Out Degreeللعقدة هي عدد الحواف التي تتر العقدة.
الزيةةارة :Visitingيررتم زيررارة العقرردة عنرردما يصررل المسرريطر الررى العقرردة ,عرادة
نجاز بعض العمليات على العقدة وليس للمرور فقط ,مثل فحص القيمرة لواحرد
من حقول البيانات او عرضها .ان المرور على عقدة في الطريل الذي ينقلنا من
عقدة الى اخرى ال يعتبر زيارة للعقدة.
المةةرور :Traversingالمرررور علررى ش رجرة يعنرري زيررارة كررل العقررد بترتيررب
معين .مثال ,ربما تزور كل العقد بترتيب تصراعدي حسرب قيمرة المفتراح .هنرا
العديد من طرق المرور على العقد كما سنراها الحقا.
في الشكل 8.9فان العقدة Aهي الجذر .اما العقدة Bوالعقدة Cهم ابناء العقدة .A
العقدة Bمع العقدة Dتكون شجرة فرعية .العقدة Bلها اينان من االبناء وهما
هياكل البيانات باستخدام C++
االبن االيسر وهنا هو شجرة خالية واالبن االيمن الذي هو .Dالعقد A, C, Fهم
اسالف العقدة .Hالعقد D, E, Fتكون المستوى الثاني من الشجرة .العقدة Aهي
في المستوى صفر .المسار من Aالى Cالى Eالى Gيمثل طريل بطول .3العقد
D, G, H, Iهي اوراق الشجرة .العقد A, B, C, E, Fهي عقد داخلية .العقد G,
H, Iهي عقد خارجية .عمل العقدة Iهو .3اما ارتفاع هذل الشجرة هو .4
351
هياكل البيانات باستخدام C++
الشجرة الثنائية هي هيكل بياني خاص يستخدم الغراض خزن البيانرات .واالشرجار
الثنائية لها الصفات التالية:
اذا كانت كل عقدة في الشجرة لها على االكثر اينان من االبناء ,فان هذل الشجرة
تسمى ينائية .و تعتبرر االشرجار الثنائيرة هري االبسرط واالكثرر عموميرة .االشرجار
الثنائيررة تتكررون مررن مجموعررة مررن العقررد ,بحيررث ان كررل عقرردة تحترروي علررى حقررل
مؤشر ايمن وحقل مؤشر ايسر فضال عن حقل اخر للبيانات ,العقردة االعلرى فري
الشجرة تسمى جذر.
االبناء االينان لكل عقدة في الشجرة الثنائية تدعى االبرن االيسرر واالبرن االيمرن,
وفقا لموقعهم عند رسم شجرة معينة ,كمرا فري الشركل .8.11العقردة فري الشرجرة
الثنائية ليس من الضروري ان يكون لها اينان من االبنراء ,فربمرا يكرون لهرا ابرن
واحد سواء باليمين او باليسار ,او من الممكن ان ال يكرون لهرا اي ابرن (فري هرذل
الحالة تسمى ورقة).
.Aشجرة ثنائيةة مجةذرة :Rooted Binary Treeهري شرجرة بجرذر حيرث ان
كل عقدة لها على االكثر اينان من االبناء كما في الشكل .8.12
353
هياكل البيانات باستخدام C++
355
هياكل البيانات باستخدام C++
357
هياكل البيانات باستخدام C++
االشجار المنحرفة هري اشرجار بحيرث تكرون كرل عقردة )عردا العقرد الورقيرة( لهرا ابرن
واحد فقط .هنا نوعين من هذل االشجار
هي نوع خاص من االشرجار الثنائيرة .الفكررة االساسرية خلرف هرذا النروع مرن هياكرل
البيانررات هررو ان لهررا مسررتودع خررزن يرروفر طريقررة كفرروءة لترتيررب البيانررات ,البحررث,
واستعادة البيانات.
اشجار البحرث الثنرائي تظهرر سرلو خراص ,هرو ان العقردة التري فري الجانرب االيسرر
(االبن االيسر) يجب ان يكون له قيمة (مفتاح) اقل من قيمرة االب ,والعقردة التري فري
اليمين (االبن االيمن) يجب ان تكون له قيمة (مفتاح) اكبر من قيمة االب ,وال يسمح
بتكرار المفتاح في الشجرة.
الشجرة الثنائية التي لها المواصفات التالية تسمى شجرة البحث الثنائي
كررل قيمررة (مفترراح) فرري الشررجرة تكرررر بالضرربط كحررد اعلررى مرررة واحرردة (اي
اليوجد تكرار)
العالقات اكبر من واقل من ,تعرف بشكل جيد لقيم البيانات.
محددات الترتيب :لكل عقدة )(n
كل البيانات فري الشرجرة الفرعيرة اليسررى للعقردة ) (nهري اقرل مرن
البيانات في الجذر لتلك الشجرة الفرعية.
كل البيانرات فري الشرجرة الفرعيرة اليمنرى للعقردة ) (nهري اكبرر مرن
البيانات التي في الجذر لتلك الشجرة الفرعية.
مثال :1ارسم شجرة بحث ينائي للبيانات 12, 16, 5, 7, 4, 13, 21, 2
359
هياكل البيانات باستخدام C++
شك :8.24شجرة بحث ثنائية لتمثي القيم 12, 16, 5, 7, 4, 13, 21, 2
مثال : 2دعنا نرى المثرال الترالي ضرافة قائمرة االرقرام التاليرة والتري سريتم توضريح
االضافة بالتفصيل ,حيث اننا سننتهي بشجرة مثل التي في الشكل :8.25
32, 16, 34, 1, 87, 13, 7, 18, 14, 19, 23, 24, 41, 5, 53
حيررث ان القيمررة 32هرري اول قيمررة سرريتم اضررافتها ,لررذا فرران 32سررتكون جررذر
الشجرة.
القيمة التالية هي ( )16والتي هي اصغر من القيمة ,32لذا فانها ستكون العقردة
التي على يسار العقدة .32
( )34وحيث ان 34اكبر من ,32عليه فان 34ستكون العقدة التي توضع على
يمين الجذر.
القيمة ( )1وهي اصغر من 32لرذا سريكون االتجرال الرى العقردة اليسررى للجرذر.
وبما ان العقدة في اليسار مشغولة بقيمة ( ,)16لذا يتم اختبار القيمرة 1مرع قيمرة
العقدة التي في اليسار ,16وحيث ان 1اصرغر مرن 16عليرة فران 1يكرون الرى
يسار العقدة .16
هياكل البيانات باستخدام C++
القيمة ( )87وحيث ان 87اكبرر مرن 32سريكون االتجرال الرى العقردة التري علرى
يمين الجذر ,وهذل العقردة مشرغولة بالقيمرة ( ,)34لرذا تقرارن مرع ,87وحيرث ان
87اكبر من 34لذا فان هذل القيمة ستكون على يمين العقدة .34
( )13وحيث ان 13اصغر من ,32سيكون االتجال الى العقدة يسرار الجرذر ,يرم
تقارن مع العقدة التي على اليسار التي قيمتها ,16وحيث ان 13اصغر من 16
تستمر الحركة الى العقدة التي على يسار العقدة 16والتي هري مشرغولة بالعقردة
التي تحمل القيمرة 1وتقرارن مرع القيمرة الجديردة ,وحيرث ان 13اكبرر مرن 1لرذا
فان القيمة المضافة 13ستكون على يمين العقدة .1
يستمر هذا النهج لبقية القيم .7, 18, 14, 19, 23, 24, 41, 5
( )53وحيث 53اكبر من 32فسيكون موقعها في فرع الشجرة االيمرن للجرذر.
كذلك 53اكبر من 34لذا استمر باتجال اليمين للعقدة .34ايضا 53اصغر مرن
87لذا يكون االتجال الى يسار العقدة .87بعردها تقرارن مرع ,41وحيرث ان 53
اكبر من 41لذا يكون االتجال الى يمين العقردة ,41ونظررا الرى ان يمرين العقردة
41فار او خالي من اي قيمة لذا فان 53ستكون العقدة التي على يمرين العقردة
.41
الخطوات اعالل تعطيك فكرة عامة عن كيفية خلل االشجار الثنائية .يجب عليك
ان تعلم ان:
ربط عقدة الى عقدة اخرى في االشرجار الثنائيرة هرو بطبيعتره واحرد الرى واحرد..
اي ان العقدة اليؤشر عليها باكثر من عقدة واحدة.
العقدة الواحدة بامكانها ان تؤشر الى اينين من العقد الثانوية كحد اعلى.
هنا في الشجرة الثنائية الناتجة في الشركل ,8.25هنرا كثيرر مرن العقرد لرديها مؤشرر
اليسار خالي ,مثل العقد
53 ,41 ,34 ,24 ,23 ,19 ,18 ,1 ,14 ,5والتي عقدها من اليسار خالية.
361
هياكل البيانات باستخدام C++
شك :8.25شجرة ثنائية تم بنائها لتمث القيم ( 32, 16, 34, 1, 87, 13, 7, 18, 14, 19,
)23, 24, 41, 5, 53
لغرض البحث عن عنصر معين عليك ان تبدا عملية البحث من الجذر ,وتتم مقارنة
قيمرة البيانرات التري تبحررث عنهرا مرع قيمررة المفتراح (قيمرة العقرردة) ,وطبعرا امرا ان يررتم
البحث في فرع الشجرة االيمن اذا كانت قيمة البيانات اكبر من قيمة المفتراح ,او يرتم
البحث في فرع الشجرة االيسر اذا كانت قيمة البيانات اصغر من قيمة المفتراح .هرذا
يتم تنفيذل على كل عقدة لحين الوصول الى البيانات المطلوب البحرث عنهرا او ربمرا
نصل الى نهاية الشجرة (االوراق) دون ان نجد البيانات .خوارزمية البحث هي:
هياكل البيانات باستخدام C++
.1اذا تساوت بيانات الجذر مع البيانات التي نبحث عنها ,عندها سيكون الجذر
هو العقدة المطلوبة.
.2اذا لم تساوي الجذر ,يتم البحث في العقد االخرى.
.Aاذا كانت البيانات التي نبحث عنها اكبر من بيانات العقدة (المفتاح):اذهب
الى فرع الشجرة االيمن.
.Bاما اذا كانت القيمة التي يتم البحث عنها اصرغر مرن قيمرة المفتراح ,اذهرب
الى فرع الشجرة االيسر.
ان اول عمليررة اضررافة هرري عمليررة خلررل شررجرة (يررتم ذلررك بخلررل عقرردة تمثررل الجررذر).
بعدها ,امكانية اضافة عنصر اخر للشجرة .لغرض اضافة عنصر للشجرة ,بداية من
المفروض ايجاد الموقع المناسب لهرذا العنصرر .ابردأ البحرث مرن عقردة الجرذر باتبراع
الطريقة التي تم شرحها سابقا بحيث يقرارن المفتراح (قيمرة العقردة) مرع بيانرات العقردة
المررراد اضررافتها فرراذا كانررت اقررل مررن قيمررة المفترراح يسررتمر البحررث فرري فرررع الشررجرة
االيسر ,واذا كانت اكبر مرن قيمرة المفتراح يسرتمر البحرث فري فررع الشرجرة االيمرن,
تس ر تمر العمليررة لحررين الوصررول الررى الموقررع الفررار فرري فرررع الشررجرة وتررتم اضررافة
البيانات .خوارزمية االضافة هي:
.1اذا كانت الشجرة خاليرة (اذا كران الجرذر يسراوي ,) nullيرتم خلرل عقردة لتكرون
هي الجذر.
.2اذا كان هنا جذر ,تتم عملية مقارنة البيانات مع المفتاح (بيانات العقدة).
.3يتم عمل حلقة تكرار يجاد الموقع المطلوب لعملية االضافة.
363
هياكل البيانات باستخدام C++
.Aاذا كانت البيانات المطلوب اضافتها اكبر من قيمرة المفتراح ,يرتم الرذهاب الرى
فرع الشجرة االيمن
.Bبخالف ذلك ,أي عندما تكون البيانات اصرغر مرن قيمرة المفتراح فيرتم الرذهاب
الى فرع الشجرة االيسر.
عمليرة ازالررة او حرذف عنصررر مرن شررجرة البحرث الثنررائي هري عمليررة معقردة نسرربة
للعمليات االخرى .حيث من المفروض بعد الحرذف يجرب ان تبقرى الشرجرة بشركل
مرتب.
ان اول خطوة قبل عملية ازالة عنصر من الشجرة هرو ايجراد هرذا العنصرر .وهرذا
يررتم تحديرردل مررن خوارزميررة البحررث .بعررد ايجرراد العنصررر المطلرروب هنررا يررالث
حاالت:
.Aاذا العقدة المراد حذفها هي ورقرة (العقردة التري لريس لهرا ابنراء) ,فري هرذل الحالرة
فان الخوارزمية ستؤشر على الرابط المقابل الذي يربط العقدة مرع االب بالقيمرة
( nullاي ان العنوان الذي يشرير للعقردة المرراد حرذفها فري حقرل العنروان للعقردة
السررابقة يجرررب ان يشررير الرررى .)nullفمررثال اذا اردنرررا حررذف القيمرررة ( )-4مرررن
الشجرة في الشكل 8.26-Aفان الناتج سيكون في الشكل ( 8.26-Bحيث سيتم
وضع nullفي حقل العنوان االيسر للعقدة التي تحمل القيمة .)2
هياكل البيانات باستخدام C++
.Bاذا العقدة لها فقط شجرة فرعيرة واحردة ,شرجرة فرعيرة يمنرى او يسررى (أي لهرا
ابن واحد) ,في هذل الحالة تحذف هذل العقدة من الشرجرة ,والخوارزميرة سرتربط
االبن الوحيد لهذل العقدة (حتى لو كان يمثل جذر لشجرة فرعية) مع العقدة التري
تمثل اب العقدة التي تم حذفها .مثال في الشكل 8.27مطلوب حذف العقدة التري
لها القيمة .18
365
هياكل البيانات باستخدام C++
شك .A :8.27شجرة ثنائية مؤشر عليها ال قدة المبلوب حذفها .B .طريقة ربط ال قد ب د
الحذذ .C .الشجرة ب د الحذذ.
الحالة الثالثة :ان تكون العقدة المطلوب حذفها لها اينان من االبناء (من الممكن ان
يكون االبناء جذور ألشجار فرعية) ,في هذل الحالة يجب ان نجد العقدة االصغر في
الشجرة الفرعية اليمنى للعقدة المراد حذفها ونبدلها مع قيمة العقدة المراد حذفها .بعد
هذا التبديل فان العقدة سيكون لها شجرة فرعية واحدة على االكثر ,عندها نحذف
العقدة وفل القواعد او الحاالت االينان اعالل .هنا سنقول من الممكن عمل تبديل
تناظري ,أي نعمل نفس الشيء عند العمل على الشجرة الفرعية اليسرى وتكون
عملية التبديل هنا مع اكبر عنصر في فرع الشجرة االيمن .في الشكل 8.28مطلوب
حذف العقدة التي تحمل القيمة .11
هياكل البيانات باستخدام C++
يرجى مالحظة ان العقدة المطلوب حذفها ستبقى بمكانها اينراء ابردال القريم لرذلك
فاننررا عنرردما نبرردل القرريم ال يعنرري ان نحررذف العقرردة الترري اصرربحت تحمررل القيمررة
المطلوب حذفها وانما حذف العقردة التري كانرت تحمرل تلرك القيمرة بمعنرى عنردما
نقول مطلوب حذف العقدة التي تحمل القيمرة 11وترم ابردال القيمرة 11مرن هرذل
العقدة فران هرذا اليغيرر الفكررة الن العقردة هري ذاتهرا مطلروب حرذفها وان حملرت
قيمة اخرى.
شك :8.28شجرة ثنائية محد عليها ال قدة المبلوب حذفها وال قدة التي يتم االبدال م ها.
العقدة 11لها شرجرتين فررعيتين ووفقرا لخوارزميتنرا ,فانره يجرب ان يرتم التبرديل مرع
اصغر عنصر من الشجرة الفرعية اليمنى ..وهي القيمة .13بعد عملية التبديل ,فاننا
من الممكن ان نزيل ( 11هي ورقة االن) .وستكون النتيجرة النهائيرة كمرا فري الشركل
.8.29
367
هياكل البيانات باستخدام C++
مثال اخر في الشكل 8.30مطلوب حذف العقدة التي لها القيمة 8وهي جذر.
ان طريقة الحذف تتبع مايلي :ابدال العقدة المراد حذفها مع العقدة التي لها اكبر قيمة
في الشجرة الفرعية اليسرى وبعدها يتم حذف العقدة التي لها القيمة الكبيرة.
هياكل البيانات باستخدام C++
مرن الممكرن ايضرا وبرنفس الطريقرة ان تبردل المفتراح (القيمرة )8مرع العقردة التري لهرا
اصغر قيمة في فرع الشجرة اليمنى ,ويتم حذف العقدة التي لهرا القيمرة االصرغر كمرا
في الشكل .8.31
مثال اخر في الشكل ) (8.32-Aمطلوب حرذف العقردة التري لهرا القيمرة .11
في هذل الحالة سيتم االبدال مع اكبر قيمرة فري فررع الشرجرة االيسرر والتري هري ,10
بعدها يتم حذف العقدة كما في الشكل ) .(8.32-Bكرذلك مرن الممكرن ان نعمرل علرى
فرع الشجرة االيمن ونبحث عرن اصرغر قيمرة فري هرذا الفررع وهري القيمرة 17ويرتم
اجراء التبديل يم حذف العقدة كما في الشكل ).(8.32-C
369
هياكل البيانات باستخدام C++
شك .A :8.32شجرة ثنائية محد بها ال قدة المبلوب حذفها .B .الشجرة ب د الحذذب تم
استالدا فر الشجرة االيسر للتبدي .C .الشجرة ب د الحذذب تم استالدا فر الشجرة االيمن
للتبدي .
هرري زيررارة كررل عقرردة فرري الشررجرة وربمررا يررتم طباعررة قيمهررا .وبسرربب ان كررل العقررد
مرتبطة بروابط او مسارات ,لذلك يجب ان تكون البداية من الجذر .مع مالحظة انره
من غير الممكن الوصول عشوائيا الى اي عقدة في الشجرة.
في هذل الطريقة فان الزيارة تبدا بفررع الشرجرة االيسرر ,بعردها الجرذر ومرن يرم فررع
الشجرة االيمرن .علينرا ان نترذكر دائمرا بران كرل عقردة فري الشرجرة تمثرل فررع شرجرة
هياكل البيانات باستخدام C++
بنفسررها .عنررد المرررور علررى الشررجرة الثنائيررة بالترتيررب االعتيررادي ,فرران المخرجررات
ستكون عبارة عن قيم (او مفاتيح) مرتبة بترتيب تصاعدي.
ايسر – ذر – ايمن
شك :8.33شجرة ثنائية تتم زيارتها ببريقة الترتيب االعتيا ي وحسب الترقيم الموضح في
الشك .
البداية تكون من الجذر ,Aوباتباع طريقة الترتيب االعتيادي ستكون الحركة لليسار
الى فررع الشرجرة .Bفررع الشرجرة Bهرو ايضرا جرذر لرذلك تسرتمر الحركرة لليسرار
(النرره ترتيررب اعتيررادي) .بعررد اكمررال فرررع الشررجرة االيسررر تررتم زيررارة الجررذر لفرررع
الشجرة ومن يم زيارة فرع الشجرة االيمن لهذا الفرع .هرذل العمليرة تسرتمر لحرين ان
تتم زيارة كل العقد .مخرجات هذل الطريقة هو
D→B→E→A→F→C→G
خوارزمية هذل الطريقة هي:
371
هياكل البيانات باستخدام C++
في هذل الطريقة فان الجذر سيتم زيارته اوال ,بعدها تتم زيارة فرع الشرجرة االيسرر,
واخيرا زيارة فرع الشجرة االيمن.
شك :8.34شجرة ثنائية تتم زيارتها ببريقة الترتيب القبلي وحسب الترقيم الموضح في
الشك .
تكون البداية من الجرذر ,Aوباتبراع طريقرة الترتيرب القبلري فران الجرذر سريكون اول
عقدة تتم زيارتها ,بعدها يتم االنتقال الرى فررع الشرجرة االيسرر .كرذلك فران العقردة B
ستعتبر جذر ويتم زيارتها وبعدها يتم االنتقال الى اليسار .هرذل العمليرة تسرتمر لحرين
زيارة جميع العقد .مخرجات هذل الطريقة هي:
A→B→D→E→C→F→G
هياكل البيانات باستخدام C++
شك :8.35شجرة ثنائية تتم زيارتها ببريقة الترتيب الب دي وحسب الترقيم الموضح في
الشك .
البدايررة مررن العقرردة ,Aوباتبرراع طريقررة الترتيررب البعرردي فسرريكون المسررار الررى فرررع
الشررجرة االيسررر .Bايضررا العقرردة Bتعتبررر جررذر فيررتم االسررتمرار الررى اليسررار .بعررد
اكمال زيارة فرع الشجرة االيسر تتم زيارة فررع الشرجرة االيمرن الي جرذر ,تسرتمر
هذل العملية لحين المرور على كامل العقد في الشجرة .نتيجة هذل الطريقة هي:
373
هياكل البيانات باستخدام C++
D→E→B→F→G→C→A
مثرال :فرري الشرركل 8.36شررجرة ينائيررة المطلرروب المرررور علررى عناصرررها باسررتخدام
طرق المرور الثالث.
مثةةال :فرري الشرركل 8.37شررجرة ينائيررة المطلرروب المرررور علررى عناصرررها باسررتخدام
خوارزميات الترتيب الثالث.
10, 14, 19, 27, 31, 35, 42 الترتيب االعتيا ي :
27, 14, 10, 19, 35, 31, 42 الترتيب القبلي :
10, 19, 14, 31, 42, 35, 27 الترتيب الب دي :
من الممكن ان نفهم او نتابع طريقة المرور من خالل االشكال (وهي طريقة تستخدم
العالمات) ,ال تختلف عن الطرقية السابقة اال انها ربما تسهل الفهم.
هرري ببسرراطة ان نضررع عالمررة علررى عقرردة واحرردة تمثررل طريقررة الترتيررب كمررا فرري
الشكل .8.38
شك : 8.38عقد مفر ة توضح كيفية المرور عليها باستالدا ع مة تمث الجذر.
375
هياكل البيانات باستخدام C++
في الشكل 8.38نالح االشارة الحمراء والتي تشير الى الجذر والفرع مرن الردائرة
تشير الى االبن االيسر وااليمرن ويكرون متابعرة المررور مرن اليسرار الرى اليمرين لكرل
عقرردة (العالمررة الحمررراء تشررير الررى موقررع الجرردر نسرربة للفررروع عنررد المرررور برراي
ترتيب) .فمرثال بالترتيرب القبلري فران االشرارة الحمرراء هري فري اقصرى اليسرار وهرذا
يعنرري انهررا سررتكون اول مرن يررتم زيارترره يررم االبررن االيسررر واخيرررا االبررن االيمررن .فرري
الترتيب االعتيادي فان العالمة الحمراء هي في الوسط وهذا يعني ان الجذر سريكون
في الوسط ,حيث نبردا مرن اليسرار وهنرا سريكون االبرن االيسرر يرم يراتي بعردها الجرذر
المعلررم بالعالمررة الحمررراء واخيرررا االبررن االيمررن .وهكررذا للترتيررب البعرردي حيررث ان
العالمة الحمراء تشير الى اليمرين وهرذا يعنري ان الجرذر سريكون براليمين ولرذلك فران
الترتيب يبدا بالعقدة اليسرى يم اليمنى وبعدها الجذر.
هذل العملية يمكن اجرائها لجميع عقد الشجرة وحسب طريقرة المررور فمرثال اذا كران
المطلوب مرور بالترتيب القبلي فيتم تعليم جميع العقرد برنفس الطريقرة التري قمنرا بهرا
في الشكل 8.38وعندها يرتم المررور علريهم جميعرا بسرهولة مرن اليسرار الرى اليمرين,
كما هو موضح في الشكل .8.39
برنامج شامل الغلب العمليات التي تجرى على االشجار الثنائي 8.8.5
#include<iostream>
#include<cstdlib>
struct tree {
int info;
};
tree *root;
class Binary_tree{
public:
Binary_tree();
void insert1(int);
void Delete(int);
};
377
C++ هياكل البيانات باستخدام
Binary_tree::Binary_tree() {
root = NULL;
if (temp==NULL) {
temp=newnode;
}
insert2(temp->Right, newnode);
if(temp->Right = =NULL)
temp->Right = newnode;
else{
insert2(temp->Left,newnode);
if(temp->Left==NULL)
C++ هياكل البيانات باستخدام
temp->Left=newnode;
return temp;
tree *temp=root,*newnode;
newnode=new tree;
newnode->Left=NULL;
newnode->Right=NULL;
newnode->info=n;
root=insert2(temp,newnode);
if(root == NULL){
cout<<"Nothing to display";
}else
if(t != NULL){
cout<<t->info<<" ";
pretrav(t->Left);
pretrav(t->Right);
379
C++ هياكل البيانات باستخدام
if(root==NULL){
cout<<"Nothing to display";
} else
if(t!=NULL){
intrav(t->Left);
cout<<t->info<<" ";
intrav(t->Right);
if(root==NULL){
cout<<"Nothing to display";
} else
if(t!=NULL){
posttrav(t->Left);
C++ هياكل البيانات باستخدام
posttrav(t->Right);
cout<<t->info<<" ";
if (temp==NULL)
else {
parent=temp;
if (temp->info<key){
temp=temp->Right;
} else {
temp=temp->Left;
marker=temp;
if (temp==NULL)
381
C++ هياكل البيانات باستخدام
else if (temp==root){
root=NULL;
else if (temp->Left==NULL){
root=temp->Right;
else if (temp->Right==NULL){
root=temp->Left;
else {
tree *temp1;
temp1 = temp->Right;
while (temp1->Left!=NULL){
temp=temp1;
temp1=temp1->Left;
if (temp1!=temp->Right){
temp->Left=temp1->Right;
temp1->Right=root->Right;
C++ هياكل البيانات باستخدام
temp1->Left=root->Left;
root=temp1;
else{
if (parent->Right==temp)
parent->Right=NULL;
else
parent->Left=NULL;
else if (temp->Left==NULL){
if (parent->Right==temp)
parent->Right=temp->Right;
else
parent->Left=temp->Right;
else if (temp->Right==NULL){
if (parent->Right==temp)
parent->Right=temp->Left;
else
383
C++ هياكل البيانات باستخدام
parent->Left=temp->Left;
}else {
tree *temp1;
parent=temp;
temp1=temp->Right;
while (temp1->Left!=NULL){
parent=temp1;
temp1=temp1->Left;
if (temp1!=temp->Right){
temp->Left=temp1->Right;
temp1->Right=parent->Right;
temp1->Left=parent->Left;
parent=temp1;
delete marker;
int main(){
Binary_tree bt;
C++ هياكل البيانات باستخدام
while (1){
cin>>choice;
switch(choice){
case 1:
cin>>n;
bt.insert1(n);
break;
case 2:
cin>>key;
bt.Delete(key);
break;
case 3:
cout<<endl;
bt.pretrav();
break;
case 4:
385
هياكل البيانات باستخدام C++
;cout<<endl
;)(bt.intrav
;break
case 5:
;cout<<endl
;)(bt.posttrav
;break
case 6:
;)exit(0
}
}
;return 0
}
ان نمثلها في شجرة ينائية كما سبل وان وضرحنا فري هرذا الفصرل .سرنحاول توضريح
كيفية تحويل هذل التعابير الرى اشرجار ينائيرة وكرذلك كيرف مرن الممكرن قرراءة شرجرة
تعبير رياضي .في الجدول 8.1توضيح للتعابير الرياضية التي سبل وان تم شرحها
في فصل المكدس.
387
هياكل البيانات باستخدام C++
م حظة :في شجرة التعبير فان كل عامل رياضي هو عقدة داخلية interiorوالتري
لهررا ابنرراء ,االبنرراء امررا معررامالت او تعررابير يانويررة .ودائمررا المعررامالت هرري فري العقررد
الورقية.
الخطررروات التاليرررة توضرررح طريقرررة تنفيرررذ خوارزميرررة بنررراء شرررجرة تعبيرررر للتعبيرررر
الرياضي a+b*cعلى شكل ترتيب بعردي .ترذكر ان التعامرل مرع التعبيرر الرياضري
سيتم عن طريل المكدس كما سبل ووضحنا في فصل المكردس .هنرا للسرهولة سروف
نمثررل المكرردس علررى شرركل افقرري (هررو بكررل الحرراالت تمثيررل افتراضرري) .تررذكر ان كررل
عنصر هو شجرة يانوية توصف بجذرها .اذن التعبير البعدي للعالقة يكون:
abc*+
.1الخطوة االولى :يتم تمييز الحررف aكمعامرل .نخلرل عقردة ورقيرة تحتروي قيمرة
الحرف او الرمز ,aيدفع في المكدس .s
هياكل البيانات باستخدام C++
.2الخطوة الثانية ,والثالثة :يتم تمييرز الحررفين b, cكمعرامالت ,ويرتم تكروين عقرد
ورقية وتدفع الى المكدس.
.3الخطول الرابعة :يرتم تمييرز * كعامرل رياضري ,فري هرذل الحالرة اليردفع للمكردس
النره عامرل رياضري وانمررا نعمرل علرى خلرل عقرردة جديردة علرى ان تكرون قيمتهررا
الرمرز )*( .يرتم سرحب اينران مرن العقرد الموجرودة فري اعلرى المكردس كمرا سربل
وتعلمنا في فصل المكدس (العقدل cوالعقدة ,) bهذل سرتكون االشرجار الثانويرة
اليمررين واليسررار للعقرردة الجديرردة )*( علررى الترروالي كمررا فرري الشرركل .8.43تررربط
االشجار الثانوية وتدفع الشجرة الثانوية المتكونة التي جذرها * الى المكدس.
389
هياكل البيانات باستخدام C++
.4الخطول الخامسة :يتم تمييز الرمز( )+كعامل .يتم خلل عقدة جديدة تكون قيمتها
هي ( ,)+هنا يتم سحب العقدتين الثانويتين من اعلى المكدس وتربطان الى هذل
العقدة الجديدة يم يعاد دفع العقردة المتكونرة الرى المكردس والتري جرذرها هرو ()+
كما في الشكل .8.44
.5الخطررول السادسررة :عنررد الوصررول الررى اخررر عنصررر فرري التعبيررر ,فرران العنصررر
الوحيد المتبقي في المكدس هو جذر شجرة التعبير.
الخطوة االولى :العوامل يتم دفعها الرى المكردس ,لرذلك يرتم اوال دفرع العامرل )(a
الى المكدس ,يم دفع العامل ) ,(bوبعردها يرتم دفرع العامرل ) (cالرى المكردس كمرا
في الشكل ).(8.45-A
الخطوة الثانيرة :االن الردور للعامرل الرياضري )*( ,فري هرذل الحالرة سريتم سرحب
عنصرين من اعلى المكردس ويربطران بواسرطة العامرل الرياضري )*( ,يرم يعراد
الناتج الى اعلى المكدس كما في الشكل ).(8.45-B
الخطوة الثالثة :االن جاء دور المعامل ) (dفيتم دفعه الرى المكردس ,يرم يراتي
دور المعامل ) (eفيتم ايضا دفعه الى المكدس كما في الشكل .8.46
391
هياكل البيانات باستخدام C++
الخطوة الرابعة :في هذل الخطوة سيتم تنفيذ العامرل الرياضري )*( ,ودائمرا العوامرل
الرياضية تتطلب سرحب اينران مرن المعرامالت فري اعلرى المكردس ويربطران بالعامرل
الرياضي )*( ,نؤكد علرى ان العنصرر الرذي فري اعلرى المكردس يكرون االبرن االيمرن
للجذر بينما الذي يليه يكون االبن االيسر للجذر ,بعد انجاز الربط يعاد فررع الشرجرة
المتولدة الى المكدس ,بعدها ياتي دور المعامل ) (fوالذي يدفع الى المكدس مباشررة,
كما في الشكل .8.47-Aيم ياتي دور العامل ( )+والمعامل ) (gشكل .8.47-B
تستمر العملية لحين اكمال جميع عناصر التعبير الرياضي وبذلك سيكون الناتج مرن
هذل العملية في المكدس وهو عبارة عن شجرة تعبير كما في الشكل .8.48-B
االسهم الحمراء توضح كيفية حساب قيم التعبير بطريقة الترتيب االعتيادي.
393
C++ هياكل البيانات باستخدام
#include <iostream.h>
#include <conio.h>
struct node {
char data;
node *left;
node *right;
};
char postfix[35];
int top=-1;
node *arr[35];
int r (char inputchar){ //لفحص الرمز اذا كان معامل او عامل رياضي
if (inputchar = = '+' || inputchar = = '-' || inputchar = = '*' || inputchar =
= '/')
return (-1);
C++ هياكل البيانات باستخدام
node *pop(){
top--;
return(arr[top+1]);
}
int flag; // ) عندما يكون معامل1( ) عندما يكون عامل و1-( مؤشر تكون قيمته
395
C++ هياكل البيانات باستخدام
ptr1=pop();
ptr2=pop();
newl = new node;
newl->data = symbol;
newl->left = ptr2;
newl->right = ptr1;
push(newl);
}
symbol=suffix[i];
}
}
void main(){
هياكل البيانات باستخدام C++
;)(clrscr
;"cout<<"*****Expression Tree*****\n
;" cout<<"Enter Postfix Expression :
;cin>>postfix
في التمثيل على شكل مصفوفة فاننا سنستخدم مصفوفة احاديرة البعرد لتمثيرل الشرجرة
الثنائية.
السررؤال االن كيررف مررن الممكررن تمثيررل الشررجرة الثنائيررة باسررتخدام المصررفوفات؟ ف ري
الحقيقة ,هنا عدد من الطرق لعمل ذلك ,سوف نناقش واحدة منها.
397
هياكل البيانات باستخدام C++
االشررجار الثنائيررة مررن الممكررن ان تخررزن بمصررفوفة احاديررة فرري الررذاكرة وفقررا للقواعررد
التالية:
.1قيم االشجار الثنائية تخزن في المصفوفة وتسمى المصفوفة شجرة ).(tree
.2جرررذر الشرررجرة يخرررزن فررري الموقرررع االول للمصرررفوفة (أي فررري الموقرررع ,0
].) tree[0
.3االبن االيسر للشجرة يخزن في الموقع ) ,(2i + 1بينما يخزن االبن االيمرن
فري الموقرع ) i( (2i + 2يمثرل الررقم التسلسرلي indexاو موقرع االب فري
المصفوفة) ,الح الشكل .8.50
.4ان اكبررر حجررم لمصررفوفة الشررجرة هررو ) ,(2d+1 -1حيررث ان dيمثررل عمررل
الشجرة.
.5الشررجرة الخاليررة او فرررع الشررجرة الخررالي يحرردد بالقيمررة .nullفرراذا كانررت
الشجرة tree [0] = nullفان هذا يعني ان الشجرة خالية.
.6الحر ان الشررجرة الترري لهررا ) (Nمررن العقررد لرريس دائمررا تشررغل اول ) (Nمررن
مواقع المصفوفة ,كما في الشكل .8.51
.7اذا كان موقع االبن في المصفوفة هو ) (iفان موقع االب في المصرفوفة يرتم
حسابة باستخدام العالقة ) ( (i – 1) / 2مع مالحظرة اهمرال الكسرر النراتج
من القسمة.
من فوائد هرذل الطريقرة الخطيرة هرو سرهولة الوصرول الرى عناصرر الشرجرة وسرهولة
المررور علررى عناصررر الشررجرة ,كررذلك الكفراءة باسررتخدام المسرراحة الخزنيررة اذا كانررت
الشجرة كاملة .بالمقابرل فران مرن مسراو اسرتخدام هرذل الطريقرة هرو عردم االسرتخدام
الكفوء للمساحة الخزنية خصوصا اذا لم تكن الشجرة كاملة.
هياكل البيانات باستخدام C++
شك :8.51شجرة منحرفة يمينا يتم وزنها في مصفوفةب الحظ عد وزل قيمها بمواقع
متجاورة من بداية المصفوفة.
في هرذل الحالرة يرتم اسرتخدام قروائم موصرولة ينائيرة ) (double linked listلتمثيرل
الشرررجرة الثنائيرررة .فررري القررروائم الموصرررولة الثنائيرررة وكمرررا عرفنرررا فررري فصرررل القررروائم
ا لموصولة فان كل عقدة تتكون من يالث حقول .الحقل االول يخزن به عنوان االبرن
399
هياكل البيانات باستخدام C++
االيسررر ,الحقررل الثرراني يخررزن برره البيانررات الحقيقيررة والحقررل الثالررث يخررزن برره عنرروان
االبن االيمن.
في هذا التمثيل فان العقدة سيكون لها الهيكل التالي
عملية تمثيل الشجرة على شكل قوائم موصولة موضحة بالشكل .8.52
البرنامج ي نفيذ اشجار البحث الثنائي بطرقة المصفوفات مع اجراء عدد من عمليرات
المرور التي تجرى على االشجار.
C++ هياكل البيانات باستخدام
اليحبذ استخدام المصفوفات مرع اشرجار البحرث الثنرائي وذلرك النهرا تحتراج: م حظة
يفضل استخدام القوائم الموصولة والتي سبل وان ترم كتابرة.الى مساحة ذاكرة كبيرة
.شفرتها خالل هذا الفصل
#include<iostream>
using namespace std;
public:
int size;
int* array;
401
C++ هياكل البيانات باستخدام
};
int BinarySearchTree::extendSize(int x) {
int value = 0;
for (int y = 0; y < x + 1; y++) {
value = (2 * value) + 2;
}
return value;
}
void BinarySearchTree::insertElement(int x) {
int currentIndex = 0;
cout << "Adding: " << x;
while(true) {
if(array[currentIndex] == NULL){
array[currentIndex] = x;
cout << " Inserted at index: " << currentIndex << endl;
break;
}else if(array[currentIndex] <= x) {
if (array[currentIndex] == x){
cout << "ERROR!-- Repeating element" << endl;
break;
}else
cout << " Right ";
currentIndex = (2 * currentIndex + 2);
}else if(array[currentIndex] >= x) {
C++ هياكل البيانات باستخدام
if (array[currentIndex] == x){
cout << "ERROR!-- Repeating element" << endl;
break;
}else
cout << " Left ";
currentIndex = (2 * currentIndex + 1);
}
}
}
403
C++ هياكل البيانات باستخدام
}
}
int main () {
BinarySearchTree frank(5);
frank.insertElement(4);
frank.insertElement(6);
frank.insertElement(9);
frank.insertElement(3);
frank.insertElement(2);
frank.searchElement(1);
frank.inOrder(0);
};
405
هياكل البيانات باستخدام C++
الفص التاسع
تكديس هياك البيانات
هياكل البيانات باستخدام C++
9.1المقدمة
تطرقنا في الفصرل السرابل الرى االشرجار الثنائيرة بشركل عرام ,واالشرجار الثنائيرة مرن
الممكررن ان تكررون بياناتهررا مرتبررة او ال تكررون مرتبررة ,عنرردما تكررون بيانررات االشررجار
الثنائية مرتبة فان ذلك يدعى التكديس ) .(Heapوبرالرغم مرن ان التكرديس هرو لريس
مرتب بشكل كامل ,اال انه يتوافل مرع مبراديء الترتيرب .والتكرديس عرادة هرو شرجرة
كاملة مرتبة البيانات.
407
هياكل البيانات باستخدام C++
في الشكل 9.1فقط Aهو تكديس .بينما Bهو ليس تكديس بسبب كونه غير كامل,
وكذلك فان Cهو ليس تكرديس برالرغم مرن انره كامرل ولكرن اليحقرل الشررط الثراني
حيث ان القيم غير مرتبة.
التكديس هي حالة خاصة من االشجار الثنائية الموزونة ,حيث ان عقدة الجذر تقارن
مع ابنائها وتنظم او ترتيب وفقا لذلك .هنا نوعان من التكديس.
.1التكديس االصغر Min-Heap
وهو التكديس الذي تكون فيه قيمة الجذر اصغر من او تساوي قيم ابناءل.
فلو فرضنا لدينا المدخالت التالية ,فسيكون تمثيله كما في الشكل .9.2
)(35, 33, 42, 10, 14, 19, 27, 44, 26, 31
ووارزمية التكديس
.1خلل عقدة جديدة ,توضع في نهاية التكديس (تكون متصلة مع اخر عقدة في
الشجرة).
.2اسناد قيمة جديدة لهذل العقدة.
.3تقارن قيمة هذل العقدة مع قيمة االب لها.
.4اذا كانت قيمة العقدة الجديدة اكبرر مرن قيمرة العقردة االب ,ترتم عمليرة االبردال
بينهما.
.5تعراد الخطروات ) (3, 4لحرين ان يرتم ضربط مواصرفات التكرديس (اي تكرون
قيمة االب اكبر او تساوي من قيمة االبناء).
409
هياكل البيانات باستخدام C++
بداية ستكون القيمة االولى ( )35هي العقدة االولى وتمثل عقدة الجذر.
القيمة الثانية هي 33وستكون في نهاية التكرديس ,تقرارن قيمتهرا بقيمرة االب
التي هي االن الجذر ) (33≤35نالح انهرا اصرغر اذن سرتكون ابرن لعقردة
الجذر.
القيمررة االخرررى فرري المجموعررة هرري 10وهرري سررتكون فرري اسررفل التكررديس
(الشجرة) وتكون العقدة ( )33اب لها ,تقارن القيمتين ) (10 ≤ 33ونالحر
ان 33اكبر منها .لذا ستكون العقدة 33اب للعقدة .10
هياكل البيانات باستخدام C++
411
هياكل البيانات باستخدام C++
القيمة التالية ( )44وهي ستكون ابن العقدة ( ,)10تقارن القيمة المضافة مع
قيمة االب ) ,(44 ≤ 10في هذل الحالة فان 10اصغر مرن 44لرذا البرد مرن
التبديل.
هياكل البيانات باستخدام C++
413
هياكل البيانات باستخدام C++
بعررد اتمررام عمليررات االبرردال يررتم قررراءة القيمررة االخرررى مررن مجموعررة قرريم
المدخالت وهي القيمة ( ,)26ستكون هذل القيمرة االبرن الثراني للعقردة (,)33
تقارن هذل القيم ) (26 ≤ 33وهنا القيمرة ( )26اصرغر مرن القيمرة ( )33لرذا
ال يوجد تبديل.
بعد التبديل ستكون العقدة ( )31ابنا للعقدة ( )42واذا ما قارنا القيمتين سنجد
ان العقرردة ( )31اصررغر مررن العقرردة ( )42لررذا اليوجررد تبررديل .بهررذا نكررون ق رد
خلقنا شجرة التكديس.
415
هياكل البيانات باستخدام C++
مثةةال علررى تطبيررل خوارزميررة الحررذف علررى التكررديس الررذي تررم انشررا ل سررابقا.
موضح في الشكل .9.15
هياكل البيانات باستخدام C++
9.5تمثي التكديس
من الممكن تنفيذ التكديس باستخدام القوائم الموصولة كما فعلنا مع االشجار الثنائيرة,
ولكن من المفضل واالسهل تنفيذ التكديس باستخدام المصفوفات.
لتنفيررذ ذلررك يررتم ترررقيم شررجرة التكررديس مررن االعلررى لالسررفل ابتررداءا مررن الجررذر الررذي
سيكون رقمه صفر ( وذلك الن المصفوفات في C++تبردأ بصرفر) ,تررقم العقرد فري
كل مستوى من اليسار الى اليمرين .بعرد التررقيم تخرزن كرل عقردة فري الموقرع المقابرل
للرقم الذي وضع على العقدة ,حيث تخرزن العقردة iفري الموقرع iمرن المصرفوفة .ان
حجم المصفوفة سيكون بقدر عدد العقد في التكديس.
417
C++ هياكل البيانات باستخدام
#include <stdio>
int array[100], n ;
main() {
cin>> choice ;
C++ هياكل البيانات باستخدام
switch (choice)
{
case 1:
cout<< "Enter the element to be inserted to the list : " ;
cin>> num ;
insert (num, n) ;
n = n + 1;
break;
case 2:
cout<< "Enter the elements to be deleted from the list: " ;
cin>> num ;
delete (num);
break;
case 3:
display();
break;
case 4:
exit(0);
default:
cout<< "Invalid choice \n" ;
} /* switch *نهاية/
} /* while *نهاية/
} /* main() *نهاية/
display()
{
int i;
if (n == 0)
{
cout<< "Heap is empty \n" ;
return;
}
for (i = 0; i < n; i++)
cout<< array[i] << endl ;
} /* display() *نهاية/
419
C++ هياكل البيانات باستخدام
{
parentnode =(location - 1)/2;
if (num <= array[parentnode])
{
array[location] = num;
return;
}
array [location] = array[parentnode];
location = parentnode;
} /* while *نهاية/
} /* insert() *نهاية/
delete(int num)
{
int left, right, i, temp, parentnode;
421
هياكل البيانات باستخدام C++
الفص ال اشر
المالببات GRAPHS
هياكل البيانات باستخدام C++
10.1المقدمة
في عالم الرياضيات وعلوم الحاسروب ,نظريرة المخططرات هري دراسرة المخططرات
التي تتعامل مع العالقة بين الروابط والر وس (العقد).
االشجار الثنائية توفر طرق مفيدة جردا لتمثيرل العالقرات عنردما يكرون التنظريم تنظريم
هرمي ,حيث ان العقدة يشار اليها بعقدة واحدة هي االب وكل عقردة تشرير الرى اينرين
مررن العقررد كحررد اعلررى (فرري حررال االشررجار الثنائيررة) كمررا سرربل وشرررحنا فرري فصررل
االشجار .اذا تم ازالة الشرط الخاص بان يكون لكل عقدة اينان من االبنراء ,فسريكون
لنا شجرة عامة كما في الشكل .10.1
ايضا اذا تم ازالة الشرط الخاص بان يكون لكل عقردة اب واحرد فسريكون لردينا شركل
يسمى مخطط .graph
423
هياكل البيانات باستخدام C++
شبكات الحاسوب ..العالقة بين الحواسيب المترابطرة مرع بعرض فري الشربكة
تتبع مبدأ نظرية المخططات.
العلوم ..هيكل الجزي ات والهيكل الكيميائي للمواد ,هيكرل DNAكلهرا تمثرل
بالمخططات.
اللغات ..شجرة التعبير للغة وقواعد اللغة تستخدم المخططات.
امور عامة ..الطرق Routesبين المدن مرن الممكرن تمثيلهرا بالمخططرات.
بالرغم من الترتيب الهرمي فان المعلومات مثل شجرة العائلرة مرن الممكرن
ان تستخدم كنوع خاص من المخططات تدعى االشجار.
10.4مصبلحات المالببات
هياكل البيانات باستخدام C++
النقبة :Pointهي موقع خاص فري فضراء يتكرون مرن بعردين او يرالث ابعراد .لكري
نفهمهررا اكثررر ف ران النقطررة تمثررل باحررد حررروف الهجرراء .مررن الممكررن تمثيلهررا كمررا ف ري
الشكل .10.2
الالط :Lineهو الرابط بين نقطتين .وهو يمثل كما في الشكل 10.3 .
الراس او ال قدة :Vertexهي النقطة التري تكرون ملتقرى لعردد مرن الخطروط .وهري
تسررمى عقرردة .وكمررا فرري النقرراط فرران هررذل العقررد تمثررل بررالحروف الهجائيررة .كمررا فرري
الشكل .10.2
الحواذ :Edgeالحواف هو مصطلح رياضي للخط الرابط بين راسين (عقردتين).
العديرد مرن الحرواف التري مرن الممكرن ان نرسرمها او نخلقهرا مرن عقردة مفرردة .بردون
العقد فان الحواف ال يمكن خلقها .يجب ان يكون هنا عقدة بداية وعقردة نهايرة .هرذل
الحواف سنسميها روابط لكي تكون اكثر وضوحا .كما في الشكل .10.3
مما تقدم من الممكن تعريف المخطط على انه عبارة عرن مجموعرة مرن العقرد تردعى
ر وس ومجموعة من الخطوط التي تربط كل اينين من الر وس تدعى روابط.
المخطط يحتوي على االقل رابط واحد يربط مجموعة من العقرد تتكرون مرن عقردتين
دون ان تكون هنا عقدة ترتبط بنفسها.
)G = (V, E يمثل المخطط رياضيا
حيث ان
) ( Vتمثل مجموعة العقد.
425
هياكل البيانات باستخدام C++
المخططات من الممكن ان تكون متجهرة او غيرر متجهرة ,فري االشركال المتجهرة فران
كل خط والذي يسمى قوس Arcيكون له اتجال يمثل كيفيرة المررور او االنتقرال ,امرا
غير المتجهة فان لها خطوط تسمى حواف edgeويمكن ان تتنقل بكال االتجاهين.
الحلقة :Loopفي المخطط فان الرابط الذي يرسم من عقدة الى نفس العقدة ,يردعى
حلقة .الشكل 10.5يوضح ذلك.
هياكل البيانات باستخدام C++
في الشكل 10.5نالح ان ) (Vهي عقدة ولها الرابط )e = (V, V
427
هياكل البيانات باستخدام C++
فرري المخططررات المتجرره فرران كررل عقرردة لهررا نرروعين مررن الرردرجات .درجررة الرردخول
) (indegreeوهرررري تمثررررل عرررردد الررررروابط الترررري ترررردخل الررررى العقرررردة ويرمررررز لهررررا
)) ,(deg+(Vواالخرى هي درجة الخروج ) (outdegreeوهي تمثل عدد الروابط
التي تخرج من العقدة ويرمز لها )).(deg-(V
شك :10.7مالبط متجه تم تحديد .A :ر ة الالروج لك عقدة .B .ر ة الدوول لك عقدة.
عقةةدة م لقةةة :Pendent Vertexالعقرردة الترري مقرردار درجتهررا تسرراوي 1 .1
تسمى عقدة معلقة ,في الشكل 10.8فان كل مرن ) (a, bلهرا درجرة ,1لرذلك كالهمرا
يعتبران معلل.
.2ال قدة الم زولةة :Isolated Vertexهري العقردة التري لهرا درجرة تسراوي
صفر .الح الشكل 10.9ان كل واحدة من هذل العقد لها درجة صفر ,لذلك
تدعى معزولة اي ليس لها ارتباط مع اي عقدة اخرى.
مثال اور :الشكل 10.11يحتوي على العقرد } {a, b, c, d, e, fوالتري لهرا درجرة
توالي هي }{2, 2, 2, 2, 2, 0
429
هياكل البيانات باستخدام C++
431
هياكل البيانات باستخدام C++
.6المسار البسيط :Simple Pathهو المسرار الرذي لريس فيره عقرد مكرررة ,كمرا
في الشكل .10.16
م حظة :الفشل في اي رابط يؤدي الى عدم تررابط النظرام (المخطرط بشركل عرام
اقل سماح لالخطاء او الفشل) الح الشكل .10.18
في المقابل فان الشكل الدائري هو اكثر سماح للفشرل او االخطراء ,هرو يحتراج عردد
من الروابط بقدر عدد الر وس او العقد ,انظر الشكل .10.19
433
هياكل البيانات باستخدام C++
.1مالبط موزول :Weighted Graphهو المخطط الذي يكون فيه كرل رابرط
يحتوي على قيمة محددة.
هياكل البيانات باستخدام C++
435
هياكل البيانات باستخدام C++
كل ) (nمن العقد سيكون لها ) (n-1من الروابط .لكن نحن نحسب كل رابرط مررتين
لذلك فان العالقة تكون )((n ( n-1) /2
في الشكل 10.23فان عدد العقد هي ( )5ولذلك فان عدد الروابط ستكون
5 (5 – 1) / 2 = 10
عليه ,فاذا لم يكن المخطط كامل فان عدد الروابط سيكون اقل من ).(n (n-1)/2
بالنسبة للشجرة فان عدد الروابط تساوي
m=n–1
م حظة :اذا كانت ) ,(m < n-1فان المخطط سيكون غيرر متررابط كمرا واضرح فري
الشكل .10.24
هياكل البيانات باستخدام C++
المسافة بين عقدتين ) :d(U, Vان المسافة بين العقدة ) (Uوالعقدة ) (Vهري
اقصر مسار بين العقدتين .فاذا كان هنا اكثر من مسار يصل بين العقدتين فان
اقصر واحد هو يمثل المسافة.
437
هياكل البيانات باستخدام C++
هنا في الشكل 10.26فان المسرافة مرن العقردة ) (dالرى العقردة ) (eاو ببسراطة )(de
هو واحد حيث يوجد رابط واحد بينهم .هنا عدة مسارات من العقدة ) (dالى العقردة
) (eوهي
انحراف العقدة هو اكبر مسافة بين عقدة واي عقدة اخرى في المخطرط ,ويرمرز
لهررا ) .e(Vبالنسرربة للمخططررات غيررر المتصررلة فرران جميررع العقررد لهررا انحررراف
يساوي ما النهاية.
في الشكل 10.26فان انحراف العقدة ) (aهو .3
لحسراب االنحررراف يررتم تحديررد المسررافات بررين العقرردة ) (aوجميررع العقررد االخرررى
وتكون المسافة االكبر هي التي تمثل االنحراف .لنحسب المسافة بين العقدة )(a
وباقي العقد وهي:
)(ab الى العقدة ) (bالمسافة = 1
)(ac الى العقدة ) (cالمسافة = 1
المسافة الى العقدة
)d = 1 (ad
e(b) = 3
e(c) = 3
e(d) = 2
e(e) = 3
e(f) = 3
e(g) = 3
439
هياكل البيانات باستخدام C++
اذن يتم حساب االنحراف لجميع العقرد كمرا فعلنرا مرع نصرف القطرر ,واالنحرراف
االكبرررر مرررن برررين هرررذل القررريم يعتبرررر هرررو قطرررر المخطرررط .القطرررر للمخطرررط فررري
الشكل 10.26هو ) (d(G) = 3والتي هي اكبر قيمة انحراف.
للمخطط مع نصف قطرل فان هذل النقطة تسمى نقطرة التمركرز .بكرالم اخرر عنرد
حسرراب نصررف القطررر ,وعنررد تطررابل هررذل القيمررة مررع انحررراف عقرردة مررا فرران هررذل
العقرردة تعتبررر المركررز .فرري الشرركل 10.26فرران نصررف القطررر يسرراوي ,2وهرري
تطابل درجة االنحراف للعقدة ).(d
المركز :Centerهي مجموعة نقاط التمركرز فري المخطرط .فري مثرال المخطرط
تسمى المحيط .في الشكل 10.26فان المحيط يسراوي ) ,(6والرذي يمثرل اطرول
دائرة في المخطط متمثلة
هياكل البيانات باستخدام C++
المقاس :Girthعدد الروابط فري اقصرر دائررة فري المخطرط ) .(Gويرمرز لره
ربما الكثير منا سمع بلغز الجسور التري موضرحة بالشركل ,10.28المطلروب براللغز
هو المرور على كل جسر في الشركل مررة واحردة فقرط والعرودة الرى نقطرة االنطرالق
(الجسور هي المستطيالت السوداء في الشكل) ,فهل حاولرت ان تحرل هرذل المسرالة؟,
وان كنت لم تحاول فهل من الممكن ان تحل المسالة قبل ان تنتقل الى موضوع اخرر
في هذا الفصل .اتمنى لكم وقتا ممتعا.
441
هياكل البيانات باستخدام C++
"أي مالبط من الممكن ال تمر علل ك رابط فيه مرة واحدة فقط وت و الل نقبة
البداية او ال قةدة التةي بةدأت منهةاب اكا واكا فقةط كانةت كة ال قةد لهةا ر ةة زو يةة
( ر تها رقم زو ي)".
االن نعود الى اللغز ,وعليك االستعانه بنظرية اولر لالجابة عن اللغز.
10.6انوا المالببات
هنا انواع متعددة مرن المخططرات تعتمرد علرى عردد العقرد ,عردد الرروابط ,التررابط,
وكامل هيكل المخطط .سنتطرق ألنواع قليلة مهمة من المخططات في هذا الفصل.
هياكل البيانات باستخدام C++
443
هياكل البيانات باستخدام C++
.4مالبط متجه :Directed Graphهو المخطط الذ يكون فيه كل رابرط محردد
اتجاهه من عقدة الى اخرى (او من الممكن نفس العقدة).
ان اكبر عدد للروابط في مخطط مفرد ,عدد عقدل يساوي ) (nهو
= n (n – 1) / 2
كما سبل ووضحنا في هذا الفصل .ان اكبرر عردد مرن المخططرات البسريطة الممكنرة
عندما يكون لدينا ) (nمن العقد هو
= 2n(n-1)/2
.6المالبةةط المتةةرابط :Connected Graphاي عقرردتين فرري المخطررط تكررون
مترابطة بواسطة مسار ,الشكل .10.34
445
هياكل البيانات باستخدام C++
.9المالبط الكام :Complete Graphهو المخطط الذي تكون فيره كرل عقردة
مرتبطة مع جميع العقد في المخطط .الح الشكل .10.36
هياكل البيانات باستخدام C++
447
هياكل البيانات باستخدام C++
المرور على كل العقد ابتداء من عقدة بداية مقترحرة (نحرن مرن يحردد نقطرة البدايرة).
عليه ,فان المرور سيتبع مبدا المجاور اوال مع االخذ بنظر االعتبار مايلي:
عدم المرور على أي عقدة ألكثر من مرة واحدة فقط.
فقط تلك العقد التي من الممكن الوصول لها يتم زيارتها.
من المهم هنا ,ان نستخدم مكدس هياكل البيانات .سيتم اضافة العقد التي يرتم
زيارتها الى المكدس بترتيب يتم سحبها من المكدس بحيث ان الداخل اخيررا
يخرج اوال.
زيارة العقدة يتضمن اعادة البيانات التي في العقردة واضرافة مجاورهرا الرى المكردس.
على كل ,فاننا النعمل أي فعل في الحاالت التالية:
اذا كان المجاور اساسا في المكدس ,او اذا كان المجاور قد سبل وان تمت زيارته.
ووارزمية المرور
القاعدة االولل :تحديد عقدة البداية ,زيارة العقد المجاورة لها التي لم يتم زيارتها.
القاعةدة الثانيةة :اذا لررم يرتم ايجرراد عقرد مجرراورة ,يرتم سررحب عقردة مررن المكردس( .يررتم
سحب جميع العقد من المكدس التي ليس لها عقرد مجراورة .اي يرتم السرحب لحرين ان
نجد عقدة لها مجاور او لحين ان يفر المكدس).
القاعدة الثالثة :اعد القاعدة االولى والثانية لحين ان يفر المكدس.
م حظة :العقدة التي يتم زيارتها يفضل تعليمها او تاشيرها لتسهيل العمل.
لنتابع هذل الخوارزمية باستخدام الشكل .10.38
هياكل البيانات باستخدام C++
449
هياكل البيانات باستخدام C++
.4االن جاء دور زيارة العقدة ) (Dوتدفع الى المكدس بعد ان تعلم على انهرا تمرت
زيارتها ,كما في الشكل .10.41
نبحث عن العقد المحاورة لها وسنجد ان العقدتين ) (B, Cكالهما تجاور العقدة
) (Dوكالهما لم يتم زيارتهما .وسنختار حسب الحروف االبجدية ,لتكون العقدة
) (Bهي العقدة المختارة.
.5يتم زيارة العقدة ) (Bوتعلم على انها تمرت زيارتهرا وتردفع الرى المكردس فيكرون
شكل المكدس كما في الشكل .10.42
هنا عند الفحص عن العقد المجاورة سوف ال نجد اي عقدة مجاورة للعقردة )(B
لم يتم زيارتها سابقا .فري هرذل الحالرة يرتم سرحب العقردة التري فري اعلرى المكردس
) ,(Bوتفحررص العقررردة التررري بعررردها ) (Dان كانرررت لهرررا عقررردة مجررراورة لرررم يرررتم
هياكل البيانات باستخدام C++
زيارتها ,سنجد هنا العقدة )( (Cاذا لم نجرد نسرتمر بسرحب العقرد التري فري اعلرى
المكدس) .سيكون شكل المكدس بعد سحب العقدة التي في االعلرى ) (Bكمرا فري
الشكل .10.43
451
هياكل البيانات باستخدام C++
.6تتم زيارة العقدة ) (Cوتعلم على انها تمرت زيارتهرا يرم تردفع الرى المكردس ,كمرا
هو واضح من الشكل .10.44
يتم البحث عن العقد المجاورة والتري لرم ترتم زيارتهرا .ونظررا لعردم وجرود عقردة
مجاورة للعقدة ) (Cلم تتم زيارتها نسحب العقردة التري فري اعلرى المكردس ).(C
نستمر بسرحب العقردة التري فري االعلرى واحردة بعرد االخررى (وفري كرل مررة يرتم
فحص العقدة في اعلى المكدس ان كران لهرا مجراور) لحرين ان يرتم العثرور علرى
عقدة لها مجاور لم يتم زيارته .فري مثالنرا هرذا سريتم سرحب جميرع العقرد دون ان
نعثر على عقدة مجاورة ولذلك متى مرا فرر المكردس فران عمليرة المررور علرى
عقد المخطط تكون قد تمت.
C++ هياكل البيانات باستخدام
#include <stdlib>
#include <stdbool>
#define MAX 5
struct Vertex {
char label;
bool visited;
};
// متغيرات المكدس
int stack[MAX];
// مصفوفة العقد
453
C++ هياكل البيانات باستخدام
// مصفوفة التجاور
int adjMatrix[MAX][MAX];
int vertexCount = 0;
// دوال المكدس
stack[++top] = item;
int pop() {
return stack[top--];
int peek() {
return stack[top];
bool isStackEmpty() {
// دوال المخطط
vertex->label = label;
vertex->visited = false;
lstVertices[vertexCount++] = vertex;
adjMatrix[start][end] = 1;
adjMatrix[end][start] = 1;
// عرض العقدة
cout<< lstVertices[vertexIndex]->label);
455
C++ هياكل البيانات باستخدام
int i;
return i;
}
return -1;
void depthFirstSearch() {
int i;
lstVertices[0]->visited = true;
// عرض العقدة
displayVertex(0);
C++ هياكل البيانات باستخدام
push(0);
while(!isStackEmpty()) {
// استدعاء العقد المجاورة التي لم يتم زيارتها للعقدة التي في اعلى المكدس
if(unvisitedVertex == -1) {
pop();
}else {
lstVertices[unvisitedVertex]->visited = true;
displayVertex(unvisitedVertex);
push(unvisitedVertex);
lstVertices[i]->visited = false;
457
C++ هياكل البيانات باستخدام
int main() {
int i, j;
adjMatrix[i][j] = 0;
}
addVertex('S'); // 0
addVertex('A'); // 1
addVertex('B'); // 2
addVertex('C'); // 3
addVertex('D'); // 4
addEdge(0, 1); // S - A
addEdge(0, 2); // S - B
addEdge(0, 3); // S - C
addEdge(1, 4); // A - D
addEdge(2, 4); // B - D
addEdge(3, 4); // C - D
هياكل البيانات باستخدام C++
;)(depthFirstSearch
;return 0
}
ان خوارزمية البحث الجانبي اوال سرتمر علرى المخطرط برالتحر علرى كرل مسرتوى
فرري المخطررط ويسررتخدم الطررابور للتررذكير بالعقررد الترري ترراتي الحقررا لبرردا البحررث عنررد
الوصول الى نهاية ميتة.
الخوارزمية تمر على جميرع العقرد ,بدايرة مرن عقردة بدايرة محرددة .هنرا ,المررور يتبرع
مبدا التجاور اوال مع االخذ بنظر االعتبار مايلي:
ال تتم زيارة عقدة اكثر من مرة واحدة فقط.
فقط العقد التي من الممكن الوصول لها يتم زيارتها.
مهم هنا ,ان نستخدم طابور هياكل البيانات .سيستخدم لحفر العقرد التري يرتم زيارتهرا
اخيرا بترتيب االضافة الى الطابور بمعنى من يدخل اوال يخرج اوال.
زيارة العقدة يتضمن اعادة البيانات التي في العقدة واضافة العقدة التي تجاورها الرى
الطابور .على كل ,سوف ال يتم اتخاذ أي فعل تحت الشروط التالية:
اذا كان المجاور موجودا اساسا في الطابور.
اذا المجاور سبل وان تم زيارتة.
459
هياكل البيانات باستخدام C++
شك :10.45المالبط الذي سيتم زيارة عقد وكذلو تمثي لبابور فارغ.
.1تتم عملية المرور بداية بزيارة العقدة االولى هنا ) ,(Sوعند زيارتها تعلم (نغير
لونهررا فقررط للمتابعررة ,برمجيررا مررن الممكررن ان تكررون عالمتهررا ,) trueكمررا فرري
الشكل .10.46
.2يتم البحث عن العقردة المجراورة ,الحر وجرود يرالث عقرد مجراورة )(A, B, C
ويتم االختيار وفقا للحروف االبجدية فتكون العقردة التاليرة هري العقردة ) .(Aيرتم
تعليم العقدة ) (Aوحشرها في الطابور كما في الشكل .10.47
461
هياكل البيانات باستخدام C++
.4العقدة االخرى المجاورة للعقدة ) (Sوالتي لرم يرتم زيارتهرا بعرد هري العقردة )(C
فيصار الى زيارتها وتعلم على انها تمت زيارتهرا ,يرم تحشرر فري الطرابور ,كمرا
في الشكل .10.49
هياكل البيانات باستخدام C++
.5االن العقدة ) (Sليس لها اي عقدة مجاورة لم يتم زيارتها لرذلك يرتم سرحب عقردة
مررن الطررابور وهنررا سرريتم سررحب العقرردة ) .(Aويكررون شرركل الطررابور كمررا فرري
الشكل .10.50
.6االن بالنسربة للعقردة الترري ترم سررحبها مرن الطررابور نفحرص عررن مجاوراتهرا فنجرد
العقدة ) (Dمجاورة لها ,لذلك تعلم هذل العقدة علرى انهرا تمرت زيارتهرا ,وتحشرر
في الطابور .كما في الشكل .10.51
463
هياكل البيانات باستخدام C++
.7االن ال توجررد عقررد مجرراورة للعقرردة ) (Aلررم يررتم زيارتهررا ,فيررتم سررحب عقرردة م رن
الطررابور وتفحررص ان كرران لهررا مجرراورات لررم يررتم زيارتهررا .هررذل العمليررة تسررتمر
لحين ان نجد عقدة مجاورة لم يتم زيارتها او يفر الطابور وبذلك تكون عمليرة
المرور قد انتهت.
نتيجة عملية المرور ستكون S A B C D
برنررامج المرررور علررى المخطررط باسررتخدام طريقررة ( Breadth Firstسررنركز
على المثال اعالل كنموذج للبرمجة).
>#include <stdio
>#include <stdlib
>#include <stdbool
{ struct Vertex
;char label
;bool visited
;}
;]int queue[MAX
int front = 0;
int queueItemCount = 0;
// متغيرات المخطط
// مصفوفة العقد
// مصفوفة المجاورات
int adjMatrix[MAX][MAX];
int vertexCount = 0;
// دوال الطابور
queue[++rear] = data;
queueItemCount++;
int removeData() {
queueItemCount--;
return queue[front++];
465
C++ هياكل البيانات باستخدام
bool isQueueEmpty() {
return queueItemCount == 0;
// دوال المخطط
vertex->label = label;
vertex->visited = false;
lstVertices[vertexCount++] = vertex;
adjMatrix[start][end] = 1;
adjMatrix[end][start] = 1;
}
C++ هياكل البيانات باستخدام
// عرض العقدة
cout<< lstVertices[vertexIndex]->label ;
int i;
return i;
return -1;
void breadthFirstSearch() {
int i;
lstVertices[0]->visited = true;
// عرض العقدة
467
C++ هياكل البيانات باستخدام
displayVertex(0);
insert(0);
int unvisitedVertex;
while(!isQueueEmpty()) {
// استدعاء العقد التي لم يتم زيارتها للعقدة التي في بداية الطابور
lstVertices[unvisitedVertex]->visited = true;
displayVertex(unvisitedVertex);
insert(unvisitedVertex);
for(i = 0;i<vertexCount;i++) {
lstVertices[i]->visited = false;
}
C++ هياكل البيانات باستخدام
int main() {
int i, j;
adjMatrix[i][j] = 0;
addVertex('S'); // 0
addVertex('A'); // 1
addVertex('B'); // 2
addVertex('C'); // 3
addVertex('D'); // 4
addEdge(0, 1); // S - A
addEdge(0, 2); // S - B
addEdge(0, 3); // S - C
addEdge(1, 4); // A - D
addEdge(2, 4); // B - D
addEdge(3, 4); // C - D
breadthFirstSearch();
469
هياكل البيانات باستخدام C++
;return 0
}
هناك عدد من العمليات التي تجرى على المخططات ,غالبا هذه العمليات تؤدي الى
تحوير المخططات .سنحاول ان نتطرق الى اغلب هذه العمليات .بعض
العمليات ستكون سهلة جدا وواضحة عند التطرق لتمثيل المخططات.
471
هياكل البيانات باستخدام C++
.2القوائم الموصولة :كذلك من الممكن تمثيلها على شكل قوائم موصرولة ,كمرا فري
الشكل .10.56
473
هياكل البيانات باستخدام C++
475
هياكل البيانات باستخدام C++
في المخططات الموزونة المتجهة ,نضع الوزن على الرابط المتجه بدال من
.1
هياكل البيانات باستخدام C++
في هذا التمثيل ,فان عمليرات اضرافة رابرط ,حرذف رابرط ,وفحرص وجرود رابرط فقرط
تتضمن اضافة قيم للمصفوفة او قراءة مدخالت المصفوفة )] (a[i][jكما يلي:
{ )void addEdge(int i, int j
;a[i][j] = true
}
;a[i][j] = false
}
;]return a[i][j
}
477
هياكل البيانات باستخدام C++
في طريقة قوائم التجاور فان اضافة رابط برين ) (i, jتتطلرب فقرط اضرافة العقردة )(j
الى القائمة )].(ad[i
;int n
;List *ad
;)ad[i].add(j
}
عملية ازالة رابط والذي يربط بين العقدتين ) (i, jيتم باجراء عملية البحرث اوال فري
القائمة )] (ad[iلحين ايجاد ) (jوعندها يتم ازالتها.
{ )void removeEdge(int i, int j
;)adj[i].remove(k
;return
479
هياكل البيانات باستخدام C++
هي خوارزمية اليجاد اقصر طريل (االقل وزنرا) برين اينران مرن العقرد المحرددة فري
مخطط موزون ال يحتوي على روابط لها اوزان سالبة.
هي تحل مشكلة المسار االقصر لعقدة واحدة
مفرراهيم الخوارزميررة االساسررية :خلررل جرردول معلومررات حررول افضررل الطرررق
المعروفة حاليا للوصول الى كل عقدة ( تشمل المسافة ,العقردة السرابقة) وتحسرينها
لحين الحصول على افضل حل.
ابسررط عمليررة تنفيررذ لهررذل الخوارزميررة هررو بحف ر العقررد فرري قائمررة موصررولة او
مصفوفة.
ان طرول المسرار )) ( p = (v0, v1, v2 … vkهرو مجمروع االوزان لروابطهرا
المتتالية.
= )Length (p
المسرافة برين ) (u, vتمثرل برالترميز )) ( (u, vوهرو طرول اقصرر مسرار اذا كران
هنا مسار بين ) ,(u, vوبخالف ذلك فالطول يساوي )∞(.
في الشكل ,10.64فان المسافة )) (p= (a, b, c, eتساوي (.)6
وذلك الن اقصر مسافة بين ) (a, eتساوي (.)6
481
هياكل البيانات باستخدام C++
ان هدف الخوارزمية هو المحافظة على )] (d[Vالن يكون بطول .1
) (S, Vوالذي هو اقصر مسار بين عقدة المصدر وكل عقدة ).(V
دائما ) ) (d[V] ≥ (S, Vوان المسافة ] d[Vتساوي طول المسار المعلروم, .2
الن )∞ = ] (d[Vاذا لم يكن هنا مسار.
بد اية يتم تحديد العقدة المصدر وهي العقدة التي سريتم حسراب المسرافة نسربة لهرا .3
(من الممكن اعتبارها عقدة البداية).
تحدد المسافة لكل عقد المخطط )] ,(d[Vفي البداية تكون مسافة عقدة المصردر .4
تساوي صفر ,بينما مسافة جميع العقد االخرى تساوي ما النهاية.
الخوارزمية تعالج العقد واحدة بعد االخرى حسب ترتيب معين .هنا ان معالجرة .5
العقرردة ) (uمررثال تعنرري ايجرراد المسررارات الجديرردة وتحررديث المسررافات )](d[V
لجميررع العقررد ) (vوالترري تنتمرري الررى مجرراورات العقرردة ) (uاذا كرران التحررديث
ضررروري .ان عمليررة حسرراب المسررافات والتحررديث الررذي يحرردث بموجبرره تسررمى
) .(relaxationعند معالجة جميع العقد فان
هياكل البيانات باستخدام C++
) ) ( d[v] = (S, Vاي اننرا نكرون قرد حسربنا اقصرر مسرافة لكرل العقرد فري
المخطط.
.6عملية التحديث تتم في حالة ان يكون المسار من عقدة المصردر) (Sالرى العقردة
) (Vهو اصغر من المسافة )] (d[Vالموجودة على العقدة ) ,(Vفي هذل الحالة
يتم وضع المسافة الجديدة للعقدة ) ,(Vبخالف ذلك ال تتم عملية التحديث.
.7ترررتم عمليرررة تنفيرررذ الخوارزميرررة وذلرررك بخرررزن العقررردة فررري طرررابور االفضرررلية
) (priority queueعند قرائتها ,بحيث تكون القيمرة المفتاحيرة لكرل عقردة هري
مسافتها ] .d[Vفتكون قيمة االفضلية للعقدة ) (Sتساوي صفر.
تخررزن المسررافة الررى العقرردة )( (Sوالترري هرري صررفر) وجميررع العقررد االخرررى (قيمتهررا
االبتدائية ما النهاية) في جدول خاص يسمى جدول المسافات.
.8يررتم ايجرراد العقرردة ) (Vالترري لهررا اقررل قيمررة (مسررافة) مررن الطررابور (سرريكون موقررع
العقدة المختارة في بداية الطابور) ,ويتم ازالتها من الطابور.
.9نحررردد العقرررد المجررراورة ) (Wللعقررردة ) .(Vلكرررل عقررردة مرررن العقرررد المجررراورة
للعقدة ) (Vنقوم بما يلي:
.Aحساب المسافة الجديدة من العقدة ) (Vالى العقدة المجاورة ).(W
المسافة الجديدة ) = (dالمسافة الى العقدة ) + (Vوزن ).(V, W
.Bاذا كانت المسافة القديمة (المسافة التري خزنرت فري جردول المسرافات) تسراوي (
∞) (اي انها لم تحسب سابقا) فنقوم بما يلي:
.Iخزن المسافة الجديدة ) (dفي جدول المسافات.
.IIحشر او خزن العقدة ) (Wفي طابور االفضلية مع افضلية قيمتها ).(d
.IIIخزن ( المسار = ) V
.Cاذا كانت المسافة التي تم حسابها ) (dاصغر من المسافة القديمة ,فنقوم بما يلي:
.Iتحديث المسافة القديمرة للعقردة بحيرث تكرون قيمتهرا تسراوي المسرافة الجديردة
).(d
.IIتحديث االفضلية للعقدة ) (Wبحيث تكون قيمتها تساوي ).(d
.IIIتحديث المسار الى ) (Wليكون ).(V
483
هياكل البيانات باستخدام C++
.10يتم فحص الطابور ,اذا كان الطابور غير فار ,العودة الى الخطوة ( .)8اما اذا
كان الطابور فار فهذا يعني ان عملية ايجاد المسافات االقصر قد انتهت.
مثةةال :مطلرروب ايجرراد المسررافات االقصررر بررين عقرردة المصرردر ) (Aوجميررع العقررد
االخرى في المخطط في الشكل .10.65
شك :10.65مالبط موزول مبلوب ايجا اقصر مسافة بين ال قدة Aوباقي ال قد.
هياكل البيانات باستخدام C++
.1اختار العقدة التي لها اقل مسرافة فري القائمرة ,واضرح هنرا ان العقردة المصردرهي
التي لها اقل مسافة وتساوي صفر .مجاورات العقدة ) (Aهي ).(B, D
485
هياكل البيانات باستخدام C++
.4يتم اختيار العقدة التي لها اقل مسافة من القائمة بعد التحديث وهنا ستكون العقدة
) .(Bيتم تحديث مسافات العقد المجاورة لها وهي العقد ) .(D, Eالحر هنرا ان
المسافة ) (Dال يتم تحديثها وذلك النها معروفة اساسا ,كذلك فان المسافة للعقدة
) (Eال يتم تحديثها الن المسافة الجديدة اكبر من المسافة التي حسبت سابقا.
487
هياكل البيانات باستخدام C++
.6اختيررار العقرردة الترري لهررا اقررل مسررافة مررن القائمررة وهرري العقرردة ) ,(Cوتحررديث
مجاوراتها .كما في الشكل .10.71
مسافة العقدة ).8 = 5 + 3 = (F
.8اختيار العقدة التي لها اقل مسافة وهي العقدة ) (Fوتحديث المجاورات.
489
هياكل البيانات باستخدام C++
شك :10.74مالبط مبلوب حساب اقصر مسافة بين ال قدة وباقي ال قد.
رتررب القرريم االبتدائيررة علررى شرركل جرردول (وهرري القرريم المستسررقاة مررن المخطررط فرري
الشكل 10.74قبل اجراء اي عملية عليه).
دول :10.1القيم االبتدائية للمالبط في الشك . 10.71
م حظة:
سيتم استخدام ) DT(i, jلالشارة الى الخلية في الصف ) (iوالعمود ) (jفي
جدول المسافات (تمثل المسافة المحسوبة).
كذلك فان ) (V:nتعني ان المسافة من ) (V3الى العقدة ) (Vتساوي ).(n
(PQ) سيرمز الى طابور االفضلية ). (Priority Queue
491
هياكل البيانات باستخدام C++
م حظةةة :العناصررر فرري الطررابور مرتبررة حسررب االفضررلية (القيمررة االقررل تمثررل
افضرلية عليررا) .لرذلك فرران عمليرة سررحب عنصرر مررن الطرابور سرريكون دائمرا مررن
اقصى يسار الطابور.
∞ V2
0 V3
∞ V5
∞ V7
.Bيرررتم سرررحب وازالرررة عقررردة مرررن بدايرررة الطرررابور وسرررتكون العقررردة ( ,)V4:3
مجاورات هذل العقدة هي (} ,)V2{4}, V6{7لكل واحد من مجاورات العقردة
V4قم باالعمال التالية:
493
هياكل البيانات باستخدام C++
0 V3
∞ V5
∞ V7
.Cيتم سحب وازالة عقدة من بدايرة الطرابور وسرتكون العقردة ( ,)V1:6مجراورات
هذل العقدة هي (} .)V2{3}, V4{2لكل واحد من مجاورات العقدة V1اعمرل
ما يلي:
أقرأ العقدة المجاورة }V2{3 .I
المسررافة الررى العقرردة V2تررم حسررابها سررابقا وهرري تسرراوي 7وهرري
اصغر مرن المسرافة ( الرى + V1وزن الررابط برين العقردتين = (V1, V2
) ,)6+3لذلك ال تعمل اي شيء.
المسررافة الررى العقرردة V4تررم حسررابها سررابقا وهرري تسرراوي ,3وهرري
اصغر من المسافة ( الى V1يضاف لها وزن الررابط برين العقردتين (V1,
) ,)V4 = 6+2لذلك ال تعمل اي شيء.
0 V3
495
هياكل البيانات باستخدام C++
∞ V7
.Eيررتم سررحب وازالررة عقرردة مررن بدايررة الطررابور وسررتكون العقرردة ( ,)V6:10
مجاورات هذل العقردة هري (} .)V7{4لكرل واحرد مرن مجراورات العقردة V6قرم
باالعمال التالية:
أقرا العقدة المجاورة }V7{4 .I
أحسب المسافة الى V7النها لم تحسب سابقا.
مسرررافة ) = DT(7,1المسرررافة الرررى )( .. (V6متمثلرررة فررري )+ ) DT(6,1
الرابط بين )14 = (V6, V7
سجل المسارDT(7,2) = V6 ,
خزن V7:14في طابور.
PQ: V7:14, V5:15 وضع الطابور سيكون
0 V3
.Fيررتم سررحب وازالررة عقرردة مررن بدايررة الطررابور وسررتكون العقرردة (,)V7:14
مجرراورات هررذل العقرردة هرري (} .)V4{5لكررل واحررد مررن مجرراورات العقرردة V7
تجرى االعمال التالية:
أقرا العقدة المجاورة }V4{5 .I
المسرافة الرى العقردة V4ترم حسرابها سرابقا وهري تسراوي ,3وهري
اصغر من المسافة الى العقدة + V7وزن الرابط بين العقدتين
) ,(V7, V4 = 14+5 = 19لذلك ال تعمل اي شيء.
الجدول سوف ال يتغير
PQ: V5:15 وضع البابور سيكول
.Gيررتم سررحب وازالررة عقرردة مررن بدايررة الطررابور وسررتكون العقرردة (,)V5:15
مجاورات هذل العقدة هي (} .)V4{1}, V7{2لكل واحد من مجاورات العقردة
V5قم باالعمال التالية:
أقرأ العقدة المجاورة االولى }V4{1 .I
497
هياكل البيانات باستخدام C++
المسافة الى العقدة V4تم حسابها سابقا وهي تساوي ,3وهري اصرغر مرن
المسافة الرى + V5وزن الررابط برين العقردتين ),(V5, V4 = 15+1=16
لذلك ال تعمل اي شيء.
ان اقصر المسالك بين العقدة V3وباقي العقد في المخطط هي في الجدول .10.6
0 V3
C++ هياكل البيانات باستخدام
V3 V4 V3 3 V4
V3 V4 V2 V5 V2 15 V5
V3 V4 V6 V4 10 V6
V3 V4 V6 V7 V6 14 V7
/* Dijkstra's Algorithm in C */
#include<stdio>
#include<conio>
#include<process>
#include<string>
#include<math>
#define IN 99
#define N 6
int main()
int cost[N][N],i,j,w,ch,co;
for(i=1;i< N;i++)
499
C++ هياكل البيانات باستخدام
for(j=1;j< N;j++)
cost[i][j] = IN;
cout<< "Enter the weight of the path between nodes "<< x<< y ;
cin>> w ;
cin>> source ;
cin>> target ;
co = dijsktra(cost,source,target);
int dist[N],prev[N],selected[N]={0},i,m,min,start,d,j;
C++ هياكل البيانات باستخدام
char path[N];
for(i=1;i< N;i++)
dist[i] = IN;
prev[i] = -1;
start = source;
selected[start]=1;
dist[start] = 0;
while(selected[target] ==0)
min = IN;
m = 0;
for(i=1;i< N;i++)
d = dist[start] +cost[start][i];
if(d< dist[i]&&selected[i]==0)
dist[i] = d;
prev[i] = start;
501
C++ هياكل البيانات باستخدام
min = dist[i];
m = i;
start = m;
selected[start] = 1;
}
start = target;
j = 0;
while(start != -1)
path[j++] = start+65;
start = prev[start];
path[j]='\0';
strrev(path);
cout<< path ;
return dist[target];
}
C++ هياكل البيانات باستخدام
REFRENCES المصا ر
503
C++ هياكل البيانات باستخدام
505
1