簡體   English   中英

使用迭代方法反轉鏈接列表

[英]Reversing a Link List using Iterative Approach

我的反轉鏈表的代碼有什么問題?

void rev(node* &head)
{
    int flag=0;
    node* head1=NULL;
    while(head->next!=NULL)
    {
        node* temp1=head;
        node* temp2=head;
        while(temp1->next!=NULL)
        {
            temp2=temp1;
            temp1=temp1->next;
        }
        if(flag==0)
        {
            head1=temp1;
            flag++;
        }
        temp1->next=temp2;
        temp2->next=NULL;
    }
    head=head1;
    delete head1;
}

我試圖解決反轉鏈接列表的標准問題。 所以我嘗試實施這種方法,但它似乎進入了無限循環,我無法理解為什么。

您的 function 無效。

例如,傳遞的指針head可以等於nullptr 在這種情況下,這個 while 循環

while(head->next!=NULL)

已經可以調用未定義的行為。

或者列表中沒有要刪除的內容但是 function 有這些語句

head=head1;
delete head1;

那沒有意義。

即使通過調用 delete 刪除語句,這也不會使 function 正確。 例如,如果列表只包含一個節點,那么這個 while 循環

while(head->next!=NULL)

不會被執行。 結果,由於循環后的這條語句,指針head將設置為NULL

head=head1;

因為在循環之前指針head1被設置為NULL

node* head1=NULL;

也似乎在這個嵌套的 while 循環中

while(temp1->next!=NULL)
{
    temp2=temp1;
    temp1=temp1->next;
}

您正試圖在列表中找到至少效率低下的最后一個節點。

而你的function不清楚,太復雜了。

要編寫 function,學習 header <functional>中聲明的標准 C++ function std::exchange就足夠了,這將使 function 的代碼更加簡單明了。

下面是一個演示程序,展示了如何實現 function 反轉單鏈表。

#include <iostream>
#include <functional>
#include <iterator>

struct node
{
    int data;
    node *next;
};

void clear( node * &head )
{
    while ( head ) delete std::exchange( head, head->next );
}

void assign( node * &head, const int a[], size_t n )
{
    clear( head );

    for (node **current = &head; n--; current = &( *current )->next)
    {
        *current = new node{ *a++, nullptr };
    }
}

std::ostream & display( const node *const &head, std::ostream &os = std::cout )
{
    for (const node *current = head; current != nullptr; current = current->next)
    {
        os << current->data << " -> ";
    }

    return os << "null";
}

void reverse( node * &head )
{
    for ( node *current = head, *previous = nullptr; current != nullptr; previous = head )
    {
        head = std::exchange( current, current->next );
        head->next = previous;
    }
}

int main()
{
    node *head = nullptr;
    const int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    assign( head, a, std::size( a ) );

    display( head ) << '\n';

    reverse( head );

    display( head ) << '\n';

    clear( head );
}

程序 output 是

0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> null

可以看到function只有一個for循環,其復合語句只包含兩條語句

void reverse( node * &head )
{
    for ( node *current = head, *previous = nullptr; current != nullptr; previous = head )
    {
        head = std::exchange( current, current->next );
        head->next = previous;
    }
}

如果不使用標准 function std::exchange ,則反轉列表的 function 將多一個語句,例如

void reverse( node * &head )
{
    for ( node *current = head, *previous = nullptr; current != nullptr; previous = head )
    {
        head = current;
        current = current->next; 
        head->next = previous;
    }
}

首先,一個迷你代碼審查:

//
// Bigger issues implied by this function are that it is not a very good
// linked list, likely an extremely basic C-style list. However, that is
// beyond the scope of this question.
//
void rev(node* &head)
{
    int flag=0;  // Unnecessary
    node* head1=NULL;  // Prefer nullptr
    while(head->next!=NULL)
    {
        node* temp1=head;
        node* temp2=head;  // Choose better names
        while(temp1->next!=NULL)  // Traverse the entire list at every iteration
        {
            temp2=temp1;
            temp1=temp1->next;
        }
        if(flag==0)
        {
            head1=temp1;
            flag++;
        }
        temp1->next=temp2;  // Always and only swaps the last two elements
        temp2->next=NULL;

        // Never updates head in the loop; loop is infinite
    }
    head=head1;
    delete head1;  // head1 was pointing to a valid node; you just nuked your
                   // entire list
}

該算法非常簡單,當用紙和鉛筆畫出問題時,它就會自動顯示出來。 你只需要讓箭頭指向另一個方向,然后重新分配頭部。 您正在嘗試這樣做,但除了最后兩個節點外,您沒有更改任何指針。 您需要在列表中移動時更改它們。

不需要特殊的head檢查和flag 您自然會到達尾部,並且可以在這樣做時重新分配頭部。

這是重新設計的算法:

#include <iostream>

struct node {
  int data;
  node* next;

  node(int d) : data(d), next(nullptr) {}
};

//
// Bigger issues implied by this function is that it is not a very good
// linked list, likely an extremely basic C-style list. However, that is
// beyond the scope of this question.
//
void rev(node*& head) {
  node* prev = nullptr;
  node* curr = head;
  node* next = nullptr;  // Not immediately assigned to account for
                         // empty list.

  while (curr) {
    next = curr->next;  //
    curr->next = prev;  // This order of operations is very important
    prev = curr;        //
    curr = next;        //
  }

  head = prev;
}

int main() {
  node* list = new node{1};
  list->next = new node{2};
  list->next->next = new node{3};
  list->next->next->next = new node{4};
  list->next->next->next->next = new node{5};

  node* walker = list;
  std::cout << "Original list: ";
  while (walker != nullptr) {
    std::cout << walker->data << ' ';
    walker = walker->next;
  }
  std::cout << '\n';

  rev(list);
  std::cout << "Reversed list: ";
  walker = list;
  while (walker != nullptr) {
    std::cout << walker->data << ' ';
    walker = walker->next;
  }
  std::cout << '\n';

  // On the one hand, I don't delete my nodes. On the other,
  // the program is ending and the OS will clean up my mess.
  // This is generally a bad practice.
}

Output:

❯ ./a.out
Original list: 1 2 3 4 5 
Reversed list: 5 4 3 2 1 

雖然它需要更多代碼,但強烈建議使用正確的 C++ 鏈表 class 以避免main()中所需的徹頭徹尾的愚蠢初始化。

而且我知道這段代碼很可能只是為了理解這個特定的算法,但是 C++ 標准庫確實提供了單鏈表和雙向鏈表,這兩種鏈表都很容易逆向。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM