تسمح لك الكائنات بتخزين مجموعات من القيم ذات المفاتيح. هذا جيّد.
ولكن في كثير من الأحيان نجد أننا بحاجة إلى مجموعة مرتبة ، حيث لدينا العنصر الأول والثاني والثالث وما إلى ذلك. على سبيل المثال، نحتاج إلى ذلك لتخزين قائمة بشيء ما: المستخدمين والسلع وعناصر HTML وما إلى ذلك.
ليس من المناسب استخدام كائن هنا، لأنه لا يوفر طرقًا لإدارة ترتيب العناصر. لا يمكننا إدراج خاصية جديدة "بين" الخصائص الموجودة. الكائنات ليست مخصصة لمثل هذا الاستخدام.
توجد بنية بيانات خاصة تسمى Array
لتخزين المجموعات المطلوبة.
هناك صيغتان لإنشاء مصفوفة فارغة:
Let arr = new Array(); دع arr = [];
في كل الأوقات تقريبًا، يتم استخدام بناء الجملة الثاني. يمكننا توفير العناصر الأولية بين قوسين:
Let Fruits = ["Apple"، "Orange"، "Plum"]؛
يتم ترقيم عناصر المصفوفة بدءًا من الصفر.
يمكننا الحصول على العنصر من خلال رقمه بين قوسين مربعين:
Let Fruits = ["Apple"، "Orange"، "Plum"]؛ تنبيه(الفواكه[0]); // تفاحة تنبيه(الفواكه[1]); // البرتقالي تنبيه(الفواكه[2]); // البرقوق
يمكننا استبدال عنصر:
Fruits[2] = 'كمثرى'; // الآن ["Apple"، "Orange"، "Pear"]
…أو قم بإضافة واحدة جديدة إلى المصفوفة:
Fruits[3] = 'ليمون'; // الآن ["تفاح"، "برتقالي"، "كمثرى"، "ليمون"]
العدد الإجمالي للعناصر في المصفوفة هو length
:
Let Fruits = ["Apple"، "Orange"، "Plum"]؛ تنبيه (الثمار. الطول)؛ // 3
يمكننا أيضًا استخدام alert
لإظهار المصفوفة بأكملها.
Let Fruits = ["Apple"، "Orange"، "Plum"]؛ تنبيه (الفواكه) ؛ // أبل، أورانج، بلوم
يمكن للمصفوفة تخزين العناصر من أي نوع.
على سبيل المثال:
// مزيج من القيم Let arr = [ 'Apple', { name: 'John' }, true, function() { تنبيه('hello'); } ]; // احصل على الكائن في الفهرس 1 ثم أظهر اسمه تنبيه (arr[1].name ); // جون // احصل على الوظيفة في الفهرس 3 وقم بتشغيلها arr[3](); // مرحبًا
فاصلة زائدة
المصفوفة، مثل الكائن، قد تنتهي بفاصلة:
دع الفواكه = [ "تفاحة"، "البرتقالي"، "البرقوق"، ];
يسهل نمط "الفاصلة الزائدة" إدراج/إزالة العناصر، لأن جميع الأسطر تصبح متشابهة.
إضافة حديثة
هذه إضافة حديثة للغة. قد تحتاج المتصفحات القديمة إلى عمليات تعبئة متعددة.
لنفترض أننا نريد العنصر الأخير في المصفوفة.
تسمح بعض لغات البرمجة باستخدام الفهارس السالبة لنفس الغرض، مثل fruits[-1]
.
على الرغم من أنه في جافا سكريبت لن يعمل. ستكون النتيجة undefined
، لأن الفهرس الموجود بين قوسين مربعين يتم التعامل معه حرفيًا.
يمكننا حساب فهرس العنصر الأخير بشكل صريح ومن ثم الوصول إليه: fruits[fruits.length - 1]
.
Let Fruits = ["Apple"، "Orange"، "Plum"]؛ تنبيه(الفواكه[الفواكه.الطول-1]); // البرقوق
مرهقة بعض الشيء، أليس كذلك؟ نحن بحاجة إلى كتابة اسم المتغير مرتين.
لحسن الحظ، هناك صيغة أقصر: fruits.at(-1)
:
Let Fruits = ["Apple"، "Orange"، "Plum"]؛ // مثل الفواكه[fruits.length-1] تنبيه(fruits.at(-1)); // البرقوق
بمعنى آخر، arr.at(i)
:
هو تمامًا نفس arr[i]
، إذا i >= 0
.
بالنسبة للقيم السالبة لـ i
، فإنه يتراجع عن نهاية المصفوفة.
تعد قائمة الانتظار أحد الاستخدامات الأكثر شيوعًا للمصفوفة. في علوم الكمبيوتر، يعني ذلك مجموعة مرتبة من العناصر التي تدعم عمليتين:
push
يقوم بإلحاق عنصر بالنهاية.
shift
احصل على عنصر من البداية، مما يؤدي إلى تقدم قائمة الانتظار، بحيث يصبح العنصر الثاني هو الأول.
المصفوفات تدعم كلتا العمليتين.
في الممارسة العملية، نحن في حاجة إليها في كثير من الأحيان. على سبيل المثال، قائمة انتظار من الرسائل التي يجب عرضها على الشاشة.
هناك حالة استخدام أخرى للمصفوفات - بنية البيانات المسماة المكدس.
وهو يدعم عمليتين:
يضيف push
عنصرًا إلى النهاية.
يأخذ pop
عنصرا من النهاية.
لذلك تتم إضافة عناصر جديدة أو أخذها دائمًا من "النهاية".
عادةً ما يتم توضيح المكدس كحزمة من البطاقات: تتم إضافة بطاقات جديدة إلى الأعلى أو مأخوذة من الأعلى:
بالنسبة للمكدسات، يتم استلام العنصر المدفوع الأخير أولاً، وهذا ما يسمى أيضًا مبدأ LIFO (آخر ما يدخل أولاً يخرج). بالنسبة لقوائم الانتظار، لدينا FIFO (أولاً يدخل أولاً يخرج أولاً).
يمكن للمصفوفات في JavaScript أن تعمل كقائمة انتظار ومكدس. إنها تسمح لك بإضافة/إزالة العناصر، من/إلى البداية أو النهاية.
في علوم الكمبيوتر، تسمى بنية البيانات التي تسمح بذلك، deque.
الطرق التي تعمل مع نهاية المصفوفة:
pop
يستخرج العنصر الأخير من المصفوفة ويعيده:
Let Fruits = ["Apple"، "Orange"، "Pear"]؛ تنبيه(fruits.pop()); // قم بإزالة "الكمثرى" وقم بتنبيهها تنبيه (الفواكه) ؛ // أبل، أورانج
يقوم كل من fruits.pop()
و fruits.at(-1)
بإرجاع العنصر الأخير في المصفوفة، لكن fruits.pop()
يقوم أيضًا بتعديل المصفوفة عن طريق إزالتها.
push
إلحاق العنصر بنهاية المصفوفة:
Let Fruits = ["Apple"، "Orange"]؛ Fruits.push("الكمثرى"); تنبيه (الفواكه) ؛ // التفاح، البرتقال، الكمثرى
استدعاء fruits.push(...)
يساوي fruits[fruits.length] = ...
.
الطرق التي تعمل مع بداية المصفوفة:
shift
يستخرج العنصر الأول من المصفوفة ويعيده:
Let Fruits = ["Apple"، "Orange"، "Pear"]؛ تنبيه(fruits.shift()); // قم بإزالة Apple ونبهها تنبيه (الفواكه) ؛ // البرتقال، الكمثرى
unshift
أضف العنصر إلى بداية المصفوفة:
Let Fruits = ["برتقالي"، "كمثرى"]؛ Fruits.unshift('أبل'); تنبيه (الفواكه) ؛ // التفاح، البرتقال، الكمثرى
يمكن لطرق push
unshift
إضافة عناصر متعددة في وقت واحد:
دع الفواكه = ["تفاحة"]؛ Fruits.push("برتقالي", "خوخ"); Fruits.unshift("Pineapple", "Lemon"); // ["أناناس"، "ليمون"، "تفاح"، "برتقالي"، "خوخ"] تنبيه (الفواكه) ؛
المصفوفة هي نوع خاص من الكائنات. الأقواس المربعة المستخدمة للوصول إلى الخاصية arr[0]
تأتي في الواقع من بناء جملة الكائن. هذا هو في الأساس نفس obj[key]
، حيث arr
هو الكائن، بينما يتم استخدام الأرقام كمفاتيح.
إنها توسع الكائنات وتوفر طرقًا خاصة للعمل مع مجموعات البيانات المرتبة وكذلك خاصية length
. ولكن في جوهره لا يزال كائنًا.
تذكر أنه لا يوجد سوى ثمانية أنواع بيانات أساسية في JavaScript (راجع فصل أنواع البيانات لمزيد من المعلومات). المصفوفة هي كائن وبالتالي تتصرف ككائن.
على سبيل المثال، يتم نسخه حسب المرجع:
دع الفواكه = ["الموز"] دع آر = الفواكه؛ // نسخ حسب المرجع (متغيران يشيران إلى نفس المصفوفة) تنبيه (arr === ثمار) ؛ // حقيقي arr.push("الكمثرى"); // تعديل المصفوفة حسب المرجع تنبيه (الفواكه) ؛ // الموز، الكمثرى - 2 العناصر الآن
…ولكن ما يجعل المصفوفات مميزة حقًا هو تمثيلها الداخلي. يحاول المحرك تخزين عناصره في منطقة الذاكرة المتجاورة، واحدًا تلو الآخر، تمامًا كما هو موضح في الرسوم التوضيحية في هذا الفصل، وهناك تحسينات أخرى أيضًا، لجعل المصفوفات تعمل بسرعة كبيرة.
لكنها جميعًا تنقطع إذا توقفنا عن العمل مع المصفوفة كما هو الحال مع "المجموعة المرتبة" وبدأنا العمل معها كما لو كانت كائنًا عاديًا.
على سبيل المثال، من الناحية الفنية يمكننا القيام بذلك:
دع الفواكه = []؛ // اصنع مصفوفة الفواكه[99999] = 5؛ // قم بتعيين خاصية بفهرس أكبر بكثير من طولها عمر الفاكهة = 25؛ // إنشاء خاصية باسم عشوائي
هذا ممكن، لأن المصفوفات هي كائنات في قاعدتها. يمكننا إضافة أي خصائص لهم.
لكن المحرك سيرى أننا نعمل مع المصفوفة كما هو الحال مع كائن عادي. التحسينات الخاصة بالمصفوفة ليست مناسبة لمثل هذه الحالات وسيتم إيقاف تشغيلها، وتختفي فوائدها.
طرق إساءة استخدام المصفوفة:
أضف خاصية غير رقمية مثل arr.test = 5
.
اصنع ثقوبًا، مثل: أضف arr[0]
ثم arr[1000]
(ولا يوجد شيء بينهما).
املأ المصفوفة بترتيب عكسي، مثل arr[1000]
و arr[999]
وما إلى ذلك.
من فضلك فكر في المصفوفات باعتبارها هياكل خاصة للتعامل مع البيانات المطلوبة . أنها توفر أساليب خاصة لذلك. يتم ضبط المصفوفات بعناية داخل محركات JavaScript للعمل مع البيانات المرتبة المتجاورة، يرجى استخدامها بهذه الطريقة. وإذا كنت بحاجة إلى مفاتيح عشوائية، فمن المحتمل جدًا أنك تحتاج بالفعل إلى كائن عادي {}
.
تعمل طرق push/pop
بسرعة، بينما تكون shift/unshift
بطيئة.
لماذا يكون العمل مع نهاية المصفوفة أسرع من العمل مع بدايتها؟ دعونا نرى ما يحدث أثناء التنفيذ:
Fruits.shift(); // خذ عنصرًا واحدًا من البداية
لا يكفي أخذ العنصر وإزالته باستخدام الفهرس 0
. وتحتاج العناصر الأخرى إلى إعادة ترقيمها أيضاً.
يجب أن تقوم عملية shift
بثلاثة أشياء:
قم بإزالة العنصر الذي يحتوي على الفهرس 0
.
انقل جميع العناصر إلى اليسار، وأعد ترقيمها من الفهرس 1
إلى 0
، ومن 2
إلى 1
، وهكذا.
قم بتحديث خاصية length
.
كلما زاد عدد العناصر في المصفوفة، زاد الوقت لنقلها، والمزيد من العمليات في الذاكرة.
يحدث الشيء نفسه مع unshift
: لإضافة عنصر إلى بداية المصفوفة، نحتاج أولاً إلى نقل العناصر الموجودة إلى اليمين، وزيادة فهارسها.
وما هو مع push/pop
؟ لا يحتاجون إلى تحريك أي شيء. لاستخراج عنصر من النهاية، يقوم التابع pop
بتنظيف الفهرس وتقصير length
.
إجراءات عملية pop
:
Fruits.pop(); // خذ عنصرًا واحدًا من النهاية
لا تحتاج طريقة pop
إلى نقل أي شيء، لأن العناصر الأخرى تحتفظ بفهرساتها. ولهذا السبب فهو سريع للغاية.
الشيء نفسه مع طريقة push
.
إحدى أقدم الطرق لدورة عناصر المصفوفة هي حلقة for
فوق الفهارس:
Let arr = ["Apple"، "Orange"، "Pear"]؛ لـ (دع i = 0; i < arr.length; i++) { تنبيه (arr[i])؛ }
ولكن بالنسبة للمصفوفات هناك شكل آخر من أشكال الحلقة، for..of
:
Let Fruits = ["Apple"، "Orange"، "Plum"]؛ // يتكرر على عناصر المصفوفة لـ (فليثمر من الثمرات) { تنبيه (الفاكهة)؛ }
لا يتيح for..of
الوصول إلى رقم العنصر الحالي، بل إلى قيمته فقط، ولكن هذا يكفي في معظم الحالات. وهي أقصر.
من الناحية الفنية، نظرًا لأن المصفوفات عبارة عن كائنات، فمن الممكن أيضًا استخدامها for..in
:
Let arr = ["Apple"، "Orange"، "Pear"]؛ لـ (دع المفتاح يدخل) { تنبيه (arr[مفتاح])؛ // التفاح، البرتقال، الكمثرى }
ولكن هذه في الواقع فكرة سيئة. هناك مشاكل محتملة معها:
تتكرر الحلقة for..in
على جميع الخصائص ، وليس فقط الخصائص الرقمية.
هناك ما يسمى بالكائنات "الشبيهة بالمصفوفات" في المتصفح وفي البيئات الأخرى، والتي تبدو مثل المصفوفات . أي أنها تحتوي على خصائص length
والفهارس، ولكن قد يكون لها أيضًا خصائص وطرق أخرى غير رقمية، والتي لا نحتاج إليها عادةً. بالرغم من ذلك، فإن حلقة for..in
ستدرجها. لذلك، إذا كنا بحاجة إلى العمل مع كائنات تشبه المصفوفة، فيمكن أن تصبح هذه الخصائص "الإضافية" مشكلة.
تم تحسين حلقة for..in
للكائنات العامة، وليس المصفوفات، وبالتالي فهي أبطأ بمقدار 10-100 مرة. وبطبيعة الحال، فإنه لا يزال سريعا جدا. قد يكون التسريع مهمًا فقط في الاختناقات. ولكن لا يزال يتعين علينا أن ندرك الفرق.
بشكل عام، لا يجب أن نستخدم for..in
للمصفوفات.
يتم تحديث خاصية length
تلقائيًا عندما نقوم بتعديل المصفوفة. على وجه الدقة، فهو في الواقع ليس عدد القيم في المصفوفة، ولكن أكبر مؤشر رقمي زائد واحد.
على سبيل المثال، عنصر واحد ذو فهرس كبير يعطي طولًا كبيرًا:
دع الفواكه = []؛ Fruits[123] = "Apple"; تنبيه (الثمار. الطول)؛ // 124
لاحظ أننا عادة لا نستخدم المصفوفات من هذا القبيل.
شيء آخر مثير للاهتمام حول خاصية length
هو أنها قابلة للكتابة.
إذا قمنا بزيادته يدويًا، فلن يحدث شيء مثير للاهتمام. ولكن إذا قمنا بتقليلها، فسيتم قطع المصفوفة. هذه العملية لا رجعة فيها، إليك المثال:
دع arr = [1, 2, 3, 4, 5]; طول = 2; // اقتطاع إلى عنصرين تنبيه(آر); // [1، 2] طول = 5; // طول العودة مرة أخرى تنبيه(ar[3]); // غير محدد: لا يتم إرجاع القيم
لذا، فإن أبسط طريقة لمسح المصفوفة هي: arr.length = 0;
.
هناك بناء جملة آخر لإنشاء مصفوفة:
Let arr = new Array("Apple"، "Pear"، "etc");
ونادرا ما يستخدم، لأن الأقواس المربعة []
أقصر. أيضا، هناك ميزة صعبة معها.
إذا تم استدعاء new Array
باستخدام وسيطة واحدة عبارة عن رقم، فسيتم إنشاء مصفوفة بدون عناصر، ولكن بالطول المحدد .
دعونا نرى كيف يمكن للمرء أن يطلق النار على قدمه:
Let arr = new Array(2); // هل سيتم إنشاء مصفوفة من [2]؟ تنبيه (arr[0])؛ // غير محدد! لا عناصر. تنبيه (arr.length)؛ // الطول 2
لتجنب مثل هذه المفاجآت، نستخدم عادةً الأقواس المربعة، إلا إذا كنا نعرف حقًا ما نفعله.
يمكن أن تحتوي المصفوفات على عناصر هي أيضًا صفائف. يمكننا استخدامه للمصفوفات متعددة الأبعاد، على سبيل المثال لتخزين المصفوفات:
دع المصفوفة = [ [1، 2، 3]، [4، 5، 6]، [7، 8، 9] ]; تنبيه (مصفوفة [0] [1])؛ // 2، القيمة الثانية للمصفوفة الداخلية الأولى
المصفوفات لها تطبيقها الخاص لطريقة toString
التي تُرجع قائمة عناصر مفصولة بفواصل.
على سبيل المثال:
دع arr = [1, 2, 3]; تنبيه(آر); // 1,2,3 تنبيه( String(arr) === '1,2,3' ); // حقيقي
أيضاً، دعونا نجرب هذا:
تنبيه( [] + 1 ); // "1" تنبيه( [1] + 1 ); // "11" تنبيه( [1,2] + 1 ); // "1,21"
لا تحتوي المصفوفات على Symbol.toPrimitive
.toPrimitive، ولا valueOf
قابلة للتطبيق، فهي تنفذ تحويل toString
فقط، لذا هنا يصبح []
سلسلة فارغة، و [1]
يصبح "1"
و [1,2]
يصبح "1,2"
.
عندما يضيف عامل الجمع الثنائي "+"
شيئًا ما إلى سلسلة، فإنه يحوله إلى سلسلة أيضًا، وبالتالي تبدو الخطوة التالية كما يلي:
تنبيه(""+1); // "1" تنبيه("1"+1); // "11" تنبيه("1,2"+1); // "1,21"
لا ينبغي مقارنة المصفوفات في JavaScript، على عكس بعض لغات البرمجة الأخرى، مع عامل التشغيل ==
.
ليس لدى هذا العامل معاملة خاصة للمصفوفات، فهو يعمل معها كما هو الحال مع أي كائنات.
لنتذكر القواعد:
هناك كائنان متساويان ==
فقط إذا كانا يشيران إلى نفس الكائن.
إذا كانت إحدى وسيطات ==
كائنًا، والأخرى بدائية، فسيتم تحويل الكائن إلى بدائي، كما هو موضح في الفصل من كائن إلى تحويل بدائي.
…باستثناء القيمة null
undefined
التي تساوي ==
بعضها البعض ولا شيء آخر.
المقارنة الصارمة ===
أبسط من ذلك، لأنها لا تقوم بتحويل الأنواع.
لذا، إذا قارنا المصفوفات بـ ==
، فلن تكون متماثلة أبدًا، إلا إذا قارنا متغيرين يشيران إلى نفس المصفوفة تمامًا.
على سبيل المثال:
تنبيه( [] == [] ); // خطأ شنيع تنبيه( [0] == [0] ); // خطأ شنيع
هذه المصفوفات هي كائنات مختلفة من الناحية الفنية. لذا فهم ليسوا متساوين. لا يقوم عامل التشغيل ==
بإجراء مقارنة لكل عنصر على حدة.
المقارنة مع البدائيين قد تعطي نتائج تبدو غريبة أيضًا:
تنبيه( 0 == [] ); // حقيقي تنبيه('0' == [] ); // خطأ شنيع
هنا، في كلتا الحالتين، نقارن كائنًا بدائيًا بكائن مصفوفة. لذلك يتم تحويل المصفوفة []
إلى مصفوفة بدائية لغرض المقارنة وتصبح سلسلة فارغة ''
.
ثم تستمر عملية المقارنة مع الأوليات، كما هو موضح في فصل تحويلات النوع:
// بعد تحويل [] إلى '' تنبيه(0 == ''); // صحيح، عندما يتحول '' إلى الرقم 0 تنبيه('0' == '' ); // خطأ، لا يوجد تحويل للنوع، سلاسل مختلفة
إذن، كيف يمكن مقارنة المصفوفات؟
الأمر بسيط: لا تستخدم عامل التشغيل ==
. بدلاً من ذلك، قم بمقارنتها عنصرًا بعنصر في حلقة أو باستخدام طرق التكرار الموضحة في الفصل التالي.
المصفوفة هي نوع خاص من الكائنات، مناسب لتخزين عناصر البيانات المطلوبة وإدارتها.
الإعلان:
// قوسين مربعين (عادي) Let arr = [item1, item2...]; // مصفوفة جديدة (نادرة بشكل استثنائي) Let arr = new Array(item1, item2...);
يؤدي استدعاء new Array(number)
إلى إنشاء مصفوفة بالطول المحدد، ولكن بدون عناصر.
خاصية length
هي طول المصفوفة، أو على وجه الدقة، فهرسها الرقمي الأخير بالإضافة إلى واحد. يتم ضبطه تلقائيًا بواسطة طرق الصفيف.
إذا قمنا بتقصير length
يدويًا، فسيتم اقتطاع المصفوفة.
الحصول على العناصر:
يمكننا الحصول على العنصر من خلال فهرسه، مثل arr[0]
يمكننا أيضًا استخدام طريقة at(i)
التي تسمح بالفهارس السالبة. بالنسبة للقيم السالبة لـ i
، فإنه يتراجع من نهاية المصفوفة. إذا i >= 0
، فإنه يعمل بنفس طريقة arr[i]
.
يمكننا استخدام المصفوفة كـ deque مع العمليات التالية:
push(...items)
يضيف items
إلى النهاية.
يقوم pop()
بإزالة العنصر من النهاية وإعادته.
يقوم shift()
بإزالة العنصر من البداية وإعادته.
unshift(...items)
بإضافة items
إلى البداية.
للتكرار فوق عناصر المصفوفة:
for (let i=0; i<arr.length; i++)
- يعمل بشكل أسرع، وهو متوافق مع المتصفحات القديمة.
for (let item of arr)
– الصيغة الحديثة للعناصر فقط،
for (let i in arr)
- لا تستخدم أبدًا.
لمقارنة المصفوفات، لا تستخدم عامل التشغيل ==
(بالإضافة إلى >
و <
وغيرها)، حيث أنه ليس لديهم معاملة خاصة للمصفوفات. إنهم يتعاملون معها كأي أشياء، وهذا ليس ما نريده عادة.
بدلًا من ذلك، يمكنك استخدام حلقة for..of
لمقارنة المصفوفات عنصرًا بعنصر.
سنستمر في دراسة المصفوفات ودراسة المزيد من الطرق لإضافة العناصر وإزالتها واستخراجها وفرز المصفوفات في الفصل التالي طرق المصفوفات.
الأهمية: 3
ما الذي سيظهره هذا الرمز؟
Let Fruits = ["Apples"، "Pear"، "Orange"]؛ // ادفع قيمة جديدة إلى "النسخة" دع عربة التسوق = الفواكه؛ ShoppingCart.push("الموز"); // ماذا يوجد في الفواكه؟ تنبيه (الثمار. الطول)؛ // ؟
النتيجة هي 4
:
Let Fruits = ["Apples"، "Pear"، "Orange"]؛ دع عربة التسوق = الفواكه؛ ShoppingCart.push("الموز"); تنبيه (الثمار. الطول)؛ // 4
ذلك لأن المصفوفات هي كائنات. لذا فإن كلا من shoppingCart
fruits
هما مرجعان لنفس المصفوفة.
الأهمية: 5
دعونا نجرب 5 عمليات صفيف.
قم بإنشاء مصفوفة من styles
تحتوي على العناصر "جاز" و"بلوز".
ألحق "موسيقى الروك أند رول" بالنهاية.
استبدل القيمة الموجودة في المنتصف بـ "الكلاسيكيات". يجب أن يعمل الكود الخاص بك للعثور على القيمة الوسطى مع أي صفائف ذات طول فردي.
قم بإزالة القيمة الأولى للمصفوفة وأظهرها.
أضف Rap
Reggae
مسبقًا إلى المجموعة.
المصفوفة في العملية:
موسيقى الجاز والبلوز موسيقى الجاز، البلوز، الروك أند رول موسيقى الجاز، والكلاسيكية، والروك أند رول الكلاسيكيات، والروك أند رول الراب، الريغي، الكلاسيكيات، الروك أند رول
Let style = ["Jazz"، "Blues"]؛ style.push("روك أند رول"); styles[Math.floor((styles.length - 1) / 2)] = "كلاسيكيات"; تنبيه(styles.shift()); style.unshift("راب"، "الريغي")؛
الأهمية: 5
ما هي النتيجة؟ لماذا؟
Let arr = ["a"، "b"]؛ arr.push(وظيفة() { تنبيه(هذا); }); arr[2](); // ؟
النداء arr[2]()
هو من الناحية النحوية النداء القديم الجيد obj[method]()
، في دور obj
لدينا arr
، وفي دور method
لدينا 2
.
لذلك لدينا استدعاء الدالة arr[2]
كطريقة كائن. وبطبيعة الحال، فإنه يتلقى this
المرجع للكائن arr
ويخرج المصفوفة:
Let arr = ["a"، "b"]؛ arr.push(وظيفة() { تنبيه(هذا); }) arr[2](); // أ، ب، الدالة (){...}
يحتوي المصفوفة على ثلاث قيم: في البداية كان يحتوي على قيمتين، بالإضافة إلى الوظيفة.
الأهمية: 4
اكتب الدالة sumInput()
التي:
يطلب من المستخدم القيم باستخدام prompt
ويخزن القيم في المصفوفة.
ينتهي من السؤال عندما يُدخل المستخدم قيمة غير رقمية، أو سلسلة فارغة، أو يضغط على "إلغاء".
حساب وإرجاع مجموع عناصر الصفيف.
ملاحظة: الصفر 0
هو رقم صالح، يرجى عدم إيقاف الإدخال عند الصفر.
قم بتشغيل العرض التوضيحي
يرجى ملاحظة التفاصيل الدقيقة ولكن المهمة للحل. لا نقوم بتحويل value
إلى رقم فورًا بعد prompt
، لأنه بعد value = +value
لن نكون قادرين على التمييز بين سلسلة فارغة (علامة التوقف) من الصفر (رقم صالح). نحن نفعل ذلك لاحقا بدلا من ذلك.
دالة مجموع الإدخال () { دع الأرقام = []؛ بينما (صحيح) { Let value = موجه("رقم من فضلك؟", 0); // هل يجب علينا الإلغاء؟ if (value === "" || value === null || !isFinite(value))break; number.push(+value); } دع المبلغ = 0؛ لـ (دع عدد الأرقام) { مجموع += رقم؛ } مبلغ الإرجاع؛ } تنبيه (مجموع الإدخال ())؛
الأهمية: 2
الإدخال عبارة عن مصفوفة من الأرقام، على سبيل المثال arr = [1, -2, 3, 4, -9, 6]
.
المهمة هي: العثور على المصفوفة الفرعية المتجاورة لـ arr
مع الحد الأقصى لمجموع العناصر.
اكتب الدالة getMaxSubSum(arr)
التي ستعيد هذا المجموع.
على سبيل المثال:
getMaxSubSum([-1, 2, 3, -9]) == 5 (مجموع العناصر المميزة) getMaxSubSum([2, -1, 2, 3, -9]) == 6 getMaxSubSum([-1, 2, 3, -9, 11]) == 11 getMaxSubSum([-2, -1, 1, 2]) == 3 getMaxSubSum([100, -9, 2, -3, 5]) == 100 getMaxSubSum([1, 2, 3]) == 6 (خذ الكل)
إذا كانت جميع العناصر سالبة، فهذا يعني أننا لم نأخذ شيئًا (المصفوفة الفرعية فارغة)، وبالتالي يكون المجموع صفرًا:
getMaxSubSum([-1, -2, -3]) = 0
من فضلك حاول التفكير في حل سريع: O(n 2 ) أو حتى O(n) إذا استطعت.
افتح صندوق الرمل مع الاختبارات.
يمكننا حساب جميع المجاميع الفرعية الممكنة.
إن أبسط طريقة هي أخذ كل عنصر وحساب مجموع كل المصفوفات الفرعية بدءًا منه.
على سبيل المثال، بالنسبة إلى [-1, 2, 3, -9, 11]
:
// بدءًا من -1: -1 -1+2 -1 + 2 + 3 -1 + 2 + 3 + (-9) -1 + 2 + 3 + (-9) + 11 // ابتداء من 2: 2 2 + 3 2 + 3 + (-9) 2 + 3 + (-9) + 11 // ابتداء من 3: 3 3 + (-9) 3 + (-9) + 11 // ابتداء من -9 -9 -9 + 11 // ابتداء من 11 11
الكود هو في الواقع حلقة متداخلة: الحلقة الخارجية فوق عناصر المصفوفة، والحسابات الداخلية تبدأ بالعنصر الحالي.
الدالة getMaxSubSum(arr) { دع maxSum = 0؛ // إذا لم نأخذ أي عنصر، فسيتم إرجاع الصفر لـ (دع i = 0; i < arr.length; i++) { دع sumFixedStart = 0; لـ (دع j = i; j < arr.length; j++) { sumFixedStart += arr[j]; maxSum = Math.max(maxSum, sumFixedStart); } } إرجاع الحد الأقصى؛ } تنبيه( getMaxSubSum([-1, 2, 3, -9]) ); // 5 تنبيه( getMaxSubSum([-1, 2, 3, -9, 11])); // 11 تنبيه( getMaxSubSum([-2, -1, 1, 2])); // 3 تنبيه( getMaxSubSum([1, 2, 3]) ); // 6 تنبيه( getMaxSubSum([100, -9, 2, -3, 5])); // 100
الحل له تعقيد زمني O(n 2 ). بمعنى آخر، إذا قمنا بزيادة حجم المصفوفة مرتين، فستعمل الخوارزمية لفترة أطول بأربع مرات.
بالنسبة للمصفوفات الكبيرة (1000، 10000 أو أكثر من العناصر) يمكن أن تؤدي هذه الخوارزميات إلى تباطؤ خطير.
دعونا نسير في المصفوفة ونحتفظ بالمجموع الجزئي الحالي للعناصر في المتغير s
. إذا أصبحت s
سالبة في مرحلة ما، فقم بتعيين s=0
. سيكون الحد الأقصى لكل s
هو الجواب.
إذا كان الوصف غامضًا للغاية، فيرجى الاطلاع على الكود، فهو قصير بدرجة كافية:
الدالة getMaxSubSum(arr) { دع maxSum = 0؛ دع المجموع الجزئي = 0؛ for (let item of arr) { // لكل عنصر من عناصر arr PartialSum += item; // أضفه إلى المجموع الجزئي maxSum = Math.max(maxSum, PartialSum); // تذكر الحد الأقصى إذا (المجموع الجزئي < 0) المجموع الجزئي = 0؛ // صفر إذا كان سلبيا } إرجاع الحد الأقصى؛ } تنبيه( getMaxSubSum([-1, 2, 3, -9]) ); // 5 تنبيه( getMaxSubSum([-1, 2, 3, -9, 11])); // 11 تنبيه( getMaxSubSum([-2, -1, 1, 2])); // 3 تنبيه( getMaxSubSum([100, -9, 2, -3, 5])); // 100 تنبيه( getMaxSubSum([1, 2, 3]) ); // 6 تنبيه( getMaxSubSum([-1, -2, -3]) ); // 0
تتطلب الخوارزمية تمريرة مصفوفة واحدة بالضبط، وبالتالي فإن التعقيد الزمني هو O(n).
يمكنك العثور على مزيد من المعلومات التفصيلية حول الخوارزمية هنا: الحد الأقصى لمشكلة المصفوفة الفرعية. إذا كان سبب نجاح ذلك لا يزال غير واضح، فيرجى تتبع الخوارزمية في الأمثلة أعلاه، ومعرفة كيفية عملها، فهذا أفضل من أي كلمات.
افتح الحل بالاختبارات في وضع الحماية.