หมายเหตุ:นี่เป็นตอนที่ 4 ของชุดข้อมูลที่ใช้อัลกอริทึมการจัดเรียงต่างๆ กับ Ruby ส่วนที่ 1 สำรวจการเรียงลำดับแบบฟอง ส่วนที่ 2 สำรวจการเลือกแบบสำรวจ และส่วนที่ 3 ที่สำรวจการเรียงลำดับการรวม
ในขณะที่เราสำรวจวิธีการต่างๆ สำหรับการจัดเรียงข้อมูลต่อไป เราจึงหันไปใช้การเรียงลำดับการแทรก มีหลายเหตุผลที่ชอบการเรียงลำดับการแทรก! อันดับแรก การเรียงลำดับการแทรก เสถียร ซึ่งหมายความว่าจะไม่เปลี่ยนลำดับสัมพัทธ์ขององค์ประกอบที่มีคีย์เท่ากัน นอกจากนี้ยังเป็นอัลกอริทึมแบบแทนที่ หมายความว่าไม่ได้สร้างอาร์เรย์ใหม่เพื่อจัดเก็บองค์ประกอบที่จัดเรียง สุดท้าย การจัดเรียงการแทรกเป็นอัลกอริธึมที่ค่อนข้างง่ายในการใช้งาน ดังที่คุณเห็นในเร็วๆ นี้!
ทำไมต้องแคร์
เป็นการยากที่จะหลีกเลี่ยงไม่ให้เสียงเหมือนเป็นประวัติการณ์ที่เสียที่นี่ แต่ดังที่เราได้พูดคุยกันในโพสต์ก่อนหน้านี้ทั้งหมด สิ่งสำคัญคือต้องมีความเข้าใจเกี่ยวกับกลไกต่างๆ ในการจัดเรียงข้อมูลและสิ่งที่แลกเปลี่ยนกับวิธีการต่างๆ ตัวอย่างเช่น แม้ว่าการเรียงลำดับการแทรกจะไม่ค่อยมีประโยชน์สำหรับชุดข้อมูลขนาดใหญ่ (เราจะสำรวจเพิ่มเติมด้านล่าง) แต่ก็อาจใช้ได้ดีและมีประสิทธิภาพทีเดียวสำหรับชุดข้อมูลขนาดเล็กและชุดข้อมูลที่ใกล้จะจัดเรียงแล้ว คุณจะได้เรียนรู้ว่าทำไมเมื่อเราดำเนินการใช้งานแล้ว แน่นอนว่าคุณมักจะใช้วิธีการที่มีอยู่แล้วภายในซึ่งภาษาโปรแกรมที่คุณเลือกมีไว้สำหรับการเรียงลำดับ แต่อาจปรากฏขึ้นเป็นคำถามในการสัมภาษณ์เป็นแบบฝึกหัดคู่หรืออาจเกี่ยวข้องกับความซับซ้อนของเวลา โชคดีที่เมื่อเราทำโพสต์นี้เสร็จแล้ว คุณจะสามารถโค้ดการเรียงลำดับการแทรกและทำความเข้าใจความซับซ้อนของเวลาได้อย่างง่ายดาย
การนำเสนอด้วยภาพ
ก่อนที่เราจะเริ่มต้นเขียนโค้ด เราขอแนะนำให้คุณดูวิดีโอต่อไปนี้ มันอธิบายการจัดเรียงแบบแทรกโดยใช้การเต้น และโดยส่วนตัวแล้ว ฉันยังไม่พอ! :)

คำแนะนำทีละขั้นตอนของโค้ด
มาดูโค้ดกันเลย!
def insertion_sort(array)
for i in 1...(array.length) # Step 1
j = i # Step 2
while j > 0 # Step 3
if array[j-1] > array[j] # Step 4
temp = array[j]
array[j] = array[j-1]
array[j-1] = temp
else
break
end
j = j - 1 # Step 5
end
end
return array
end
ขั้นตอนที่ 1:
เราเริ่มต้นด้วย for วงที่ตั้งค่าตัวแปร i เป็น 1 และเพิ่มขึ้นเรื่อย ๆ จนถึง i เท่ากับความยาวของอาร์เรย์ของเรา
ขั้นตอนที่ 2:
เราสร้างตัวแปรอื่น j และเริ่มต้นด้วยค่า 1 (เนื่องจากนั่นคือสิ่งที่ i คือ)
ขั้นตอนที่ 3:
ต่อไป เรามี while . ซ้อนกันอยู่ วนที่จะดำเนินต่อไปตราบเท่าที่ j มีค่ามากกว่าศูนย์ เนื่องจากเราเริ่มต้นด้วย j เท่ากับ 1 เรารู้ว่าการดำเนินการนี้จะดำเนินการอย่างน้อยหนึ่งครั้ง
ขั้นตอนที่ 4:
if... else บล็อกอาจจะดูน่ากลัวในตอนแรก แต่ไม่ต้องกังวล มันจะสมเหตุสมผลเมื่อเราเดินผ่านมันไป (และคุณสามารถทบทวนตัวอย่างการเต้นของ YouTube ได้เสมอ!)
สำหรับ if เงื่อนไขเราตรวจสอบว่า [j-1] มีค่ามากกว่า array[j] . ตั้งแต่ j ปัจจุบันคือ 1 เราจะเปรียบเทียบ array[0] ด้วย array[1] . เรื่องนี้สมเหตุสมผลเพราะเรากำลังเปรียบเทียบสององค์ประกอบแรกของอาร์เรย์
ถ้าองค์ประกอบแรก (array[0] ) ใหญ่กว่าอันถัดไป (array[1] ) แน่นอนว่าเราต้องสลับกัน ซึ่งเป็นสิ่งที่เกิดขึ้นภายใน if บล็อก. อย่างไรก็ตาม หากค่าใน array[0] น้อยกว่าค่าใน array[1] ดีมาก! เราไม่ต้องดำเนินการใดๆ เพราะมันได้จัดเรียงไว้แล้ว ดังนั้นเราจึงกด break ใน else บล็อก
ขั้นตอนที่ 5:
จากตรงนี้ เราลดค่า j . ตอนนี้เรากลับมาที่ for วนซ้ำ และ i ตอนนี้จะเป็น 2 . ลองนึกดูว่าเราจะเปรียบเทียบ array[1] . ได้อย่างไร ด้วย array[2] สำหรับการวนซ้ำครั้งแรกภายใน while วนซ้ำแล้วเราจะผ่าน while วนซ้ำอีกครั้งเพราะว่า j . ของเรา เริ่มที่ 2 เทียบกับ 1 .
ตัวอย่างที่มีข้อมูลจริง
มาดูโค้ดนี้กันโดยใช้อาร์เรย์ตัวอย่างต่อไปนี้ [5,7,2,10,9,12]
ในการทำซ้ำครั้งแรก เราจะเปรียบเทียบ 5 และ 7 . ตั้งแต่ 5 < 7 เราแยกส่วน if/else . ออกอย่างรวดเร็ว แล้วไปต่อ
ในการทำซ้ำครั้งต่อไป เราจะเปรียบเทียบ 7 และ 2 . ตอนนี้ ค่าเหล่านี้จะต้องสลับกัน ดังนั้นเราจะมี [5, 2, 7, 10, 9, 12] . จากนั้นเราจะสลับ 2 อีกครั้งกับ 5 ลงท้ายด้วย [2, 5, 7, 10, 9, 12] .
ในการทำซ้ำครั้งต่อไปภายใน for วนซ้ำ เราจะเปรียบเทียบ 10 และ 7 -- เย้! เรียบร้อยค่ะ
ต่อไปเราจะเปรียบเทียบ 10 และ 9 และพบว่าเราต้องแลก จากนั้น 7 น้อยกว่า 9 ดังนั้นเราจึงไม่ต้องทำการสลับอื่นใด ตอนนี้เหลือ [2, 5, 7, 9, 10, 12] .
การวนซ้ำครั้งสุดท้ายพบ 12 ซึ่งมากกว่า 10 ว้าว ว้าว! เสร็จเรียบร้อยและจัดเรียง
การวิเคราะห์ประสิทธิภาพ
แม้ว่าอัลกอริธึมการจัดเรียงบางตัวที่เราได้ดูไป ซึ่งก็คือ การเรียงลำดับแบบฟองสบู่ นั้นไม่ค่อยได้ฝึกฝนในชีวิตจริง การเรียงลำดับการแทรกอาจเป็นวิธีแก้ปัญหาที่สมเหตุสมผล ลองนึกภาพว่าอาร์เรย์ของเราได้รับการจัดเรียงแล้ว -- การเรียงลำดับการแทรกจะทำงานอย่างรวดเร็วและมีประสิทธิภาพมาก ในทางกลับกัน จะเกิดอะไรขึ้นหากเราจำเป็นต้องจัดเรียงอาร์เรย์ที่อยู่ในลำดับย้อนกลับ นั่นน่าจะเป็นสถานการณ์ที่เลวร้ายสำหรับการเรียงลำดับการแทรก
หากจัดเรียงอาร์เรย์แล้ว รหัสการจัดเรียงการแทรกจะทำงานที่ O(n) เนื่องจากจะต้องวนซ้ำ n . เท่านั้น ครั้ง หากคุณต้องการรับสิ่งนี้ ให้เพิ่ม puts i ที่ด้านบนของเมธอดและรันโปรแกรมที่ส่งผ่านอาร์เรย์ที่จัดเรียงไว้แล้ว
หากอาร์เรย์มีการเรียงลำดับย้อนกลับ รหัสการจัดเรียงการแทรกจะทำงานที่ O(n^2) คุณอาจจะนึกภาพสิ่งนี้ในหัวของคุณได้ เนื่องจากจะต้องทำการสลับติดต่อกัน มันจะกด if เงื่อนไขสำหรับทุกองค์ประกอบเดียว อ๊ะ! อีกครั้ง อย่าลังเลที่จะลองใช้สิ่งนี้โดยส่งผ่านอาร์เรย์ที่เรียงลำดับย้อนกลับและสร้างตัวแปรตัวนับที่พิมพ์ออกมา
แม้ว่ากรณีที่เลวร้ายที่สุดคือ O(n^2) ซึ่งคุณอาจจำได้เหมือนกันสำหรับการเรียงลำดับแบบฟองและการเรียงลำดับการเลือก การเรียงลำดับการแทรกมักจะดีกว่า เนื่องจากอย่างที่เราได้เห็น กรณีที่ดีที่สุดอาจเป็น O(n) ในขณะที่กรณีที่ดีที่สุดสำหรับการเรียงลำดับการเลือกคือ O(n^2) . การเรียงลำดับการแทรกยังมีการสลับน้อยกว่าการเรียงลำดับแบบฟอง ดังนั้นมันจึงชนะการต่อสู้ครั้งนี้
สรุปผล
ฉันหวังว่าโพสต์นี้จะเป็นประโยชน์และคุณรู้สึกมั่นใจเกี่ยวกับการทำความเข้าใจข้อดีข้อเสียของการเรียงลำดับการแทรก ตลอดจนวิธีการทำงานของอัลกอริทึม หากคุณยังมีอาการคันอยู่อีก เราขอแนะนำให้คุณตรวจสอบการเรียงลำดับการแทรกที่หน้าวิกิพีเดีย