หมายเหตุ:นี่เป็นตอนที่ 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)
. การเรียงลำดับการแทรกยังมีการสลับน้อยกว่าการเรียงลำดับแบบฟอง ดังนั้นมันจึงชนะการต่อสู้ครั้งนี้
สรุปผล
ฉันหวังว่าโพสต์นี้จะเป็นประโยชน์และคุณรู้สึกมั่นใจเกี่ยวกับการทำความเข้าใจข้อดีข้อเสียของการเรียงลำดับการแทรก ตลอดจนวิธีการทำงานของอัลกอริทึม หากคุณยังมีอาการคันอยู่อีก เราขอแนะนำให้คุณตรวจสอบการเรียงลำดับการแทรกที่หน้าวิกิพีเดีย