علومي
Would you like to react to this message? Create an account in a few clicks or log in to continue.

هياكل البيانات Data Structures

Go down

هياكل البيانات Data Structures Empty هياكل البيانات Data Structures

Post by evergreen Sat Mar 13, 2010 6:35 am

وسنتناول فيها المواضيع التالية:
المكدس Stack
الطابور Queues
القوائم المرتبطة Linked List
الاشجار Trees
والمخططات Graph
ال Hash Table

:: Search & Sort , Template , STL


سنستخدم لغة البرمجة C++/C ، اي
سنفرق بين كود سي وسي ++ ، وذلك لايضاح الفرق بين اللغتين ، وكذلك لتكون
حافز لمبرمجي السي (عاشقي السي ) الى الانتقال الى العالم الاوسع ،،
عالم ال OOP.

+++++++++++++++++++++++++++++
Data Structure + هياكل البيانات او تراكيب البيانات
Class + فئة او طبقة او نوع او صنف ،، بصراحة لا أدري ما هو الافضل
Stack + الرصة او المكدس
Queue + الطابور او الصف
+++++++++++++++++++++++++++++


ما هي هياكل البيانات Data Structures:
هي عبارة عن اوعية "حاويات" للبيانات ، تتبع طرق معينة لتخزين البيانات فيها
والمقصود بالوعاء هو مساحة تخرين.

و اول مثال لهياكل البيانات (والتي لا تحتاج الى تعريف definition ) هي المصفوفات Arrays
المصفوفة
هي عبارة عن حاوية للبيانات ،، حجمها ثابت ،، ويجب ان يكون معلوم في وقت
الترجمة .وجميع محتوياتها من نفس النوع . كلام جميل
اعلان عن مصفوفة A :

CODE:
int A[5];



هنا قمنا بحجز مساحة لتخزين 5 اعداد صحيحة ،، ونستطيع استخدامها دون اي مشاكل تذكر.

طيب هممممممممم ما هي الحاجة الى هياكل "تراكيب" بيانات اخرى ! ما دام ان المصفوفة تعمل ؟!

الاجابة:
المصفوفة غير مرنة ،، بمعنى انك لا تستطيع اضافة عنصر اخر وقت تشغيل البرنامج
وكذلك لا تستطيع حذف عنصر وقت التشغيل ،، اي تجعلها مكونة من 3 اعداد صحيحة فقط
وكذلك لا تستطيع ان تجعل عدد عناصرها مجهول ، يحدده المستخدم مثلا او تحدده قيمة متغير اخر.
كل هذه اسباب تجعل المبرمجين يلجؤون الى التفكير بهياكل اخرى.

اعتقد
انك راح تفكر في ان نجعل المصفوفة كبيرة ،، وذلك حتى لا نحتاج الى اضافة
"فهي كبيرة بما تكفي" ، وبالنسبة للحذف راح تدبر اي طريقة للتلاعب "لانه
لا يمكن حذف اي عنصر من المصفوفة"

الاجابة:
المصفوفة الكبيرة تعنى استهلاك كمية من الذاكرة ،، صحيح ان الذاكرة لدي كبيرة ، ولكن هل هي كذلك عند س من الناس ؟

الامر
الاخر وهو الاهم عند الجميع هو سرعة البحث !! ذاكرة كبيرة يعني ان البحث
سيكون ابطأ ، ونحن كمبرمجين نريد ان نعطي المستخدم ذاكرة على قدر حاجته .

امر اخير عن المصفوفات ،، ليس فيها اي نوع حماية ، للاسف
جرب باستخدام مصفوفتنا الكريمة A ان تدخل اكثر من 5 اعداد ، وشاهد النتجية

كذلك هناك مشاكل كثيرة problems لايمكن حلها باستخدام المصفوفات العادية ،،
وراح نتطرق لبعض المشاكل عندما نتقدم قليلا بالدروس.

اعتقد ان الان وضح سبب الحاجة الى ال Data Structures

الان راح تقول : تبا للمصفوفات،، وعطنا اللي عندك
الاجابة : المصفوفات هي الاساس في معظم هياكل البيانات
evergreen
evergreen

الجنس : Female

عدد المساهمات : 1497
النقاط : 61948
التقييم : 34
تاريخ التسجيل : 2010-02-03

https://3loomi.forumotion.com

Back to top Go down

هياكل البيانات Data Structures Empty Re: هياكل البيانات Data Structures

Post by evergreen Sat Mar 13, 2010 6:36 am

الان وبعد ان تحدثنا قليلا عن المصفوفات وعيوبها ،، جاء دور الانتقال الى اولى هياكل البيانات
الا وهي المكدس Stack

ما هو المكدس Stack ؟؟
هو احد هياكل البيانات ، الاكثر سهولة ، يستخدم بكثرة في عالم الحواسيب : مترجمات ، الات حاسبة ، المعالجات و...الخ.
ومفهمومها بسيط جدا ،، ادرس المثال الاتي:

تخيل ان لدينا مجموعة من الاطباق "الصحون" ، مرصوصة فوق بعضها ،اي صحن وفوقه واحد اخر الى ان نصل الى اخر صحن .
الان
لكي نضيف صحن اخر الى المجموعة ، يجب ان نضعه على رأسهم ، يعني اعلى شيء
top واذا اردنا ان نسحب اي عنصر ، يجب ان نسحب الذي فوقه اولا .
اي لا تستطيع سحب الصحن الرابع مثلا دون ان نسحب الصحون التي تقع فوقه ، والا ستنكسر الصحون.

مثلا
اذا اردنا سحب الصحن الثاني ، يجب ان نسحب كل الصحون التي تقع فوقها ، حتى
نصل الى الصحن الثاني و نسحبه ،، وتكون المجموعة الجديدة مكونة من الصحن
الاول فقط ، وذلك لاننا سحبنا كل الصحون التي تقع فوقها.

ويتضح المثال بهذا التعريف :
المكدس
هو عبارة عن فكرة "طريقة" تطبق على المصفوفة "ليس في كل الحالات ، ولكن
سنستخدم المصفوفة هنا" ،، بحيث ان ادخال العناصر يتم من اعلى "كما في حالة
الصحون" ، وكذلك سحب العناصر يتم من اعلى .

وذلك على خلاف المصفوفة العادية ، مثلا اذا ادخلنا في اي مصفوفة العناصر
2 ثم 4 ، 6 ، 8 ، 10 .
واردنا عرضها على الشاشة فان النتيجة هي :
2 ، 4 ، 6 ، 8 ، 10 اي على نفس ترتيب الادخال .

ولكن اذا ادخلنا الاعداد السابقة في مكدس ، وعرضنا عناصر المكدس على الشاشة ، فالنتيجة هي:
10 ، 8 ، 6 ، 4 ،2 اي على عكس ترتيب الادخال .

اي ان العنصر الداخل الى المكدس اولا ، هو الذي سيخرج في الاخير
والعنصر الداخل الى اعلى المكدس ، هو الذي سيخرج اولا .
Last in First Out
لذلك تجد دائما مع المكدس هذه العباره ، وتختصر ب LIFO

خلاصة :
* المكدس هو عبارة عن هيكل بيانات .
* يتم تطبيق المكدس من خلال المصفوفات او من خلال القوائم المرتبطة "سنتحدث عنها لاحقا"
* يتبع المكدس مفهوم LIFO ، اي العنصر الذي يدخل في الاخير ، هو الذي يخرج اولا.
* لادخال عنصرفي المكدس يجب ان نضعه فوق اعلى عنصر .
كيف سنعرف ان العنصر هو اعلى ام لا ؟

سنحتاج الى مؤشر للمصفوفة "عدد صحيح " ، وذلك لكي نعرف من هو اعلى عنصر ،، وليكن اسمه top.

في
الحقيقة top ليس مؤشر pointer وانما هو عدد int ، ولكن نستخدمه كدليل الى
العنصر الاعلى في المصفوفة ، يعني اذا كان عندي مصفوفة من 10 عناصر ،
والمستخدم ادخل قيمة اول عنصرين ، فان المتغير top سيحمل القيمة 1 ، دلالة
على ان العنصر الثاني هو اعلى عنصر.

* عملية ادخال العنصر الى المكدس ، تسمى push ، والتي تعني دفع العنصر الى اعلى المكدس.

* عملية سحب العنصر من المكدس ، تسمى pop

صورة توضيحية :

هياكل البيانات Data Structures 200px-Data_stack.svg

اعتقد ان المفهوم اتضح قليلا ،، والكود هو خير الكلام ،، ما " قل و دل "
سنضع كودين ، واحد للغة السي والاخر سي ++ .

وان كنت افضل كود السي++ وذلك لدعمه "البرمجة كائنية المنحى" هياكل البيانات Data Structures Icon_lol هياكل البيانات Data Structures Icon_lol هياكل البيانات Data Structures Icon_lol

كود مبدئي للمكدس بلغة سي++ :


CODE:
// Stack using OOD
#include <iostream>
#include <stdlib.h>

using namespace std;

class Stack
{
private:
enum { MAX=10,MIN=-1}; // MAXIMUM,MINIMUM STACK CONTENT
int array[MAX]; // Contains all the Data
int top; //Contains location of Top most Data pushed onto Stack
public:
Stack();

void push(int); // push number to array
int pop(); // pop it from array

bool isEmpty(); // check if array is Empty!
bool isFull(); // check if it full

};

Stack::Stack():
top(MIN)
{}

bool Stack::isEmpty()
{
if(top == MIN)
return true;
else
return false;
}

bool Stack::isFull()
{
if(top == MAX -1)
return true;
else
return false;
}

void Stack::push(int i)
{
if(isFull())
{
cout<<"\nArray is Full";
exit(1);
}

else
{
++top;
array[top]=i;
}

}


int Stack::pop()
{
int number;

if(isEmpty())
{
cout<<"\nArray is Empty";
exit(1);
}

else
{
number=array[top];
top--;

}

return number;

}

int main()
{
Stack s;

// push numbers 2,4,8
s.push(2);
cout<<"\n2 is pushed\n";
s.push(4);
cout<<"\n4 is pushed\n";
s.push(8);
cout<<"\n8 is pushed\n";

// pop numbers
cout<<"\n"<<s.pop()<<" is poped"<<endl;
cout<<s.pop()<<" is poped"<<endl;
cout<<s.pop()<<" is poped"<<endl;
return 0;
}




كود سي :

CODE:
// Stack using C Language
#include <stdio.h>
#include <stdlib.h>

#define MAX 10
#define MIN -1


struct Stack
{
int array[MAX];
int top;
};

void push(Stack *,int);
int pop(Stack *);
void initializeStack(Stack *);

int main()
{
Stack s;
initializeStack(&s);

push(&s,2);
printf("\n2 is pushed\n");
push(&s,4);
printf("\n4 is pushed\n");
push(&s,8);
printf("\n8 is pushed\n");

// pop numbers
printf("\n %d is pushed\n",pop(&s));
printf("\n %d is pushed\n",pop(&s));
printf("\n %d is pushed\n",pop(&s));
return 0;
}


void initializeStack(Stack *s)
{
(*s).top=MIN;
}
void push(Stack *s,int number)
{
if( (*s).top == MAX -1 )
{
printf("\nArray is Full");
exit(1);
}

(*s).top+=1;
(*s).array[(*s).top]=number;
}

int pop(Stack *s)
{
if( (*s).top == MIN )
{
printf("\nArray is Empty");
exit(1);
}

int n=(*s).array[(*s).top];
(*s).top--;
return n;
}



تمت الترجمة ب g++ compiler

ملاحظات :

*
بقي جزء شرح الكود ، وخاصة المتغير top ، ولكن ان لم تجرب البرامج ، فلن
تفهم ما سأقوله ،، لذلك سأنتظر الاسئلة من الاعضاء الذين جربوا البرنامج.
* هذه البرامج ليست بالشكل النهائي ،اي سنقوم ببعض التعديلات .
* برنامج السي اعقد قليلا ، وهذا اسهل شيء ، فما بالك ب Trees !!
* تفضل لغة السي++ في كتابة برامج هياكل البيانات.
* قم بكتابة هذه البرامج عدة مرات ، حتى يتضح لك المفهوم.

مميزات وعيوب البرنامج "باللغتين" :
ميزة واحدة وهي انشاء مكدس.

نأتي للعيوب:
المصفوفة ثابتة الحجم !!
يعني لم نفعل شيء حتى الان !! كل ما فعلناه يمكن ان يتم بمصفوفة بيسطة، شوف المثال:

CODE:
int array[10];
for(int i=0;i<10;i++)
array[i]=i*2+1;

for(int i=9;i>=0;i--)
cout<<array[i];



في المثال السابق ادخلنا اعداد فردية على المصفوفة : 1 ، 3 ، ... ، 19
والنتيجة كانت : 19 ، 17 ، ... ، 1

هممممممممم ،، وطيب وليش متعبنا مع المكدس وسالفته !!
الجواب : لاننا سنستخدم برنامج المكدس اللي عملناه ، ونقوم بتطويره .

طيب ،، ليش ما تعطينا النسخة المطورة وبس ؟؟ هياكل البيانات Data Structures Icon_e_confused هياكل البيانات Data Structures Icon_e_confused
الجواب : اذا ما فهمت الاولى لن تفهم الثانية هياكل البيانات Data Structures Icon_e_biggrin
evergreen
evergreen

الجنس : Female

عدد المساهمات : 1497
النقاط : 61948
التقييم : 34
تاريخ التسجيل : 2010-02-03

https://3loomi.forumotion.com

Back to top Go down

هياكل البيانات Data Structures Empty Re: هياكل البيانات Data Structures

Post by evergreen Sat Mar 13, 2010 6:37 am

قبل ما نبدأ بتطوير برنامج المكدس السابق ،، احب ان اشرح بعض النقاط
والتي لم اوضحهها سابقا .

العمليات على المكدس :
حتى الان لدينا عملتين two operations وهم :
1- عملية ادخال العناصر الى المكدس وسميناها push
2- عملية اخراج العناصر من المكدس وسميناها pop

العملية push تقوم بادخال العنصر الى المكدس ،، هذا العنصر سيقوم المستخدم بتمريره الى الدالة ()push
اي اذا اردنا ادخال العنصر 7 مثلا الى المكدس ، كل ما علينا هو استدعاء الدالة وتمرير العنصر 7 اليها كالاتي:

CODE:
push(7);



العملية الاخرى وهي pop تقوم بسحب العناصر من المكدس ،، ولتنفيذ هذه العملية يجب ان تستدعي الدالة
pop() ووضع متغير لكي يحمل قيمة العنصر الذي تم سحبه من المكدس، بهذا الشكل:

CODE:
int var=pop();



+++++++++++++++++++

خلينا الحين نتتبع برنامجنا السابق ،،لكي نعرف طريقة عمله ،، وراح اتحدث عن النسخة المكتوبة بلغة سي++
واذا في احد ما فهم اللي بالسي يقولنا ،،

على طول ندخل بالدالة الرئسية :

CODE:
- int main()
2- {
3- Stack s;

4- // push numbers 2,4,8
5- s.push(2);
6- cout<<"\n2 is pushed\n";
7- s.push(4);
8- cout<<"\n4 is pushed\n";
9- s.push(8);
10- cout<<"\n8 is pushed\n";

11- // pop numbers
12- cout<<"\n"<<s.pop()<<" is poped"<<endl;
13- cout<<s.pop()<<" is poped"<<endl;
14- cout<<s.pop()<<" is poped"<<endl;
15- return 0;
16- }



===========================

في السطر 3 قمنا بعمل متغير من النوع Stack .
الان ما الذي سيحدث هنا !!
طبعا سيتم حجز مساحة للمتغير s وكذلك استدعاء الدالة Stack() بشكل تلقائي ، تعمل هذه الدالة على
تهيئة المتغير s ، اي اعطائه قيمة ابتدائية ، قبل ان نستخدمه . للمعلومية الدالة stack تسمى بال constructor.
واذا ذهبنا الى المتغير s في الذاكرة ، راح نشوف انه يتكون من :

"مصفوفه ثابتة حجمها 10 عناصر وهي array + عدد صحيح وهو top"

ما يهمنا الان هو القيمة الابتدائية ل top والتي سوف تكون -1 ،، انظر الى ال constructor جيدا .

+++++++++++++++

في السطر 5 قمنا باستدعاء الدالة push وتمرير العدد الصحيح 2 ، وذلك حتى ندفعه الى المكدس.
كيف سيتم ذلك ؟؟

اولا سوف تقوم الدالة push بعمل اختبار ،، هل المصفوفة ذات ال 10 خانات ممتلئة ام لا ؟؟
اذا
كانت الاجابة نعم ، راح تطلع لك رسالة تنبيه وينتهي البرنامج "طبعا هذا حل
غير عملي ، ولكن هذا لغرض التوضيح والتركيز على المشكلة الاساسية "

واذا كانت الاجابة لا ،، اي ان هناك مساحة خالية لاستقبال عناصر ، فسوف يتم ادخال العدد 2 الى المكدس
دون مشكلة.

الاسئلة التي تدور بذهنك حاليا هي: هياكل البيانات Data Structures Icon_idea هياكل البيانات Data Structures Icon_idea
كيف نعرف ان المصفوفة ممتلئة او خالية ؟؟
كيف يتم ادخال العنصر 2 الى المكدس ؟؟


الفكرة كلها تتمحور حول المتغير top ،، وسوف نطلق عليه الدليل او index .

كيف يتم ادخال العنصر 2 الى المكدس ؟؟
الاجابة:
كما ذكرنا سابقا ،، المكدس يحوي على "مصفوفة من 10 خانات ومتغير top "اطلقنا عليه اسم دليل ذو قيمة ابتدائية -1.
بالنسبة للمصفوفة فهي تستخدم لتخزين العناصر التي تقوم بتمريرها بواسطة الدالة push

اما
الدليل فهو الذي يخبرنا ان المصفوفة خالية ام لا ؟ وايضا من خلاله نستطيع
ادخال العناصر الى المصفوفة اي ان الدليل يعمل ك index للمصفوفة ، وسوف
نرى كيف يتم ذلك.

نرجع الى اجابة السؤال ؟؟ كيف يتم ادخال العنصر 2 الى المكدس ؟؟
شوف الدالة push


CODE:
push(int i)
{
if(isFull())
{
cout<<"\nArray is Full";
exit(1);
}

else
{
++top;
array[top]=i;
}

}




اولا
تقوم الدلة باجراء اختبار للتأكد من ان المصفوفة ممتلئة ام لا ، لانها لو
امتلئت فلا نستطيع ادخال عناصر اخرى،ولنفرض ان المصفوفة خالية.
بعد ذلك سيتم زيادة المتغير top او الدليل بمقدار واحد "وحط مليون خط على كلمة زيادة"
اذا ستكون قيمة الدليل هي : -1 + 1 = كم ... اين الالة الحاسبة هياكل البيانات Data Structures Icon_e_biggrin ، نعم = 0

وبعدها سيتم ادخال العنصر "وفي مثالنا 2" داخل المصفوفة وبالتحديد داخل array[0]


+++++++++++++++++++++++

الاسطر 7 و 9 بنفس الاسلوب السابق ،
ستكون قمية المتغير top الدليل بعد تنفيذ هذه الاسطر هي 2 ، وذلك لاننا ادخلنا عنصرين الى المكدس.

اذا سوف تكون لدينا 3 عناصر داخل المصفوفة ، وقيمة الدليل الحالية هي 2.

++++++++++++++++++++++++

اعتقد اننا تجاهلنا هذا السؤال؟ كيف نعرف ان المصفوفة ممتلئة او خالية ؟؟
الاجابة:
تكون خالية اذا كانت قيمة الدليل هي -1 "اي لم ندخل اي عدد"
وتكون ممتلئة اذا كانت قيمة الدليل تساوي "قيمة اكبر عدد من العناصر يمكن ادخاله في المصفوفة ".
اي اذا كان الدليل يساوي 9 ،، وهو اكبر عدد من العناصر يمكن ادخاله في المصفوفة .

++++++++++++++++++++++

السطر 12و13و14 كلها تعمل بنفس النمط.
قمنا باستدعاء الدالة pop والتي لديها قيمة معادة ،وهي قيمة العنصرالذي سيتم سحبه من المكدس.

كيف سيتم ذلك ؟؟ اي كيف سيتم سحب العناصر من المكدس ؟؟
قبل ان اجاوب على السؤال يجب ان تعرف شيء مهم جدا ،، اعتقد اني تحدثت عنه في احد مواضيع الاخ storm-man
لا يوجد شيء اسمه حذف قيمة من الذاكرة . يوجد عملية تعيين ، وعملية استعادة
ولكن حذف فلا يوجد .

خذ مثال:

CODE:
int x;
cout<<x;



لما لم تحذف هذه القيمة ؟؟ ما السر في هذا ؟؟ هياكل البيانات Data Structures Icon_exclaim
ببساطة شديدة عندما تحجز مساحة لمتغير وتقوم باعطاء المتغير قيمة :
int x=1;
ثم قمت بتحرير الذاكرة ،، مثلا انتهى نطاق المتغير scope ، فان الذاكرة ستكون غير محجوزة ،ولكن عليها قيمة grabage
وهي 1 ،، وممكن تتغير ،، اذا جاء برنامج اخر وطلب هذه الذاكرة (بدون كتشب) ثم استغنى عنها .

نرجع الى السؤال ؟كيف سيتم سحب العناصر من المكدس ؟؟
كما ذكرت سابقا ،، المكدس هو مصفوفة +دليل "سنغير التعريف عندما نتقدم "
ولكن الان :

CODE:
while(there is no new lesson)
{
cout<<"المكدس هو مصفوفة + دليل ";
}



المصفوفة تستخدم لحفظ العناصر او الاعداد التي يدخلها المستخدم (10 اعداد في مثالنا )
والدليل يعمل على اخبارنا بان هناك مساحة خالية ام لا ؟ هل المصفوفة ممتلئة ام لا ؟
وكذلك يعمل ك index في المصفوفة .

نشوف الدالة pop :

CODE:
pop()
{
int number;

if(isEmpty())
{
cout<<"\nArray is Empty";
exit(1);
}

else
{
number=array[top];
top--;

}

return number;

}



اولا تختبر هل المصفوفة خالية ام لا ؟ لانها ان كانت خالية ، فماذا سنسحب !!
وستكون خالية اذا كانت قيمة المتغير top الدليل هي -1 ،، وما عدا هذه القيمة فهي ليست خالية.

الان نفرض ان المصفوفة تحوي العناصر التي ادخلناها سابقا باستخدام push ، وان top يساوي 2.

انظر الى هذا الامر جيدا:


CODE:
number=array[top];



سيعمل top كدليل في المصفوفة ، اي ستكون هذا الجملة عند اول استدعاء للدالة بهذا الشكل:


CODE:
number=array[2];



وبعدها سينقص top بمقدار واحد ، اي سيصبح 1.
وكهذا تم سحب العنصر من المصفوفة ووضعه في متغير اخر .

وبنفس الطريقة مع باقي الاستدعاءات.
++++++++++++++++++++++

سؤال للاعضاء : هياكل البيانات Data Structures Icon_question هياكل البيانات Data Structures Icon_question

CODE:
Stack s;
s.push(10);
s.push(20);
s.pop();
s.push(30);



كم عدد العناصر داخل المكدس بعد تنفيذ هذه الجمل ؟ وماهي ؟
كم قيمة المتغير top ؟

+++++++++++++++++++++

الى هنا انتيهنا من شرح النسخة الاولى لبرنامج المكدس !!
وسنعمل على تطويرها الان ،، وذلك لانها لاتقبل الا عدد معين من العناصر .
اتمنى ان اكون قد وفقت في ايصال المعلومة ،،
اي اخطاء ؟؟ اي سؤال ؟؟
هياكل البيانات Data Structures Icon_redface
تابع معنا النسخة المطورة .. هياكل البيانات Data Structures Icon_arrow
evergreen
evergreen

الجنس : Female

عدد المساهمات : 1497
النقاط : 61948
التقييم : 34
تاريخ التسجيل : 2010-02-03

https://3loomi.forumotion.com

Back to top Go down

هياكل البيانات Data Structures Empty Re: هياكل البيانات Data Structures

Post by evergreen Sat Mar 13, 2010 6:38 am

المشكلة الاساسية في النسخة السابقة هو عدد العناصر ثابت .
يعني قيمة ثابتة 10 عناصر او 3 او 200 .
ومعظم البرامج تستخدم 10 ، ولكن السؤال ؟ هل 10 تكفي لكل انواع واشكال البرامج ؟
طبعا لا والف لا .

اذا تفضل وخذ نسختك المجانية من المكدس المطور ،، وللعلم انها ايضا لا تطبق بشكل كبير في الواقع !!

سوال : انت ذكرت ان النسخة السابقة غير مفيدة بسبب ان عدد العناصر محدود ، وذكرت ان نسختك المطورة هي الحل !!
الاجابة :
النسخة
المطورة هي حل جيد ، فهي تقوم بحل المشكلة الرئسية في النسخة السابقة ،
حيث تجعل المستخدم هو الذي سيدخل عدد العناصر، وليس الشخص الذي قام بكتابة
ال class .
اي اذا اردت ان يكون عدد العناصر 7 فهو كذلك ، 20 ايضا ، 1 ، على كيفك.

سؤال : واااااااااو انتهت المشاكل ، ؟ اذا لماذا لا تطبق في الواقع ؟
الاجابة : طيب لنفرض يا عزيزي المحترم جاء مستخدم وطلب 20 عنصر ، اوكي.
فجأة غير رأيه وقال :20 لاتكفي اريد 30 . هنا المشكلة الاولى .
المشكلة الثانية : ان المستخدم من الاول لا يعرف كم يحتاج ، واحد او 10 او 53 .

هذه النسخة تتطلب من المستخدم ان يدخل عدد العناصر التي يحتاج اليها ، بدون تغيير الرأي
يعني كلمتك تكون كلمة رجال هياكل البيانات Data Structures Icon_cool

اذا سنحتاج الى نسخة ثالثة واخيرة "ابسط يا عم " تطبق في الواقع بكل المقاييس .
ولكن قبل النسخة الاخيرة ، يجب ان ندرس هذه النسخة اولا ،، لانها مفيدة جدا وبسيطة جدا ولكن بشرط "بسيط ايضا ":
وهو ان تدخل عدد العناصر التي تحتاج اليها بالضبط.

النسخة المطورة : Stack v0.1 :


CODE:
// Stack v0.1 using OOD
#include <iostream>
#include <stdlib.h>
using namespace std;

class Stack
{
private:
enum { MAX=10,MIN=-1};// MAXIMUM,MINIMUM STACK CONTENT
int *pArray; // pointer to array.
int top; //Contains location of Top most Data pushed onto Stack
int size; // contain the number of item(integer) you want

public:
Stack(int n=MAX);
~Stack();
void push(int); // push number to array
int pop(); // pop it from array
bool isEmpty(); // check if array is Empty!
bool isFull(); // check if it full

};

Stack::Stack(int n):
top(MIN),size(n)
{
pArray=new int[n];
}

Stack::~Stack()
{
delete pArray;
pArray=0;
}


bool Stack::isEmpty()
{
return (top==MIN?ture:false);
}

bool Stack::isFull()
{
return (top==size -1 ?ture:false);
}


void Stack::push(int i)
{
if(isFull())
{
cout<<"\nArray is Full";
exit(1);
}

else
{
++top;
*(pArray+top)=i;
// or use this : pArray[top]=i;
}
}


int Stack::pop()
{
int number;
if(isEmpty())
{
cout<<"\nArray is Empty";
exit(1);
}

else
{
number=pArray[top];
//or use this: number=*(pArray+top);
top--;
}
return number;

}


int main()
{
int number;
cout<<"\nEnter The Number you want\n\t\t>> ";
cin>>number;

Stack s(number);

// push numbers 2,4,8
s.push(2);
cout<<"\n2 is pushed\n";
s.push(4);
cout<<"\n4 is pushed\n";
s.push(8);
cout<<"\n8 is pushed\n";

// pop numbers
cout<<"\n"<<s.pop()<<" is poped"<<endl;
cout<<s.pop()<<" is poped"<<endl;
cout<<s.pop()<<" is poped"<<endl;
return 0;
}



فكرة هذه النسخة هي نفس الفكرة السابقة ، اي نفس الدالة push و pop وكذلك نفس الدليل top
الاختلاف الوحيد هو في المصفوفة .
في المثال السابق كانت مصفوفة ثابتة


CODE:
array[10];
[color=blue]



اما هنا فاصبحت مؤشر لمصفوفة ديناميكية !!
ديناميكية يعني ان حجزها يتم في وقت التنفيذ ،، يعني البرنامج بيشتغل عادي الى ان يصل الى الاعلان:


CODE:
pArray=new int[x];[color=blue]



ثم
يقوم بحجز المساحة المطلوبة ،، لاحظ في المصفوفة الديناميكية Dynamic
Array ان حجمها هو عدد غير ثابت !!يستطيع المستخدم ان يدخله .
اذا المصفوفة الدينامييكية dynamic هي السبب في النسخة المطورة

طيب ما هو الفرق بين المصفوفة الثابتة static array والمصفوفة الديناميكية dynamic array ؟؟
هياكل البيانات Data Structures Icon_question هياكل البيانات Data Structures Icon_question
"قبل
ما اجاوب ،، احب اذكر عشاق لغة السي الى ان المصفوفة الديناميكية يتم
الاعلان عنها باستخدام الدالة malloc والوجودة في الملف الراسي stdlib.h ".

الفرق الاول هو ان المصفوفة الثابتة يتم حجزها وقت ترجمة البرنامج ،، اي قبل التشغيل ،، لذلك يجب ان تكون ثابتة الحجم.
وبالنسبة للمصفوفة الديناميكية فيتم حجزها وقت التشغيل ،، وبالتحديد عندما يصل البرنامج الى الامر ، مثلا :

CODE:
int x=2;
if(x==3)
int a=new int;[color=blue]



في هذا المثال لن يتم حجز ذاكرة ديناميكية للمتغير a ، مسكين هياكل البيانات Data Structures Icon_cry هياكل البيانات Data Structures Icon_cry

ولكن في هذا المثال:


CODE:
int x=2;
if(x==3)
int a;[color=blue]



سيتم حجز ذاكرة للمتغير a ،، وقت ترجمة البرنامج .


الفرق الثاني والاهم ، وهو ان المصفوفة الثابتة يتم حجزها في ذاكرة تسمى stack
هذه
الذاكرة stack "هي من التطبيقات على موضوعنا ،تعمل بنفس طريقتنا ، لكن لا
دخل لنا بها " يتم بها حجز اي متغير ،واي وسيط في دالة ، وكذلك مصفوفتنا
الثابتة.
وما يميزها هو ان المتغيرات التي بداخلها تحذف بمجرد خروج المتغير من المدى scope تلقائيا ، شوف مثال على الطاير:


CODE:
int main()
{
int x=2;

{
int y=4;
}

cout<<x;
cout<<y;

return 0;
}



هذا البرنامج لن يعمل ،، والسبب هو ان المتغير y قد تم حذفه تلقائيا وذلك لانه خرج من المدى scope

اما بالنسبة للمصفوفة الديناميكية (او المتغير الديناميكي )فيتم حجزها على ذاكرة تسمى ب المخزن الحر free store او heap
وما يميز هذا ال heap هو ان المتغيرات التي بداخله لا تحذف الا عندما تقوم انت بحذفها ، او عند انتهاء البرنامج .
يعني مواضيع ال scope هذه لا تخص الheap
شوف المثال :

CODE:
int main()
{
int x=2;
int *y;
{
y=new int;
*y=4;
}

cout<<x;
cout<<*y;
delete y;

return 0;
}



هذا البرنامج سيعمل 100% .
تم حجز مساحة ديناميكية للمتغير y ،، واعطائها القيمة 4 .
هذه المساحة لن تحذف حتى لو طلعنا من المدى .

اغتقد ان بعض الناس راح يسألو ؟؟ ليه ما كتبت داخل ال block الامر :

CODE:
int *y=new int;



اي لماذا فصلت الاعلان عن المتغير y وعن حجز الذاكرة ؟؟
قبل ما اجاوب اقرأ الملاحظة :

"عدم سدادك لفاتورة الهاتف سيؤدي الى .... " اووو معليش ،، اصل التلفون قاطع
هياكل البيانات Data Structures Icon_cry

"عدم حذفك للذاكرة يسبب احد الاخطاء المعروفة ب "تسرب الذاكرة " Memory Leak
وهو خطأ قاتل fatal error

ولكن في مثالنا البيسط اللى فوق لا يوجد داعي لحذف المتغير y لان البرنامج سوف يتنهي ، ويقوم بحذفه تلقائيا ."

طيب لو تجاوبنا مع اخينا السائل ،، وكتبنا البرنامج بهذا الشكل :

CODE:
int main()
{
int x=2;

{
int *y=new int;
*y=4;
}

cout<<x;
cout<<*y;

return 0;
}



الاجابة : يوجد مشكلتين كبيرتين :
الاولى :المؤشر y تم حجزه داخل ال block ،، وتم استخدامه خارجها . تذكر انه قد تم حجزه على ال stack
المشكلة الثانية :
عندما نطلع من ال block مين حيقدر يحرر الذاكرة الديناميكية ، المؤشر y قد
مات "اهئ اهئ " بعد الخروج من ال block وهو الوحيد الذي كان يستطيع ان
يقوم بحذف الذاكرة .

بهذا الشرح الغير مباشر ،، اتمنى ان تكون
النسخة المطوره من برنامج المكدس قد وضحت،، واي احد عنده سؤال فلا يتردد ،
او يريد شرح مباشر على الهوا ، ايضا لا يتردد.
وبالنسبة للنسخة الاخيرة ،، فراح نأجلها كمان جلستين او ثلاث ، وذلك لانها تتعلق بال linked List
وهي النسخة المفضلة للجميع ،، وللناس اللى حابه تعرف شكلها ، شوف المثال:

CODE:
int main()
{
Stack s;
s.push(2);
s.push(4);

s.push(8);

...etc

// pop numbers

return 0;}




يعني راح نحل مشكلة ادخال عدد العناصر ،،
وكل ماعليك هو ان تدخل الاعداد ، تسحب ، على كيفك .
وكمان راح نعمل منها واحدة سوبر "او خليها كمبو هياكل البيانات Data Structures Icon_lol هياكل البيانات Data Structures Icon_lol "
والتي
تستخدم حاليا ،، في كل التطبيقات .بحيث تقبل منك اعداد ، حروف ، سلاسل
نصية ، قطط ، طائرات ،...الخ.وذلك بواسطة الملكة template

الى ذلك الحين ، نستودعكم الله ،، والى اللقاء في حلقة قادمة مع اخو المكدس وهو" ال Queue وبالعربي الطابور وتلفظ كيو".

وقبل ما انسى ،، من المفروض حاليا ان اضع الاسئلة "المسخنة" على المكدس ،، ولكني سأنتظر حتى يجهز الجميع.
طبعا سنحاول حلها مع بعض اذا لم يتمكن احد من حلها ،، ولكن ان شاء الله الجميع سوف يحلونها
evergreen
evergreen

الجنس : Female

عدد المساهمات : 1497
النقاط : 61948
التقييم : 34
تاريخ التسجيل : 2010-02-03

https://3loomi.forumotion.com

Back to top Go down

هياكل البيانات Data Structures Empty Re: هياكل البيانات Data Structures

Post by evergreen Sat Mar 13, 2010 6:38 am

اليوم راح نتحدث عن ال Queue : "و تلفظ كيو وليس كيوي "

ما هو ال Queue ؟؟
لا ارى اجابات !! هياكل البيانات Data Structures Icon_eek

طيب ،، نترجم السؤال بالعربي : ما هو الطابور ؟؟
ماشاء الله ،، كمية من الاجابات . هياكل البيانات Data Structures Icon_e_smile

يقول احدهم : هذا طابور المقصف !!
ويقول العامل : هذا طابور الجوازات !!
ويقول واحد "مسوي خبير" هذا الطابور الموجود داخل ال CPU والذي يقوم المعالج بأخذ الاوامر منه وتنفيذها واحدة تلو الاخرى .

الاجابات كلها صحيحة ،، ولكنها ناقصة شيء بسيط :
ال
Queue هو عبارة عن هيكل بيانات Data Structure ،، يستخدم لتخزين البيانات
"لغرض المعالجة " بحيث ان العنصر "البيانات" الذي يدخل اولا الى هذا
الطابور هو الذي يخرج اولا او "هو الذي يتم معالجته اولا " ، الا في حالات
خاصة .


وراح نأخذ مثال اخونا العامل لكي نوضح التعريف : طابور الجوازات :
اخونا
العامل ،، جاء الساعة السابعة صباحا الى الجوازات لكي يخلص مبكرا ،، ولكنه
فوجيء بأن الطابور ممتلئ بكمية من البشر ، حاول ان يتجاوز الصف "الطابور"
ويذهب الى الامام ،، ولكن هيهات .
هياكل البيانات Data Structures Icon_evil شروط الطابور ان اي شخص "او عنصر" يأتي اليه ،، سوف يدخل في اخره "عملية الادخال تسمى Enqueue".

يعد حوالي نصف ساعة ،، تحرك الصف خطوة الى الامام ،، لماذا ؟؟
اكيد لان اول شخص "او عنصر" قد جاء دروه لكي يتم معالجته ، لذا خرج من هذا الصف الممل.
"عملية اخراج العنصر من الطابور تسمى Dequeue "

بعد فترة بسيطة ، جاء واحد جديد الى هذا الطابور ، واصبح خلف اخونا العامل ، فجأة ،، وبدون سابق انذار
تحرك هذا الرجل الجديد الى بداية الطابور !! هياكل البيانات Data Structures Icon_eek

العامل يصيح بأعلى صوته ،، لماذا قد تقدم عليه هذا الشخص !!
هياكل البيانات Data Structures Icon_e_surprised تقدم اليه احد الحراس وقال:اصمت هذا ابوه "مدير الجوازات" !!


هممممممممممم ،، بعد هذا المثال الحزين ،، اكيد انك قلت : ياريت كنت تعطينا مثال المعالج CPU
لا فيه ظلم ولا شيء ،، العنصر الذي يأتي اولا سيخرج اولا ، حتى وان كان خلفه شيطان.


استنتاج بسيط : يوجد نوعين من الطوابير :
طابور "فيتامين و " ، وطابور عادي .

الاول يطلق عليه ال Priority Queue "طابور الاولية او طابور الاسبقية "
ولكن خلينا نسميه " طابور الواسطة "

والثاني يطلق عليه طابور فيفو FIFO .
مين هو فيفو ؟؟
هذا ليس شخص ،، وانما طريقة عمل وهي اختصار ل first in first out
اي العنصر الذي يأتي اولا ،، هو الذي يخرج اولا .

النوع الاول اشمل من الثاني ،، لانه يعمل بمبدأ FIFO ويزيد عليه مبدأ الاسبقية ،،
اي ان العنصر الذي يأتي اولا ،، هو الذي سيخرج اولا "اذا وفقط اذا " لم يوجد عنصر اعلى في الاسبقية.
يعني طابور الاسبقية هذا كيف يعمل ؟؟
يقوم بفحص اولوية كل عنصر في الطابور ،، ويقوم باخراجهم اولا .
وبعد ذلك بيرجع FIFO عادي .

الاولوية تحدد بأي شيء تريده ،، ولكن راح نعملها قيمة متغير مثلا .

الى هنا نكون قد اعطينا مقدمة جيدة عن ال Queue وانواعه ،، وراح نتحدث حاليا عن النوع الثاني
وهو FIFO Queue .

++++++++++++++++++++++++++++++++

بعد هذا المقدمة البسيطة ،، بقي ان نذكر اين تستخدم ال Queue عمليا !!

الاجابة : في اي برنامج يطلب معالجة العناصر بحسب الترتيب الذي دخلت به ،، والامثلة كثيرة ، ناخذ مثلا ،، برنامج لمطعم "بيتزا هت " راح يستخدم طابور ،، بحيث ان الزبون الذي يطلب اولا هو الذي سيحصل على طلبه اولا .

اي راح يوجد داخل البرنامج طابور "مماثل للذي راح نتكتبه ان شاء الله "، يحوي على رقم الطلب والطلب نفسه .
شوف شكل تخيلي للطابور: الطلب ، "الرقم"

"بيتزا صغير"،"بيبسي"،125 -> "بيتزا كبير بدون مشروم "،"مياه"،124 ->"بيتزا وسط"،123

صورة توضيحية :
هياكل البيانات Data Structures Petza

طابور مشهي هياكل البيانات Data Structures Icon_razz هياكل البيانات Data Structures Icon_razz
وكذلك برامج تسجيل الطلاب ،، وبرامج المعاملات ،، و...والخ.

+++++++++++++++++++++++++

تطبيق ال Queue :
بنفس فكرة المكدس ،، يمكن ان نتطبق ال Queue باستخدام المصفوفات ، او باستخدام القوائم المرتبطة.
وراح نتحدث عن تطبيقها باستخدام المصفوفات حاليا ،،


العمليات على Queue :
العمليات كثيرة ،، ولكن لغرض التوضيح سنستخدم العمليات الاساسية ، وهما اثنين :
عملية ادخال العنصر وتسمى Enqueue
عملية اخراج العنصر وتسمى dequeue


وقبل ان ندخل في الحديث عن هذه العمليات ،، يجب ان نذكر ،، ماهي مكونات ال Queue :
يتكون ال Queue في شكله العام من:

مصفوفة + دليلين "وليس واحد كما هو الحال مع المكدس".

المصفوفة تستخدم لتخزين العناصر.
الدليل الاول وراح نسميه front وهذا متغير يدلنا على بداية الصف.
اما الدليل الثاني فهو back وهذا ايضا متغير يدلنا على نهاية الصف.

راح نؤجل الحديث عن العمليات ،، لاني وجدت صعوبة في الوصف ،،
لذلك سأضع الكود ،، وبعدها سنشرح العمليات.
لكن للتذكر : ان الادخال في الطابور يتم من اللخلف ،، والاخراج من الامام .

وبالنسبة
لكود السي ،، فما راح اضعه الان ،، حتى ما يثقل الصفحة ،، وحتى ما يتلخبط
الاعضاء، وان شاء الله مع نهاية الدورة سأضع كل الاكواد للتحميل C ,C++
مع انه بسيط جدا ، لكن اذا اراد احد كود سي الان فليطلب وحنا امرين

=====================

كود مبدئي للطابور FIFO Queue بلغة C++:


CODE:
#include <iostream>
#include <stdlib.h>

using namespace std;


class Queue
{
private:
enum {MAX=10};
int array[MAX];
int back,front;

public:
Queue();

void enqueue(int e);
int dequeue();
bool isFull();
bool isEmpty();
};


Queue::Queue():
back(0),front(0)
{}

void Queue::enqueue(int e)
{
if(isFull())
{
cout<<"\nQueue is Full";
exit(1);
}

back=(back+1)%MAX;
array[back]=e;
}


int Queue::dequeue()
{
if(isEmpty())
{
cout<<"\nQueue is Empty";
exit(1);
}

front=(front+1)%MAX;
return array[front];
}


bool Queue::isFull()
{
if( (back+1)%MAX == front )
return true;
else
return false;
}

bool Queue::isEmpty()
{
if( back == front )
return true;
else
return false;
}


int main()
{
Queue q;
cout<<"\nenqueue(2)";
q.enqueue(2);
cout<<"\nenqueue(4)";
q.enqueue(4);
cout<<"\nenqueue(6)";
q.enqueue(6);

cout<<"\nDequeue elements\n";

cout<<"dequeue: "<<q.dequeue()<<endl;
cout<<"dequeue: "<<q.dequeue()<<endl;
cout<<"dequeue: "<<q.dequeue()<<endl;

return 0;
}



نفس مشكلة برنامج المكدس المبدئي ،، وهو عدد العناصر ثابت ،، سنحل المشكلة بالنسخة المطورة فورا،
ارجع الى المكدس لتعرف المزيد.

=====================

الكود المطور للبرنامج ال Queue:

CODE:
#include <iostream>
#include <stdlib.h>

using namespace std;


class Queue
{
private:
enum {MAX=10};
int *pArray;
int back,front;
int size;

public:
Queue(int s=MAX);
~Queue();

void enqueue(int e);
int dequeue();
bool isFull();
bool isEmpty();
};


Queue::Queue(int s):
size(s),back(0),front(0)
{
pArray=new int[s];
}

Queue::~Queue()
{
delete [] pArray;
pArray=0;
}

void Queue::enqueue(int e)
{
if(isFull())
{
cout<<"\nQueue is Full";
exit(1);
}

back=(back+1)%size;
pArray[back]=e;
}


int Queue::dequeue()
{
if(isEmpty())
{
cout<<"\nQueue is Empty";
exit(1);
}

front=(front+1)%size;
return pArray[front];
}


bool Queue::isFull()
{
if( (back+1)%size == front )
return true;
else
return false;
}

bool Queue::isEmpty()
{
if( back == front )
return true;
else
return false;
}


int main()
{
Queue q(10);
cout<<"\nenqueue(2)";
q.enqueue(2);
cout<<"\nenqueue(4)";
q.enqueue(4);
cout<<"\nenqueue(6)";
q.enqueue(6);

cout<<"\nDequeue elements\n";

cout<<"dequeue: "<<q.dequeue()<<endl;
cout<<"dequeue: "<<q.dequeue()<<endl;
cout<<"dequeue: "<<q.dequeue()<<endl;

return 0;
}





الشرح ::

ندخل بالدالة main على طول:

CODE:
1-int main()
2-{
3- Queue q(10);
4- cout<<"\nenqueue(2)";
5- q.enqueue(2);
6- cout<<"\nenqueue(4)";
7- q.enqueue(4);
8- cout<<"\nenqueue(6)";
9- q.enqueue(6);

10- cout<<"\nDequeue elements\n";

11- cout<<"dequeue: "<<q.dequeue()<<endl;
12- cout<<"dequeue: "<<q.dequeue()<<endl;
13- cout<<"dequeue: "<<q.dequeue()<<endl;

14- return 0;
15-}



في السطر الثالث ،، تم عمل كائن من النوع Queue.
الدالة البانية او ال constrcutor سوف تعمل تلقائيا وذلك لاعطاء q قيم ابتدائية.
المتغير q يتكون من:
مؤشر لمصفوفة ديناميكية + متغير front + متغير back + متغير size

المصفوفة الديناميكية يتم بها تخزين العناصر ،، والمؤشر pArray هو الوحيد القادر على الوصول الى هذه المصفوفة الديناميكية.

المتغير front يعمل كدليل الى بداية الطابور ، بمعنى انه يحمل رقم اول خانة في الطابور
اما المتغير back فهو يحمل رقم اخر خانة في الطابور

والقيم الابتدائية لهم هي الصفر.

اما المتغير size فهو يحمل عدد العناصر التي يحتاج اليها المستخدم.

صورة توضيحية :

هياكل البيانات Data Structures Q1

++++++++++++++++++++++++++

السطر 5 ،، وهي اول عملية ادخال enqueue ،، حيث تم ادخال العدد 2 .
دعنا ندخل الى تفاصيل هذه العملية ، شاهد الكود :


CODE:
void Queue::enqueue(int e)
{
if(isFull())
{
cout<<"\nQueue is Full";
exit(1);
}

back=(back+1)%size;
pArray[back]=e;
}




صورة توضيحة قبل الادخال :
هياكل البيانات Data Structures Q2

اولا تقوم الدالة بعمل اختبار لكي تعرف ان المصفوفة ممتلئة ام لا ، واذا كانت الاجابة نعم ، فسوف تطلع رسالة تنبيه ويخرج البرنامج.
اما اذا كانت الاجابة لا ،،
قبل ما نشوف ماذا سيحدث ،،
هل تتذكر مثال العامل في الجوازات !! اين وقف العامل في الطابور؟؟ واين وقف الرجل الذي اتي اخيرا؟
الاجابة : في المؤخرة back
"اي عنصر يأتي جديد سوف يدخل في اخر الطابور" وهو ما اطلقنا عليه back.

نرجع الى الكود.
بما ان العدد 2 جاء الى الطابور ، فيجب ان يدخل في اخر الطابور ،راقب كيف يحدث ذلك :
يتم زيادة الدليل back بمقدار واحد "انسى حاليا الجزء الباقي وهو %size "
اي ستصبح قيمة الدليل = 0 + 1 = 1 .
اذا back=1 , front =0

بعد ذلك سيتم ادخال العدد 2 في المصفوفة ، وبالتحديد عند العنصر pArray[back] اي في

CODE:
pArray[1]



وهذا هو اول واحد في الطابور.
هياكل البيانات Data Structures Q3


ناتي الان للسطر 7 ،، وهي عملية ادخال العنصر 4 ،، راقب كيف يحدث ذلك :
اولا الاختبار سيفشل ،، لماذا ؟؟ كيف نعرف ان المصفوفة ممتلئة ؟؟
راح نأجل الاجابة ،، ونكمل على فرض ان المصفوفة ليست ممتلئة ،،

سيتم زيادة الدليل back بمقدار واحد ايضا ،،
اي سيصبح back=2 ,front=0

هياكل البيانات Data Structures Q4

ارجو منكم ان تقوموا برسم توضيحي للمصفوفة على الورق + الدليلين ،، وتقوموا بتتبع البرنامج.

الان سنفرض اننا ادخلنا العديد من العنصر داخل الطابور ، بهذا الشكل:

هياكل البيانات Data Structures Q5

واردنا ادخال عنصر جديد ،، مالذي سيحدث !!
طبعا لاحظ ان المصفوفة امتلئت !!
وان قيمة الدليل back تساوي حجم المصفوفة ناقص واحد
اي
اذا كانت مصفوفتنا من 10 عناصر فان الدليل back راح يساوي 9 ،، وهو اخر
شيء يمكن ادخاله في المصفوفه ،، لاننا نعلم ان المصفوفة ترقم من الصفر
وليس من الواحد.

الاجابة : لن يسمح لك بالادخال ، راح يعطيك رسالة تنيبه بان المصفوفة امتلئت وينتهي البرنامج.
وطيب كيف عرف ان المصفوفة امتلئت !!
انظر الشرط التالي داخل الدالة isFull() :

CODE:
if( (back+1)%size == front )



اذا كان الدليل front يقع بعد الدليل back فهذا يعني ان المصفوفة امتلئت .
او بمعنى اخر اذا كان back+1 == front

طيب لماذا استخدمنا معامل الباقي من القسمة؟؟
الاجابة لاننا ان لم نستخدمه راح يحصل خطأ كبير .
الان بفرض ان back=9 وهو اخر خانة في المصفوفة
اذا قمنا بكتابة الشرط بهذه الطريقة :

CODE:
if( back+1 == front )




فان
المتغير back راح تكون قيمته تساوي 9+1=10 وهو خارج حدود المصفوفة out of
boundary لذلك استخدمنا هذا المعامل "لكي نضمن اننا داخل حدود المصفوفة "

اتمنى ان تكون عملية الادخال enqueue قد وضحت ، وكما ذكرت قم بالرسم + التطبيق على الجهاز للحصول على افضل نتيجة

++++++++++++++++++++++++++++

نتنقل الى العملية الاخرى وهي سحب العناصر dequeue من الطابور:
السطر 11و12و13 :

طيب لنفرض ان مصفوفتنا تحوي الثلاث عناصر.
والدليل back=3 , front=0
انظر الصورة:
هياكل البيانات Data Structures Q6

الان نريد سحب (اخراج) اول عنصر ،، طبعا سيكون 2 .
بكل بساطة نستدعي الدالة dequeue والتي ترجع بالعدد الاول في الطابور.
خلينا نشوف الدالة dequeue اثناء العمل :


CODE:
int Queue::dequeue()
{
if(isEmpty())
{
cout<<"\nQueue is Empty";
exit(1);
}

front=(front+1)%size;
return pArray[front];
}



اولا ستختبر المصفوفة ، هل هي خالية ام لا ..
اذا نعم ،، سوف ينتهي البرنامج ، واذا لا :

سوف تقوم بسحب العناصر من المقدمة "كما هو الحال في الطوابير ، يخرج من في الامام اولا"
لذلك سوف يتم زيادة الدليل front بمقدار واحد ، واستخدام ال % كما ذكرت للتأكد من اننا داخل حدود المصفوفة.
وبعد ذلك تقوم بارجاع العنصر الموجود في pArray[front];

شاهد الصورة ، بعد الاخراج العنصر الاول:
هياكل البيانات Data Structures Q7

اخراج العنصر الثاني:
هياكل البيانات Data Structures Q8

اخراج العنصر الثالث :
هياكل البيانات Data Structures Q9

المزيد من السحب:
طبعا لن يتم ،، لان الشرط سيصبح صحيح اي ان المصفوفة ستكون خالية.
كيف نعرف انها خالية :
انظر الشرط داخل الدالة isEmpty()

CODE:
if(back == front)



اذا الدليلين اصبحوا متساويين ،، فهذا يعني بأن الطابور قد اصبح خالي.

+++++++++++++++++++++++++


ملاحظات:
* فكرة المكدس كانت تعتمد على مصفوفة ودليل ، اما الطابور فيعتمد على مصفوفة ودليلين.
* الدليل يعمل ك index للمصفوفة.
* سنقوم بانشاء المكدس و الطابور باستخدام القوائم المرتبطة ، وهي اسهل وافضل واكثر كفاءة.

الى هنا ينتهي موضوع الطابور ،، ولكن احس انه الشرح ما مفهوم !!
لاني كتبت هذا الدرس على أساس انك انتهيت من تطبيق الدرس السابق وهو المكدس!!
فاي واحد يريد ان يسأل فليتفضل ،،

هل اضع بعض الاسئلة على المكدس والطابور الان ،، ام انتظر حتى تنتهون من تطبيق الدروس !!!

اتمنى ان اي واحد خلص من القراءة فليخبرنا ،،

الى اللقاء مع اجمل موضوع في عالم هياكل البيانات Linked List. هياكل البيانات Data Structures Icon_mrgreen
evergreen
evergreen

الجنس : Female

عدد المساهمات : 1497
النقاط : 61948
التقييم : 34
تاريخ التسجيل : 2010-02-03

https://3loomi.forumotion.com

Back to top Go down

هياكل البيانات Data Structures Empty Re: هياكل البيانات Data Structures

Post by evergreen Sat Mar 13, 2010 6:39 am

ما هي القائمة المتصلة !!
قبل ما نذكر الاجابة ،، نسوي refresh بسيط لاول موضوع تحدثنا وهو المصفوفات ،،
شوف صورة توضيحية :
هياكل البيانات Data Structures Ary

كما ذكرنا العيب الاساسي وهو انها ليست مرنة ،، بمعنى انها ليست قابلة للزيادة او النقصان وقت تشغيل البرنامج.

هنا جاء دور القائمة المتصلة لكي تحل لنا هذه المشكلة !!

انظر الصور التوضيحية :
هياكل البيانات Data Structures L1

هياكل البيانات Data Structures L2

القائمة المتصلة هي عبارة عن هيكل بيانات مكون من عقد nodes مرتطبة مع بعضها البعض ، ولها بداية ونهاية.

التعريف جميل بعض الشيء ،، ولكن اول سؤال يبرز هنا ؟؟ ما هي العقدة ؟؟ ولماذا ترتبط مع بعضها ؟ واين البداية والنهاية ؟؟

العقدة هي "عنصر في القائمة المتصلة" ، ومجموعة العقد تكون لنا "قائمة متصلة" .
بنفس فكرة المصفوفة ،، فكل عنصر فيها يسمى" عنصر في المصفوفة "، ومجموعة العناصر تكون لنا "مصفوفة"
.

كل عقده تحوي حاجتين :
1- بيانات data
2- مؤشر الى عقدة اخرى pointer to node


نشوف العقدة في صورة توضيحية :
هياكل البيانات Data Structures L3


وكما ذكرت : مجموعة العقد تكون لنا قائمة متصلة ،،

اما بالنسبة للسؤال : لماذا ترتبط العقد مع بعضها ؟
وذلك حتى تكون لنا قائمة متصلة .

بقي شيء مهم لم نذكره في الصورة الاولى للقائمة المتصلة: head , NULL

اولا :ال head هو مؤشر لاول عقدة في القائمة المتصلة ،، واذا كانت القائمة خالية فانه سيشير الى NULL والتي تعني فارغ او 0
صور توضيحية "انفع اصير مصمم ؟؟،، هياكل البيانات Data Structures Icon_lol هياكل البيانات Data Structures Icon_lol "

هياكل البيانات Data Structures L4

هياكل البيانات Data Structures L5

هياكل البيانات Data Structures L6

طيب
انتهينا من التعريف ،، لكن احب اقول مرة ثانية ان المؤشر head هو مؤشر
لاول عقدة ،، واول عقدة تشير الى الثانية ،،والعقدة الثانية تشير الى
العقدة الثالثة ،و... وهكذا حتى نصل الى عقدة تشير الى NULL والتي راح
تكون هي العقدة الاخيرة في القائمة بتاعتنا .

اين الخطورة هنا !!
الخطورة تكمن في المؤشر head !!
لانه لو انحذف او تبدلت قيمته ما راح نستطيع التعامل مع هذه القائمة ،، وراح تظهر لنا مشكلة تسرب الذاكرة !!
شوف الصورة "وش رايكم نقلب الدروس صور هياكل البيانات Data Structures Icon_e_wink "
هذه الصورة لقائمة بدون مشاكل :

هياكل البيانات Data Structures L1

اما هذه:
هياكل البيانات Data Structures L7

هنا تأتي الكارثة !!
المؤشر head وهو الذي كان يشير الي اول عقدة ،، اصبح يشير الى شيء اخر !!
حاليا في هذا الوضع المخيف ،، ما راح نقدر نوصل للقائمة بتاعتنا ،، وما راح نقدر نحذفها .

ما هو الحل :
الحل في انك تتذكر ما هي القيمة التي سوف تكون للمؤشر head في كل سطر تقوم بكتابته


سؤال: ما هي عدد العقد التي نستطيع انشائها ؟؟
عدد
كبير جدا ، يعتمد بالمقام الاول على حجم الذاكرة عندك ،، وماهو الجزء الذي
خصص للذاكرة heap ، فكل العقد راح نعملها في ال heap ،، وقد تحدثنا عنها
سابقا .

سوال ثاني: القائمة المتصلة تشبه المصفوفات باستثناء الروابط بين العقد ؟؟
نعم
، القائمة تشبه المصفوفة ،، والذي جعلها مرنه هي الروابط ، حيث تستطيع حذف
العقدة "بتغيير الروابط" وكذلك انشاء عقدة جديدة "ايضا بتغيير الروابط ".


وقبل ان ننهي هذه المقدمة ،، اريد التحدث قليلا عن ذاكرة ال heap ::

كيف تعمل ال heap :

ال heap هي منطقة من الذاكرة RAM ،، وغالبا مساحتها اكبر من المنطقة Stack
ونستطيع تخيلها بالصورة :
هياكل البيانات Data Structures L8


طيب كيف نحجز مساحة للعدد 5 مثلا.
الاجابة : بما ان العدد 5 هو int ،، اذن راح نحجز مساحة من النوع int.

احد
الفروق بين حجز ذاكرة في ال heap وبين الحجز في ال stack ، هو ان حجز
الذاكرة في ال stack لا يتم بدون وجود اسم معرف identifier ، اي لابد من
اعطاء اسم للمتغير ، مثلا

CODE:
int x;



فلا يمكن ان نقول :
CODE:
int



وكذلك يمكن الوصول للمتغير x عن طريق اسمه او عن طريق عنوانه ،، شوف :
CODE:
x=3;



او:
CODE:
int *p=&x;
*p=3;



النتيجة هي نفسها في البرنامجين .


اما بالنسبة لل heap ، فحجز الذاكرة يتم بدون اسم معرف ،،فكل ما عليك تحديده هو نوع المتغير الذي تريد ان تحجزله مساحة، شوف:

CODE:
int *p=new int;
*p=3;



طيب لماذا استخدمنا new و *p ؟؟
السبب لاننا نريد ذاكرة في الheap وذلك لا يتم الا عن طريق المعامل new
وهذا المعامل يقوم بارجاع عنوان المساحة التي قام بحجزها .

صورة توضيحية ،، مهمة جدا : هياكل البيانات Data Structures Icon_eek

هياكل البيانات Data Structures L9


طيب اين المشكلة ؟؟ لماذا الصورة مهمة جدا !!
الاجابة:
هل تذكر المؤشر head والذي يشير لاول عقدة في القائمة المتصلة !! ماذا يحدث لو تغيرت قيمته ؟؟
هي نفس القصة الان.
لو تغيرت قيمة المؤشر p واصبح يشير الى شيء اخر ،، او طلع من المدى scope الموجود فيه "تذكر انه محجوز على ال stack"
لحدثت كارثة ،، تسمى .... تسرب الذاكرة .

ما الذي يمنع من تسرب الذاكرة Memory Leak ؟؟
الاجابة :
حذف الذاكرة الموجودة على ال heap ،، ويتم ذلك بالمعامل delete ،، كالاتي:

CODE:
delete p;



الان منعنا من تسرب الذاكرة ،، ولكن يوجد مش.... خلينا نشوف صورة هياكل البيانات Data Structures Icon_rolleyes

هياكل البيانات Data Structures L10

كما ذكرت الصورة ،،
المؤشر p يشير الى منطقة محذوفة ،، ما هو المصطلح العلمي لهذا الشيء ؟؟
الموشر الطائش او المؤشر الضال stray pointer ؟؟ هياكل البيانات Data Structures Icon_idea
ما هو ال stary pointer ؟
هو مؤشر يشير الى منطقة من الذاكرة ،، لا تخص برنامجنا !!
مثل المؤشر p والذي يشير الى ذاكرة تم التخلص منها ، اي ليست ملكنا .
ولكي نهديه من الضلال ،، يجب ان نعمل له عملية تعيين .
يعني يا نستخدمه لكي يؤشر الى اي متغير لدينا ، او نعطيه قيمة NULL
شوف الصورة بعد التعيين:
هياكل البيانات Data Structures L11


معليش يا شباب ،، بس في ملاحظة "صغنونة " قبل ما ننهي المقدمة ،، مع انها مكررة مرتين ، وهذه الثالثة :

المؤشر
p هو الذي يملك الحرية في التخلص من مشكلة تسرب الذاكرة ،، لذلك يجب ان
ننتبه اليه ، حتى لا يخرج من المدى او تتغير قيمته ،، ويصبح لدينا تسرب
للذاكرة ،، والذي لا خلاص منه الا عندما ينتهي البرنامج ، صورة اخيرة :


هياكل البيانات Data Structures L12

هياكل البيانات Data Structures L13



تااااااااااااااابع ،، المزيد
evergreen
evergreen

الجنس : Female

عدد المساهمات : 1497
النقاط : 61948
التقييم : 34
تاريخ التسجيل : 2010-02-03

https://3loomi.forumotion.com

Back to top Go down

هياكل البيانات Data Structures Empty Re: هياكل البيانات Data Structures

Post by evergreen Sat Mar 13, 2010 6:40 am

تحدثنا في المقدمة السابقة عن تعريف القائمة المتصلة ،،
وذكرنا
بأنها مجموعة من العقد ،، كل عقدة مرتبطة مع العقدة التي تليها ،، وهكذا
حتى نصل الى عقدة تشير الى NULL والتي راح تكون العقدة الاخيرة.

هذا التعريف لا ينطبق مع كل انواع القوائم المتصلة !!
وبالتحديد هذا تعريف القائمة المتصلة المفردة signle Linked List

عنوان جديد على رأس صفحة جديدة: انواع القوائم المتصلة :

يوجد عدة انواع اهمهما :
القائمة المتصلة المفردة single Linked List
القائمة المتصلة المزدوجة double Linked List
القائمة المتصلة الدائرية circual Linked List



اولا : القائمة المتصلة المفردة single Linked List :
تحدثنا في المقدمة السابقة عنها ،،ولكن للتذكير فقط :
كل عقدة تشير الى التي امامها ،، اي في كل عقدة يوجد مؤشر واحد فقط.
شوف الصورة:
هياكل البيانات Data Structures L14


وهذا شكل العقدة ، وقد تحدثنا عنها سابقا:
هياكل البيانات Data Structures L17


ثانيا : القائمة المتصلة المزدوجة double Linked List:
هذا النوع شبيه بالنوع السابق ،، ولكن الفرق ان كل عقدة node تحوي على مؤشرين :
مؤشر للعقدة التالية ، ومؤشر للعقدة السابقة .
شوف الصورة :
هياكل البيانات Data Structures L15

وهذا شكل العقدة :
هياكل البيانات Data Structures L18



ثالثا: القائمة المتصلة الدائرية circual Linked List
شيبه
القائمة المتصلة المزدوجة ،، لكن بفرق ان العقد الاخيرة تشير الى الاولى ،
وليس الى NULL وكذلك المؤشر الخلفي للعقدة الاولى يشير الى العقدة الاخير
، وليس الى NULL
شوف الصورة :
هياكل البيانات Data Structures L16



طيب السؤال الان : هل سندرس كل هذه الانواع ؟؟
الاجابة :
دراستها جميعا شيء جيد ،، ولكني افضل النوع الثاني double Linked List ،، لماذا ؟؟
لانه الاكثر استخداما + يشمل النوع الاول + بتعديل بسيط نحصل على النوع الثالث والذي لا يستخدم كثيرا .

انتهت المقدمة II ،، الان ننتقل الى :
القائمة المتصلة المزدوجة تحت المجهر ،،

++++++++++++++++++++

القائمة المتصلة المزدوجة double Linked List

تعريف : هي عبارة عن هيكل بيانات ،، مكون من عقد ، كل عقدة تحوي على ثلاث حقول:
1- حقل البيانات : يستخدم لتخزين ال data
2- مؤشر للعقدة السابقة : تحوي عنوان العقدة السابقة ، وسوف تكون NULL اذا كنا نتحدث عن العقدة الاولى.
3- مؤشر للعقدة التالية : تحوي عنوان العقدة التالية ، وسوف تكون NULL اذا كنا نتحدث عن العقدة الاخيرة .


وسوف نستخدم الاسم data للدلالة على حقل البيانات ، و pNext للدلالة على انه مؤشر الى العقدة التالية ،
و pPrevious للدلالة على انه مؤشر للعقدة التالية .

استخدامات القوائم المرتبطة :
اي برنامج في العالم يمكن ان يستخدم القائمة المتصلة ،، حتى برنامج hello world الصغير .

هل تذكر برنامج "بيتزا هت " باستخدام الطابور !! دعنا نشاهده باستخدام القوائم المتصلة المفردة ، ونشوف ما هو الفرق بينهم ، عمليا :

هذا هو شكله باستخدام الطابور Queue :
هياكل البيانات Data Structures Petza


وهذا هو بالاسطورة Linked List :
هياكل البيانات Data Structures L19


ما هو الفرق يا احبتي الكرام ؟؟
شاهد معي المشهد:
*قرر صاحب الطلب الثاني ،، ان يلغي طلبه ،،بكل سهولة باستخدام القائمة المتصلة ، راح نلغي الطلب :

هياكل البيانات Data Structures L20


هياكل البيانات Data Structures L21

*جاء واحد مهم "مدير المطعم ، ومعه المدام " هياكل البيانات Data Structures Icon_e_wink ،، وتخيل انه يوجد حوالى 20 طلب في الطابور ،، همممممم
هل سينتظر المدير 20 طلب ،، طبعا لأ ،، لانه ليس فيتامين واو ، وانما هو الواو نفسه هياكل البيانات Data Structures Icon_lol

فاذا كان الطابور قبل حضور المدير بهذا الشكل :
هياكل البيانات Data Structures L21


فانه سيصبح هكذا :
هياكل البيانات Data Structures L22



ما أود ان اقوله باختصار هو ان فكرة المكدس stack والطابور queue يمكن ان تطبق بالقائمة المرتبطة ،
وهي
افضل بكثيييييييير من ان تطبق بالمصفوفات العادية ،، لذلك عزيزي القارئ ،،
ارم الاكواد التي تعلمتها حتى الان الى سهلة المهملات "لا تصدق هههه "،،
ومرحبا بك مجددا في عالم القوائم المرتبطة ،،



++++++++++++++++++++++++

يوجد عمليات كثيرة ،، راح ندرس معظمها وذلك لاهميتها ،، وهذا هو سرد بسيط لبعضها:

CODE:
1-ادخال في البداية addFirst
2- ادخال في النهاية addLast
3- ادخال في اي مكان addAt
4- حذف من البداية removeFirst
5- حذف من النهاية removeLast
6- حذف من اي مكان removeFrom



وكذا يكفي كبداية
لاحظ ان اسامي الدوال قد تختلف من مبرمج لاخر ،، ولكن الفكرة هي نفسها

ناتي الى مرحلة كتابة الكود :
اجمل شيء في كل مواضيع ال data structures هو كتابة البرامج ،،
جميع البرامج بسيطة جدا ،، ولكنها تتطلب اولا ان تكون قد فهمت الناحية النظرية ، وبعد ذلك عملية الكتابة بسيطة جدا،،

سأتبع نفس النهج الذي سرنا عليه مع المكدس والطابور، لو تتذكر انه كان على شكل class .
وكان ال class يحتوي على مصفوفة "للتخزين" + متغير "دليل" ،، "ولا نسيتوا هذا الكلام " هياكل البيانات Data Structures Icon_evil

الان بنفس الفكرة ،، القائمة المتصلة تتكون من عقد ،، ولا يوجد نوع في سي++ اسمه عقدة .
لذلك اولا سنقوم بانشاء نوع جديد ونسميه عقدة ، وبعد ذلك ننشئ نوع اخر ونسميه قائمة متصلة .
القائمة المتصلة هي المسؤولة عن انشاء العقد وحذفها وعن جميع العمليات.

العقدة تتكون من مؤشرين و متغير للبيانات وراح نعتبر ان البيانات هي عدد صحيح int ، اذا مكونات العقدة هي:
int
Node *pPrevious
Node *pNext

اما القائمة المتصلة ،، فهي المسؤولة عن جميع العمليات من انشاءالعقد وحذفها وعرضها على الشاشة.

هذا كود سي++ ،، وان شاء الله راح اكتب نسخة سي ،، ولكن ليس الان حتى لا اخرج من الهدف الاساسي ،،

CODE:
// LinkedList , by Ahmad Essam.

#include <iostream>
using namespace std;

/////// Node Class //////

class Node
{
private:
int data;
Node *pNext;
Node *pPrevious;

public:
Node(int d):data(d),pNext(NULL),pPrevious(NULL){}

int getData()const{return data;}
void setData(int d){data=d;}

Node * getNext()const{return pNext;}
void setNext(Node *p){pNext=p;}

Node * getPrevious()const {return pPrevious;}
void setPrevious(Node *p){pPrevious=p;}

};

/////// LinkedList class ///////

class LinkedList
{
protected:
Node *pHead; //pointer to the first node
Node *pTail; //pointer to the last node
int nodeCounter; // count the number of nodes

Node * makeNode(int data); // make & return new node
void remove(Node *tmp); // remove node from linked list

public:
LinkedList();
~LinkedList();

// operations

void addFirst(int data);
void addLast(int data);
void addBefore(int index,int data);

void removeFirst();
void removeLast();

void displayInOrder()const;
void displayInReverseOrder()const;

void destroyList();
int getSize()const{return nodeCounter;}
};


LinkedList::LinkedList():
pHead(NULL),pTail(NULL),nodeCounter(0)
{}


LinkedList::~LinkedList()
{
destroyList();
}


void LinkedList::displayInOrder()const
{
if( !pHead )
{
cout<<"\nNULL";
return ;
}

for(Node *cur=pHead;cur!=NULL;cur=cur->getNext())
cout<<cur->getData()<<" -> ";

cout<<"NULL"<<endl;
}

void LinkedList::displayInReverseOrder()const
{
if( !pHead )
{
cout<<"\nNULL";
return ;
}

for(Node *cur=pTail;cur!=NULL;cur=cur->getPrevious())
cout<<cur->getData()<<" -> ";

cout<<"NULL"<<endl;
}


Node * LinkedList::makeNode(int data)
{
nodeCounter++;
return new Node(data);
}


void LinkedList::addFirst(int data)
{
// make new node
Node *pNew=makeNode(data);

// make the new node as the first node in the linked list

if( !pHead ) // if linkedlist is empty
pHead=pTail=pNew;

else // if there are nodes
{
pNew->setNext(pHead);
pHead->setPrevious(pNew);

pHead=pNew;
}
}


void LinkedList::addLast(int data)
{
// make new node
Node *pNew=makeNode(data);

// make the new node as the last node in the linked list

//if there is no node in the linked list , add node.
//otherwise move the new node to the last.

if( !pHead )
pHead=pTail=pNew;

else
{
pTail->setNext(pNew);
pNew->setPrevious(pTail);

pTail=pNew;
}
}





void LinkedList::addBefore(int index,int data)
{
// check the index to see if it's legal

if( index <1 || index > getSize() )
{
cerr<<"\nError: index is too hight/low ";
return;
}

if(index == 1)
{
addFirst(data);
return ;
}

// make new Node
Node *pNew=makeNode(data);
Node *pCur=pHead;

for(int i=1;i<index -1;i++,pCur=pCur->getNext());

pNew->setNext(pCur->getNext());
pCur->setNext(pNew);

}



void LinkedList::removeFirst()
{
if( !pHead )
{
cerr<<"\nThere is no any node to remove!!";
return ;
}

Node *pTmp=pHead;
pHead=pHead->getNext();

if(pHead)
pHead->setPrevious(NULL);
else
pHead=pTail=NULL;

remove(pTmp);
}


void LinkedList::remove(Node *tmp)
{
delete tmp;
nodeCounter--;
};


void LinkedList::removeLast()
{
if( !pHead )
{
cerr<<"\nThere is no any node to remove!!";
return ;
}

Node *pTmp=pTail;
pTail=pTail->getPrevious();

if(pTail)
pTail->setNext(NULL);
else
pTail=pHead=NULL;

remove(pTmp);
}



void LinkedList::destroyList()
{
Node *pTmp=pHead;
Node *pCur;

while( pTmp != NULL)
{
pCur=pTmp;
pTmp=pTmp->getNext();
remove(pCur);
}

}

/////// main //////////

int main()
{
LinkedList *p=new LinkedList;

for(int i=0;i<5;i++)
p->addLast(2*i+1);

cout<<"\nPrint linked list in order\n";
p->displayInOrder();

cout<<"\nPrint linked list in reverse order\n";
p->displayInReverseOrder();

cout<<"\nadd 20 befor the second node. \n";
p->addBefore(2,20);

cout<<"\nPrint linked list in order\n";
p->displayInOrder();

return 0;
}

// EOF




الفئة "الطبقة" Node :


CODE:
class Node
{
private:
int data;
Node *pNext;
Node *pPrevious;

public:
Node(int d):data(d),pNext(NULL),pPrevious(NULL){}

int getData()const{return data;}
void setData(int d){data=d;}

Node * getNext()const{return pNext;}
void setNext(Node *p){pNext=p;}

Node * getPrevious()const {return pPrevious;}
void setPrevious(Node *p){pPrevious=p;}

};



تحوي
هذه الطبقة على متغير data يستخدم لتخزين البيانات ، ومؤشر pNext من النوع
Node والذي يعني انه سيشير الى عقدة اخرى وتحديدا العقدة التالية ، واخيرا
تحوي مؤشر pPrevious والذي سوف يشير الى العقدة السابقة.

وكذلك تحوي على ستة دوال ،، هذه الدوال تسمى دوال وصول access method لانها لاتفعل شيئا غير احضار قيمة المتغيرات او تعيين قيم
جديدة للمتغيرات.

واهم
دالة هي الدالة البانية constructor والتي تقوم بتصفير قيم المؤشرات"اي
اعطائها قيم ابتدائية=0 ، بدلا من القيم المهملة garbage" ، وكذلك اعطاء
المتغير data قيمة ابتدائية ، والتي سوف تأتي عند انشاء عقدة Node.

++++++++++++++++++++++++

الطبقة LinkedList

كما ذكرت مسبقا ،، هذه الطبقة هي المسؤولة عن انشاء العقد Node وعن حذفها وعن عمليات الادخال واي شيء اخر.

وراح نقسمها الى قسمين حتى يسهل تتبعها .

القسم الاول وهو ما بداخل المحدد protected :

CODE:
protected:
Node *pHead;
Node *pTail;
int nodeCounter;

Node * makeNode(int data);
void remove(Node *tmp);



وتحوي الاتي:
* مؤشرين ،، الاول pHead مؤشر لاول عقدة ، والثاني pTail مؤشر لاخر عقدة .
* متغير nodeCounter عداد للعقد ، يزيد بواحد عن انشاء عقدة ، وينقص عند حذف العقد.
* دالة makeNode تعمل على انشاء عقدة، تاخذ عدد int كوسيط parameter ، وترجع بمؤشر للعقدة الجديدة.
* دالة remove تعمل على حذف العقدة ، والتي تمرر اليها ، ولا ترجع بشيء.


السؤال الان ، لماذا استخدمنا المحدد protected ؟؟
الاجابة : المحدد protected يشبه المحدد private ،، الا في حالة استخدام التوارث inheritance
فالطبقات المشتقة من هذه الطبقة تستطيع الموصول الى ما بداخل المحدد protected , public ولكنها لا تستطيع الوصول الى
ما بداخل المحدد private ، سنتكتشف المزيد لاحقاااااااااااااا . هياكل البيانات Data Structures Icon_e_geek


ننتقل الى القسم الاخر ، public

CODE:
public:
LinkedList();
~LinkedList();

// operations

void addFirst(int data);
void addLast(int data);
void addBefore(int index,int data);

void removeFirst();
void removeLast();

void displayInOrder()const;
void displayInReverseOrder()const;

void destroyList();
int getSize()const{return nodeCounter;}




وتحوي الاتي :

* الدالة البانية LinkedList،، تعمل على اعطاء المتغيرات قيم ابتدائية .
* الدالة الهادمة ~LinkedList،، تعمل على حذف القائمة المتصلة.
* الدالة addFirst والتي تقوم بانشاء عقدة وجعلها في مقدمة القائمة المتصلة.
* الدالة addLast والتي تقوم بانشاء عقدة ووضعها في اخر القائمة المتصلة.
* الدالة addBefore والتي تقوم بانشاء عقدة ووضعها قبل عقدة محددة.
* الدالة removeFirst تقوم بحذف العقدة الاولى من القائمة المتصلة.
* الدالة removeLast تقوم بحذف العقدة الاخيرة من القائمة المتصلة.
* الدالة displayInOrder تقوم بعرض جميع العقد في القائمة المتصلة بالترتيب.
* الدالة displayInReverseOrder تقوم بعرض العقد بترتيب عكسي.
* الدالة destroyList تقوم بحذف القائمة المتصلة.
* الدالة getSize تقوم بارجاع عدد العقد في القائمة المتصلة.

+++++++++++++++++

الان ندخل بالدالة الرئيسية :

CODE:
int main()
{
1- LinkedList *list=new LinkedList;

2- for(int i=0;i<5;i++)
3- list->addLast(2*i+1);

4- cout<<"\nPrint linked list in order\n";
5- list->displayInOrder();

6- cout<<"\nPrint linked list in reverse order\n";
7- list->displayInReverseOrder();

8- cout<<"\nadd 20 befor the second node. \n";
9- list->addBefore(2,20);

10- cout<<"\nPrint linked list in order\n";
11- list->displayInOrder();

return 0;
}



السطر الاول ،، انشأنا متغير من النوع LinkedList على ال heap وليس على ال stack.
وحقيقة لا يوجد حاليا اي فائدة من انشائها على ال heap ،، لذلك يمكنك كتابة هذا السطر بالشكل:
CODE:
LinkedList list;



انشاء المتغير على ال heap او على ال stack سيؤدي الى استدعاء الدالة البانية constructor تلقائيا.
والذي سيقوم بتصفير العداد nodeCounter ، وكذلك تعيين القيمة NULL=0 للمؤشرين pHead و pTail

اذا شكل القائمة المتصلة سوف يكون :
هياكل البيانات Data Structures L24


+++++++++++++++++++++++++

السطر 2و3 : قمنا بعمل loop خمس مرات ،، لاننا نريد اضافة خمس عقد الى القائمة المتصلة ، كل عقدة تاتي الى الامام ،
اي ان اخر عقدة هي راح تكون اول عقدة ،، نشوف ماذا سيحدث في كل تكرار :

\\\\\\\\\\\\\
التكرار الاول:

هياكل البيانات Data Structures L25


لاحظ ان القائمة المتصلة اصبحت تتكون من عقدة واحدة فقط .للذلك فان المؤشر pHead سيشير اليها ، لانها اول عقدة.
واما pTail فهو يشير الى اخر عقدة ، وبما اننا لدينا عقدة واحدة فهي بالتالي ستكون الاخير ، لذلك فان pTail يشير اليها ايضا.

ونذكر ان العقدة تتكون من ثلاث حاجات ، مؤشرين ومتغير للبيانات.
المتغير قيمته راح تكون هي 1 في مثالنا ، واما المؤشرين فقيمتهم NULL وذلك لعدم وجود عقدة سابقة ولاحقة.


\\\\\\\\\\\\

التكرار الثاني :
هياكل البيانات Data Structures L26

اصبح لدينا عقدتين في القئمة المتصلة ،، راقب قيم pHead و pTail ،، وكذلك قيم الموشرين pNext و pPrevious لكل عقدة.


\\\\\\\\\\\\\

التكرار الثالث:

هياكل البيانات Data Structures L27

\\\\\\\\\\\\\\\

التكرار الخامس والاخير :

هياكل البيانات Data Structures L28

+++++++++++++++++++

في السطر 5 قمنا باستدعاء الدالة displayInOrder والتي تقوم بعرض محتويات القائمة المتصلة على الشاشة.

وفي السطر 7 قمنا ياستدعاء displayInReverseOrder والتي تعرض محتويات القائمة المتصلة ولكن بترتيب عكسي
اي انها تبدأ من اخر عقدة ،، الى ان تصل الى اول عقدة.

وفي السطر التاسع قمنا باستدعاء الدالة addBefore ،، حيث قمنا باضافة عقدة جديدة داخل القائمة المتصلة ووضعناها قبل العقدة الثانية:

هياكل البيانات Data Structures L29


واخيرا في السطر ال 11 قمنا بعرض القائمة مرة اخرى للتاكد من ان الاضافة الاخيرة تمت بنجاح.


الى هنا انتهينا من شرح القائمة المتصلة بشكل موجز ، ولم نـتطرق الى التفصيل في الكود
والذي سيكون واجب "هربت من الشرح " هياكل البيانات Data Structures Icon_lol

اعتقد ان الكود شكله معقد ،، ولكن قم بكتابته وتنفيذه ،، وسيتضح لك انه بسيط جدا
واي واحد عنده سؤال في الكود او في عمل اي دالة فليتفضل ،،،

الان ننتقل الى موضوع اخر ..
evergreen
evergreen

الجنس : Female

عدد المساهمات : 1497
النقاط : 61948
التقييم : 34
تاريخ التسجيل : 2010-02-03

https://3loomi.forumotion.com

Back to top Go down

هياكل البيانات Data Structures Empty Re: هياكل البيانات Data Structures

Post by evergreen Sat Mar 13, 2010 6:40 am

* تطبيق المكدس باستخدام القائمة المتصلة.
* تطبيق الطابور باستخدام القائمة المتصلة .


اولا : تطبيق المكدس stack باستخدام القائمة المتصلة linked list

مسبقا ،، تحدثنا عن نسخة مطورة "كمبو دبل مكدس " باستخدام القائمة المتصلة ،،
والتي ذكرنا انها تطبق في معظم البرامج ،، ولا غبار عليها .

وفكرتها باختصار :
نفس القائمة المتصلة اللى فوق ،، بس راح يكون في دالتين فقط ، وليست ستة دوال ،،
الدالة الاولى فهي لادخال العقد الى اخر القائمة واللى اطلقنا عليها addLast
والثانية هي لسحب العقد من اخر القائمة المتصلة واللي اطلقنا عليها removeLast

وهذه الدوال توافق فكرة المكدس : "الادخال يتم من اعلى ، والسحب ايضا من اعلى "


كتابة الكود ،،
"نسخ+لصق" من كود القائمة المتصلة ، مع تغيير اسم الدالة addLast الى push
وتغيير اسم الدالة removeLast الى pop


CODE:
// Stack using Linked List, by Ahmad Essam.

#include <iostream>
using namespace std;

/////// Node Class //////

class Node
{
private:
int data;
Node *pNext;
Node *pPrevious;

public:
Node(int d):data(d),pNext(NULL),pPrevious(NULL){}

int getData()const{return data;}
void setData(int d){data=d;}

Node * getNext()const{return pNext;}
void setNext(Node *p){pNext=p;}

Node * getPrevious()const {return pPrevious;}
void setPrevious(Node *p){pPrevious=p;}

};

/////// LinkedStack class ///////

class LinkedStack
{
protected:
Node *pHead; //pointer to the first node
Node *pTail; //pointer to the last node
int nodeCounter; // count the number of nodes

Node * makeNode(int data); // make & return new node
void remove(Node *tmp); // remove node from linked list

public:
LinkedStack();
~LinkedStack();

// operations
void push(int data);
void pop();
void destroyList();
int getSize()const{return nodeCounter;}
int top()const
{
if (pTail == NULL)
return 0;
return pTail->getData();
}
};


LinkedStack::LinkedStack():
pHead(NULL),pTail(NULL),nodeCounter(0)
{}


LinkedStack::~LinkedStack()
{
destroyList();
}


Node * LinkedStack::makeNode(int data)
{
nodeCounter++;
return new Node(data);
}


void LinkedStack::push(int data)
{
// make new node
Node *pNew=makeNode(data);

// make the new node as the last node in the linked list

//if there is no node in the linked list , add node.
//otherwise move the new node to the last.

if( !pHead )
pHead=pTail=pNew;

else
{
pTail->setNext(pNew);
pNew->setPrevious(pTail);

pTail=pNew;
}
}


void LinkedStack::remove(Node *tmp)
{
delete tmp;
nodeCounter--;
};


void LinkedStack::pop()
{
if( !pHead )
{
cerr<<"\nThere is no any node to remove!!";
return ;
}

Node *pTmp=pTail;
pTail=pTail->getPrevious();

if(pTail)
pTail->setNext(NULL);

else
pTail=pHead=NULL;

remove(pTmp);
}



void LinkedStack::destroyList()
{
Node *pTmp=pHead;
Node *pCur;

while( pTmp != NULL)
{
pCur=pTmp;
pTmp=pTmp->getNext();
remove(pCur);
}

}

/////// main //////////

int main()
{
LinkedStack *stack=new LinkedStack;

for(int i=0;i<5;i++)
stack->push(2*i+1);

for(int i=0;i<5;i++)
{
cout<<"\npop: "<<stack->top();
stack->pop();

}


return 0;
}



من فهم القائمة المتصلة جيدا ،، سيجد ان تطبيق القائمة المتصلة ، اشمل من تطبيق المكدس باستخدام القائمة المتصلة .
فلا يوجد اي اضافات باستثناء الدالة top ،، وانما حذفنا العديد من الدوال ، وكذلك قمنا بتغيير بعض الاسماء ،
مثلا اسم الطبقة تم تغييره من LinkedList الى LinkedStack
وكذلك الدوال pop و push.

وبالنسبة للدالة top فهي تقوم بارجاع قيمة العقدة الاخيرة .


ثانيا : تطبيق الطابور Queues باستخدام القائمة المتصلة LinkedList
اعتقد انك تستطيع تخمين الكود الان .
نحتاج الى دالتين من القائمة المتصلة ، هما
دالة ادخال العناصر الى اول القائمة المتصلة addFirst
ودالة سحب العناصر من اخر القائمة المتصلة removeLast

وراح نطلق عليهم ، enQueue و deQueue


CODE:
// LinkedQueue , by Ahmad Essam.

#include <iostream>
using namespace std;

/////// Node Class //////

class Node
{
private:
int data;
Node *pNext;
Node *pPrevious;

public:
Node(int d):data(d),pNext(NULL),pPrevious(NULL){}

int getData()const{return data;}
void setData(int d){data=d;}

Node * getNext()const{return pNext;}
void setNext(Node *p){pNext=p;}

Node * getPrevious()const {return pPrevious;}
void setPrevious(Node *p){pPrevious=p;}

};

/////// LinkedQueue class ///////

class LinkedQueue
{
protected:
Node *pHead; //pointer to the first node
Node *pTail; //pointer to the last node
int nodeCounter; // count the number of nodes

Node * makeNode(int data); // make & return new node
void remove(Node *tmp); // remove node from linked list

public:
LinkedQueue();
~LinkedQueue();

// operations

void enQueue(int data);
void deQueue();
void destroyList();
int getSize()const{return nodeCounter;}
int top()const
{
if (pTail == NULL)
return 0;
return pTail->getData();
}
};


LinkedQueue::LinkedQueue():
pHead(NULL),pTail(NULL),nodeCounter(0)
{}


LinkedQueue::~LinkedQueue()
{
destroyList();
}




Node * LinkedQueue::makeNode(int data)
{
nodeCounter++;
return new Node(data);
}


void LinkedQueue::enQueue(int data)
{
// make new node
Node *pNew=makeNode(data);

// make the new node as the first node in the linked list

if( !pHead ) // if linkedlist is empty
pHead=pTail=pNew;

else // if there are nodes
{
pNew->setNext(pHead);
pHead->setPrevious(pNew);

pHead=pNew;
}
}


void LinkedQueue::remove(Node *tmp)
{
delete tmp;
nodeCounter--;
};


void LinkedQueue::deQueue()
{
if( !pHead )
{
cerr<<"\nThere is no any node to remove!!";
return ;
}

Node *pTmp=pTail;
pTail=pTail->getPrevious();

if(pTail)
pTail->setNext(NULL);

else
pTail=pHead=NULL;


remove(pTmp);
}



void LinkedQueue::destroyList()
{
Node *pTmp=pHead;
Node *pCur;

while( pTmp != NULL)
{
pCur=pTmp;
pTmp=pTmp->getNext();
remove(pCur);
}

}

/////// main //////////

int main()
{
LinkedQueue *queue=new LinkedQueue;

for(int i=0;i<5;i++)
queue->enQueue(2*i+1);


for(int i=0;i<5;i++)
{
cout<<"\ndeQueue: "<<queue->top();
queue->deQueue();

}

return 0;
}



لاحظ ان المشاكل القديمة اتحلت ،، هنا نستطيع ادخال اي عدد من العناصر
evergreen
evergreen

الجنس : Female

عدد المساهمات : 1497
النقاط : 61948
التقييم : 34
تاريخ التسجيل : 2010-02-03

https://3loomi.forumotion.com

Back to top Go down

هياكل البيانات Data Structures Empty Re: هياكل البيانات Data Structures

Post by evergreen Sat Mar 13, 2010 6:41 am

استخدام التوارث Inheritance لتسهيل التعديلات والتحديثات :

طبعا لاحظتوا ان تطبيق المكدس stack والطابور Queue باستخدام القائمة المتصلة ،،
افضل بكثير من التطبيق باستخدام المصفوفات بنوعيها العادية والديناميكية dynamic .

تطبيق المكدس و الطابور باستخدام القائمة المتصلة لايختلف كثيرا عن القائمة المتصلة نفسها ،
اللهم الا تغيير بسيط في الدوال.

الطريقة التي استخدمناها وهي اعادة كتابة القائمة المتصلة مع المكدس و الطابور غير محبذة !! لماذا ؟؟

لانه لو حصل تغيير بسيط في القائمة المتصلة ،، فيجب علينا كتابة التعديلات في جميع البرامج التي تستخدمها
وفي حالتنا هنا المكدس والطابور .

لذلك سنستخدم تقنية التوارث !!
حيث سيقوم المكدس والطابور بوراثة جميع خصائص القائمة المتصلة ، ومن ثم نقوم بتعديل ما يلزم .

الان لو حصل تغيير في القائمة المتصلة ،، فلا يوجد داعي لتغيير المكدس ولا الطابور ،، لانهما سوف يرثان التعديلات

وسوف
نستخدم التوارث الخاص وليس العام ، لان التوارث العام سيجعل مستخدم المكدس
او الطابور لدية القدرة على استخدام اي دالة سواءا كانت داخل نفس الفئة او
الفئة الاب وهي LinkedList.

اما التوارث الخاص private inheritance فيسمنعك من استدعاء دالة ليست موجودة في الفئة المشتقة.

نشوف الاكواد كاملة ، وان شاء الله تكون واضحة ،،
وراح اقسمها الى ملفات متتعدة ،، لكي نقوم بعمل مكتبة خاصة بهياكل البيانات.

اولا ملفات الرأس Header Files

ملف Node.h

]CODE:
#ifndef NODE_H
#define NODE_H

class Node
{
private:
int data;
Node *pNext;
Node *pPrevious;

public:
Node(int d=0);

int getData()const;
void setData(int);

Node * getNext()const;
void setNext(Node *p);

Node * getPrevious()const;
void setPrevious(Node *p);

};

#endif




ملف LinkedList.h

CODE:
#ifndef LINKEDLIST_H
#define LINKEDLIST_H

#include "Node.h"

class LinkedList
{
protected:
Node *pHead; //pointer to the first node
Node *pTail; //pointer to the last node
int nodeCounter; // count the number of nodes

Node * makeNode(int data); // make & return new node
void remove(Node *tmp); // remove node from linked list

public:
LinkedList();
~LinkedList();

// operations

void addFirst(int data);
void addLast(int data);
void addBefore(int index,int data);

void removeFirst();
void removeLast();

void displayInOrder()const;
void displayInReverseOrder()const;

void destroyList();
int getSize()const{return nodeCounter;}
};

#endif



ملف LinkedStack.h

CODE:
#ifndef LINKEDSTACK_H
#define LINKEDSTACK_H

#include "LinkedList.h"

class LinkedStack:private LinkedList
{
public:
// operations
void push(int data);
void pop();
int top()const;
void display()const;
};

#endif



ملف LinkedQueue.h

CODE:
#ifndef LINKEDQUEUE_H
#define LINKEDQUEUE_H

#include "LinkedList.h"

class LinkedQueue :private LinkedList
{

public:
// operations
void enQueue(int data);
void deQueue();
int top()const;
void display()const;
};

#endif




ثانيا: ال Implementation :

ملف Node.cpp

CODE:
#include "Node.h"
#include <iostream>

Node::Node(int d):
data(d),pNext(NULL),pPrevious(NULL)
{}

int Node::getData()const
{
return data;
}

void Node::setData(int d)
{
data=d;
}


Node* Node:: getNext()const
{
return pNext;
}

void Node::setNext(Node *p)
{
pNext=p;
}


Node* Node::getPrevious()const
{
return pPrevious;
}


void Node::setPrevious(Node *p)
{
pPrevious=p;
}





ملف LinkedList.cpp

CODE:
#include "Node.h"
#include "LinkedList.h"
#include <iostream>

using std::cout;
using std::endl;
using std::cerr;

LinkedList::LinkedList():
pHead(NULL),pTail(NULL),nodeCounter(0)
{}


LinkedList::~LinkedList()
{
destroyList();
}


void LinkedList::displayInOrder()const
{
if( !pHead )
{
cout<<"\nNULL";
return ;
}

for(Node *cur=pHead;cur!=NULL;cur=cur->getNext())
cout<<cur->getData()<<" -> ";

cout<<"NULL"<<endl;
}

void LinkedList::displayInReverseOrder()const
{
if( !pHead )
{
cout<<"\nNULL";
return ;
}

for(Node *cur=pTail;cur!=NULL;cur=cur->getPrevious())
cout<<cur->getData()<<" -> ";

cout<<"NULL"<<endl;
}


Node * LinkedList::makeNode(int data)
{
nodeCounter++;
return new Node(data);
}


void LinkedList::addFirst(int data)
{
// make new node
Node *pNew=makeNode(data);

// make the new node as the first node in the linked list

if( !pHead ) // if linkedlist is empty
pHead=pTail=pNew;

else // if there are nodes
{
pNew->setNext(pHead);
pHead->setPrevious(pNew);

pHead=pNew;
}
}


void LinkedList::addLast(int data)
{
// make new node
Node *pNew=makeNode(data);

// make the new node as the last node in the linked list

//if there is no node in the linked list , add node.
//otherwise move the new node to the last.

if( !pHead )
pHead=pTail=pNew;

else
{
pTail->setNext(pNew);
pNew->setPrevious(pTail);

pTail=pNew;
}
}



void LinkedList::addBefore(int index,int data)
{
// check the index to see if it's legal

if( index <1 || index > getSize() )
{
cerr<<"\nError: index is too hight/low ";
return;
}

if(index == 1)
{
addFirst(data);
return ;
}

// make new Node
Node *pNew=makeNode(data);
Node *pCur=pHead;

for(int i=1;i<index -1;i++,pCur=pCur->getNext());

pNew->setNext(pCur->getNext());
pCur->setNext(pNew);

}



void LinkedList::removeFirst()
{
if( !pHead )
{
cerr<<"\nThere is no any node to remove!!";
return ;
}

Node *pTmp=pHead;
pHead=pHead->getNext();

if(pHead)
pHead->setPrevious(NULL);
else
pHead=pTail=NULL;
remove(pTmp);

}


void LinkedList::remove(Node *tmp)
{
delete tmp;
nodeCounter--;
};


void LinkedList::removeLast()
{
if( !pHead )
{
cerr<<"\nThere is no any node to remove!!";
return ;
}

Node *pTmp=pTail;
pTail=pTail->getPrevious();

if(pTail)
pTail->setNext(NULL);
else
pHead=pTail=NULL;

remove(pTmp);
}



void LinkedList::destroyList()
{
Node *pTmp=pHead;
Node *pCur;

while( pTmp != NULL)
{
pCur=pTmp;
pTmp=pTmp->getNext();
remove(pCur);
}

}





ملف LinkedStack.cpp

CODE:
#include "LinkedStack.h"
#include <iostream>

int LinkedStack::top()const
{
if (pTail == NULL)
return 0;
return pTail->getData();
}


void LinkedStack::push(int data)
{
addLast(data);
}


void LinkedStack::pop()
{
removeLast();
}

void LinkedStack::display()const
{
displayInOrder();
}





ملف LinkedQueue.cpp

CODE:
#include "LinkedQueue.h"
#include <iostream>

int LinkedQueue::top()const
{
if (pTail == NULL)
return 0;
return pTail->getData();
}


void LinkedQueue::enQueue(int data)
{
addFirst(data);
}


void LinkedQueue::deQueue()
{
removeLast();
}

void LinkedQueue::display()const
{
displayInOrder();
}





ثالثا: ال Driver program

والمقصود به هو الملف الذي يحوي الدالة main
وراح اكتب ثلاثة نسخ ،،
الاولى تستخدم ال Linked List
والثانية تستخدم ال Stack
والثالثة تستخدم ال Queue

ملف linkedListDriverProgram.cpp :


CODE:
#include <iostream>
using namespace std;

#include "LinkedList.h"

int main()
{
LinkedList *list=new LinkedList;

for(int i=0;i<5;i++)
{
cout<<"\nadd "<<i+1<<" to list";
list->addLast(i+1);
}

cout<<"\nDisplay list\n";
list->displayInOrder();

cout<<"remove First element in the list\n";
list->removeFirst();

cout<<"add 300 to the last\n";
list->addLast(300);

cout<<"Display list\n";
list->displayInOrder();

cout<<"Display list in reverse\n";
list->displayInReverseOrder();

return 0;
}




ملف StackDriverProgram.cpp :

CODE:
#include <iostream>
using namespace std;

#include "LinkedStack.h"

int main()
{
LinkedStack *stack=new LinkedStack;

for(int i=0;i<5;i++)
{
cout<<"\npush "<<i+1<<" to stack";
stack->push(i+1);
}

cout<<"\nDisplay stack\n";
stack->display();

cout<<"pop two elements from the stack\n";
stack->pop();
stack->pop();

cout<<"push 300 to the stack\n";
stack->push(300);

cout<<"Display stack\n";
stack->display();


return 0;
}




ملف QueueDriverProgram.cpp :

CODE:
#include <iostream>
using namespace std;

#include "LinkedQueue.h"

int main()
{
LinkedQueue *queue=new LinkedQueue;

for(int i=0;i<5;i++)
{
cout<<"\nenqueue "<<i+1<<" to queue";
queue->enQueue(i+1);
}

cout<<"\nDisplay queue\n";
queue->display();

cout<<"dequeue two elements from queue\n";
queue->deQueue();
queue->deQueue();

cout<<"enqueue 300 to the queue\n";
queue->enQueue(300);

cout<<"Display queue\n";
queue->display();

return 0;
}



لترجمة البرنامج ،، اولا عليك انشاء مجلد جديد وليكن :dataStructures
وبعد ذلك قم بنسخ الاكواد ووضعها في ملفات .cpp كما هو موضح.
ولكن لا تستخدم الثلاث نسخ من ال driver program في ان واحد ، حتى لا يكون عندك ثلاث دوال main
وهذا خطأ .

ثم نفذ:
CODE:
g++ *.cpp



ولتشغيل البرنامج

CODE:
./a.out



واذا كنت تستخدم انظمة ويندوز فراجع ملفات ال help التي تاتي مع المترجم عن كيفية ترجمة عدة ملفات .

او استخدم المترجم g++ من خلال تنزيل الباكج MinGw


تاااااااااابع ،،
evergreen
evergreen

الجنس : Female

عدد المساهمات : 1497
النقاط : 61948
التقييم : 34
تاريخ التسجيل : 2010-02-03

https://3loomi.forumotion.com

Back to top Go down

هياكل البيانات Data Structures Empty Re: هياكل البيانات Data Structures

Post by evergreen Sat Mar 13, 2010 6:42 am

القوالب template

"راح نتحدث عنها بشكل واسع في موضوع منفصل ، وذلك لاهميتها "

حاليا ،، برامجنا فل الفل ،، لا يوجد مشاكل ، سواء في ادخال عنصر جديد وقت التشغيل او حذفه .
ولكن من مشاهدة الموضوع بزاوية اخرى ، نجد هناك مشكلة بسيطة !! ما هي ؟؟

جميع ما كتبناه يتعامل مع اعداد صحيحة فقط !!


ماذا لو اراد مستخدم البرنامج ان يتعامل مع حروف او سيارت او اي كائن !!
هل سيذهب الى الاكواد ويقوم باستبدال كل int ب char مثلا !!
طبعا هذا حل ولكن سخيف جدا ،، لانه لا يضع في الحسبان ان المستخدم قد يريد التعامل مع حروف وققط وبيتزا وسيارات في ان واحد !
هل سيقوم بعمل كل هذه النسخ من الاكواد !!


هنا جاءت القوالب لكي تحل هذه المشكلة ،، فهي المسؤولة عن انشاء النسخ التي تريدها .
نشوف مثال صغير لدالة تطبع العدد او الحرف الذي يرسل اليها ،، بدون قوالب:

CODE:
void function(int x)
{
cout<<x;
}

void function(char x)
{
cout<<x;
}

....



وهكذا مع كل نوع نريد ان نطبعه على الشاشة ، مثلا :

CODE:
void function(petza x)
{
cout<<x;
}



هنا استخدمنا ال function overloading او اعادة توصيف الدالة ،، ونلاحظ ان الاختلاف فقط في ال parameter

طيب ماذا لو اردنا تغيير عمل هذه الدوال باضافة عمليات حسابية مثلا !!
سوف نمر على كل دالة ونقوم بالاضافات .
تخيل لو ان عدد الدوال كبير نوعا ، العملية متعبة جدا .


ولكن باستخدام ال function template سوف نتحل هذه المشكلة ،
ويصبح لدينا دالة واحدة ، تقبل اي نوع .

شوف المثال :

CODE:
template <class T>
void function(T x)
{
cout<<x;
}



الان اي تعديل سيتم في مكان واحد فقط.
وكذلك هذه الدالة سوف تأخذ argument من اي نوع ، شوف الاستدعاءات للدالة :

باستخدام عدد صحيح
CODE:
function(18);



باستخدام عدد حقيقي
CODE:
function(3.14);



باستخدام حرف
CODE:
function('A');



كلها صحيحة 100% .


"تستخدم القوالب مع الدوال function template والفئات class template وذلك لجعلها تعمل مع اي نوع او اي كائن" .


نشوف الاكواد باستخدام القوالب template : هياكل البيانات Data Structures Icon_e_ugeek

اولا ملفات الرأس Header Files

ملف Node.h

CODE:
#ifndef NODE_H
#define NODE_H

template <class T>
class Node
{
private:
T data;
Node *pNext;
Node *pPrevious;

public:
Node(T d=0);

T getData()const;
void setData(T);

Node * getNext()const;
void setNext(Node *p);

Node * getPrevious()const;
void setPrevious(Node *p);

};

#endif




ملف LinkedList.h

CODE:
#ifndef LINKEDLIST_H
#define LINKEDLIST_H



#include "Node.h"

template <class T>
class LinkedList
{
protected:
Node<T> *pHead; //pointer to the first node
Node<T> *pTail; //pointer to the last node
int nodeCounter; // count the number of nodes

Node<T> * makeNode(T data); // make & return new node
void remove(Node<T> *tmp); // remove node from linked list

public:
LinkedList();
~LinkedList();

// operations

void addFirst(T data);
void addLast(T data);
void addBefore(int index,T data);

void removeFirst();
void removeLast();

void displayInOrder()const;
void displayInReverseOrder()const;

void destroyList();
int getSize()const{return nodeCounter;}
};

#endif




ملف LinkedStack.h

CODE:
#ifndef LINKEDSTACK_H
#define LINKEDSTACK_H

#include "LinkedList.h"

template <class T>
class LinkedStack:private LinkedList<T>
{
public:
// operations
void push(T data);
void pop();
T top()const;
void display()const;
};

#endif



ملف LinkedQueue.h

CODE:
#ifndef LINKEDQUEUE_H
#define LINKEDQUEUE_H

#include "LinkedList.h"

template <class T>
class LinkedQueue :private LinkedList<T>
{
public:
// operations
void enqueue(T data);
void dequeue();
T top()const;
void display()const;
};

#endif



ثانيا: ال Implementation :

ملف Node.cpp

CODE:
#include "Node.h"
#include <iostream>

template <class T>
Node<T>::Node(T d):
data(d),pNext(NULL),pPrevious(NULL)
{}

template <class T>
T Node<T>::getData()const
{
return data;
}

template <class T>
void Node<T>::setData(T d)
{
//if T is a user-defined class , it should overloading
// the assignment operrator = or use the default.
data=d;
}


template <class T>
Node<T>* Node<T>:: getNext()const
{
return pNext;
}

template <class T>
void Node<T>::setNext(Node<T> *p)
{
pNext=p;
}


template <class T>
Node<T>* Node<T>::getPrevious()const
{
return pPrevious;
}


template <class T>
void Node<T>::setPrevious(Node<T> *p)
{
pPrevious=p;
}




ملف LinkedList.cpp

CODE:
//#include "Node.h"
#include "LinkedList.h"
#include <iostream>
using namespace std;


template <class T>
LinkedList<T>::LinkedList():
pHead(NULL),pTail(NULL),nodeCounter(0)
{}


template <class T>
LinkedList<T>::~LinkedList()
{
destroyList();
}


template <class T>
void LinkedList<T>::displayInOrder()const
{
if( !pHead )
{
cout<<"\nNULL";
return ;
}

for(Node<T> *cur=pHead;cur!=NULL;cur=cur->getNext())
cout<<cur->getData()<<" -> ";

cout<<"NULL"<<endl;
}


template <class T>
void LinkedList<T>::displayInReverseOrder()const
{
if( !pHead )
{
cout<<"\nNULL";
return ;
}

for(Node<T> *cur=pTail;cur!=NULL;cur=cur->getPrevious())
cout<<cur->getData()<<" -> ";

cout<<"NULL"<<endl;
}

template <class T>
Node<T> * LinkedList<T>::makeNode(T data)
{
nodeCounter++;
return new Node<T>(data);
}


template <class T>
void LinkedList<T>::addFirst(T data)
{
// make new node
Node<T> *pNew=makeNode(data);

// make the new node as the first node in the linked list

if( !pHead ) // if linkedlist is empty
pHead=pTail=pNew;

else // if there are nodes
{
pNew->setNext(pHead);
pHead->setPrevious(pNew);

pHead=pNew;
}
}


template <class T>
void LinkedList<T>::addLast(T data)
{
// make new node
Node<T> *pNew=makeNode(data);

// make the new node as the last node in the linked list

//if there is no node in the linked list , add node.
//otherwise move the new node to the last.

if( !pHead )
pHead=pTail=pNew;

else
{
pTail->setNext(pNew);
pNew->setPrevious(pTail);

pTail=pNew;
}
}


template <class T>
void LinkedList<T>::addBefore(int index,T data)
{
// check the index to see if it's legal

if( index <1 || index > getSize() )
{
cerr<<"\nError: index is too hight/low ";
return;
}

if(index == 1)
{
addFirst(data);
return ;
}

// make new Node
Node<T> *pNew=makeNode(data);
Node<T> *pCur=pHead;

for(int i=1;i<index -1;i++,pCur=pCur->getNext());

pNew->setNext(pCur->getNext());
pCur->setNext(pNew);

}


template <class T>
void LinkedList<T>::removeFirst()
{
if( !pHead )
{
cerr<<"\nThere is no any node to remove!!";
return ;
}

Node<T> *pTmp=pHead;
pHead=pHead->getNext();

if(pHead)
pHead->setPrevious(NULL);
else
pHead=pTail=NULL;

remove(pTmp);


}


template <class T>
void LinkedList<T>::remove(Node<T> *tmp)
{
delete tmp;
nodeCounter--;
};


template <class T>
void LinkedList<T>::removeLast()
{
if( !pHead )
{
cerr<<"\nThere is no any node to remove!!";
return ;
}

Node<T> *pTmp=pTail;
pTail=pTail->getPrevious();


if(pTail)
pTail->setNext(NULL);
else
pTail=pHead=NULL;

remove(pTmp);
}



template <class T>
void LinkedList<T>::destroyList()
{
Node<T> *pTmp=pHead;
Node<T> *pCur;

while( pTmp != NULL)
{
pCur=pTmp;
pTmp=pTmp->getNext();
remove(pCur);
}

}




ملف LinkedStack.cpp

CODE:
#include "LinkedStack.h"

#include <iostream>


template <class T>
T LinkedStack<T>::top()const
{
if (LinkedList<T>::pTail == NULL)
return 0;
return LinkedList<T>::pTail->getData();
}

template <class T>
void LinkedStack<T>::push(T data)
{
LinkedList<T>::addLast(data);
}


template <class T>
void LinkedStack<T>::pop()
{
LinkedList<T>::removeLast();
}

template <class T>
void LinkedStack<T>::display()const
{
LinkedList<T>::displayInOrder();
}



ملف LinkedQueue.cpp

CODE:
#include "LinkedQueue.h"
#include <iostream>

template <class T>
T LinkedQueue<T>::top()const
{
if (LinkedList<T>::pTail == NULL)
return 0;
return LinkedList<T>::pTail->getData();
}

template <class T>
void LinkedQueue<T>::enqueue(T data)
{
LinkedList<T>::addFirst(data);
}


template <class T>
void LinkedQueue<T>::dequeue()
{
LinkedList<T>::removeLast();
}

template <class T>
void LinkedQueue<T>::display()const
{
LinkedList<T>::displayInOrder();
}





ثالثا: ال Driver program

ملف linkedListDriverProgram.cpp

CODE:
#include <iostream>
using namespace std;

#include "LinkedList.h"

int main()
{
LinkedList<char> list;

for(int i=0;i<5;i++)
{
cout<<"\nadd "<<i+1<<" to list";
list->addLast(i+1);
}

cout<<"\nDisplay list\n";
list->displayInOrder();

cout<<"remove First element in the list\n";
list->removeFirst();

cout<<"add 300 to the last\n";
list->addLast(300);

cout<<"Display list\n";
list->displayInOrder();

cout<<"Display list in reverse\n";
list->displayInReverseOrder();

return 0;
}



قم بتشغيل البرنامج بالطريقة السابقة ،، ولكن ما راح يشتغل !!
لماذا ؟؟ لا ادري صراحة !! هياكل البيانات Data Structures Icon_eek هياكل البيانات Data Structures Icon_eek

يوجد مشكلة undefined reference ،، وهي مشكلة تحدث اثناء الربط Linking اي لا يوجد مشكلة في الكود syntax فهو صحيح 100 %

حاولت احل المشكلة ،، بس ما قدرت هياكل البيانات Data Structures Icon_redface ،، واي واحد عنده حل فلا يبخل علينا .

هياكل البيانات Data Structures Icon_idea لكن في طريقة وحيدة لكي تجعله يعمل ، وهي نسخ محتويات كل الملفات ووضعها داخل ملف واحد وليكن main.cpp
لاني كما ذكرت ان المشكلة تحدث اثناء ربط ال object code مع بعض.
وبوضع الاكواد داخل ملف واحد ،، راح ينتج من عملية الترجمة object code واحد ، وسيتم ربطه مع المكتبات المستخدمة.

الحل كالاتي: ملف main.cpp

CODE:
// LinkedList by Ahmad Essam "SudaNix"


#include <iostream>
using namespace std;


/////////// Node definition ////////////////////
template <class T>
class Node
{
private:
T data;
Node *pNext;
Node *pPrevious;

public:
Node(T d=0);

T getData()const;
void setData(T);

Node * getNext()const;
void setNext(Node *p);

Node * getPrevious()const;
void setPrevious(Node *p);

};

/////////// Node Implementation ////////////////////

template <class T>
Node<T>::Node(T d):
data(d),pNext(NULL),pPrevious(NULL)
{}

template <class T>
T Node<T>::getData()const
{
return data;
}

template <class T>
void Node<T>::setData(T d)
{
//if T is a user-defined class , it should overloading
// the assignment operrator = or use the default.
data=d;
}


template <class T>
Node<T>* Node<T>:: getNext()const
{
return pNext;
}

template <class T>
void Node<T>::setNext(Node<T> *p)
{
pNext=p;
}


template <class T>
Node<T>* Node<T>::getPrevious()const
{
return pPrevious;
}


template <class T>
void Node<T>::setPrevious(Node<T> *p)
{
pPrevious=p;
}


/////////// LinkedList definition ////////////////////

template <class T>
class LinkedList
{
protected:
Node<T> *pHead; //pointer to the first node
Node<T> *pTail; //pointer to the last node
int nodeCounter; // count the number of nodes

Node<T> * makeNode(T data); // make & return new node
void remove(Node<T> *tmp); // remove node from linked list

public:
LinkedList();
~LinkedList();

// operations

void addFirst(T data);
void addLast(T data);
void addBefore(int index,T data);

void removeFirst();
void removeLast();

void displayInOrder()const;
void displayInReverseOrder()const;

void destroyList();
int getSize()const{return nodeCounter;}
};




/////////// LinkedList Implementation ////////////////////

template <class T>
LinkedList<T>::LinkedList():
pHead(NULL),pTail(NULL),nodeCounter(0)
{}


template <class T>
LinkedList<T>::~LinkedList()
{
destroyList();
}


template <class T>
void LinkedList<T>::displayInOrder()const
{
if( !pHead )
{
cout<<"\nNULL";
return ;
}

for(Node<T> *cur=pHead;cur!=NULL;cur=cur->getNext())
cout<<cur->getData()<<" -> ";

cout<<"NULL"<<endl;
}


template <class T>
void LinkedList<T>::displayInReverseOrder()const
{
if( !pHead )
{
cout<<"\nNULL";
return ;
}

for(Node<T> *cur=pTail;cur!=NULL;cur=cur->getPrevious())
cout<<cur->getData()<<" -> ";

cout<<"NULL"<<endl;
}

template <class T>
Node<T> * LinkedList<T>::makeNode(T data)
{
nodeCounter++;
return new Node<T>(data);
}


template <class T>
void LinkedList<T>::addFirst(T data)
{
// make new node
Node<T> *pNew=makeNode(data);

// make the new node as the first node in the linked list

if( !pHead ) // if linkedlist is empty
pHead=pTail=pNew;

else // if there are nodes
{
pNew->setNext(pHead);
pHead->setPrevious(pNew);

pHead=pNew;
}
}


template <class T>
void LinkedList<T>::addLast(T data)
{
// make new node
Node<T> *pNew=makeNode(data);

// make the new node as the last node in the linked list

//if there is no node in the linked list , add node.
//otherwise move the new node to the last.

if( !pHead )
pHead=pTail=pNew;

else
{
pTail->setNext(pNew);
pNew->setPrevious(pTail);

pTail=pNew;
}
}


template <class T>
void LinkedList<T>::addBefore(int index,T data)
{
// check the index to see if it's legal

if( index <1 || index > getSize() )
{
cerr<<"\nError: index is too hight/low ";
return;
}

if(index == 1)
{
addFirst(data);
return ;
}

// make new Node
Node<T> *pNew=makeNode(data);
Node<T> *pCur=pHead;

for(int i=1;i<index -1;i++,pCur=pCur->getNext());

pNew->setNext(pCur->getNext());
pCur->setNext(pNew);

}


template <class T>
void LinkedList<T>::removeFirst()
{
if( !pHead )
{
cerr<<"\nThere is no any node to remove!!";
return ;
}

Node<T> *pTmp=pHead;
pHead=pHead->getNext();

if(pHead)
pHead->setPrevious(NULL);
else
pHead=pTail=NULL;

remove(pTmp);


}


template <class T>
void LinkedList<T>::remove(Node<T> *tmp)
{
delete tmp;
nodeCounter--;
};


template <class T>
void LinkedList<T>::removeLast()
{
if( !pHead )
{
cerr<<"\nThere is no any node to remove!!";
return ;
}

Node<T> *pTmp=pTail;
pTail=pTail->getPrevious();


if(pTail)
pTail->setNext(NULL);
else
pTail=pHead=NULL;

remove(pTmp);
}



template <class T>
void LinkedList<T>::destroyList()
{
Node<T> *pTmp=pHead;
Node<T> *pCur;

while( pTmp != NULL)
{
pCur=pTmp;
pTmp=pTmp->getNext();
remove(pCur);
}

}


//////////// Driver Program ///////////////////

int main()
{
LinkedList<char> *list=new LinkedList<char>;

for(char i='A';i<'H';i++)
{
cout<<"\nadd "<<i<<" to list";
list->addLast(i);
}

cout<<"\nDisplay list\n";
list->displayInOrder();

cout<<"remove First element in the list\n";
list->removeFirst();

cout<<"add Z to the last\n";
list->addLast('Z');

cout<<"Display list\n";
list->displayInOrder();

cout<<"Display list in reverse\n";
list->displayInReverseOrder();


cout<<"\n\nAhmad Essam in Service, Any Questions?\n\n";
return 0;
}




ملاحظات:
* الان تستطيع استخدام االقائمة المتصلة او المكدس او الطابور مع اي نوع بيانات .
* عدد العناصر غير محدد ، على حسب حاجتك.
* استخدام رائع للذاكرة .
* سوف نضيف بعض الدوال عندما نتقدم اكثر ، مثل دوال الترتيب والبحث والدمج و....الخ.

الى هنا نكون قد انهينا جولتنا حول المكدس ، الطابور ، القائمة المتصلة .
ننتقل الى الاسئلة
evergreen
evergreen

الجنس : Female

عدد المساهمات : 1497
النقاط : 61948
التقييم : 34
تاريخ التسجيل : 2010-02-03

https://3loomi.forumotion.com

Back to top Go down

هياكل البيانات Data Structures Empty Re: هياكل البيانات Data Structures

Post by evergreen Sat Mar 13, 2010 6:42 am

وطبعا الحل بأي لغة برمجة "يفضل سي او سي++" ،، وبأي طريقة ،، على كيفك .
المهم ان البرنامج يعمل ،، وبعد ذلك سننظر الى امور اخرى .

السؤال الاول :
اكتب برنامج "الة حاسبة" لحساب العمليات الرياضية ، مثلا :

المستخدم راح يدخل:
2+4

الناتج:
طبعا 6

++++++++++++

مثال اخر:
المدخلات:
3+3*2

الناتج:
يجب ان يكون 9
وليس 12

++++++++++++++

المدخلات:
4/2*2+2*5-1

وهكذا ،،

طبعا يجب ان تراعي اسبقية المعاملات operator precedence اثناء الحساب ،،
يعنى الضرب والقسمة اولا ،، وبعدها الجمع والطرح.
والالولية دائما لما بداخل الاقواس ،، شوف لحساب هذا المدخل:

CODE:
3*(1+2)+5كود:
=3*3+5كود:
=9+5كود:
=14النتيجة : 14



شكل البرنامج بسيط ،، ولكنه يحوي بعض الخفايا ،،
قم بالمحاولة ،، وهذه بعض النقاط المساعدة :

* هذا البرنامج يسمى بـمقيم التعابير Expression Evaluator
* يجب ان تقوم بتحويل الصيغة التي يدخلها المستخدم والتي تسمى infix notation الى الصيغة postfix
* وبعدها تقوم بحساب القيم.
* طريقة التحويل بسيطة جدا ،، سأضعها في المشاركة القادمة ،، ولكن ببحث بسيط في جووجل عن infix to postfix راح تلاقى ما يسرك .



توضيح اكثر:
السلسلة التي يدخلها المستخدم تسمى infix
والتي يصعب حسابها بدون تحويلها الى صيغة اخرى تسمى postfix
هذا التحويل يتم بخوارزمية بسيطة محددة تسمى infix to postfix

بعد التحويل الى postfix ،، يجب حساب النتيجة ، ولكن ايضا لا تتم بصورة مباشرة
يجب استخدام خورازمية evaluate postfix ،، وبعدها راح تجيك النتيجة.

العملية بسيطة جدا ، ابحث عن الخوارزميات وقم بتتطبيقها بلغتك المفضلة ، وعطنا الكود + البرنامج.

وحتى نسهل الموضوع ،، الاعداد التي تدخلها يجب ان تكون اقل من 10 .
يعني تتكون من رقم واحد فقط ، من 0 الى 9

صورة لنسخة برمجتها من فترة :
هياكل البيانات Data Structures Expeval

evergreen
evergreen

الجنس : Female

عدد المساهمات : 1497
النقاط : 61948
التقييم : 34
تاريخ التسجيل : 2010-02-03

https://3loomi.forumotion.com

Back to top Go down

هياكل البيانات Data Structures Empty Re: هياكل البيانات Data Structures

Post by Sponsored content


Sponsored content


Back to top Go down

Back to top

- Similar topics

 
Permissions in this forum:
You cannot reply to topics in this forum