English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

تحليل استخدام map في stl في c++

Map هو حاوية مرتبطة في STL، يقدم قدرة معالجة البيانات التكاملية (حيث يمكن أن يُدعى الأول مفتاحًا، لا يمكن أن يظهر المفتاح مرتين في map، والثاني يمكن أن يُدعى قيمة المفتاح) بسبب هذه الخاصية، يمكن أن يقدم طريقًا سريعًا في البرمجة عند معالجة البيانات التكاملية. دعونا نتحدث عن تنظيم البيانات في map، حيث يبني map شجرة أحمر-أسود (بالنسبة لشجرة ثنائية متوازنة غير صارمة) داخليًا، هذه الشجرة تتمتع بقدرة الترتيب التلقائي للبيانات، لذا تكون جميع البيانات في map مرتبة، سنرى قريبًا مزايا الترتيب.

إليك مثال على ما هو تطابق البيانات التكاملية. على سبيل المثال، في فصل دراسي، كل رقم التسجيل لطالب مع اسمه يشكل علاقة تكاملية، يمكن أن يصف هذا النموذج بسهولة باستخدام map، واضح أن الرقم التسجيل يمكن أن يوصف باستخدام int، واسم الطالب يمكن أن يوصف باستخدام string (في هذا المقال لن نستخدم char * لوصف السلسلة، بل سنستخدم string في STL)، إليك كود map وصف البيانات:

Map<int, string> mapStudent;

1. بناءات map

يقدم map ستة بناءات مسبقة، هذا الأمر يتعلق بمحلل الذاكرة وما إلى ذلك، سيتم تجاوزه هنا، في الجزء التالي سنواجه بعض طرق بناء map، ولكن يجب أن نقول أننا نستخدم عادة الطريقة التالية لبناء map:

Map<int, string> mapStudent;

2. إدراج البيانات

بعد بناء محول map، يمكننا إدراج البيانات فيه. سأتحدث عن ثلاث طرق لتحديث البيانات هنا:

النوع الأول:باستخدام دالة insert إدراج زوج data، إليك مثال على ذلك (الخطوط التالية كانت مكتوبة عشوائياً، يجب أن يتم ترميزها بنجاح في VC و GCC، يمكن للجميع تشغيلها لرؤية التأثير، في VC يرجى إضافة هذه الجملة لتغطية تحذير 4786 #pragma warning (disable:4786))

#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
    Map<int, string> mapStudent;
    mapStudent.insert(pair<int, string>(1, "student_one"));
    mapStudent.insert(pair<int, string>(2, "student_two"));
    mapStudent.insert(pair<int, string>(3, "student_three"));
    iterator<int, string> iter;
    لـfor(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
{
    Cout << iter->first << ”  ” << iter->second << end;
}
}

النوع الثاني:باستخدام دالة insert إدراج قيمة_type

#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
    Map<int, string> mapStudent;
    mapStudent.insert(map<int, string>::value_type (1, “student_one”));
    mapStudent.insert(map<int, string>::value_type (2, “student_two”));
    mapStudent.insert(map<int, string>::value_type (3, “student_three”));
    iterator<int, string> iter;
    لـfor(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
{
    Cout << iter->first << ”  ” << iter->second << end;
}
}

الطريقة الثالثة:إليك مثالاً يوضح كيفية إدراج البيانات باستخدام طريقة الأنواع

#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
    Map<int, string> mapStudent;
    mapStudent[1] = ”student_one”;
    mapStudent[2] = “student_two”;
    mapStudent[3] = ”student_three”;
    iterator<int, string> iter;
    لـfor(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
{
    Cout << iter->first << ”  ” << iter->second << end;
}
}

بالنسبة لثلاثة استخدامات أعلاه، يمكنها جميعًا تحقيق إدراج البيانات، ولكن هناك فرق بينها، وبالطبع، تكون الأوائلتين متشابهتين في التأثير، حيث يتم إدراج البيانات باستخدام دالة insert، حيث يتعلق إدراج البيانات بمفهوم استقلالية مجموعة البيانات، أي عندما يكون لدينا هذا المفتاح في map، لا يمكن إدراج البيانات، ولكن باستخدام طريقة الأنواع يمكن تغطية القيمة السابقة للمفتاح، ويمكن توضيح ذلك عن طريق البرنامج

mapStudent.insert(map<int, string>::value_type (1, “student_one”));
mapStudent.insert(map<int, string>::value_type (1, “student_two”));

بعد تنفيذ هذه السطرين، القيمة المرتبطة بكلمة المفتاح 1 في map هي “student_one”، والسطر الثاني لم يكن فعالًا، لذا فهذا يتعلق بكيفية معرفة ما إذا كان إدراج السطر insert ناجحًا، يمكننا استخدام pair للحصول على نجاح إدراج البيانات، كما في البرنامج التالي

Pair<map<int, string>::iterator, bool> Insert_Pair;
Insert_Pair = mapStudent.insert(map<int, string>::value_type (1, “student_one”));

نستطيع معرفة ما إذا تم إدراج البيانات بنجاح من خلال المثال الثاني من المتغير pair، الذي يعود أول متغير له هو معادلة منحنى map، وإذا تم إدراج البيانات بنجاح، يجب أن يكون Insert_Pair.second صحيحًا، وإلا سيكون خطأً

إليك الآن الكود المكتمل، لشرح كيفية تحديد نجاح إدراج البيانات

#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
    Map<int, string> mapStudent;
Pair<map<int, string>::iterator, bool> Insert_Pair;
    Insert_Pair = mapStudent.insert(pair<int, string>(1, “student_one”));
    If(Insert_Pair.second == true)
    {
       Cout<<”Insert Successfully”<<endl;
    }
    Else
    {
       Cout<<”Insert Failure”<<endl;
    }
    Insert_Pair = mapStudent.insert(pair<int, string>(1, “student_two”));
    If(Insert_Pair.second == true)
    {
       Cout<<”Insert Successfully”<<endl;
    }
    Else
    {
       Cout<<”Insert Failure”<<endl;
    }
    iterator<int, string> iter;
    لـfor(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
{
    Cout << iter->first << ”  ” << iter->second << end;
}
}

يمكن للجميع استخدام البرنامج التالي لرؤية تأثير إدراج الأنواع في تغطية البيانات

#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
    Map<int, string> mapStudent;
    mapStudent[1] = ”student_one”;
    mapStudent[1] = “student_two”;
    mapStudent[2] = “student_three”;
    iterator<int, string> iter;
    لـfor(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
{
    Cout << iter->first << ”  ” << iter->second << end;
}
}

3. حجم map

بعد إدراج البيانات في map، كيف يمكننا معرفة عدد البيانات التي تم إدراجها حالياً؟ يمكن استخدام دالة size، وطرق الاستخدام كالتالي:

Int nSize = mapStudent.size();

4. استعراض البيانات

نقدم هنا ثلاث طرق للاستعراض لـmap

النوع الأول: التطبيق باستخدام م迭代ر متجه، يمكن العثور على هذا في البرنامج المثالي السابق، سيتم تجاوزه

النوع الثاني: التطبيق باستخدام م迭代ر معكوس، سنقدم مثالاً لذلك، لتشعر بالتأثير، من فضلك قم بتنفيذ البرنامج بنفسك

#include <string>
#include <iostream>
Using namespace std;
Int main()
{
    Map<int, string> mapStudent;
    mapStudent.insert(pair<int, string>(1, "student_one"));
    mapStudent.insert(pair<int, string>(2, "student_two"));
    mapStudent.insert(pair<int, string>(3, "student_three"));
    map<int, string>::reverse_iterator iter;
    لـfor(iter = mapStudent.rbegin(); iter != mapStudent.rend(); iter++)
{
    Cout << iter->first << ”  ” << iter->second << end;
}
}

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

#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
    Map<int, string> mapStudent;
    mapStudent.insert(pair<int, string>(1, "student_one"));
    mapStudent.insert(pair<int, string>(2, "student_two"));
    mapStudent.insert(pair<int, string>(3, "student_three"));
    int nSize = mapStudent.size()
//هناك خطأ، يجب أن يكون for(int nIndex = 1; nIndex <= nSize; nIndex++)
//by rainfish
    لـfor(int nIndex = 0; nIndex < nSize; nIndex++)
{
    Cout << mapStudent[nIndex] << end;
}
}

5. البحث عن البيانات (بما في ذلك تحديد ما إذا كانت الكلمة المفتاحية موجودة في map)

في هذا المكان سنشعر بفوائد ضمان الترتيب في إدراج البيانات في map

هناك العديد من الطرق لتحديد ما إذا كانت البيانات (الكلمة المفتاحية) موجودة في map، على الرغم من أن العنوان هو البحث عن البيانات، إلا أننا سنقوم هنا بمزج العديد من الاستخدامات الأساسية لـmap

في هذا المكان نقدم ثلاث طرق للبحث عن البيانات

النوع الأول:باستخدام دالة count للتحديد ما إذا كانت الكلمة المفتاحية تظهر، عيوبها هي أنها لا يمكن تحديد موقع ظهور البيانات، بسبب خصائص map، العلاقة التبادلية الواحدة إلى الواحدة، تجعل قيمة العودة لدالة count تتكون فقط من إثنين، إما 0 أو 1، في الحالة التي تظهر فيها، طبعاً تعود بالـ1

النوع الثاني:باستخدام دالة find للتحديد موقع البيانات التي تظهر، تعود لها م迭代ر، عندما تظهر البيانات، تعود لها دالة الـiterator الموجود في الموقع، إذا لم يكن هناك بيانات لبحث عنها في map، فإن الـiterator الذي تعود به دالة end هو نفسه الذي تعود به دالة end، شرح البرنامج

#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
    Map<int, string> mapStudent;
    mapStudent.insert(pair<int, string>(1, "student_one"));
    mapStudent.insert(pair<int, string>(2, "student_two"));
    mapStudent.insert(pair<int, string>(3, "student_three"));
    iterator<int, string> iter;
    iter = mapStudent.find(1);
if(iter != mapStudent.end())
{
    Cout << ”يجد، القيمة هي ” << iter->second << endl;
}
Else
{
    Cout << ”لا يجد” << endl;
}
}

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

استخدام وظيفة Lower_bound، هذه الوظيفة تعود الحد الأدنى للكلمة المطلوبة (هو معرف)

استخدام وظيفة Upper_bound، هذه الوظيفة تعود الحد الأعلى للكلمة المطلوبة (هو معرف)

على سبيل المثال: إذا تم إدراج 1، 2، 3، 4 في map، فإن lower_bound(2) يعود هو 2، بينما upper-bound(2) يعود هو 3

Equal_range وظيفة تعود زوج، حيث يكون المتغير الأول في الزوج هو معرف الحد الأدنى، ومعرف الثاني هو معرف الحد الأعلى، إذا كانت هاتان المعرفتان متساويتين، فإن ذلك يعني أن هذا الكلمة لا تظهر في map، شرح البرنامج

#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
    Map<int, string> mapStudent;
    mapStudent[1] = ”student_one”;
    mapStudent[3] = ”student_three”;
    mapStudent[5] = ”student_five”;
    iterator<int, string> iter;
iter = mapStudent.lower_bound(2);
{
    //يعود هو معرف الحد الأدنى 3
    Cout << iter->second << endl;
}
iter = mapStudent.lower_bound(3);
{
    //يعود هو معرف الحد الأدنى 3
    Cout << iter->second << endl;
}
iter = mapStudent.upper_bound(2);
{
    //يعود هو معرف الحد الأعلى 3
    Cout << iter->second << endl;
}
iter = mapStudent.upper_bound(3);
{
    //يعود هو معرف الحد الأعلى 5
    Cout << iter->second << endl;
}
Pair<map<int, string>::iterator, map<int, string>::iterator> mapPair;
mapPair = mapStudent.equal_range(2);
if(mapPair.first == mapPair.second)
    {
    cout << ”لا يجد” << endl;
}
Else
{
Cout << "Find" << endl;
}
mapPair = mapStudent.equal_range(3);
if(mapPair.first == mapPair.second)
    {
    cout << ”لا يجد” << endl;
}
Else
{
Cout << "Find" << endl;
}
}

6. إفراغ البيانات والتحقق من الفراغ

يمكن إفراغ بيانات map باستخدام function clear، يمكن التحقق من وجود بيانات في map باستخدام function empty، إذا عادت بالقيمة true فإن ذلك يعني أن map فارغ

7. حذف البيانات

سيتم استخدام function erase، لديها ثلاثة overloads، سأوضح استخداماتها في هذا المثال

#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
    Map<int, string> mapStudent;
    mapStudent.insert(pair<int, string>(1, "student_one"));
    mapStudent.insert(pair<int, string>(2, "student_two"));
    mapStudent.insert(pair<int, string>(3, "student_three"));
//إذا كنت تريد عرض تأثيرات النسخة، يمكنك اختيار واحد من التالي، ستجد أن التأثير جيد
    //إذا كنت تريد حذف 1، استخدم حذف باستخدام iterator
    iterator<int, string> iter;
    iter = mapStudent.find(1);
    mapStudent.erase(iter);
    //إذا كنت تريد حذف 1، استخدم الحذف باستخدام الكلمة المفتاحية
    Int n = mapStudent.erase(1);//سيقوم بالعودة ب1 إذا تم الحذف، وإلا سيقوم بالعودة ب0
    //استخدام iterator لتحذيف قطعة
    //يؤدي الكود التالي إلى إفراغ map بالكامل
    mapStudent.earse(mapStudent.begin(), mapStudent.end());
    //عند إزالة قطعة يجب الانتباه، وهي خاصية STL، إزالة النطاق هي مجموعة مغلقة من الأمام مفتوحة من الخلف
    //أضف كود الاستدعاء، واطبع النتائج
}

8. استخدامات بعض functions

هناك functions مثل swap، key_comp، value_comp، get_allocator، يبدو أن هذه functions ليست تستخدم بكثرة في البرمجة، يمكن تجاوزها، إذا كنت مهتمًا يمكنك البحث فيها بنفسك

9. الترتيب

هناك استخدامات متقدمة يجب التحدث عنها، مشكلة الترتيب، STL يستخدم بشكل افتراضي علامة الافتراض لترتيب، لا يوجد أي مشكلة في الترتيب في الكود أعلاه، لأن الكلمة المفتاحية هي نوع int، يدعم عمليات العلامة الافتراض، في بعض الحالات الخاصة، مثل أن تكون الكلمة المفتاحية هي هيكل، عند الترتيب ستظهر مشكلة، لأنها لا تدعم عمليات العلامة الافتراض، لا يمكن تشغيل functions مثل insert عند التشغيل، سأقدم طريقتين لحل هذه المشكلة

第一种:小于号重载,程序举例

#include <map>
#include <string>
Using namespace std;
Typedef struct tagStudentInfo
{
    Int   nID;
    String  strName;
}StudentInfo, *PStudentInfo; //学生信息
Int main()
{
  int nSize;
    //استخدام معلومات الطالب لتوجيه النقاط
    map<StudentInfo, int>mapStudent;
  map<StudentInfo, int>::iterator iter;
    StudentInfo studentInfo;
    studentInfo.nID = 1;
    studentInfo.strName = “student_one”;
    mapStudent.insert(pair<StudentInfo, int>(studentInfo, 90));
    studentInfo.nID = 2;
    studentInfo.strName = “student_two”;
mapStudent.insert(pair<StudentInfo, int>(studentInfo, 80));
for (iter=mapStudent.begin(); iter!=mapStudent.end(); iter++)
  cout<<iter->first.nID<<endl<<iter->first.strName<<endl<<iter->second<<endl;
}

以上程序是无法编译通过的,只要重载小于号,就OK了,如下:

Typedef struct tagStudentInfo
{
    Int   nID;
    String  strName;
    Bool operator < (tagStudentInfo const& _A) const
    {
       //这个函数指定排序策略,按nID排序,如果nID相等的话,按strName排序
       If(nID < _A.nID) return true;
       If(nID == _A.nID) return strName.compare(_A.strName) < 0;
       Return false;
    }
}StudentInfo, *PStudentInfo; //学生信息

第二种:仿函数的应用,这个时候结构体中没有直接的小于号重载,程序说明

#include <map>
#include <string>
Using namespace std;
Typedef struct tagStudentInfo
{
    Int   nID;
    String  strName;
}StudentInfo, *PStudentInfo; //学生信息
Classs sort
{
    Public:
    Bool operator() (StudentInfo const &_A, StudentInfo const &_B) const
    {
       If(_A.nID < _B.nID) return true;
       If(_A.nID == _B.nID) return _A.strName.compare(_B.strName) < 0;
       Return false;
    }
;
Int main()
{
    //استخدام معلومات الطالب لتوجيه النقاط
    Map<StudentInfo, int, sort>mapStudent;
    StudentInfo studentInfo;
    studentInfo.nID = 1;
    studentInfo.strName = “student_one”;
    mapStudent.insert(pair<StudentInfo, int>(studentInfo, 90));
    studentInfo.nID = 2;
    studentInfo.strName = “student_two”;
mapStudent.insert(pair<StudentInfo, int>(studentInfo, 80));
}

10. بالإضافة إلى ذلك

بما أن STL هو كيان موحد، فإن العديد من استخدامات map تتداخل مع أشياء أخرى في STL، مثل الترتيب، حيث يستخدم هذا البرنامج القياسي العلامة الـ <، إذا كنت ترغب في الترتيب من الأكبر إلى الأصغر، فإن هناك الكثير من الأشياء التي لا يمكن شرحها جميعها هنا.

يجب أن نوضح أيضًا أن العديد من العمليات في map لها معقدية وقتية log2N، بسبب ترتيبها الداخلي الذي يضمنه الشجرة الرمزية، إذا كان بإمكانك تنفيذ وظيفة باستخدام map، فإن STL Algorithm يمكن أن يكمل نفس الوظيفة، نوصي باستخدام وظائف map المدمجة، لأنها أكثر كفاءة.

دعونا نتحدث عن خصائص map في مجال الفضاء، وإلا فإنك قد تشعر بالإحباط في بعض الأحيان، لأن كل بيانات map تتوافق مع نقطة في شجرة الرمزية (red-black tree)، و هذه النقطة تستهلك 16 بيتاً عند عدم حفظ بياناتك، بما في ذلك سلسلة من الإشارات الوراثية، وإشارات الأطفال، وعدد (ممثل للرمزية، مماثل لميزان التوازن في شجرة البينية المتوازنة)، أعتقد أنكم تعرفون أن هذه الأماكن تستخدم الكثير من الذاكرة، لن أتحدث عن ذلك بعد الآن...

وهذا هو كل محتوى الشرح الموجز لمستخدمي STL في C++، آمل أن تؤيدوا وتبشروا بـ دروس النفخ~

الذوق يرشدك