อาร์เรย์กับรายการที่เชื่อมโยง


200

ทำไมบางคนต้องการใช้ลิสต์ที่เชื่อมโยงกับอาร์เรย์?

ไม่ต้องสงสัยเลยว่าการเขียนโค้ดที่เชื่อมโยงกับลิสต์จะมีงานมากกว่าการใช้อาเรย์และอาจสงสัยว่าอะไรคือเหตุผลที่เพิ่มความพยายาม

ฉันคิดว่าการแทรกองค์ประกอบใหม่นั้นไม่สำคัญในรายการที่เชื่อมโยง แต่มันก็เป็นงานที่สำคัญมากในอาเรย์ มีข้อได้เปรียบอื่น ๆ ในการใช้รายการที่เชื่อมโยงเพื่อเก็บชุดของข้อมูลเมื่อเทียบกับการเก็บไว้ในอาร์เรย์หรือไม่?

คำถามนี้ไม่ได้ซ้ำกับคำถามนี้เพราะคำถามอื่น ๆ ถามเฉพาะเกี่ยวกับคลาส Java เฉพาะในขณะที่คำถามนี้เกี่ยวข้องกับโครงสร้างข้อมูลทั่วไป


1
เกี่ยวข้อง - เมื่อใดที่จะใช้ LinkedList <> มากกว่า ArrayList <> - เป็น Java แต่อาร์เรย์ (ArrayList) และรายการเชื่อมโยงน่าจะมีประสิทธิภาพเหมือนกันในภาษาใด ๆ
Bernhard Barker


1
@rootTraveller จริงๆแล้วคำถามนั้นจะซ้ำกับคำถามนี้เพราะคำถามของฉันถูกโพสต์ไว้ก่อน
Onorio Catenacci

คำตอบ:


147
  • ง่ายกว่าในการจัดเก็บข้อมูลขนาดต่าง ๆ ในรายการที่เชื่อมโยง อาร์เรย์ถือว่าทุกองค์ประกอบมีขนาดเท่ากันทุกประการ
  • ดังที่คุณกล่าวไว้ง่ายกว่าสำหรับรายการที่เชื่อมโยงเพื่อการเติบโตแบบอินทรีย์ ขนาดของอาร์เรย์จำเป็นต้องทราบล่วงหน้าหรือสร้างขึ้นใหม่เมื่อต้องการเติบโต
  • การสับเปลี่ยนรายการที่เชื่อมโยงเป็นเพียงเรื่องของการเปลี่ยนแปลงสิ่งที่ชี้ไปที่อะไร การสับอาร์เรย์มีความซับซ้อนมากขึ้นและ / หรือใช้หน่วยความจำมากขึ้น
  • ตราบใดที่การวนซ้ำของคุณเกิดขึ้นในบริบท "foreach" คุณจะไม่สูญเสียประสิทธิภาพในการทำซ้ำ

19
ไอเท็มที่มีขนาดแตกต่างกันอย่างไร รายการที่เชื่อมโยงอาจใช้โครงสร้างแบบตายตัวกับเขตข้อมูลถัดไป (ต้องใช้ขนาดคงที่) หรือเก็บตัวชี้ไปยังข้อมูลในรถยนต์ (ขนาดตัวแปรตกลง) ทั้งสองวิธีนั้นง่ายเหมือนกันกับเวกเตอร์ เช่นเดียวกันสำหรับการสับ
ไบรอัน

32
ฉันจะบอกว่าการสับเปลี่ยนอาเรย์นั้นซับซ้อนน้อยกว่า
Hugh Allen

23
คำตอบนี้ไม่ถูกต้องและทำให้เข้าใจผิด (ยกเว้นที่จำเป็นต้องรู้ขนาดของอาร์เรย์เมื่อคุณประกาศ)
Robert Paulson

35
การวนซ้ำรายการที่ลิงก์จะไม่ช้าลงเพราะตำแหน่งข้อมูลหรือไม่
Firas Assaad

6
@Rick โดยทั่วไปเวกเตอร์จะกำหนดพื้นที่ที่ต้องการโดยไม่จำเป็นต้องจัดสรรพื้นที่ใหม่ทุกครั้งที่มีขนาดเพิ่มขึ้น ผลที่ได้คือเวคเตอร์มักจะมีหน่วยความจำน้อยกว่าและไม่เชื่อมโยงกับรายการ
Winston Ewert

179

อีกเหตุผลที่ดีคือรายการที่เชื่อมโยงให้ประโยชน์กับการใช้งานแบบมัลติเธรดที่มีประสิทธิภาพ เหตุผลของเรื่องนี้คือการเปลี่ยนแปลงมีแนวโน้มที่จะเป็นท้องถิ่น - ส่งผลกระทบต่อเพียงหนึ่งหรือสองตัวชี้สำหรับการแทรกและลบในส่วนของโครงสร้างข้อมูล ดังนั้นคุณสามารถมีหลายหัวข้อที่ทำงานในรายการเชื่อมโยงเดียวกัน ยิ่งไปกว่านั้นมันเป็นไปได้ที่จะสร้างเวอร์ชั่นปลอดล็อคโดยใช้การทำงานแบบ CAS และหลีกเลี่ยงการล็อคที่มีน้ำหนักมาก

ด้วยรายการที่ถูกเชื่อมโยงตัววนซ้ำสามารถข้ามรายการในขณะที่การแก้ไขเกิดขึ้น ในกรณีที่การมองโลกในแง่ดีที่การปรับเปลี่ยนไม่ได้ขัดแย้งกันตัวทำซ้ำสามารถดำเนินการต่อโดยไม่มีการโต้แย้ง

ด้วยอาเรย์การเปลี่ยนแปลงใด ๆ ที่ปรับเปลี่ยนขนาดของอาเรย์นั้นจำเป็นต้องล็อคส่วนใหญ่ของอาเรย์และในความเป็นจริงมันเป็นเรื่องยากที่จะทำเช่นนี้โดยไม่มีการล็อคระดับโลกในอาเรย์ทั้งหมด


9
อเล็กซ์ - นั่นเป็นการพิจารณาที่น่าสนใจที่ไม่เคยเกิดขึ้นกับฉัน คำตอบที่ดีมาก ฉันจะโหวตให้คุณสองครั้งถ้าทำได้ :-)
Onorio Catenacci

5
ลองดูที่รายการข้าม (โดยเฉพาะอย่างยิ่ง ConcurrentSkipListMap ใน Java 6) สำหรับแนวคิดที่ดีว่าคุณจะไปที่ไหน CSLM เป็นแผนที่ที่เรียงลำดับพร้อมกันพร้อมประสิทธิภาพที่ยอดเยี่ยม ไกลดีกว่า TreeMap ที่ทำให้ตรงกัน tech.puredanger.com/2007/10/03/skip-lists
Alex Miller

... ยกเว้นว่ามีConcurrentSkipListMapหรือConcurrentSkipListMapไม่มีรายการแม้ว่า "รายการ" จะเกิดขึ้นที่ใดที่หนึ่งในชื่อของพวกเขา ทั้งสองต้องการคีย์ที่จะถูกจัดเรียงและไม่ซ้ำกัน หากคุณต้องการListคือโครงสร้างข้อมูลที่อนุญาตให้องค์ประกอบที่ซ้ำกันตามลำดับโดยพลการนั้นไม่เหมาะสมและคุณจะต้องใช้ความพยายามอย่างมากในการสร้างโครงสร้างข้อมูลเหมือนLinkedListสิ่งที่อัปเดตพร้อมกันได้ หากคุณต้องการคิวหรือ deque ที่เกิดขึ้นพร้อมกันใช่แล้วยังมีตัวอย่างที่มีอยู่ แต่มีพร้อม ๆ กันList…ฉันไม่เชื่อว่านี่จะเป็นไปได้
Holger

128

Wikipedia มีส่วนที่ดีมากเกี่ยวกับความแตกต่าง

รายการที่เชื่อมโยงมีข้อดีหลายประการมากกว่าอาร์เรย์ องค์ประกอบสามารถแทรกลงในรายการที่เชื่อมโยงไปเรื่อย ๆ ในขณะที่อาร์เรย์จะเติมหรือจำเป็นต้องปรับขนาดในที่สุดการดำเนินการที่มีราคาแพงซึ่งอาจไม่สามารถทำได้หากหน่วยความจำมีการแยกส่วน ในทำนองเดียวกันอาร์เรย์ที่องค์ประกอบหลายอย่างถูกลบอาจว่างเปล่าสิ้นเปลืองหรือจำเป็นต้องทำให้เล็กลง

ในทางกลับกันอาร์เรย์อนุญาตการเข้าถึงแบบสุ่มในขณะที่รายการที่เชื่อมโยงจะอนุญาตการเข้าถึงองค์ประกอบตามลำดับเท่านั้น ในความเป็นจริงแล้วรายการที่เชื่อมโยงด้วยตนเองนั้นสามารถไปในทิศทางเดียวเท่านั้น สิ่งนี้ทำให้รายการที่ลิงก์ไม่เหมาะสมสำหรับแอปพลิเคชันที่มีประโยชน์ในการค้นหาองค์ประกอบตามดัชนีอย่างรวดเร็วเช่น heapsort การเข้าถึงลำดับของอาร์เรย์ยังเร็วกว่ารายการที่ลิงก์ในหลาย ๆ เครื่องเนื่องจากตำแหน่งอ้างอิงและแคชข้อมูล รายการที่เชื่อมโยงได้รับเกือบจะไม่ได้รับประโยชน์จากแคช

ข้อเสียอีกอย่างหนึ่งของรายการที่เชื่อมโยงคือพื้นที่เก็บข้อมูลเพิ่มเติมที่จำเป็นสำหรับการอ้างอิงซึ่งมักจะไม่สามารถใช้กับรายการข้อมูลขนาดเล็กเช่นอักขระหรือค่าบูลีน นอกจากนี้ยังสามารถช้าและด้วยตัวจัดสรรไร้เดียงสาสิ้นเปลืองเพื่อจัดสรรหน่วยความจำแยกต่างหากสำหรับองค์ประกอบใหม่แต่ละปัญหามักแก้ไขโดยใช้พูลหน่วยความจำ

http://en.wikipedia.org/wiki/Linked_list


4
นี่คือคำตอบที่สมบูรณ์แบบ อธิบายถึงข้อดีและข้อเสียของทั้งสองอย่างชัดเจน
Rick

ขอขอบคุณ)) ตายง่าย แต่ฉันไม่ได้เห็นมันในวิกิ)
ทิโมเตอร์ Fayzrakhmanov

58

ฉันจะเพิ่มอีก - รายการสามารถทำหน้าที่เป็นโครงสร้างข้อมูลที่ ทำงานได้อย่างหมดจด

ตัวอย่างเช่นคุณสามารถมีรายการที่แตกต่างกันโดยสิ้นเชิงที่แชร์ในส่วนท้ายเดียวกัน

a = (1 2 3 4, ....)
b = (4 3 2 1 1 2 3 4 ...)
c = (3 4 ...)

เช่น:

b = 4 -> 3 -> 2 -> 1 -> a
c = a.next.next  

โดยไม่ต้องมีการคัดลอกข้อมูลที่ถูกชี้ไปตามaเข้าไปและbc

นี่คือเหตุผลที่พวกเขาได้รับความนิยมในภาษาที่ใช้งานได้ซึ่งใช้ตัวแปรที่ไม่เปลี่ยนรูปแบบprependและtailการดำเนินการสามารถเกิดขึ้นได้อย่างอิสระโดยไม่ต้องคัดลอกข้อมูลดั้งเดิม - คุณสมบัติที่สำคัญมากเมื่อคุณจัดการข้อมูลที่ไม่เปลี่ยนรูป


4
การพิจารณาที่น่าสนใจอีกอย่างหนึ่งที่ฉันจะไม่เกิดขึ้น ขอบคุณ.
Onorio Catenacci

ฉันจะทำสิ่งนี้ในไพ ธ อนได้อย่างไร
แลกเปลี่ยน

29

นอกเหนือจากการแทรกลงในช่วงกลางของรายการได้ง่ายขึ้น - นอกจากนี้ยังง่ายต่อการลบออกจากตรงกลางของรายการที่เชื่อมโยงกว่าอาร์เรย์

แต่ตรงไปตรงมาฉันไม่เคยใช้รายการเชื่อมโยง เมื่อใดก็ตามที่ฉันต้องการแทรกและลบอย่างรวดเร็วฉันก็ต้องค้นหาอย่างรวดเร็วดังนั้นฉันจึงไปที่ HashSet หรือพจนานุกรม


2
จริงมากการแทรกและการลบเกิดขึ้นหลังจากค้นหาเวลาส่วนใหญ่ดังนั้นต้องพิจารณาผลรวมของความซับซ้อนของเวลาด้วย
MA Hossain Tonu

28

การผสานสองรายการที่เชื่อมโยง (โดยเฉพาะอย่างยิ่งสองรายการที่เชื่อมโยงเป็นสองเท่า) จะเร็วกว่าการรวมสองอาร์เรย์ (สมมติว่าการผสานเป็นการทำลาย) อดีตใช้ O (1) หลังใช้ O (n)

แก้ไข:เพื่อชี้แจงฉันหมายถึง "การรวม" ที่นี่ในความหมายที่ไม่เรียงลำดับไม่เหมือนกับการผสาน บางที "การต่อข้อมูล" อาจเป็นคำที่ดีกว่าก็ได้


2
เฉพาะในกรณีที่คุณเพียงต่อท้ายหนึ่งรายการ หากคุณกำลังรวมรายการที่เรียงสองรายการจริง ๆ แล้วมันจะใช้บันทึกมากกว่า O (1)
Herms

3
@ Herms แต่คุณสามารถรวมรายการที่ลิงก์ที่เรียงสองรายการโดยไม่ต้องจัดสรรหน่วยความจำเพิ่มเติมใด ๆ เพียงแค่สำรวจทั้งสองรายการและตั้งค่าพอยน์เตอร์ให้เหมาะสม การรวมสองอาร์เรย์โดยปกติจะใช้อาร์เรย์พิเศษอย่างน้อยหนึ่งอาร์เรย์
Paul Tomblin

1
ใช่รายการที่รวมกันเป็นหน่วยความจำมีประสิทธิภาพมากขึ้น แต่นั่นไม่ใช่สิ่งที่ฉันแสดงความคิดเห็น การบอกว่าการรวมรายการเชื่อมโยงคือ O (1) ทำให้เข้าใจผิดมากโดยไม่มีการชี้แจงกรณี
Herms

@Herms การรวมรายการไม่ได้มีประสิทธิภาพหน่วยความจำมากขึ้นกว่าการรวมอาร์เรย์ในรูปแบบข้อมูลที่เหมาะสม
Alexei Averchenko

2
Alexei Averchenko: การเชื่อมสองรายการเข้าด้วยกันหรือรวมการเรียงลำดับสองรายการที่เรียงลำดับสามารถทำได้โดยใช้หน่วยความจำ O (1) การต่อสองอาร์เรย์จะต้องใช้หน่วยความจำ O (n) เว้นแต่ว่าอาร์เรย์นั้นติดกันในหน่วยความจำแล้ว ฉันคิดว่าจุดที่คุณตั้งเป้าหมายไว้คือที่ซึ่งรายการขององค์ประกอบ n และอาร์เรย์ขององค์ประกอบทั้งสองใช้หน่วยความจำ O (n) แต่สัมประสิทธิ์จะสูงกว่าสำหรับรายการที่เชื่อมโยง
rampion

17

อาร์กิวเมนต์ทำบุญกันอย่างแพร่หลายสำหรับ ArrayList และต่อต้าน LinkedList คือLinkedLists ไม่สบายใจในขณะที่การแก้จุดบกพร่อง เวลาที่นักพัฒนาด้านการบำรุงรักษาเข้าใจโปรแกรมเช่นการหาบั๊กเพิ่มขึ้นและบางครั้ง IMHO ไม่ได้พิสูจน์ให้เห็นถึงนาโนวินาทีในการปรับปรุงประสิทธิภาพหรือไบต์ในการใช้หน่วยความจำในแอพพลิเคชั่นขององค์กร บางครั้ง (แน่นอนว่ามันขึ้นอยู่กับประเภทของแอพพลิเคชั่น) มันจะดีกว่าที่จะเสียเวลาไม่กี่ไบต์ แต่มีแอพพลิเคชั่นที่สามารถบำรุงรักษาได้มากกว่าหรือเข้าใจง่ายกว่า

ตัวอย่างเช่นในสภาพแวดล้อม Java และการใช้ดีบักเกอร์ Eclipse การดีบัก ArrayList จะเปิดเผยโครงสร้างที่เข้าใจง่ายมาก:

arrayList   ArrayList<String>
  elementData   Object[]
    [0] Object  "Foo"
    [1] Object  "Foo"
    [2] Object  "Foo"
    [3] Object  "Foo"
    [4] Object  "Foo"
    ...

ในอีกทางหนึ่งการดูเนื้อหาของ LinkedList และการค้นหาวัตถุที่เฉพาะเจาะจงจะกลายเป็นฝันร้ายขยายคลิกต้นไม้ไม่พูดถึงค่าใช้จ่ายทางปัญญาที่จำเป็นในการกรอง internals LinkedList:

linkedList  LinkedList<String>
    header  LinkedList$Entry<E>
        element E
        next    LinkedList$Entry<E>
            element E   "Foo"
            next    LinkedList$Entry<E>
                element E   "Foo"
                next    LinkedList$Entry<E>
                    element E   "Foo"
                    next    LinkedList$Entry<E>
                    previous    LinkedList$Entry<E>
                    ...
                previous    LinkedList$Entry<E>
            previous    LinkedList$Entry<E>
        previous    LinkedList$Entry<E>

17

ก่อนอื่นในรายการ C ++ linked- ไม่น่าจะมีปัญหาในการทำงานกับอาเรย์มากกว่า คุณสามารถใช้รายการstd ::หรือรายการตัวชี้การเพิ่มสำหรับรายการที่เชื่อมโยง ประเด็นสำคัญที่มีรายการที่เชื่อมโยงกับอาร์เรย์เป็นพื้นที่เพิ่มเติมที่จำเป็นสำหรับพอยน์เตอร์และการเข้าถึงแบบสุ่มที่แย่มาก คุณควรใช้รายการที่เชื่อมโยงถ้าคุณ

  • คุณไม่จำเป็นต้องเข้าถึงข้อมูลแบบสุ่ม
  • คุณจะเพิ่ม / ลบองค์ประกอบโดยเฉพาะที่อยู่ตรงกลางของรายการ

14

สำหรับฉันมันเป็นแบบนี้

  1. เข้าไป

    • รายการที่เชื่อมโยงอนุญาตการเข้าถึงองค์ประกอบต่อเนื่องเท่านั้น ดังนั้นความซับซ้อนของอัลกอริทึมคือลำดับของ O (n)
    • อาร์เรย์อนุญาตให้เข้าถึงแบบสุ่มไปยังองค์ประกอบต่างๆได้ดังนั้นความซับซ้อนจึงเป็นลำดับ O (1)
  2. การเก็บรักษา

    • รายการที่เชื่อมโยงต้องการพื้นที่เก็บข้อมูลเพิ่มเติมสำหรับการอ้างอิง สิ่งนี้ทำให้ไม่สามารถใช้กับรายการของรายการข้อมูลขนาดเล็กเช่นอักขระหรือค่าบูลีน
    • อาร์เรย์ไม่ต้องการพื้นที่เก็บข้อมูลเพิ่มเติมเพื่อชี้ไปยังรายการข้อมูลถัดไป แต่ละองค์ประกอบสามารถเข้าถึงได้ผ่านดัชนี
  3. ขนาด

    • ขนาดของรายการที่เชื่อมโยงเป็นแบบไดนามิกตามธรรมชาติ
    • ขนาดของอาเรย์ถูก จำกัด ให้ประกาศ
  4. แทรก / ลบ

    • องค์ประกอบที่สามารถแทรกและลบในรายการที่เชื่อมโยงไปเรื่อย ๆ
    • การแทรก / การลบค่าในอาร์เรย์มีราคาแพงมาก ต้องมีการจัดสรรหน่วยความจำใหม่

คุณมี 2 หมายเลข 2 และ 2 หมายเลข 3 :)
Hengameh

เราสามารถประกาศอาร์เรย์ที่ว่างเปล่าแล้วเพิ่มข้อมูลตามที่ต้องการ มันทำให้ขนาดคงที่ได้อย่างไร
HebleV

11

สองสิ่ง:

ไม่ต้องสงสัยเลยว่าการเขียนรหัสรายการที่เชื่อมโยงนั้นทำงานได้มากกว่าการใช้อาร์เรย์และเขาสงสัยว่าอะไรที่จะพิสูจน์ความพยายามเพิ่มเติม

ไม่เคยรหัสรายการที่เชื่อมโยงเมื่อใช้ C ++ เพียงใช้ STL ความยากที่จะนำไปใช้นั้นไม่ควรเป็นเหตุผลในการเลือกโครงสร้างข้อมูลหนึ่งไปยังอีกโครงสร้างหนึ่งเนื่องจากส่วนใหญ่ถูกนำไปใช้งานแล้ว

สำหรับความแตกต่างที่แท้จริงระหว่างอาเรย์กับลิสต์ที่เชื่อมโยงสิ่งสำคัญสำหรับฉันคือวิธีที่คุณวางแผนที่จะใช้โครงสร้าง ฉันจะใช้คำว่าเวกเตอร์เนื่องจากเป็นคำศัพท์สำหรับอาร์เรย์ที่ปรับขนาดได้ใน C ++

การจัดทำดัชนีในรายการที่ลิงก์ช้าเพราะคุณต้องข้ามรายการเพื่อไปยังดัชนีที่กำหนดในขณะที่เวกเตอร์อยู่ติดกันในหน่วยความจำและคุณสามารถไปที่นั่นโดยใช้ตัวชี้คณิตศาสตร์

การต่อท้ายหรือจุดเริ่มต้นของรายการที่เชื่อมโยงนั้นเป็นเรื่องง่ายเนื่องจากคุณต้องอัปเดตลิงก์เดียวซึ่งในเวกเตอร์คุณอาจต้องปรับขนาดและคัดลอกเนื้อหาไป

การลบรายการออกจากรายการทำได้ง่ายเนื่องจากคุณเพียงแค่ต้องแยกลิงค์ออกเป็นคู่แล้วแนบกลับเข้าด้วยกัน การลบรายการออกจากเวกเตอร์อาจเร็วกว่าหรือช้ากว่านั้นขึ้นอยู่กับว่าคุณใส่ใจในการสั่งซื้อสินค้าหรือไม่ การสลับในรายการสุดท้ายที่อยู่ด้านบนสุดของรายการที่คุณต้องการลบนั้นเร็วกว่าในขณะที่การเลื่อนทุกอย่างหลังจากมันช้าลง แต่ยังคงคำสั่งซื้ออยู่


ตามที่ฉันบอกใครบางคนข้างต้นฉันแค่พยายามสร้างความสัมพันธ์กับคำถามในแบบที่มันทำให้ฉัน ฉันจะไม่ใช้อาเรย์ (หรือรายชื่อที่เชื่อมโยงกับม้วนของฉัน) ใน C ++ ต่อไป - ฉันจะใช้ทั้งสองเวอร์ชัน STL
Onorio Catenacci

10

Eric Lippert เพิ่งมีการโพสต์ในหนึ่งในเหตุผลที่ควรใช้อาร์เรย์อย่างระมัดระวัง


2
โพสต์ที่ดีเป็นที่ยอมรับ แต่ไม่เกี่ยวข้องในรายการที่เชื่อมโยงกับการอภิปรายของอาร์เรย์
Robert Paulson

2
ฉันขอแนะนำว่าบทความของ Eric ส่วนใหญ่มีความเกี่ยวข้องเนื่องจากกล่าวถึงทั้งข้อเสียของอาร์เรย์และข้อดีของรายการโดยไม่คำนึงถึงการใช้งานรายการ
Bevan

8

การแทรกและกำจัดอย่างรวดเร็วเป็นข้อโต้แย้งที่ดีที่สุดสำหรับรายการที่ลิงก์ หากโครงสร้างของคุณเติบโตแบบไดนามิกและการเข้าถึงองค์ประกอบคงที่ไม่จำเป็นต้องใช้ตลอดเวลา (เช่นสแต็คและคิวแบบไดนามิก) รายการที่เชื่อมโยงเป็นตัวเลือกที่ดี



7

รายการที่เชื่อมโยงมีประโยชน์อย่างยิ่งเมื่อคอลเลกชันเติบโตและลดขนาดลงเรื่อย ๆ ตัวอย่างเช่นมันยากที่จะจินตนาการว่าพยายามใช้คิว (เพิ่มไปยังจุดสิ้นสุดลบออกจากด้านหน้า) โดยใช้อาร์เรย์ - คุณจะใช้เวลาทั้งหมดของคุณในการเปลี่ยนสิ่งต่าง ๆ ลง ในทางกลับกันมันเป็นเรื่องเล็กน้อยกับรายการที่เชื่อมโยง


4
คุณสามารถมีคิวแบบอาเรย์โดยไม่ต้องทำงานมากเกินไปที่ยังคงเร็ว / มีประสิทธิภาพ คุณต้องติดตามว่าดัชนีใดเป็น "หัว" และซึ่งเป็น "หาง" วิธีนี้ใช้ได้ค่อนข้างดีหากคุณต้องการคิวที่มีขนาดคงที่ (เช่นบัฟเฟอร์แป้นพิมพ์ในเคอร์เนล)
Herms

3
และเรียกว่า "บัฟเฟอร์แบบวงกลม" หากคุณต้องการค้นหาในการอ้างอิงอัลกอริทึมที่คุณชื่นชอบ
Steve Jessop

7

นอกเหนือจากการเพิ่มและลบออกจากตรงกลางของรายการฉันชอบรายการที่เชื่อมโยงมากขึ้นเพราะพวกเขาสามารถเติบโตและหดแบบไดนามิก


6
เวกเตอร์ (= อาร์เรย์โดยทั่วไป) สามารถทำเช่นนั้นได้และค่าใช้จ่ายตัดจำหน่ายสำหรับพวกเขามักจะมีขนาดเล็กกว่าสำหรับรายการเนื่องจากปัญหาในท้องถิ่นของการอ้างอิง
Konrad Rudolph

7

ไม่มีใครเคยรหัสรายการเชื่อมโยงของตัวเองอีกต่อไป มันคงโง่ หลักฐานที่ใช้รายการเชื่อมโยงใช้รหัสเพิ่มเติมเป็นเพียงผิด

ทุกวันนี้การสร้างรายการที่เชื่อมโยงเป็นเพียงแบบฝึกหัดสำหรับนักเรียนเพื่อให้พวกเขาสามารถเข้าใจแนวคิด ทุกคนใช้รายการที่สร้างไว้ล่วงหน้าแทน ใน C ++ อ้างอิงจากคำอธิบายในคำถามของเราซึ่งอาจหมายถึง stl vector ( #include <vector>)

ดังนั้นการเลือกรายการที่เชื่อมโยงเทียบกับอาร์เรย์คือทั้งหมดเกี่ยวกับการชั่งน้ำหนักลักษณะแตกต่างกันของแต่ละโครงสร้างเทียบกับความต้องการของแอปของคุณ การเอาชนะภาระการเขียนโปรแกรมเพิ่มเติมควรไม่มีผลกระทบต่อการตัดสินใจ


2
เอ่อ .. .. std :: vector เป็นอาร์เรย์ไม่ใช่รายการที่ลิงก์ รายการที่เชื่อมโยงมาตรฐานคือดี std :: รายการ
James Curran

1
ใช่ แต่ฉันคิดว่าเวกเตอร์นั้นใกล้เคียงกับสิ่งที่ op ขอมานั่นคือการแทนที่อาเรย์แบบไดนามิก
Joel Coehoorn

@ โจเอลฉันแค่พยายามสร้างความสัมพันธ์กับคำถามตามที่เพื่อนผู้นี้กำลังพยายามเรียนรู้ C ++ ฉันจะไม่รำคาญกับการเข้ารหัสรายการเชื่อมโยงของฉันเอง แต่นั่นเป็นวิธีที่เขาถามฉัน :-)
Onorio Catenacci

ในสภาพแวดล้อมที่ จำกัด หน่วยความจำ (ไมโครคอนโทรลเลอร์) ซึ่งมีคอมไพเลอร์แบบกำหนดเองจะไม่มีการใช้ภาษาทั้งหมด (เช่นคอนเทนเนอร์ใน C ++) ดังนั้นอาจเป็นไปได้ว่าคุณต้องเขียนรหัสรายการเชื่อมโยงของคุณเอง nongnu.org/avr-libc/user-manual/FAQ.html#faq_cplusplus
Minh Tran

6

มันเป็นเรื่องของประสิทธิภาพการใช้ค่าใช้จ่ายในการแทรกลบหรือย้าย (ซึ่งคุณไม่ได้เป็นเพียงการแลกเปลี่ยน) องค์ประกอบภายในรายการที่เชื่อมโยงนั้นมีน้อยที่สุดนั่นคือการดำเนินการเองคือ O (1) ข้อ O (n) สำหรับอาร์เรย์ สิ่งนี้สามารถสร้างความแตกต่างอย่างมีนัยสำคัญหากคุณทำงานหนักในรายการข้อมูล คุณเลือกประเภทข้อมูลของคุณตามวิธีที่คุณใช้งานและเลือกประสิทธิภาพที่ดีที่สุดสำหรับอัลกอริทึมที่คุณใช้


6

อาร์เรย์ทำให้เข้าใจได้ว่าจะทราบจำนวนรายการที่แน่นอนได้จากที่ใดและการค้นหาโดยใช้ดัชนีเป็นสิ่งใด ตัวอย่างเช่นหากฉันต้องการจัดเก็บสถานะวิดีโอที่แน่นอนของฉันในช่วงเวลาที่กำหนดโดยไม่บีบอัดฉันอาจจะใช้อาร์เรย์ขนาด [1024] [768] สิ่งนี้จะช่วยให้ฉันมีสิ่งที่ฉันต้องการอย่างแน่นอนและรายการจะช้ากว่ามากเพื่อให้ได้ค่าของพิกเซลที่กำหนด ในสถานที่ที่อาร์เรย์ไม่สมเหตุสมผลโดยทั่วไปแล้วชนิดข้อมูลที่ดีกว่ารายการที่จะจัดการกับข้อมูลได้อย่างมีประสิทธิภาพ


6

อาร์เรย์เทียบกับรายการที่เชื่อมโยง:

  1. การจัดสรรหน่วยความจำอาร์เรย์จะล้มเหลวในบางครั้งเนื่องจากหน่วยความจำแยกส่วน
  2. การแคชดีกว่าในอาร์เรย์เนื่องจากองค์ประกอบทั้งหมดได้รับการจัดสรรพื้นที่หน่วยความจำต่อเนื่อง
  3. การเข้ารหัสมีความซับซ้อนมากกว่าอาร์เรย์
  4. ไม่มีข้อ จำกัด ด้านขนาดในรายการที่เชื่อมโยงซึ่งแตกต่างจากอาร์เรย์
  5. การแทรก / การลบเร็วกว่าในรายการที่เชื่อมโยงและการเข้าถึงเร็วกว่าในอาร์เรย์
  6. รายการที่เชื่อมโยงดีขึ้นจากมุมมองแบบหลายเธรด

-1: สิ่งเหล่านี้จำเป็นต้องได้รับการพิสูจน์ไม่ใช่แค่แจกแจง
จอห์นแซนเดอร์

แต่ละจุดได้รับการอธิบายในคำตอบข้างต้นแล้ว การเป็นผู้มาที่หลังฉันไม่มีตัวเลือกอื่นนอกจากการแจกแจง BTW คุณต้องการอธิบายแบบใด
AKS

หากพวกเขาได้รับการอธิบายแล้วทำไมคุณตอบ
John Saunders

2
เพื่อที่จะให้มุมมองสรุปของการอภิปราย และฉันชอบคำตอบประเภทนี้เพื่อที่ฉันจะได้ไม่ต้องอ่านคำอธิบายเดียวกันซ้ำแล้วซ้ำอีก และฉันก็ทำเพื่อคนที่มีสไตล์การคิดเช่นเดียวกับฉัน ppl ที่แตกต่างกันมีสไตล์แตกต่างกัน ไม่มีอะไรใหม่.
AKS

3

เนื่องจากอาร์เรย์เป็นแบบสแตติกดังนั้นการดำเนินการทั้งหมดเช่นการจัดสรรหน่วยความจำจึงเกิดขึ้นในเวลารวบรวมเท่านั้น ดังนั้นโปรเซสเซอร์จึงต้องใช้ความพยายามน้อยลงในการใช้งานจริง


3

สมมติว่าคุณมีชุดสั่งซื้อซึ่งคุณยังต้องการแก้ไขโดยการเพิ่มและลบองค์ประกอบ นอกจากนี้คุณต้องการความสามารถในการเก็บการอ้างอิงถึงองค์ประกอบในลักษณะที่ในภายหลังคุณจะได้รับองค์ประกอบก่อนหน้าหรือถัดไป ตัวอย่างเช่นรายการสิ่งที่ต้องทำหรือชุดของย่อหน้าในหนังสือ

ก่อนอื่นเราควรทราบว่าถ้าคุณต้องการเก็บการอ้างอิงไปยังวัตถุนอกชุดตัวเองคุณอาจจะจบลงด้วยการเก็บพอยน์เตอร์ในอาร์เรย์แทนที่จะเก็บวัตถุเอง มิฉะนั้นคุณจะไม่สามารถแทรกเข้าไปในอาร์เรย์ได้ - หากวัตถุฝังอยู่ในอาร์เรย์พวกเขาจะย้ายไปมาระหว่างการแทรกและตัวชี้ใด ๆ ก็ตามจะกลายเป็นโมฆะ เหมือนกันเป็นจริงสำหรับดัชนีอาร์เรย์

ปัญหาแรกของคุณดังที่คุณจดบันทึกตัวเองคือการแทรก - ลิ้งค์ที่เชื่อมโยงจะอนุญาตให้ใส่ใน O (1) แต่โดยทั่วไปอาเรย์จะต้องใช้ O (n) ปัญหานี้สามารถเอาชนะได้บางส่วน - เป็นไปได้ที่จะสร้างโครงสร้างข้อมูลที่ให้ส่วนต่อประสานการเข้าถึงแบบอาเรย์เรียงตามลำดับซึ่งทั้งการอ่านและการเขียนแย่ที่สุดคือลอการิทึม

ปัญหาที่สองของคุณและที่รุนแรงกว่านั้นคือการให้องค์ประกอบค้นหาองค์ประกอบถัดไปคือ O (n) หากชุดไม่ถูกแก้ไขคุณสามารถเก็บดัชนีขององค์ประกอบเป็นการอ้างอิงแทนตัวชี้จึงทำการค้นหาถัดไปเป็นการดำเนินการ O (1) แต่เนื่องจากคุณมีเพียงตัวชี้ไปยังวัตถุเองและไม่มีทาง เพื่อกำหนดดัชนีปัจจุบันในอาเรย์อื่นที่ไม่ใช่โดยการสแกน "อาเรย์" ทั้งหมด นี่เป็นปัญหาที่ผ่านไม่ได้สำหรับอาร์เรย์ - แม้ว่าคุณจะสามารถเพิ่มประสิทธิภาพการแทรกได้ แต่ก็ไม่มีอะไรที่คุณสามารถทำได้เพื่อเพิ่มประสิทธิภาพการค้นหาประเภทถัดไป


คุณช่วยอธิบายได้อย่างละเอียดว่า: "เป็นไปได้ที่จะสร้างโครงสร้างข้อมูลที่ให้ส่วนต่อประสานการเข้าถึงแบบอาเรย์เรียงตามลำดับซึ่งทั้งการอ่านและการเขียนนั้นแย่ที่สุดคือลอการิทึม"
Hengameh

1
มีบางสิ่งใน Wikipedia ภายใต้ Dynamic Array / Vriants มันไม่ใช่สิ่งที่ฉันคิดไว้ในใจ แต่ ... ลองนึกภาพต้นไม้ b + เช่นโครงสร้างที่มีหน้า แต่ไม่มีกุญแจแทนแต่ละหน้าระดับกลางจะจำได้ว่ามีองค์ประกอบจำนวนเท่าใดในแต่ละหน้าย่อย องค์ประกอบในอาร์เรย์ขนาดเล็ก เมื่อแทรกองค์ประกอบลงในหน้าใบไม้คุณต้องย้าย ~ ครึ่งหน้าเพื่อให้มีที่ว่างจากนั้นขึ้นไปและอัปเดตจำนวนรายการในหน้าบรรพบุรุษทั้งหมด เมื่อค้นหาองค์ประกอบ #N เพียงเพิ่มจำนวนรายการในหน้ารองจนกว่าคุณจะข้าม N จากนั้นลงไปที่ทรีย่อย
DenNukem

3

ในอาร์เรย์คุณมีสิทธิ์ในการเข้าถึงองค์ประกอบใด ๆ ในเวลา O (1) ดังนั้นจึงเหมาะสำหรับการดำเนินการเช่นการค้นหาแบบไบนารีการเรียงลำดับด่วน ฯลฯ รายการที่เชื่อมโยงในทางกลับกันเหมาะสำหรับการลบการแทรกตามเวลา O (1) ทั้งสองมีข้อดีเช่นเดียวกับข้อเสียและชอบ boils อื่น ๆ ลงไปในสิ่งที่คุณต้องการใช้

- คำถามที่ใหญ่กว่าคือเราจะมีลูกผสมของทั้งคู่ สิ่งที่ชอบสิ่งที่หลามและ Perl ใช้เป็นรายการ


3

รายการที่เชื่อมโยง

มันจะดีกว่าเมื่อมันมาถึงการแทรก! โดยพื้นฐานแล้วอะไรคือสิ่งที่เกี่ยวข้องกับตัวชี้

1 -> 3 -> 4

ส่วนแทรก (2)

1 ........ 3 ...... 4
..... 2

ในที่สุด

1 -> 2 -> 3 -> 4

หนึ่งลูกศรจาก 2 คะแนนที่ 3 และลูกศร 1 คะแนนที่ 2

! ง่าย

แต่จากอาเรย์

| 1 | 3 | 4 |

แทรก (2) | 1 | 3 | | 4 | | 1 | | 3 | 4 | | 1 | 2 | 3 | 4 |

ทุกคนสามารถเห็นความแตกต่าง! เพียง 4 ดัชนีเรากำลังดำเนินการ 3 ขั้นตอน

จะเป็นอย่างไรถ้าความยาวของอาร์เรย์เท่ากับหนึ่งล้าน อาร์เรย์มีประสิทธิภาพหรือไม่ คำตอบคือไม่! :)

สิ่งเดียวกันคือการลบ! ในรายการที่เชื่อมโยงเราสามารถใช้ตัวชี้และลบล้างองค์ประกอบและถัดไปในคลาสวัตถุ! แต่สำหรับอาร์เรย์เราต้องทำการ shiftLeft ()

หวังว่าจะช่วย! :)


3

รายการที่เชื่อมโยงมีค่าใช้จ่ายในการรักษามากกว่าอาเรย์มันยังต้องการหน่วยความจำเพิ่มเติมที่เก็บทุกจุด แต่มีบางสิ่งที่อาร์เรย์ไม่สามารถทำได้ ในหลายกรณีสมมติว่าคุณต้องการอาร์เรย์ที่มีความยาว 10 ^ 9 คุณไม่สามารถรับได้เพราะต้องมีตำแหน่งหน่วยความจำอย่างต่อเนื่อง รายการที่เชื่อมโยงอาจเป็นผู้ช่วยให้รอดที่นี่

สมมติว่าคุณต้องการจัดเก็บข้อมูลหลาย ๆ อย่างด้วยข้อมูลจึงสามารถขยายได้อย่างง่ายดายในรายการที่เชื่อมโยง

คอนเทนเนอร์ STL มักจะมีการเชื่อมโยงรายการการใช้งานอยู่เบื้องหลัง


3

1-รายการที่เชื่อมโยงเป็นโครงสร้างข้อมูลแบบไดนามิกเพื่อให้สามารถขยายและลดขนาดที่รันไทม์โดยการจัดสรรและการจัดสรรคืนหน่วยความจำ ดังนั้นจึงไม่จำเป็นต้องกำหนดขนาดเริ่มต้นของรายการที่เชื่อมโยง การแทรกและลบโหนดทำได้ง่ายกว่าจริงๆ

ขนาด2- รายการของการเชื่อมโยงสามารถเพิ่มหรือลดในขณะใช้งานจึงไม่มีการสูญเสียหน่วยความจำ ในกรณีของอาเรย์นั้นมีการสิ้นเปลืองหน่วยความจำมากมายเช่นถ้าเราประกาศอาเรย์ขนาด 10 และเก็บเพียง 6 องค์ประกอบในนั้นพื้นที่ 4 องค์ประกอบจะสูญเปล่า ไม่มีปัญหาดังกล่าวในรายการที่เชื่อมโยงเนื่องจากจัดสรรหน่วยความจำเมื่อจำเป็นเท่านั้น

3-โครงสร้างข้อมูลเช่นสแต็กและคิวสามารถดำเนินการได้อย่างง่ายดายโดยใช้รายการที่เชื่อมโยง


2

เหตุผลเดียวที่จะใช้รายการเชื่อมโยงคือการแทรกองค์ประกอบนั้นง่าย (ลบด้วย)

ข้อเสียอาจเป็นได้ว่าพอยน์เตอร์ใช้พื้นที่มาก

และเกี่ยวกับการเข้ารหัสนั้นยาก: โดยปกติคุณไม่จำเป็นต้องมีรายการเชื่อมโยงโค้ด (หรือเพียงครั้งเดียว) รหัสเหล่านั้นจะรวมอยู่ใน STL และมันก็ไม่ซับซ้อนหากคุณยังต้องทำ


2
พอยน์เตอร์ใช้พื้นที่มากใช่ไหม ไม่ได้จริงๆ หากคุณกำลังจัดเก็บรายการบูลีนที่เชื่อมโยงอยู่แน่นอนว่าเปอร์เซ็นต์พอยน์เตอร์ที่ฉลาดนั้นใช้พื้นที่จำนวนมาก แต่ถ้าคุณเก็บวัตถุที่ซับซ้อน (ซึ่งโดยทั่วไปแล้ว) พอยน์เตอร์อาจจะเล็กน้อย
Herms

ลืมรอยยิ้ม :) แต่พูดว่า "ทำได้" ไม่ได้ "คือ"
user15453

1

ฉันคิดว่ารายการลิงก์นั้นดีกว่าอาร์เรย์ เพราะเราเข้าไปในรายการลิงค์ แต่ไม่ใช่ในอาร์เรย์


1

ข้อเสียและข้อดีบางข้ออาจพิจารณาได้ขึ้นอยู่กับภาษาของคุณ:

ภาษาการเขียนโปรแกรม C : เมื่อใช้รายการเชื่อมโยง (โดยทั่วไปผ่านตัวชี้ struct) ต้องพิจารณาเป็นพิเศษว่าคุณจะไม่รั่วหน่วยความจำ ดังที่ได้กล่าวไว้ก่อนหน้านี้รายการที่เชื่อมโยงนั้นสามารถสับเปลี่ยนได้ง่ายเพราะทุกสิ่งที่ทำกำลังเปลี่ยนพอยน์เตอร์ แต่เราจะจำทุกอย่างให้เป็นอิสระได้หรือไม่?

Java : Java มีการรวบรวมขยะอัตโนมัติดังนั้นหน่วยความจำรั่วจะไม่มีปัญหา แต่ถูกซ่อนไว้จากโปรแกรมเมอร์ระดับสูงคือรายละเอียดการใช้งานของรายการที่เชื่อมโยงคืออะไร วิธีการเช่นการลบโหนดออกจากส่วนกลางของรายการนั้นมีความซับซ้อนของกระบวนการมากกว่าผู้ใช้บางคนของภาษาที่คาดว่าจะเป็น


1

ทำไมรายการที่เชื่อมโยงกับอาร์เรย์? อย่างที่บางคนพูดไปแล้วความเร็วที่เพิ่มขึ้นของการแทรกและการลบ

แต่บางทีเราไม่ต้องอยู่กับข้อ จำกัด ของทั้งสองและได้รับสิ่งที่ดีที่สุดทั้งสองในเวลาเดียวกัน ... เอ๊ะ?

สำหรับการลบอาร์เรย์คุณสามารถใช้ไบต์ 'ลบแล้ว' เพื่อแสดงถึงความจริงที่ว่าแถวถูกลบไปแล้วดังนั้นจึงไม่จำเป็นต้องใช้อาร์เรย์ซ้ำอีกครั้ง เพื่อลดภาระของการแทรกหรือเปลี่ยนแปลงข้อมูลอย่างรวดเร็วให้ใช้รายการที่เชื่อมโยง จากนั้นเมื่อพูดถึงสิ่งเหล่านี้ให้ใช้ตรรกะของคุณก่อนค้นหาสิ่งใดสิ่งหนึ่ง ดังนั้นเมื่อใช้ร่วมกันจะทำให้คุณได้รับสิ่งที่ดีที่สุด

หากคุณมีอาร์เรย์ขนาดใหญ่มากคุณสามารถรวมเข้ากับรายการอื่นที่มีขนาดเล็กกว่ามากหรือรายการที่ลิงก์ซึ่งรายการที่มีขนาดเล็กกว่ามีรายการที่ใช้ล่าสุด 20, 50, 100 รายการ หากสิ่งที่ต้องการไม่ได้อยู่ในรายการหรืออาเรย์ที่สั้นกว่าคุณจะไปที่อาเรย์ขนาดใหญ่ หากพบว่ามีคุณสามารถเพิ่มลงในลิสต์ / อาร์เรย์ที่เชื่อมโยงขนาดเล็กลงบนข้อสันนิษฐานว่า 'สิ่งที่ใช้บ่อยที่สุดนั้นเหมือนกันที่จะถูกนำมาใช้ซ้ำ' (และใช่อาจเป็นไปได้ว่าไอเท็มที่เพิ่งใช้น้อยที่สุดจากรายการ) ซึ่งเป็นจริงในหลายกรณีและแก้ไขปัญหาฉันต้องแก้ไขปัญหาในการตรวจสอบสิทธิ์การรักษาความปลอดภัย. ASP ด้วยความง่ายดายสง่างามและความเร็วที่น่าประทับใจ


1

ในขณะที่คุณหลายคนได้สัมผัสกับคำแนะนำที่สำคัญ / รายการของรายการที่เชื่อมโยงกับอาเรย์การเปรียบเทียบส่วนใหญ่เป็นวิธีที่ดีกว่า / แย่กว่าอื่น ๆ คุณสามารถเข้าถึงแบบสุ่มในอาร์เรย์ แต่ไม่สามารถทำได้ในรายการที่เชื่อมโยงและอื่น ๆ อย่างไรก็ตามนี่คือสมมติว่ารายการลิงก์และอาร์เรย์จะถูกนำไปใช้ในแอปพลิเคชันที่คล้ายกัน อย่างไรก็ตามคำตอบที่ถูกต้องควรเป็นวิธีการเชื่อมโยงรายการที่ต้องการมากกว่าอาร์เรย์และในทางกลับกันในการปรับใช้โปรแกรมประยุกต์เฉพาะ สมมติว่าคุณต้องการใช้แอปพลิเคชันพจนานุกรมคุณจะใช้อะไร Array: mmm มันจะช่วยให้ดึงข้อมูลได้ง่ายผ่านการค้นหาแบบไบนารี่และอัลโกค้นหาอื่น ๆ .. แต่ลองคิดดูสิว่ารายการลิสต์จะดีกว่าได้อย่างไรคุณต้องการค้นหา "Blob" ในพจนานุกรม มันจะสมเหตุสมผลไหมที่จะมีลิงค์ลิสต์ของ A-> B-> C-> D ---->

A -> B -> C -> ...Z
|    |    |
|    |    [Cat, Cave]
|    [Banana, Blob]
[Adam, Apple]

ตอนนี้วิธีการข้างต้นดีกว่าหรือเป็นแนวราบของ [Adam, Apple, Banana, Blob, Cat, Cave] มันจะเป็นไปได้ไหมกับอาเรย์ ดังนั้นข้อได้เปรียบที่สำคัญของลิสต์ลิงก์คือคุณสามารถมีอิลิเมนต์ไม่เพียงแค่ชี้ไปที่อิลิเมนต์ถัดไป แต่ยังรวมถึงลิสต์ลิงก์อื่น ๆ / อาเรย์ / ฮีป / หรือตำแหน่งหน่วยความจำอื่น ๆ Array เป็นหน่วยความจำที่ต่อเนื่องแบบแบนซึ่งแบ่งเป็นขนาดบล็อกขององค์ประกอบที่จะจัดเก็บ .. รายการลิงก์ในอีกทางหนึ่งเป็นชิ้นส่วนของหน่วยความจำที่ไม่ติดกัน (สามารถมีขนาดใดก็ได้และสามารถเก็บอะไรก็ได้) อื่น ๆ ในแบบที่คุณต้องการ ในทำนองเดียวกันสมมติว่าคุณกำลังสร้างไดรฟ์ USB ตอนนี้คุณต้องการให้ไฟล์ถูกบันทึกเป็นอาร์เรย์ใด ๆ หรือเป็นรายการลิงก์หรือไม่ ฉันคิดว่าคุณได้รับความคิดสิ่งที่ฉันกำลังชี้ไปที่ :)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.