ฉันใช้รายการและอาร์เรย์จำนวนมาก แต่ฉันยังไม่เจอสถานการณ์ที่รายการอาร์เรย์ไม่สามารถใช้งานได้ง่ายเหมือนกับถ้าไม่ง่ายกว่ารายการที่เชื่อมโยง ฉันหวังว่าจะมีบางคนยกตัวอย่างบางอย่างให้ฉันเมื่อรายการลิงก์เชื่อมโยงดีกว่าอย่างเห็นได้ชัด
ฉันใช้รายการและอาร์เรย์จำนวนมาก แต่ฉันยังไม่เจอสถานการณ์ที่รายการอาร์เรย์ไม่สามารถใช้งานได้ง่ายเหมือนกับถ้าไม่ง่ายกว่ารายการที่เชื่อมโยง ฉันหวังว่าจะมีบางคนยกตัวอย่างบางอย่างให้ฉันเมื่อรายการลิงก์เชื่อมโยงดีกว่าอย่างเห็นได้ชัด
คำตอบ:
รายการที่เชื่อมโยงจะดีกว่าอาร์เรย์เมื่อ:
คุณต้องการการแทรก / ลบแบบคงที่จากรายการ (เช่นในการคำนวณแบบเรียลไทม์ซึ่งการคาดการณ์เวลามีความสำคัญอย่างยิ่ง)
คุณไม่รู้ว่าจะมีกี่รายการในรายการ ด้วยอาร์เรย์คุณอาจต้องประกาศใหม่และคัดลอกหน่วยความจำหากอาร์เรย์ใหญ่เกินไป
คุณไม่จำเป็นต้องเข้าถึงองค์ประกอบใด ๆ แบบสุ่ม
คุณต้องการแทรกรายการที่อยู่ตรงกลางของรายการ (เช่นคิวลำดับความสำคัญ)
อาร์เรย์จะดีกว่าเมื่อ:
คุณต้องเข้าถึงการจัดทำดัชนี / การสุ่มองค์ประกอบ
คุณทราบจำนวนองค์ประกอบในอาร์เรย์ล่วงหน้าเพื่อให้คุณสามารถจัดสรรจำนวนหน่วยความจำที่ถูกต้องสำหรับอาร์เรย์
คุณต้องการความเร็วเมื่อวนซ้ำองค์ประกอบทั้งหมดตามลำดับ คุณสามารถใช้ตัวชี้คณิตศาสตร์บนอาเรย์เพื่อเข้าถึงแต่ละองค์ประกอบในขณะที่คุณต้องค้นหาโหนดตามตัวชี้สำหรับแต่ละองค์ประกอบในรายการที่เชื่อมโยงซึ่งอาจส่งผลให้เกิดข้อบกพร่องของหน้าซึ่งอาจส่งผลให้ประสิทธิภาพการทำงาน
หน่วยความจำคือความกังวล อาร์เรย์ที่เต็มไปนั้นใช้หน่วยความจำน้อยกว่ารายการที่ลิงก์ องค์ประกอบในอาร์เรย์แต่ละตัวเป็นเพียงข้อมูล โหนดรายการที่ลิงก์แต่ละโหนดต้องการข้อมูลรวมถึงพอยน์เตอร์หนึ่งตัว (หรือมากกว่า) ไปยังองค์ประกอบอื่น ๆ ในรายการที่เชื่อมโยง
รายการ Array (เช่นเดียวกับใน. Net) ให้คุณได้รับประโยชน์จากอาร์เรย์ แต่จัดสรรทรัพยากรให้คุณแบบไดนามิกเพื่อให้คุณไม่ต้องกังวลเกี่ยวกับขนาดของรายการมากเกินไปและคุณสามารถลบรายการที่ดัชนีใด ๆ โดยไม่ต้องใช้ความพยายามใด ๆ องค์ประกอบสับรอบ ๆ ผลการปฏิบัติงานที่ชาญฉลาด arraylists จะช้ากว่าอาเรย์ดิบ
อาร์เรย์มีการเข้าถึงแบบสุ่ม O (1) แต่มีราคาแพงมากในการเพิ่มข้อมูลเข้าหรือลบข้อมูลออก
รายการที่เชื่อมโยงนั้นมีราคาถูกมากเมื่อต้องการเพิ่มหรือลบรายการทุกที่และไปซ้ำ แต่การเข้าถึงแบบสุ่มคือ O (n)
Algorithm ArrayList LinkedList
seek front O(1) O(1)
seek back O(1) O(1)
seek to index O(1) O(N)
insert at front O(N) O(1)
insert at back O(1) O(1)
insert after an item O(N) O(1)
ArrayLists นั้นเหมาะสำหรับการเขียนครั้งเดียวอ่านหลาย ๆ คนหรือต่อท้าย แต่ไม่ดีที่การเพิ่ม / ลบจากด้านหน้าหรือกลาง
หากต้องการเพิ่มคำตอบอื่น ๆ การใช้งานรายการอาร์เรย์ส่วนใหญ่จะสำรองความจุพิเศษไว้ท้ายรายการเพื่อให้สามารถเพิ่มองค์ประกอบใหม่ในตอนท้ายของรายการในเวลา O (1) เมื่อเกินความสามารถของรายการอาร์เรย์อาร์เรย์ใหม่ที่ใหญ่กว่าจะถูกจัดสรรภายในและองค์ประกอบเก่าทั้งหมดจะถูกคัดลอกไป โดยปกติแล้วอาร์เรย์ใหม่จะมีขนาดใหญ่กว่าเดิมสองเท่า ซึ่งหมายความว่าโดยเฉลี่ยแล้วการเพิ่มองค์ประกอบใหม่ไปยังส่วนท้ายของรายการอาร์เรย์คือการดำเนินการ O (1) ในการใช้งานเหล่านี้ ดังนั้นแม้ว่าคุณจะไม่ทราบจำนวนองค์ประกอบล่วงหน้ารายการอาร์เรย์อาจยังเร็วกว่ารายการที่เชื่อมโยงสำหรับการเพิ่มองค์ประกอบตราบใดที่คุณเพิ่มเข้าไปในตอนท้าย เห็นได้ชัดว่าการแทรกองค์ประกอบใหม่ที่ตำแหน่งโดยพลการในรายการอาร์เรย์ยังคงเป็นการดำเนินการ O (n)
การเข้าถึงองค์ประกอบในรายการอาเรย์นั้นยังเร็วกว่ารายการที่เชื่อมโยงแม้ว่าการเข้าถึงนั้นเป็นไปตามลำดับ เนื่องจากองค์ประกอบอาร์เรย์จะถูกเก็บไว้ในหน่วยความจำต่อเนื่องและสามารถแคชได้อย่างง่ายดาย โหนดรายการที่เชื่อมโยงอาจกระจายอยู่ในหลาย ๆ หน้า
ฉันจะแนะนำให้ใช้รายการที่ลิงก์เท่านั้นหากคุณรู้ว่าคุณกำลังจะทำการแทรกหรือลบรายการในตำแหน่งที่ตั้งที่กำหนดเอง รายการอาเรย์จะเร็วขึ้นสำหรับทุกอย่าง
ข้อได้เปรียบของรายการจะปรากฏขึ้นหากคุณต้องการแทรกรายการไว้ตรงกลางและไม่ต้องการเริ่มปรับขนาดอาร์เรย์และเลื่อนสิ่งต่าง ๆ รอบ ๆ
คุณถูกต้องในกรณีนี้ไม่ปกติ ฉันมีบางกรณีที่เฉพาะเจาะจงมากเช่นนั้น แต่ไม่มากเกินไป
ทุกอย่างขึ้นอยู่กับประเภทของการดำเนินการที่คุณทำในขณะที่วนซ้ำโครงสร้างข้อมูลทั้งหมดมีการแลกเปลี่ยนระหว่างเวลาและหน่วยความจำและขึ้นอยู่กับความต้องการของเราเราควรเลือก DS ที่เหมาะสม ดังนั้นจึงมีบางกรณีที่ LinkedList นั้นเร็วกว่าอาร์เรย์และในทางกลับกัน พิจารณาการดำเนินการพื้นฐานสามประการในโครงสร้างข้อมูล
เนื่องจากอาร์เรย์เป็นโครงสร้างข้อมูลที่ใช้ดัชนีการค้นหา array.get (ดัชนี) จะใช้เวลา O (1) ในขณะที่รายการที่เชื่อมโยงไม่ใช่ดัชนี DS ดังนั้นคุณจะต้องข้ามไปที่ดัชนีซึ่งดัชนี <= n, n คือขนาดของรายการที่เชื่อมโยง ดังนั้นอาร์เรย์จึงเร็วกว่ารายการที่ลิงก์เมื่อมีการเข้าถึงองค์ประกอบแบบสุ่ม
Q. ดังนั้นความงามที่อยู่เบื้องหลังสิ่งนี้คืออะไร?
เนื่องจากอาร์เรย์เป็นบล็อกหน่วยความจำต่อเนื่องชิ้นส่วนขนาดใหญ่จะถูกโหลดลงในแคชเมื่อเข้าถึงครั้งแรกทำให้การเข้าถึงองค์ประกอบที่เหลืออยู่ของอาร์เรย์นั้นค่อนข้างรวดเร็วเมื่อเราเข้าถึงองค์ประกอบในพื้นที่อ้างอิงของอาร์เรย์จะเพิ่มขึ้นดังนั้นจึงไม่จับ คิดถึงพื้นที่ใกล้เคียงแคชหมายถึงการดำเนินการที่อยู่ในแคชและดำเนินการได้เร็วขึ้นมากเมื่อเทียบกับในหน่วยความจำโดยทั่วไปในอาเรย์เราเพิ่มโอกาสในการเข้าถึงองค์ประกอบตามลำดับที่อยู่ในแคช ในขณะที่รายการที่เชื่อมโยงไม่จำเป็นต้องอยู่ในบล็อกของหน่วยความจำที่ต่อเนื่องกัน แต่ไม่มีการรับประกันว่ารายการที่ปรากฏขึ้นตามลำดับในรายการนั้นจะถูกจัดเรียงไว้ใกล้กันในหน่วยความจำ
นี่เป็นเรื่องง่ายและรวดเร็วใน LinkedList เนื่องจากการแทรกคือการดำเนินการ O (1) ใน LinkedList (ใน Java) เมื่อเทียบกับอาเรย์พิจารณากรณีที่อาเรย์เต็มเราจำเป็นต้องคัดลอกเนื้อหาไปยังอาเรย์ใหม่ถ้าอาเรย์เต็ม องค์ประกอบใน ArrayList ของ O (n) ในกรณีที่เลวร้ายที่สุดในขณะที่ ArrayList ยังต้องอัปเดตดัชนีถ้าคุณแทรกบางสิ่งบางอย่างยกเว้นที่ท้ายอาร์เรย์ในกรณีของรายการที่เชื่อมโยงเราไม่จำเป็นต้องปรับขนาดคุณเพียงแค่ต้อง อัปเดตพอยน์เตอร์
มันทำงานเหมือนการแทรกและดีกว่าใน LinkedList กว่าอาร์เรย์
สิ่งเหล่านี้เป็นการนำไปใช้ที่เป็นที่นิยมที่สุด
ArrayList:
ส่วนท้ายแทรก / ลบโดยทั่วไป O (1) กรณีที่เลวร้ายที่สุด O (n)
แทรก / ลบตรงกลาง O (n)
ดึงตำแหน่งใด ๆ O (1)
LinkedList:
แทรก / ลบในตำแหน่งใด ๆ O (1) (หมายเหตุหากคุณมีการอ้างอิงองค์ประกอบ)
ดึงในกลาง O (n)
ดึงองค์ประกอบแรกหรือสุดท้าย O (1)
เวกเตอร์: อย่าใช้มัน มันเป็นการใช้งานแบบเก่าที่คล้ายกับ ArrayList แต่ด้วยวิธีการทั้งหมดที่ทำข้อมูลให้ตรงกัน ไม่ใช่วิธีที่ถูกต้องสำหรับรายการที่ใช้ร่วมกันในสภาพแวดล้อมแบบมัลติเธรด
HashMap
แทรก / ลบ / ดึงโดยคีย์ใน O (1)
TreeSet แทรก / ลบ / บรรจุใน O (บันทึก N)
HashSet แทรก / ลบ / มี / ขนาดใน O (1)
ในความเป็นจริงหน่วยความจำท้องถิ่นมีอิทธิพลอย่างมากในการประมวลผลจริง
การใช้ดิสก์สตรีมที่เพิ่มขึ้นในการประมวลผล "ข้อมูลขนาดใหญ่" และการเข้าถึงแบบสุ่มแสดงให้เห็นว่าการจัดโครงสร้างแอปพลิเคชันของคุณในลักษณะนี้สามารถปรับปรุงประสิทธิภาพในระดับที่ใหญ่ขึ้นได้อย่างมาก
หากมีวิธีการเข้าถึงอาเรย์ตามลำดับที่มีประสิทธิภาพที่ดีที่สุด การออกแบบด้วยสิ่งนี้เป็นเป้าหมายควรได้รับการพิจารณาอย่างน้อยหากประสิทธิภาพเป็นสิ่งสำคัญ
อืม, อาเรย์ลิสต์สามารถใช้ได้ในกรณีเช่นนี้ฉันเดาว่า:
ตัวอย่างเช่นคุณต้องนำเข้าและเข้าถึงองค์ประกอบทั้งหมดในรายชื่อผู้ติดต่อ (ขนาดที่คุณไม่รู้จัก)
ใช้รายการที่เชื่อมโยงสำหรับ Radix Sort over arrays และสำหรับการดำเนินการพหุนาม
1) ตามที่อธิบายไว้ข้างต้นการดำเนินการแทรกและลบให้ประสิทธิภาพที่ดี (O (1)) ใน LinkedList เทียบกับ ArrayList (O (n)) ดังนั้นหากมีความต้องการของการเพิ่มและการลบบ่อยครั้งในแอปพลิเคชันแล้ว LinkedList เป็นตัวเลือกที่ดีที่สุด
2) การค้นหา (get method) นั้นรวดเร็วใน Arraylist (O (1)) แต่ไม่ใช่ใน LinkedList (O (n)) ดังนั้นหากมีการเพิ่มและลบการดำเนินงานน้อยลงและความต้องการการดำเนินการค้นหาเพิ่มเติม ArrayList จะเป็นทางออกที่ดีที่สุดของคุณ
ฉันคิดว่าความแตกต่างที่สำคัญคือคุณต้องใส่หรือลบรายการจากด้านบนของรายการบ่อยๆ
ด้วยอาเรย์ถ้าคุณลบบางอย่างออกจากรายการด้านบนกว่าความซับซ้อนคือ o (n) เพราะดัชนีทั้งหมดขององค์ประกอบอาเรย์จะต้องเปลี่ยน
ด้วยรายการที่เชื่อมโยงมันเป็น o (1) เพราะคุณต้องการเพียงสร้างโหนดกำหนดหัวใหม่และกำหนดการอ้างอิงให้กับถัดไปเป็นส่วนหัวก่อนหน้า
เมื่อแทรกหรือลบบ่อยครั้งในตอนท้ายของรายการอาร์เรย์จะดีกว่าเนื่องจากความซับซ้อนจะเป็น o (1) ไม่ต้องทำการทำดัชนีใหม่ แต่สำหรับรายการที่เชื่อมโยงจะเป็น o (n) เพราะคุณต้องออกจากหัว ถึงโหนดสุดท้าย
ฉันคิดว่าการค้นหาทั้งในรายการที่เชื่อมโยงและอาร์เรย์จะเป็น o (log n) เพราะคุณอาจใช้การค้นหาแบบไบนารี
ฉันทำการเปรียบเทียบและพบว่ารายการระดับจริงเร็วกว่า LinkedList สำหรับการแทรกแบบสุ่ม:
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int count = 20000;
Random rand = new Random(12345);
Stopwatch watch = Stopwatch.StartNew();
LinkedList<int> ll = new LinkedList<int>();
ll.AddLast(0);
for (int i = 1; i < count; i++)
{
ll.AddBefore(ll.Find(rand.Next(i)),i);
}
Console.WriteLine("LinkedList/Random Add: {0}ms", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
List<int> list = new List<int>();
list.Add(0);
for (int i = 1; i < count; i++)
{
list.Insert(list.IndexOf(rand.Next(i)), i);
}
Console.WriteLine("List/Random Add: {0}ms", watch.ElapsedMilliseconds);
Console.ReadLine();
}
}
}
ใช้เวลา 900 ms สำหรับรายการที่เชื่อมโยงและ 100ms สำหรับคลาสรายการ
มันสร้างรายการของจำนวนเต็มต่อมา จำนวนเต็มใหม่แต่ละค่าจะถูกแทรกหลังจากตัวเลขสุ่มซึ่งมีอยู่แล้วในรายการ บางทีคลาส List ใช้อะไรที่ดีกว่าแค่อาร์เรย์
อาร์เรย์เป็นโครงสร้างข้อมูลที่ใช้กันอย่างแพร่หลาย อย่างไรก็ตามรายการที่เชื่อมโยงพิสูจน์แล้วว่ามีประโยชน์ในวิธีที่เป็นเอกลักษณ์ของตนเองโดยที่อาร์เรย์มีความเงอะงะหรือมีราคาแพง
รายการที่เชื่อมโยงมีประโยชน์ในการสร้างสแต็กและคิวในสถานการณ์ที่ขนาดของรายการนั้นอาจแตกต่างกันไป แต่ละโหนดในรายการที่เชื่อมโยงสามารถผลักหรือผุดโดยไม่รบกวนส่วนใหญ่ของโหนด กันไปสำหรับการแทรก / ลบโหนดในกลาง ในอาร์เรย์อย่างไรก็ตามองค์ประกอบทั้งหมดจะต้องเปลี่ยนซึ่งเป็นงานที่มีราคาแพงในแง่ของเวลาการดำเนินการ
ต้นไม้ไบนารีและต้นไม้ค้นหาแบบทวิภาคตารางแฮชและความพยายามเป็นโครงสร้างข้อมูลบางส่วนที่อย่างน้อยใน C คุณจำเป็นต้องมีลิสต์ที่เชื่อมโยงเป็นส่วนประกอบพื้นฐานในการสร้างมันขึ้นมา
อย่างไรก็ตามรายการที่เชื่อมโยงควรหลีกเลี่ยงในสถานการณ์ที่คาดว่าจะสามารถเรียกองค์ประกอบใด ๆ โดยดัชนีของมัน
คำตอบสำหรับคำถามง่ายๆสามารถให้ได้โดยใช้จุดเหล่านี้:
อาร์เรย์จะต้องใช้เมื่อต้องการรวบรวมองค์ประกอบข้อมูลประเภทที่คล้ายกัน ในขณะที่รายการที่เชื่อมโยงคือชุดขององค์ประกอบที่เชื่อมโยงข้อมูลชนิดผสมที่รู้จักในนามโหนด
ในอาร์เรย์หนึ่งสามารถเยี่ยมชมองค์ประกอบใด ๆ ในเวลา O (1) ในขณะที่ในรายการที่เชื่อมโยงเราจะต้องสำรวจรายการเชื่อมโยงทั้งหมดจากหัวไปยังโหนดที่จำเป็นต้องใช้เวลา O (n)
สำหรับอาร์เรย์จำเป็นต้องประกาศขนาดเฉพาะ แต่รายการที่เชื่อมโยงมีขนาดแบบไดนามิก