Làm thế nào để tìm phần tử giữa của danh sách được liên kết trong một lần?


12

Một trong những câu hỏi phổ biến nhất từ ​​cấu trúc dữ liệu và thuật toán, chủ yếu được hỏi về phỏng vấn qua điện thoại.


5
Nếu các câu trả lời được trình bày trong chủ đề này là những gì người phỏng vấn mong đợi, thì câu hỏi này không kiểm tra khả năng kỹ thuật, nhưng ứng viên có thể né tránh như một luật sư.
G. Bạch

2
Đây là một câu hỏi phỏng vấn khủng khiếp bởi vì nó bản lề về thuật ngữ " vượt qua " mơ hồ, mơ hồ, chủ quan. Hầu như tất cả các câu trả lời tốt cho câu hỏi này liên quan đến việc lạm dụng định nghĩa để bạn có thể bỏ qua nó một cách hiệu quả.
RBarryYoung

2
Vâng, câu hỏi đặt ra rất nhiều cuộc thảo luận ở đây. Điều đó có nghĩa là nó là một câu hỏi phỏng vấn tốt ở một khía cạnh: nó bắt đầu bạn suy nghĩ.
Hendrik ngày

3
Tôi rất muốn trả lời "đọc tất cả các phần tử vào một vectơ, sau đó truy cập phần tử ở kích thước vị trí () / 2"
Cort Ammon

1
@HendrikJan Thật ra, tôi nghĩ đó là một câu hỏi phỏng vấn hoàn toàn khủng khiếp. Đầu tiên, nó có khả năng dẫn đến tranh luận về ý nghĩa chính xác của "trong một lượt", thay vì thảo luận có ích. Thứ hai, một ứng viên có thể tìm ra câu trả lời "đúng" và sau đó từ chối vì họ nghĩ rằng nó đã vi phạm tiêu chí "trong một lần". Thứ ba, bởi vì đó là một câu hỏi nổi tiếng, nó là một bài kiểm tra tốt hơn về "Bạn có biết các câu hỏi phỏng vấn phổ biến không?" hơn "Bạn có phù hợp với công việc này không?" Bất kỳ một trong những điều đó là đủ để nhấn chìm điều này như một câu hỏi; cả ba đồng thời là một thảm họa.
David Richerby

Câu trả lời:


24

Bằng cách gian lận, và thực hiện hai đường chuyền cùng một lúc, song song. Nhưng tôi không biết liệu các nhà tuyển dụng sẽ thích điều này.

Có thể được thực hiện trên một danh sách liên kết duy nhất, với một mẹo hay. Hai con trỏ đi qua danh sách, một với tốc độ gấp đôi. Khi cái nhanh đến cuối cùng, cái kia là một nửa.


1
Vâng, không rõ liệu đây là một lần vượt qua. Câu hỏi không rõ ràng về điểm đó.
David Richerby

2
Nhân tiện, điều này có liên quan đến câu đố nơi bạn có hai cây nến, mỗi lần đốt một giờ và bạn được yêu cầu đo 45 phút.
Hendrik ngày

2
Đây có thực sự là bất kỳ khác nhau sau đó lặp lại danh sách, đếm các yếu tố, và sau đó lặp lại lần thứ hai đến điểm nửa đường? Tất cả khác nhau là khi bạn lặp lại một nửa. Như @RBarryYoung đề cập đến câu trả lời tương tự khác, nó thực sự không phải là một lần vượt qua, đó là một lần rưỡi.

2
Nếu danh sách dài, việc di chuyển cả hai con trỏ "song song" sẽ phát sinh ít lỗi nhớ cache hơn so với lặp lại từ đầu lần thứ hai.
zwol

2
Điều này sử dụng nguyên tắc tương tự như thuật toán rùa và thỏ để phát hiện chu kỳ.
Joshua Taylor

7

Nếu đó không phải là danh sách được liên kết đôi, bạn chỉ có thể đếm và sử dụng danh sách, nhưng điều đó đòi hỏi phải tăng gấp đôi bộ nhớ của bạn trong trường hợp xấu nhất và đơn giản là nó sẽ không hoạt động nếu danh sách quá lớn để lưu trữ trong bộ nhớ.

Một giải pháp đơn giản, gần như ngớ ngẩn, chỉ là tăng nút giữa mỗi hai nút

function middle(start) {
    var middle = start
    var nextnode = start
    var do_increment = false;
    while (nextnode.next != null) {
        if (do_increment) {
             middle = middle.next;
        }
        do_increment = !do_increment;
        nextnode = nextnode.next;
    }
    return middle;
}

Tùy chọn thứ hai của bạn là câu trả lời đúng (tất nhiên là IMHO).
Matthew Crumley

1
Nó thực sự làm cho 1 1/2 vượt qua danh sách được liên kết.
RBarryYoung

6

Xây dựng câu trả lời của Hendrik

Nếu đó là một danh sách liên kết đôi, lặp đi lặp lại từ cả hai đầu

function middle(start, end) {
  do_advance_start = false;
  while(start !== end && start && end) {
     if (do_advance_start) {
        start = start.next
     }
     else {
        end = end.prev
     }
     do_advance_start = !do_advance_start
  }
  return (start === end) ? start : null;
}

Được [1, 2, 3] => 2

1, 3
1, 2
2, 2

Được [1, 2] => 1

1, 2
1, 1

Được [1] => 1

Được [] => null


Làm thế nào là hiệu quả? Bạn cũng đang lặp lại n lần và không n / 2.
Karan Khanna

3

Tạo một cấu trúc với một con trỏ có khả năng trỏ đến các nút của danh sách được liên kết và với một biến số nguyên giữ số lượng các nút trong danh sách.

struct LL{
    struct node *ptr;
    int         count;
}start;

start.ptrstart.count=1
start.count

start.count


-2

Tạo một mảng động, trong đó mỗi phần tử của mảng là một con trỏ tới mỗi nút trong danh sách theo thứ tự di chuyển ngang, bắt đầu từ đầu. Tạo một số nguyên, được khởi tạo thành 1, theo dõi số lượng nút bạn đã truy cập (tăng theo mỗi lần bạn đến một nút mới). Khi bạn đi đến cuối, bạn sẽ biết danh sách này lớn như thế nào và bạn có một mảng các con trỏ theo thứ tự cho mỗi nút. Cuối cùng, chia kích thước của danh sách cho 2 (và trừ 1 cho lập chỉ mục dựa trên 0) và tìm nạp con trỏ được giữ trong chỉ mục đó của mảng; nếu kích thước của danh sách là số lẻ, bạn có thể chọn phần tử nào sẽ trả về (tôi vẫn sẽ trả về phần tử đầu tiên).

Đây là một số mã Java có điểm nhấn (mặc dù ý tưởng về một mảng động sẽ hơi phức tạp). Tôi sẽ cung cấp C / C ++ nhưng tôi rất gỉ trong khu vực đó.

public Node getMiddleNode(List<Node> nodes){

    int size = 1;
    //add code to dynamically increase size if at capacity after adding
    Node[] pointers = new Node[10];

    for (int i = 0; i < nodes.size(); i++){
        //remember to dynamically allocate more space if needed
        pointers[i] = nodes.get(i);
        size++;
    }

    return pointers[(size - 1)/2];

}

-3

Là đệ quy được xem xét nhiều hơn một vượt qua?

Di chuyển danh sách đến cuối, chuyển một số nguyên theo tham chiếu. Tạo một bản sao cục bộ của giá trị đó ở mỗi cấp để tham chiếu sau và tăng số lượng ref sẽ được thực hiện trong cuộc gọi tiếp theo.

Trên nút cuối cùng, chia số đếm cho hai và cắt bớt / sàn () kết quả (nếu bạn muốn nút đầu tiên là "giữa" khi chỉ có hai phần tử) hoặc làm tròn (nếu bạn muốn nút thứ hai là "giữa"). Sử dụng một chỉ số không hoặc một dựa trên một cách thích hợp.

Unwinding, khớp số đếm ref với bản sao cục bộ (là số của nút). Nếu bằng nhau, trả lại nút đó; khác trả về nút trả về từ cuộc gọi đệ quy.
.

Có nhiều cách khác để làm điều này; một số trong số chúng có thể ít cồng kềnh hơn (tôi nghĩ rằng tôi đã thấy ai đó nói đọc nó thành một mảng và sử dụng chiều dài mảng để xác định kudos giữa). Nhưng thành thật mà nói, không có câu trả lời tốt, bởi vì đó là một câu hỏi phỏng vấn ngu ngốc. Số một, những người vẫn sử dụng danh sách liên kết ( ý kiến ​​hỗ trợ ); Hai, tìm nút giữa là một bài tập học thuật tùy tiện, không có giá trị trong các tình huống thực tế; Thứ ba, nếu tôi thực sự cần biết nút giữa, danh sách được liên kết của tôi sẽ hiển thị số lượng nút. Thật dễ dàng để duy trì tài sản đó hơn là lãng phí thời gian đi qua toàn bộ danh sách mỗi khi tôi muốn nút giữa. Và cuối cùng, bốn, mọi người phỏng vấn sẽ thích hoặc từ chối các câu trả lời khác nhau - điều mà một người phỏng vấn nghĩ là lắt léo, người khác sẽ gọi là vô lý.

Tôi hầu như luôn trả lời các câu hỏi phỏng vấn với nhiều câu hỏi hơn. Nếu tôi nhận được một câu hỏi như thế này (tôi không bao giờ có), tôi sẽ hỏi (1) Bạn đang lưu trữ gì trong danh sách được liên kết này và có cấu trúc phù hợp hơn để truy cập hiệu quả vào nút giữa nếu thực sự cần phải làm như vậy ; (2) Những hạn chế của tôi là gì? Tôi có thể làm cho nó nhanh hơn nếu bộ nhớ không phải là một vấn đề (ví dụ như câu trả lời mảng), nhưng nếu người phỏng vấn nghĩ rằng bộ nhớ bị phồng lên là lãng phí, tôi sẽ bị chìm. (3) Tôi sẽ phát triển ngôn ngữ nào? Gần như mọi ngôn ngữ hiện đại mà tôi biết đều có các lớp dựng sẵn để đối phó với các danh sách được liên kết khiến việc duyệt qua danh sách trở nên không cần thiết - tại sao lại phát minh ra thứ gì đó đã được các nhà phát triển ngôn ngữ điều chỉnh để đạt hiệu quả?


6
Bạn không biết ai đã đánh giá thấp những gì để kết luận của bạn rằng một người đã đánh giá thấp mọi thứ có thể đúng hoặc không nhưng điều đó chắc chắn không có cơ sở trong các sự kiện có sẵn cho bạn. Nhưng tôi đang hạ thấp câu trả lời của bạn bởi vì chúng tôi đang tìm kiếm lời giải thích, chứ không phải hàng đống mã.
David Richerby

Nó có thể là một giả định, nhưng khi tôi nhìn vào một khoảnh khắc và chưa đầy 30 giây sau thì mỗi bài đăng có chính xác -1, đó không phải là một điều vô lý. Ngay cả khi đó là 5 hoặc 6 người khác nhau, không một ai trong số họ để lại bình luận tại sao. Nhưng cảm ơn bạn ít nhất là nêu một lý do. Tôi không hiểu tại sao một lời giải thích dài dòng tốt hơn mã - vâng, đó là cho một cuộc phỏng vấn qua điện thoại, nhưng tôi không đưa ra cho OP một câu trả lời đóng hộp để hồi sinh, tôi chỉ cho anh ta cách làm. IE Tôi nghĩ rằng việc hạ thấp một bài đăng bởi vì nó có mã không bị cấm, nhưng cảm ơn bạn vì ít nhất đã nói lý do tại sao bạn làm như vậy.
James K

Quan điểm công bằng - điều đó đã xảy ra với tôi rằng bạn có thể biết thời điểm bỏ phiếu (có trang được tải trong khi chúng xảy ra là, tôi nghĩ, cách duy nhất người dùng bình thường như chúng tôi có thể tìm ra điều đó). Chúng tôi có một vài bài viết meta về lý do tại sao chúng tôi cố gắng tránh mã thực tế ở đây: (1) (2) .
David Richerby

Cảm ơn các liên kết meta. Tôi đã đọc Câu hỏi thường gặp, nhưng tôi không thấy bất cứ điều gì ở đó - không phải là tôi sẽ nhận thấy một cái gì đó như thế, rất có thể, không phải là điều tôi sẽ mong đợi. Nhưng tôi cũng không thể tìm thấy bất cứ điều gì khi tôi kiểm tra lại sau khi bị chìm. Tôi có thể vẫn đang nhìn nó, nhưng tôi đã nhìn. Các lý do trong các bài viết meta có ý nghĩa; Cảm ơn vì sự trả lời.
James K

-5

Bằng cách sử dụng 2 con trỏ. Tăng một lần ở mỗi lần lặp và lần khác ở mỗi lần lặp thứ hai. Khi con trỏ thứ 1 trỏ đến cuối danh sách được liên kết, con trỏ thứ 2 sẽ trỏ đến chế độ giữa của danh sách được liên kết.


Điều này chỉ trùng lặp câu trả lời của Hendrick . Xin đừng trả lời trừ khi bạn có điều gì đó mới để nói.
David Richerby
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.