ฉันจะค้นหาตัวเลขในอาร์เรย์ 2d ที่เรียงจากซ้ายไปขวาและบนลงล่างได้อย่างไร


90

เมื่อไม่นานมานี้ฉันได้รับคำถามสัมภาษณ์นี้และฉันสงสัยว่าจะมีทางออกที่ดีอย่างไร

สมมติว่าฉันได้รับอาร์เรย์ 2d โดยที่ตัวเลขทั้งหมดในอาร์เรย์เรียงลำดับจากซ้ายไปขวาและบนลงล่าง

วิธีใดเป็นวิธีที่ดีที่สุดในการค้นหาและตรวจสอบว่ามีหมายเลขเป้าหมายอยู่ในอาร์เรย์หรือไม่

ตอนนี้ความโน้มเอียงแรกของฉันคือการใช้การค้นหาแบบไบนารีเนื่องจากข้อมูลของฉันถูกจัดเรียง ฉันสามารถระบุได้ว่าตัวเลขอยู่ในแถวเดียวในเวลา O (log N) หรือไม่ อย่างไรก็ตามมันเป็น 2 ทิศทางที่ทำให้ฉันผิดหวัง

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

ใครมีความคิดที่ดีในการแก้ปัญหานี้?

อาร์เรย์ตัวอย่าง:

เรียงจากซ้ายไปขวาบนลงล่าง

1  2  4  5  6  
2  3  5  7  8  
4  6  8  9  10  
5  8  9  10 11  

คำถามง่ายๆ: อาจเป็นไปได้ว่าคุณสามารถมีเพื่อนบ้านที่มีค่าเท่ากัน: [[1 1][1 1]]?
Matthieu M.

คำตอบ:


116

นี่เป็นแนวทางง่ายๆ:

  1. เริ่มต้นที่มุมล่างซ้าย
  2. ถ้าเป้าหมายมีค่าน้อยกว่าค่าที่จะต้องอยู่เหนือเราเพื่อเลื่อนขึ้นหนึ่ง
  3. มิฉะนั้นเรารู้ว่าเป้าหมายไม่สามารถอยู่ในคอลัมน์นั้นจึงย้ายหนึ่งที่เหมาะสม
  4. ไปที่ 2.

สำหรับอาร์เรย์นี้ทำงานในNxM O(N+M)ฉันคิดว่ามันคงยากที่จะทำได้ดีกว่านี้ :)


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

นี่คือรายละเอียดบางส่วนสำหรับผู้ที่สงสัย:

ประวัติศาสตร์

ขั้นตอนวิธีการที่เรียบง่ายนี้จะเรียกว่าการค้นหา Saddleback N == Mมันเป็นรอบในขณะที่และมันเป็นเรื่องที่ดีที่สุดเมื่อ ข้อมูลอ้างอิงบางส่วน:

  • เดวิดกรีส์, วิทยาศาสตร์ของการเขียนโปรแกรม Springer-Verlag 1989
  • Edsgar Dijkstra, Saddleback ค้นหา หมายเหตุ EWD-934 1985

อย่างไรก็ตามเมื่อN < Mสัญชาตญาณชี้ให้เห็นว่าการค้นหาแบบไบนารีควรจะทำได้ดีกว่าO(N+M): ตัวอย่างเช่นเมื่อใดN == 1การค้นหาไบนารีบริสุทธิ์จะทำงานในรูปแบบลอการิทึมแทนที่จะเป็นเวลาเชิงเส้น

กรณีที่เลวร้ายที่สุดถูกผูกไว้

Richard Bird ตรวจสอบสัญชาตญาณนี้ว่าการค้นหาแบบไบนารีสามารถปรับปรุงอัลกอริทึม Saddleback ในเอกสารปี 2006:

โดยใช้เทคนิคการสนทนาที่ผิดปกติค่อนข้างนกแสดงให้เราเห็นว่าปัญหานี้มีผูกพันที่ต่ำกว่าของN <= M Ω(N * log(M/N))ขอบเขตนี้มีความหมายเนื่องจากทำให้เรามีประสิทธิภาพเชิงเส้นN == Mและประสิทธิภาพลอการิทึมเมื่อN == 1ใด

อัลกอริทึมสำหรับอาร์เรย์สี่เหลี่ยม

แนวทางหนึ่งที่ใช้การค้นหาไบนารีทีละแถวมีลักษณะดังนี้:

  1. N < Mเริ่มต้นด้วยอาร์เรย์ที่เป็นรูปสี่เหลี่ยมผืนผ้า สมมติว่าNเป็นแถวและMเป็นคอลัมน์
  2. ทำการค้นหาแบบไบนารีในแถวกลางสำหรับvalue. ถ้าเราพบเราเสร็จแล้ว
  3. มิฉะนั้นเราได้พบคู่ที่อยู่ติดกันของตัวเลขsและที่gs < value < g
  4. สี่เหลี่ยมผืนผ้าของตัวเลขด้านบนและด้านซ้ายของsมีค่าน้อยกว่าvalueเราจึงสามารถกำจัดมันได้
  5. สี่เหลี่ยมผืนผ้าด้านล่างและทางขวาของgมากกว่าvalueเราจึงสามารถกำจัดมันได้
  6. ไปที่ขั้นตอนที่ (2) สำหรับแต่ละรูปสี่เหลี่ยมที่เหลือสองรูป

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

ขั้นตอนวิธีการนี้จะช่วยให้ความซับซ้อนของซึ่งแสดงให้เห็นว่านกจะเป็นT(N,M) = log(M) + 2 * T(M/2, N/2)O(N * log(M/N))

อีกวิธีหนึ่งที่โพสต์โดยเครก Gidneyอธิบายขั้นตอนวิธีการที่คล้ายกันวิธีการข้างต้น: M/Nมันตรวจสอบแถวในช่วงเวลาที่ใช้ขนาดขั้นตอนของ การวิเคราะห์ของเขาแสดงให้เห็นว่าสิ่งนี้ส่งผลให้เกิดO(N * log(M/N))ประสิทธิภาพเช่นกัน

การเปรียบเทียบประสิทธิภาพ

การวิเคราะห์ Big-O เป็นสิ่งที่ดีและดี แต่แนวทางเหล่านี้ทำงานได้ดีเพียงใดในทางปฏิบัติ? แผนภูมิด้านล่างตรวจสอบอัลกอริทึมสี่รายการสำหรับอาร์เรย์ "สี่เหลี่ยมจัตุรัส" ที่เพิ่มมากขึ้น:

ประสิทธิภาพของอัลกอริทึมเทียบกับกำลังสอง

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

ประเด็นเด่นบางประการ:

  • ตามที่คาดไว้อัลกอริทึม "การค้นหาแบบไบนารี" ให้ประสิทธิภาพที่ดีที่สุดสำหรับอาร์เรย์สี่เหลี่ยมและอัลกอริทึม Saddleback ทำงานได้ดีที่สุดในอาร์เรย์สี่เหลี่ยม
  • อัลกอริธึม Saddleback ทำงานได้แย่กว่าอัลกอริทึม "ไร้เดียงสา" สำหรับอาร์เรย์ 1 มิติซึ่งน่าจะเป็นเพราะทำการเปรียบเทียบหลายรายการในแต่ละรายการ
  • ประสิทธิภาพที่ยอดเยี่ยมที่อัลกอริทึม "การค้นหาแบบไบนารี" ใช้กับอาร์เรย์สี่เหลี่ยมนั้นน่าจะเกิดจากค่าใช้จ่ายในการเรียกใช้การค้นหาไบนารีซ้ำ ๆ

สรุป

การใช้การค้นหาแบบไบนารีอย่างชาญฉลาดสามารถให้O(N * log(M/N)ประสิทธิภาพสำหรับอาร์เรย์ทั้งสี่เหลี่ยมและสี่เหลี่ยม O(N + M)"Saddleback" ขั้นตอนวิธีการง่ายมาก แต่ทนทุกข์ทรมานจากการเสื่อมประสิทธิภาพอาร์เรย์กลายเป็นสี่เหลี่ยมมากขึ้น


6
ใช้การค้นหาไบนารีกับการเดินในแนวทแยงและคุณมี O (logN) หรือ O (logM) แล้วแต่จำนวนใดจะสูงกว่า
Anurag

4
@Anurag - ฉันไม่คิดว่าความซับซ้อนจะออกมาดีขนาดนั้น การค้นหาแบบไบนารีจะทำให้คุณเป็นจุดเริ่มต้นที่ดี แต่คุณจะต้องเดินไปอีกมิติหนึ่งหรืออีกมิติหนึ่งไปตลอดทางและในกรณีที่เลวร้ายที่สุดคุณยังสามารถเริ่มต้นในมุมหนึ่งและไปสิ้นสุดที่อีกมุมหนึ่งได้
Jeffrey L Whitledge

1
ถ้า N = 1 และ M = 1000000 ฉันทำได้ดีกว่า O (N + M) ดังนั้นอีกวิธีหนึ่งคือใช้การค้นหาแบบไบนารีในแต่ละแถวซึ่งนำ O (N * log (M)) โดยที่ N <M ในกรณีที่ให้ผล ค่าคงที่น้อยกว่า
Luka Rahne

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

1
ใช้อ้างอิงได้ดี! อย่างไรก็ตามเมื่อM==Nเราต้องการO(N)ความซับซ้อนไม่ใช่O(N*log(N/N))เนื่องจากหลังเป็นศูนย์ "ถูกต้องแบบครบวงจร" คมชัดที่ถูกผูกไว้คือเมื่อO(N*(log(M/N)+1)) N<=M
hardmath

35

ปัญหานี้ต้องใช้Θ(b lg(t))เวลาที่และb = min(w,h) t=b/max(w,h)ฉันพูดคุยเกี่ยวกับวิธีแก้ปัญหาในโพสต์บล็อกนี้

ขอบเขตล่าง

ฝ่ายตรงข้ามสามารถบังคับให้อัลกอริทึมทำการΩ(b lg(t))สืบค้นโดย จำกัด ตัวเองไว้ที่เส้นทแยงมุมหลัก:

ฝ่ายตรงข้ามใช้เส้นทแยงมุมหลัก

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

สังเกตว่ามีbรายการขนาดที่เรียงกันอย่างอิสระtซึ่งต้องใช้การΩ(b lg(t))สืบค้นเพื่อกำจัดทั้งหมด

อัลกอริทึม

  1. (สมมติโดยไม่สูญเสียทั่วไปว่าw >= h)
  2. เปรียบเทียบรายการเป้าหมายกับเซลล์tทางด้านซ้ายของมุมขวาบนของพื้นที่ที่ถูกต้อง
    • หากรายการของเซลล์ตรงกันให้คืนตำแหน่งปัจจุบัน
    • หากรายการของเซลล์น้อยกว่ารายการเป้าหมายให้กำจัดtเซลล์ที่เหลือในแถวด้วยการค้นหาแบบไบนารี หากพบรายการที่ตรงกันขณะทำสิ่งนี้ให้ส่งคืนพร้อมตำแหน่ง
    • มิฉะนั้นรายการของเซลล์จะมากกว่ารายการเป้าหมายโดยจะกำจัดtคอลัมน์สั้น ๆ
  3. หากไม่มีพื้นที่ที่ถูกต้องเหลืออยู่ให้ส่งคืนความล้มเหลว
  4. ไปที่ขั้นตอนที่ 2

การค้นหารายการ:

การค้นหารายการ

การกำหนดรายการไม่มีอยู่:

การกำหนดรายการไม่มีอยู่

คำอธิบาย: เซลล์สีขาวเป็นรายการที่มีขนาดเล็กเซลล์สีเทาเป็นรายการที่มีขนาดใหญ่กว่าและเซลล์สีเขียวเป็นรายการที่เท่ากัน

การวิเคราะห์

มีb*tคอลัมน์สั้น ๆ ที่จะกำจัด มีbแถวยาวให้กำจัด ขจัดค่าใช้จ่ายแถวยาวO(lg(t))เวลา การขจัดtคอลัมน์สั้น ๆ ทำให้เสียO(1)เวลา

O(lg(t)*b + b*t*1/t) = O(b lg(t))ในกรณีที่เลวร้ายที่สุดที่เราจะต้องกำจัดทุกคอลัมน์และแถวทุกสละเวลา

โปรดทราบว่าฉันกำลังสมมติว่าlgหนีบกับผลลัพธ์ที่สูงกว่า 1 (เช่นlg(x) = log_2(max(2,x))) ว่าทำไมเมื่อw=hความหมายที่เราได้รับการคาดหวังผูกพันของt=1O(b lg(1)) = O(b) = O(w+h)

รหัส

public static Tuple<int, int> TryFindItemInSortedMatrix<T>(this IReadOnlyList<IReadOnlyList<T>> grid, T item, IComparer<T> comparer = null) {
    if (grid == null) throw new ArgumentNullException("grid");
    comparer = comparer ?? Comparer<T>.Default;

    // check size
    var width = grid.Count;
    if (width == 0) return null;
    var height = grid[0].Count;
    if (height < width) {
        var result = grid.LazyTranspose().TryFindItemInSortedMatrix(item, comparer);
        if (result == null) return null;
        return Tuple.Create(result.Item2, result.Item1);
    }

    // search
    var minCol = 0;
    var maxRow = height - 1;
    var t = height / width;
    while (minCol < width && maxRow >= 0) {
        // query the item in the minimum column, t above the maximum row
        var luckyRow = Math.Max(maxRow - t, 0);
        var cmpItemVsLucky = comparer.Compare(item, grid[minCol][luckyRow]);
        if (cmpItemVsLucky == 0) return Tuple.Create(minCol, luckyRow);

        // did we eliminate t rows from the bottom?
        if (cmpItemVsLucky < 0) {
            maxRow = luckyRow - 1;
            continue;
        }

        // we eliminated most of the current minimum column
        // spend lg(t) time eliminating rest of column
        var minRowInCol = luckyRow + 1;
        var maxRowInCol = maxRow;
        while (minRowInCol <= maxRowInCol) {
            var mid = minRowInCol + (maxRowInCol - minRowInCol + 1) / 2;
            var cmpItemVsMid = comparer.Compare(item, grid[minCol][mid]);
            if (cmpItemVsMid == 0) return Tuple.Create(minCol, mid);
            if (cmpItemVsMid > 0) {
                minRowInCol = mid + 1;
            } else {
                maxRowInCol = mid - 1;
                maxRow = mid - 1;
            }
        }

        minCol += 1;
    }

    return null;
}

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

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

เพราะเข้าสู่ระบบ (1) = 0, ประมาณการซับซ้อนควรจะให้เป็นมากกว่าO(b*(lg(t)+1)) O(b*lg(t))เขียนได้ดีโดยเฉพาะ สำหรับการเรียกร้องความสนใจไปที่ "เทคนิคของฝ่ายตรงข้าม" ในการแสดง "กรณีที่เลวร้ายที่สุด"
hardmath

@hardmath ฉันพูดถึงสิ่งนั้นในคำตอบ ฉันชี้แจงมันเล็กน้อย
Craig Gidney

17

ฉันจะใช้กลยุทธ์แบ่งและพิชิตสำหรับปัญหานี้คล้ายกับที่คุณแนะนำ แต่รายละเอียดแตกต่างกันเล็กน้อย

นี่จะเป็นการค้นหาแบบวนซ้ำในส่วนย่อยของเมทริกซ์

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

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

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

และ ba-da-bing คุณพบแล้ว

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

นี่คือรหัสเทียมสำหรับคุณ:

bool numberSearch(int[][] arr, int value, int minX, int maxX, int minY, int maxY)

if (minX == maxX and minY == maxY and arr[minX,minY] != value)
    return false
if (arr[minX,minY] > value) return false;  // Early exits if the value can't be in 
if (arr[maxX,maxY] < value) return false;  // this subrange at all.
int nextX = (minX + maxX) / 2
int nextY = (minY + maxY) / 2
if (arr[nextX,nextY] == value)
{
    print nextX,nextY
    return true
}
else if (arr[nextX,nextY] < value)
{
    if (numberSearch(arr, value, minX, maxX, nextY + 1, maxY))
        return true
    return numberSearch(arr, value, nextX + 1, maxX, minY, nextY)
}
else
{
    if (numberSearch(arr, value, minX, nextX - 1, minY, maxY))
        return true
    reutrn numberSearch(arr, value, nextX, maxX, minY, nextY)
}

+1: นี่คือกลยุทธ์ O (log (N)) ดังนั้นจึงเป็นสิ่งที่ดีสำหรับคำสั่งซื้อที่จะได้รับ
Rex Kerr

3
@Rex Kerr - ดูเหมือน O (log (N)) เนื่องจากนั่นคือการค้นหาไบนารีปกติอย่างไรก็ตามโปรดทราบว่าอาจมีการเรียกซ้ำสองครั้งในแต่ละระดับ ซึ่งหมายความว่ามันแย่กว่าลอการิทึมธรรมดามาก ฉันไม่เชื่อว่ากรณีที่แย่กว่านั้นจะดีกว่า O (M + N) เนื่องจากอาจต้องค้นหาทุกแถวหรือทุกคอลัมน์ ฉันเดาว่าอัลกอริทึมนี้สามารถเอาชนะกรณีที่เลวร้ายที่สุดสำหรับค่าจำนวนมากได้ และส่วนที่ดีที่สุดก็คือมันสามารถทำให้เป็นอัมพาตได้เนื่องจากเมื่อเร็ว ๆ นี้ฮาร์ดแวร์กำลังมุ่งหน้าไป
Jeffrey L Whitledge

1
@JLW: มันคือ O (log (N)) - แต่จริงๆแล้วมันคือ O (log_ (4/3) (N ^ 2)) หรืออะไรทำนองนั้น ดูคำตอบของ Svante ด้านล่าง คำตอบของคุณเหมือนกันจริง ๆ (หากคุณหมายถึงการวนซ้ำในแบบที่ฉันคิดว่าคุณทำ)
Rex Kerr

1
@Svante - subarrays ไม่ทับซ้อนกัน ในตัวเลือกแรกพวกเขาไม่มีองค์ประกอบ y เหมือนกัน ในตัวเลือกที่สองพวกเขาไม่มีองค์ประกอบ x เหมือนกัน
Jeffrey L Whitledge

1
ฉันไม่แน่ใจว่านี่คือลอการิทึม ฉันคำนวณความซับซ้อนโดยใช้ความสัมพันธ์การเกิดซ้ำโดยประมาณ T (0) = 1, T (A) = T (A / 2) + T (A / 4) + 1 โดยที่ A คือพื้นที่ค้นหาและลงท้ายด้วย T ( A) = O (Fib (lg (A))) ซึ่งมีค่าประมาณ O (A ^ 0.7) และแย่กว่า O (n + m) ซึ่งก็คือ O (A ^ 0.5) บางทีฉันอาจจะทำพลาดโง่ ๆ ไปบ้าง แต่ดูเหมือนว่าอัลกอริทึมจะเสียเวลามากในการลงกิ่งไม้ที่ไร้ผล
Craig Gidney

6

คำตอบหลักสองข้อที่ให้มานั้นดูเหมือนจะเป็นO(log N)"วิธีซิกแซก" และO(N+M)วิธีการค้นหาแบบไบนารี ฉันคิดว่าฉันจะทำการทดสอบเปรียบเทียบทั้งสองวิธีกับการตั้งค่าต่างๆ นี่คือรายละเอียด:

อาร์เรย์คือ N x N กำลังสองในทุกการทดสอบโดย N แตกต่างกันไปตั้งแต่ 125 ถึง 8000 (ฮีป JVM ที่ใหญ่ที่สุดของฉันสามารถจัดการได้) 2สำหรับขนาดของอาร์เรย์แต่ละฉันเลือกเป็นสถานที่สุ่มในอาร์เรย์ที่จะนำเดี่ยว จากนั้นฉันใส่3ทุกที่ที่เป็นไปได้ (ทางด้านขวาและด้านล่างของ 2) จากนั้นเติมอาร์เรย์ที่เหลือด้วย1. ผู้แสดงความคิดเห็นก่อนหน้านี้บางคนดูเหมือนจะคิดว่าการตั้งค่าประเภทนี้จะให้เวลารันในกรณีที่เลวร้ายที่สุดสำหรับอัลกอริทึมทั้งสอง สำหรับอาร์เรย์แต่ละขนาดฉันเลือกตำแหน่งสุ่มที่แตกต่างกัน 100 ตำแหน่งสำหรับ 2 (เป้าหมายการค้นหา) และทำการทดสอบ ฉันบันทึกเวลาทำงานเฉลี่ยและเวลาทำงานกรณีที่เลวร้ายที่สุดสำหรับแต่ละอัลกอริทึม เนื่องจากมันเกิดขึ้นเร็วเกินไปที่จะอ่าน ms ที่ดีใน Java และเนื่องจากฉันไม่เชื่อถือ nanoTime () ของ Java ฉันจึงทำการทดสอบซ้ำทุกๆ 1,000 ครั้งเพื่อเพิ่มปัจจัยอคติที่สม่ำเสมอตลอดเวลา นี่คือผลลัพธ์:

ป้อนคำอธิบายภาพที่นี่

ZigZag เอาชนะไบนารีในทุกการทดสอบทั้งสำหรับเวลาเฉลี่ยและกรณีที่เลวร้ายที่สุดอย่างไรก็ตามทั้งหมดนี้อยู่ในลำดับความสำคัญของกันและกันไม่มากก็น้อย

นี่คือรหัส Java:

public class SearchSortedArray2D {

    static boolean findZigZag(int[][] a, int t) {
        int i = 0;
        int j = a.length - 1;
        while (i <= a.length - 1 && j >= 0) {
            if (a[i][j] == t) return true;
            else if (a[i][j] < t) i++;
            else j--;
        }
        return false;
    }

    static boolean findBinarySearch(int[][] a, int t) {
        return findBinarySearch(a, t, 0, 0, a.length - 1, a.length - 1);
    }

    static boolean findBinarySearch(int[][] a, int t,
            int r1, int c1, int r2, int c2) {
        if (r1 > r2 || c1 > c2) return false; 
        if (r1 == r2 && c1 == c2 && a[r1][c1] != t) return false;
        if (a[r1][c1] > t) return false;
        if (a[r2][c2] < t) return false;

        int rm = (r1 + r2) / 2;
        int cm = (c1 + c2) / 2;
        if (a[rm][cm] == t) return true;
        else if (a[rm][cm] > t) {
            boolean b1 = findBinarySearch(a, t, r1, c1, r2, cm - 1);
            boolean b2 = findBinarySearch(a, t, r1, cm, rm - 1, c2);
            return (b1 || b2);
        } else {
            boolean b1 = findBinarySearch(a, t, r1, cm + 1, rm, c2);
            boolean b2 = findBinarySearch(a, t, rm + 1, c1, r2, c2);
            return (b1 || b2);
        }
    }

    static void randomizeArray(int[][] a, int N) {
        int ri = (int) (Math.random() * N);
        int rj = (int) (Math.random() * N);
        a[ri][rj] = 2;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (i == ri && j == rj) continue;
                else if (i > ri || j > rj) a[i][j] = 3;
                else a[i][j] = 1;
            }
        }
    }

    public static void main(String[] args) {

        int N = 8000;
        int[][] a = new int[N][N];
        int randoms = 100;
        int repeats = 1000;

        long start, end, duration;
        long zigMin = Integer.MAX_VALUE, zigMax = Integer.MIN_VALUE;
        long binMin = Integer.MAX_VALUE, binMax = Integer.MIN_VALUE;
        long zigSum = 0, zigAvg;
        long binSum = 0, binAvg;

        for (int k = 0; k < randoms; k++) {
            randomizeArray(a, N);

            start = System.currentTimeMillis();
            for (int i = 0; i < repeats; i++) findZigZag(a, 2);
            end = System.currentTimeMillis();
            duration = end - start;
            zigSum += duration;
            zigMin = Math.min(zigMin, duration);
            zigMax = Math.max(zigMax, duration);

            start = System.currentTimeMillis();
            for (int i = 0; i < repeats; i++) findBinarySearch(a, 2);
            end = System.currentTimeMillis();
            duration = end - start;
            binSum += duration;
            binMin = Math.min(binMin, duration);
            binMax = Math.max(binMax, duration);
        }
        zigAvg = zigSum / randoms;
        binAvg = binSum / randoms;

        System.out.println(findZigZag(a, 2) ?
                "Found via zigzag method. " : "ERROR. ");
        //System.out.println("min search time: " + zigMin + "ms");
        System.out.println("max search time: " + zigMax + "ms");
        System.out.println("avg search time: " + zigAvg + "ms");

        System.out.println();

        System.out.println(findBinarySearch(a, 2) ?
                "Found via binary search method. " : "ERROR. ");
        //System.out.println("min search time: " + binMin + "ms");
        System.out.println("max search time: " + binMax + "ms");
        System.out.println("avg search time: " + binAvg + "ms");
    }
}

1
+1 เย้ข้อมูล. :) นอกจากนี้ยังอาจเป็นเรื่องที่น่าสนใจที่จะได้เห็นว่าทั้งสองวิธีมีค่าใช้จ่ายอย่างไรในอาร์เรย์ NxM เนื่องจากการค้นหาแบบไบนารีดูเหมือนว่าจะมีประโยชน์มากขึ้นโดยสัญชาตญาณเมื่อเราเข้าใกล้กรณี 1 มิติมากขึ้น
Nate Kohl

5

นี่เป็นข้อพิสูจน์สั้น ๆ ของขอบเขตล่างของปัญหา

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

1 2 3 4 *
2 3 4 * 7
3 4 * 7 8
4 * 7 8 9
* 7 8 9 10

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

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


คุณยังไม่ได้แสดงให้เห็นว่าคำตอบนั้นเหมาะสมที่สุด ยกตัวอย่างเช่นพิจารณาอาร์เรย์ที่มีค่า 10 ส่วนและ 1 ล้านดาวน์ซึ่งแถวที่ห้ามีค่าทั้งหมดที่สูงกว่าค่าเป้าหมาย ในกรณีนี้อัลกอริทึมที่เสนอจะทำการค้นหา linier ขึ้น 999,995 ค่าก่อนที่จะเข้าใกล้เป้าหมาย อัลกอริทึม bifurcating เช่นของฉันจะค้นหา 18 ค่าก่อนที่จะเข้าใกล้เป้าหมาย และมันทำงาน (asymtotically) ไม่เลวร้ายไปกว่าอัลกอริทึมที่เสนอในกรณีอื่น ๆ ทั้งหมด
Jeffrey L Whitledge

@ เจฟฟรีย์: มันเป็นขอบเขตล่างของปัญหาสำหรับกรณีที่มองโลกในแง่ร้าย คุณสามารถปรับให้เหมาะสมสำหรับอินพุตที่ดี แต่มีอินพุตที่คุณไม่สามารถทำได้ดีไปกว่าเชิงเส้น
Rafał Dowgird

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

นี่แสดงให้เห็นว่าอัลกอริทึมต้องใช้เวลา BigOmega (นาที (n, m)) ไม่ใช่ BigOmega (n + m) นั่นเป็นเหตุผลที่คุณทำได้ดีกว่ามากเมื่อมิติหนึ่งมีขนาดเล็กลงอย่างมาก ตัวอย่างเช่นถ้าคุณรู้ว่าจะมีเพียง 1 แถวคุณสามารถแก้ปัญหาในเวลาลอการิทึมได้ ฉันคิดว่าอัลกอริทึมที่ดีที่สุดจะใช้เวลา O (นาที (n + m, n lg m, m lg n))
Craig Gidney

อัปเดตคำตอบตามนั้น
Rafał Dowgird

4

ฉันคิดว่านี่คือคำตอบและใช้ได้กับเมทริกซ์ประเภทใดก็ได้


1

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

หากฉันกำลังมองหา 3 ในตัวอย่างของคุณฉันอ่านแถวแรกจนถึงตี 4 จากนั้นมองหาจำนวนที่อยู่ติดกันน้อยที่สุด (รวมทั้งเส้นทแยงมุม) ที่มากกว่า 3:

1 2 4 5 6
2 3 5 7 8
4 6 8 9 10
5 8 9 10 11

ตอนนี้ฉันทำเช่นเดียวกันสำหรับตัวเลขที่น้อยกว่า 3:

1 2 4 5 6
2 3 5 7 8
4 6 8 9 10
5 8 9 10 11

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

ฉันลองใช้สิ่งนี้กับตัวอย่างบางส่วนและดูเหมือนว่าจะใช้ได้ดี


โหวตลงโดยไม่มีความคิดเห็น? ฉันคิดว่านี่คือ O (N ^ 1/2) เนื่องจากประสิทธิภาพในกรณีที่เลวร้ายที่สุดต้องมีการตรวจสอบเส้นทแยงมุม อย่างน้อยก็แสดงตัวอย่างการตอบโต้ที่วิธีนี้ใช้ไม่ได้!
Grembo

+1: ทางออกที่ดี ... สร้างสรรค์และดีที่พบวิธีแก้ปัญหาทั้งหมด
Tony Delroy

1

การค้นหาแบบไบนารีผ่านเส้นทแยงมุมของอาร์เรย์เป็นตัวเลือกที่ดีที่สุด เราสามารถค้นหาได้ว่าองค์ประกอบนั้นน้อยกว่าหรือเท่ากับองค์ประกอบในเส้นทแยงมุม


0

A. ทำการค้นหาแบบไบนารีในบรรทัดเหล่านั้นซึ่งอาจมีหมายเลขเป้าหมายอยู่

B. ทำให้มันเป็นกราฟ: มองหาตัวเลขโดยการหาโหนดเพื่อนบ้านที่เล็กที่สุดที่ไม่ได้เยี่ยมชมและทำการย้อนรอยเมื่อพบตัวเลขที่ใหญ่เกินไป


0

การค้นหาแบบไบนารีจะเป็นแนวทางที่ดีที่สุด imo เริ่มต้นที่ 1/2 x, 1/2 y จะผ่าครึ่ง IE a 5x5 square จะเป็น x == 2 / y == 3 ฉันปัดเศษค่าหนึ่งค่าลงและค่าหนึ่งขึ้นไปยังโซนที่ดีกว่าตามทิศทางของค่าเป้าหมาย

เพื่อความชัดเจนการทำซ้ำครั้งต่อไปจะทำให้คุณได้อะไรเช่น x == 1 / y == 2 หรือ x == 3 / y == 5


0

เริ่มต้นด้วยสมมติว่าเรากำลังใช้กำลังสอง

1 2 3
2 3 4
3 4 5

1. ค้นหาสี่เหลี่ยม

ฉันจะใช้การค้นหาไบนารีบนเส้นทแยงมุม เป้าหมายคือค้นหาจำนวนที่น้อยกว่าซึ่งไม่ต่ำกว่าจำนวนเป้าหมายอย่างเคร่งครัด

บอกว่าฉันกำลังมองหา4เช่นนั้นฉันจะสิ้นสุดตำแหน่งที่5(2,2)

จากนั้นผมมั่นใจว่าถ้า4เป็นในตารางมันอยู่ที่ตำแหน่งใด(x,2)หรือ(2,x)มีในx [0,2]นั่นเป็นเพียงการค้นหาไบนารี 2 รายการ

ความซับซ้อนไม่น่ากลัว: O(log(N))(3 การค้นหาไบนารีในช่วงของความยาวN)

2. ค้นหารูปสี่เหลี่ยมผืนผ้าวิธีไร้เดียงสา

แน่นอนว่ามันจะซับซ้อนขึ้นเล็กน้อยเมื่อNและMแตกต่างกัน (ด้วยรูปสี่เหลี่ยมผืนผ้า) ให้พิจารณากรณีที่เสื่อมถอยนี้:

1  2  3  4  5  6  7  8
2  3  4  5  6  7  8  9
10 11 12 13 14 15 16 17

และสมมติว่าฉันกำลังมองหา9... แนวทแยงมุมก็ยังดี แต่นิยามของเส้นทแยงมุมเปลี่ยนไป [1, (5 or 6), 17]นี่คือเส้นทแยงมุมของฉันคือ สมมติว่าฉันหยิบขึ้นมา[1,5,17]แล้วฉันรู้ว่าถ้า9อยู่ในตารางมันอยู่ในส่วนย่อย:

            5  6  7  8
            6  7  8  9
10 11 12 13 14 15 16

สิ่งนี้ทำให้เรามีรูปสี่เหลี่ยม 2 รูป:

5 6 7 8    10 11 12 13 14 15 16
6 7 8 9

เพื่อให้เราสามารถเรียกคืนได้! อาจเริ่มต้นด้วยองค์ประกอบที่มีองค์ประกอบน้อยกว่า (แม้ว่าในกรณีนี้มันจะฆ่าเรา)

ฉันควรชี้ว่าหากมิติใดมิติหนึ่งน้อยกว่า3เราไม่สามารถใช้วิธีการเส้นทแยงมุมได้และต้องใช้การค้นหาแบบไบนารี ในที่นี้จะหมายถึง:

  • ใช้การค้นหาแบบไบนารีบน10 11 12 13 14 15 16ไม่พบ
  • ใช้การค้นหาแบบไบนารีบน5 6 7 8ไม่พบ
  • ใช้การค้นหาแบบไบนารีบน6 7 8 9ไม่พบ

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

3. ค้นหารูปสี่เหลี่ยมผืนผ้าวิธีการที่โหดร้าย

มันจะง่ายกว่ามากถ้าเราจัดการกับสี่เหลี่ยม ...

1  2  3  4  5  6  7  8
2  3  4  5  6  7  8  9
10 11 12 13 14 15 16 17
17 .  .  .  .  .  .  17
.                    .
.                    .
.                    .
17 .  .  .  .  .  .  17

ตอนนี้เรามีสี่เหลี่ยม

แน่นอนว่าเราอาจจะไม่สร้างแถวเหล่านั้นจริงๆเราสามารถจำลองแถวเหล่านั้นได้

def get(x,y):
  if x < N and y < M: return table[x][y]
  else: return table[N-1][M-1]            # the max

ดังนั้นมันจึงทำงานเหมือนสี่เหลี่ยมโดยไม่ต้องใช้หน่วยความจำมากขึ้น (ในราคาความเร็วอาจขึ้นอยู่กับแคช ... โอ้ดี: p)


0

แก้ไข:

ฉันเข้าใจคำถามผิด ดังที่ความคิดเห็นชี้ให้เห็นว่าสิ่งนี้ใช้ได้เฉพาะในกรณีที่มีการ จำกัด มากขึ้น

ในภาษาเช่น C ที่จัดเก็บข้อมูลตามลำดับแถวหลักให้ถือว่าเป็นอาร์เรย์ 1D ขนาด n * m และใช้การค้นหาแบบไบนารี


ใช่ทำไมต้องทำให้ซับซ้อนกว่าที่เป็นอยู่
erikkallen

ไม่มีการจัดเรียงอาร์เรย์จึงไม่สามารถใช้การค้นหา bin ได้
Miollnyr

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

ขอบคุณฉันแก้ไขคำตอบแล้ว ไม่ได้อ่านอย่างละเอียดเพียงพอโดยเฉพาะอาร์เรย์ตัวอย่าง
Hugh Brackett

0

ฉันมีโซลูชัน Divide & Conquer แบบวนซ้ำ แนวคิดพื้นฐานสำหรับขั้นตอนเดียวคือเรารู้ว่า Left-Upper (LU) มีขนาดเล็กที่สุดและด้านขวาล่าง (RB) เป็นหมายเลขที่ใหญ่ที่สุดดังนั้น No (N) ที่กำหนดต้อง: N> = LU และ N <= RB

IF N == LU และ N == RB :::: องค์ประกอบที่พบและยกเลิกการส่งคืนตำแหน่ง / ดัชนีถ้า N> = LU และ N <= RB = FALSE ไม่มีไม่มีและยกเลิก ถ้า N> = LU และ N <= RB = TRUE ให้แบ่งอาร์เรย์ 2D ออกเป็น 4 ส่วนเท่า ๆ กันของอาร์เรย์ 2D แต่ละส่วนในลักษณะตรรกะ .. จากนั้นใช้ขั้นตอนเดียวกันกับอาร์เรย์ย่อยทั้งสี่

Algo ของฉันถูกต้องฉันได้ติดตั้งบนพีซีของเพื่อน ความซับซ้อน: การเปรียบเทียบแต่ละ 4 ครั้งสามารถใช้ b เพื่ออนุมานจำนวนองค์ประกอบที่ไม่รวมเป็นหนึ่งในสี่ในกรณีที่เลวร้ายที่สุด .. ดังนั้นความซับซ้อนของฉันจึงเป็น 1 + 4 x lg (n) + 4 แต่คาดว่าสิ่งนี้จะใช้ได้กับ O (n)

ฉันคิดว่ามีบางอย่างผิดปกติในการคำนวณ Complexity ของฉันโปรดแก้ไขหากเป็นเช่นนั้น ..


0

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

มิฉะนั้นจากที่นี่เราสามารถดำเนินการได้สองวิธี

ยุทธศาสตร์ที่ 1:

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

กลยุทธ์ที่ 2: ให้ฉันแสดงดัชนีแถวและ j หมายถึงดัชนีคอลัมน์ขององค์ประกอบเส้นทแยงมุมที่เราหยุดอยู่ (ที่นี่เรามี i = j, BTW) ให้ k = 1

  • ทำซ้ำขั้นตอนด้านล่างจนกระทั่ง ik> = 0
    1. ค้นหาว่า [ik] [j] เท่ากับองค์ประกอบที่กำหนดหรือไม่ ถ้าใช่กลับพบว่าเป็นจริง
    2. ค้นหาว่า [i] [jk] เท่ากับองค์ประกอบที่กำหนดหรือไม่ ถ้าใช่กลับพบว่าเป็นจริง
    3. เพิ่ม k

1 2 4 5 6
2 3 5 7 8
4 6 8 9 10
5 8 9 10 11


0
public boolean searchSortedMatrix(int arr[][] , int key , int minX , int maxX , int minY , int maxY){

    // base case for recursion
    if(minX > maxX || minY > maxY)
        return false ;
    // early fails
    // array not properly intialized
    if(arr==null || arr.length==0)
        return false ;
    // arr[0][0]> key return false
    if(arr[minX][minY]>key)
        return false ;
    // arr[maxX][maxY]<key return false
    if(arr[maxX][maxY]<key)
        return false ;
    //int temp1 = minX ;
    //int temp2 = minY ;
    int midX = (minX+maxX)/2 ;
    //if(temp1==midX){midX+=1 ;}
    int midY = (minY+maxY)/2 ;
    //if(temp2==midY){midY+=1 ;}


    // arr[midX][midY] = key ? then value found
    if(arr[midX][midY] == key)
        return true ;
    // alas ! i have to keep looking

    // arr[midX][midY] < key ? search right quad and bottom matrix ;
    if(arr[midX][midY] < key){
        if( searchSortedMatrix(arr ,key , minX,maxX , midY+1 , maxY))
            return true ;
        // search bottom half of matrix
        if( searchSortedMatrix(arr ,key , midX+1,maxX , minY , maxY))
            return true ;
    }
    // arr[midX][midY] > key ? search left quad matrix ;
    else {
         return(searchSortedMatrix(arr , key , minX,midX-1,minY,midY-1));
    }
    return false ;

}

0

ฉันขอแนะนำให้เก็บอักขระทั้งหมดในไฟล์ 2D list . จากนั้นค้นหาดัชนีขององค์ประกอบที่ต้องการหากมีอยู่ในรายการ

หากไม่มีให้พิมพ์ข้อความที่เหมาะสมให้พิมพ์แถวและคอลัมน์เป็น:

row = (index/total_columns) และ column = (index%total_columns -1)

สิ่งนี้จะเกิดขึ้นเฉพาะเวลาค้นหาไบนารีในรายการ

กรุณาแนะนำการแก้ไขใด ๆ :)


0

ถ้าโซลูชันO (M log (N))ใช้ได้สำหรับอาร์เรย์ MxN -

template <size_t n>
struct MN * get(int a[][n], int k, int M, int N){
  struct MN *result = new MN;
  result->m = -1;
  result->n = -1;

  /* Do a binary search on each row since rows (and columns too) are sorted. */
  for(int i = 0; i < M; i++){
    int lo = 0; int hi = N - 1;
    while(lo <= hi){
      int mid = lo + (hi-lo)/2;
      if(k < a[i][mid]) hi = mid - 1;
      else if (k > a[i][mid]) lo = mid + 1;
      else{
        result->m = i;
        result->n = mid;
        return result;
      }
    }
  }
  return result;
}

การสาธิตการทำงาน C ++

โปรดแจ้งให้เราทราบหากวิธีนี้ใช้ไม่ได้ผลหรือมีข้อผิดพลาดเกิดขึ้น


0

ฉันถามคำถามนี้ในการสัมภาษณ์ในช่วงที่ดีขึ้นของทศวรรษและฉันคิดว่ามีเพียงคนเดียวที่สามารถสร้างอัลกอริทึมที่เหมาะสมที่สุดได้

วิธีแก้ปัญหาของฉันคือ:

  1. (rows.count/2, columns.count/2)ค้นหาแบบไบนารีเส้นทแยงมุมตรงกลางซึ่งเป็นที่ทำงานในแนวทแยงลงและขวามีรายการที่

  2. หากพบหมายเลขเป้าหมายให้ส่งคืนจริง

  3. มิฉะนั้นจะพบตัวเลขสองตัว ( uและv) ซึ่งuมีขนาดเล็กกว่าเป้าหมายvมีขนาดใหญ่กว่าเป้าหมายและvเป็นตัวเลขที่ถูกต้องและต่ำuกว่า

  4. ซ้ำค้นหาย่อยเมทริกซ์ไปทางขวาของuและด้านบนของvและหนึ่งไปยังด้านล่างของและด้านซ้ายของuv

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

นี่คือรหัสใน (อาจจะไม่ใช่ Swifty มาก) Swift:

import Cocoa

class Solution {
    func searchMatrix(_ matrix: [[Int]], _ target: Int) -> Bool {
        if (matrix.isEmpty || matrix[0].isEmpty) {
            return false
        }

        return _searchMatrix(matrix, 0..<matrix.count, 0..<matrix[0].count, target)
    }

    func _searchMatrix(_ matrix: [[Int]], _ rows: Range<Int>, _ columns: Range<Int>, _ target: Int) -> Bool {
        if (rows.count == 0 || columns.count == 0) {
            return false
        }
        if (rows.count == 1) {
            return _binarySearch(matrix, rows.lowerBound, columns, target, true)
        }
        if (columns.count == 1) {
            return _binarySearch(matrix, columns.lowerBound, rows, target, false)
        }

        var lowerInflection = (-1, -1)
        var upperInflection = (Int.max, Int.max)
        var currentRows = rows
        var currentColumns = columns
        while (currentRows.count > 0 && currentColumns.count > 0 && upperInflection.0 > lowerInflection.0+1) {
            let rowMidpoint = (currentRows.upperBound + currentRows.lowerBound) / 2
            let columnMidpoint = (currentColumns.upperBound + currentColumns.lowerBound) / 2
            let value = matrix[rowMidpoint][columnMidpoint]
            if (value == target) {
                return true
            }

            if (value > target) {
                upperInflection = (rowMidpoint, columnMidpoint)
                currentRows = currentRows.lowerBound..<rowMidpoint
                currentColumns = currentColumns.lowerBound..<columnMidpoint
            } else {
                lowerInflection = (rowMidpoint, columnMidpoint)
                currentRows = rowMidpoint+1..<currentRows.upperBound
                currentColumns = columnMidpoint+1..<currentColumns.upperBound
            }
        }
        if (lowerInflection.0 == -1) {
            lowerInflection = (upperInflection.0-1, upperInflection.1-1)
        } else if (upperInflection.0 == Int.max) {
            upperInflection = (lowerInflection.0+1, lowerInflection.1+1)
        }

        return _searchMatrix(matrix, rows.lowerBound..<lowerInflection.0+1, upperInflection.1..<columns.upperBound, target) || _searchMatrix(matrix, upperInflection.0..<rows.upperBound, columns.lowerBound..<lowerInflection.1+1, target)
    }

    func _binarySearch(_ matrix: [[Int]], _ rowOrColumn: Int, _ range: Range<Int>, _ target: Int, _ searchRow : Bool) -> Bool {
        if (range.isEmpty) {
            return false
        }

        let midpoint = (range.upperBound + range.lowerBound) / 2
        let value = (searchRow ? matrix[rowOrColumn][midpoint] : matrix[midpoint][rowOrColumn])
        if (value == target) {
            return true
        }

        if (value > target) {
            return _binarySearch(matrix, rowOrColumn, range.lowerBound..<midpoint, target, searchRow)
        } else {
            return _binarySearch(matrix, rowOrColumn, midpoint+1..<range.upperBound, target, searchRow)
        }
    }
}

-1

ให้ตารางเมทริกซ์ดังนี้:

[abc]
[def]
[ijk]

เรารู้ว่า a <c, d <f, i <k. สิ่งที่เราไม่รู้ก็คือ d <c หรือ d> c เป็นต้นเรามีการค้ำประกันใน 1 มิติเท่านั้น

เมื่อมองไปที่องค์ประกอบสุดท้าย (c, f, k) เราสามารถจัดเรียงตัวกรอง: คือ N <c? ค้นหา (): next () ดังนั้นเราจึงมีการวนซ้ำ n ในแถวโดยแต่ละแถวจะใช้ O (log (n)) สำหรับการค้นหาแบบไบนารีหรือ O (1) หากกรองออก

ขอฉันให้ตัวอย่างโดยที่ N = j,

1) ตรวจสอบแถวที่ 1. j <c? (ไม่ไปต่อ)

2) ตรวจสอบแถวที่ 2. j <f? (ใช่การค้นหา bin ไม่ได้รับอะไรเลย)

3) ตรวจสอบแถวที่ 3 j <k? (ใช่การค้นหา bin พบ)

ลองอีกครั้งโดยใช้ N = q

1) ตรวจสอบแถวที่ 1 q <c? (ไม่ไปต่อ)

2) ตรวจสอบแถวที่ 2 q <f? (ไม่ไปต่อ)

3) ตรวจสอบแถวที่ 3 q <k? (ไม่ไปต่อ)

อาจมีทางออกที่ดีกว่านี้ แต่อธิบายได้ง่าย .. :)


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