ส่วนใหญ่คำตอบที่นี่มีความผิด คำตอบที่ถูกต้องคือมันขึ้นอยู่กับ ตัวอย่างเช่นนี่คือฟังก์ชัน C สองฟังก์ชันที่เดินผ่านต้นไม้ ก่อนอื่น recursive:
static
void mm_scan_black(mm_rc *m, ptr p) {
SET_COL(p, COL_BLACK);
P_FOR_EACH_CHILD(p, {
INC_RC(p_child);
if (GET_COL(p_child) != COL_BLACK) {
mm_scan_black(m, p_child);
}
});
}
และนี่คือฟังก์ชั่นเดียวกับที่ใช้กับการวนซ้ำ:
static
void mm_scan_black(mm_rc *m, ptr p) {
stack *st = m->black_stack;
SET_COL(p, COL_BLACK);
st_push(st, p);
while (st->used != 0) {
p = st_pop(st);
P_FOR_EACH_CHILD(p, {
INC_RC(p_child);
if (GET_COL(p_child) != COL_BLACK) {
SET_COL(p_child, COL_BLACK);
st_push(st, p_child);
}
});
}
}
มันไม่สำคัญที่จะเข้าใจรายละเอียดของรหัส เพียงแค่นั่นp
คือโหนดและนั่นP_FOR_EACH_CHILD
จะเป็นการเดิน ในรุ่นที่ซ้ำกันเราต้องการสแต็คอย่างชัดเจนst
ไปที่โหนดจะถูกผลักแล้วผุดและจัดการ
ฟังก์ชั่นวนซ้ำทำงานเร็วกว่าฟังก์ชั่นวนซ้ำมาก เหตุผลเป็นเพราะในระยะหลังสำหรับแต่ละรายการที่CALL
จะฟังก์ชั่นที่จำเป็นและจากนั้นอีกครั้งเพื่อให้st_push
st_pop
ในอดีตคุณมีการเรียกซ้ำCALL
สำหรับแต่ละโหนดเท่านั้น
นอกจากนี้การเข้าถึงตัวแปรบน callstack นั้นทำได้รวดเร็วอย่างไม่น่าเชื่อ หมายความว่าคุณกำลังอ่านจากหน่วยความจำซึ่งน่าจะอยู่ในแคชด้านในสุดเสมอ ในทางกลับกันสแต็กที่ชัดเจนต้องได้รับการสนับสนุนโดยmalloc
: ed memory จาก heap ซึ่งเข้าถึงได้ช้ากว่ามาก
ด้วยการปรับให้เหมาะสมอย่างระมัดระวังเช่น inlining st_push
และst_pop
ฉันสามารถเข้าถึงความเท่าเทียมกันอย่างคร่าวๆด้วยวิธีเรียกซ้ำ แต่อย่างน้อยในคอมพิวเตอร์ของฉันค่าใช้จ่ายในการเข้าถึงหน่วยความจำฮีปนั้นใหญ่กว่าค่าใช้จ่ายของการโทรซ้ำ
แต่การสนทนานี้ส่วนใหญ่จะเป็นที่สงสัยเพราะเดินต้นไม้ recursive เป็นที่ไม่ถูกต้อง หากคุณมีต้นไม้ขนาดใหญ่พอคุณจะหมดพื้นที่ callstack ซึ่งเป็นสาเหตุที่ต้องใช้อัลกอริทึมซ้ำ