【问题标题】:Any improvements over my linked-list method?我的链表方法有什么改进吗?
【发布时间】:2010-07-26 16:09:45
【问题描述】:

我一直在编写一个程序来提高我对链表及其功能的了解。我想知道你们中的一些人是否可以查看我的代码并注意到您可能熟悉的任何错误,或者一般的错误。这些函数适用于我的测试函数,但显然,我并没有测试所有可能的场景。

    // LinkedList.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>

using std::cout;
using std::cin;
using std::endl;

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

node* push(node*, int);
node* pop(node*);
node* pop(node*, int);
node* insert(node*, int, int);
void printList(const node*);

int _tmain(int argc, _TCHAR* argv[])
{
    node* head = NULL;

    for(int i = 1; i <= 15; ++i)
    {
        head = push(head, i);
    }

    for(int j = 14; j >= 0; j = j - 2)
    {
        head = pop(head, j);
        printList(head);
    }

    head = pop(head);
    head = insert(head, 4, 27);
    printList(head);
    cin.ignore();
}

node* push(node* read, int val)
{
    node* write = read;
    if(read == NULL)
    {
        read = new node;
        read->next = NULL;
        read->value = val;
        cout << "Node Head: " << read->value << endl;
        return read;
    }
    else
    {
        while(read->next != NULL)
        {
            read = read->next;
        }
        read->next = new node;
        read->next->next = NULL;
        read->next->value = val;
        read = read->next;
        cout << "Node Link: " << read->value << endl;
        return write;
    }
}

node* pop(node* read)
{
    node* write = read;
    if(read->next == NULL)
    {
        delete read;
        read = NULL;
        return write;
    }
    else
    {
        while(read->next != NULL)
        {
            if(read->next->next == NULL)
            {
                cout << "Pop: " << read->next->value << endl;
                delete read->next;
                read->next = NULL;
            }
            else
            {
                read = read->next;
            }
        }
        return write;
    }
}

node* pop(node* read, int pos)
{
    node* write = read;
    if(read->next == NULL)
    {
        delete read;
        return write;
    }
    else
    {   
        if(pos == 0)
        {
            node* old = read;
            cout << "Pop: " << read->value << endl;
            read = read->next;
            delete old;
            return read;
        }
        else
        {
            for(int i = 0; i < (pos - 1); i++)
            {
                read = read->next;
            }
            node* old = read->next;
            cout << "Pop: " << old->value << endl;
            read->next = read->next->next;
            delete old;
            return write;
        }
    }
}

node* insert(node* read, int pos, int val)
{
    node* write = read;
    for(int i = 0; i < (pos - 1); i++)
    {
        read = read->next;
    }
    node* ins = new node;
    ins->value = val;
    cout << "Insert: " << ins->value << endl;
    ins->next = read->next;
    read->next = ins;
    return write;
}

void printList(const node* read)
{
    if(read != NULL)
    {
        cout << "List Item: " << read->value << endl;
        printList(read->next);
    }
}

/****************OUTPUT*********************
Node Head: 1
Node Link: 2
Node Link: 3
Node Link: 4
Node Link: 5
Node Link: 6
Node Link: 7
Node Link: 8
Node Link: 9
Node Link: 10
Node Link: 11
Node Link: 12
Node Link: 13
Node Link: 14
Node Link: 15
Pop: 15
List Item: 1
List Item: 2
List Item: 3
List Item: 4
List Item: 5
List Item: 6
List Item: 7
List Item: 8
List Item: 9
List Item: 10
List Item: 11
List Item: 12
List Item: 13
List Item: 14
Pop: 13
List Item: 1
List Item: 2
List Item: 3
List Item: 4
List Item: 5
List Item: 6
List Item: 7
List Item: 8
List Item: 9
List Item: 10
List Item: 11
List Item: 12
List Item: 14
Pop: 11
List Item: 1
List Item: 2
List Item: 3
List Item: 4
List Item: 5
List Item: 6
List Item: 7
List Item: 8
List Item: 9
List Item: 10
List Item: 12
List Item: 14
Pop: 9
List Item: 1
List Item: 2
List Item: 3
List Item: 4
List Item: 5
List Item: 6
List Item: 7
List Item: 8
List Item: 10
List Item: 12
List Item: 14
Pop: 7
List Item: 1
List Item: 2
List Item: 3
List Item: 4
List Item: 5
List Item: 6
List Item: 8
List Item: 10
List Item: 12
List Item: 14
Pop: 5
List Item: 1
List Item: 2
List Item: 3
List Item: 4
List Item: 6
List Item: 8
List Item: 10
List Item: 12
List Item: 14
Pop: 3
List Item: 1
List Item: 2
List Item: 4
List Item: 6
List Item: 8
List Item: 10
List Item: 12
List Item: 14
Pop: 1
List Item: 2
List Item: 4
List Item: 6
List Item: 8
List Item: 10
List Item: 12
List Item: 14
Pop: 14
Insert: 27
List Item: 2
List Item: 4
List Item: 6
List Item: 8
List Item: 27
List Item: 10
List Item: 12
*******************************************/

【问题讨论】:

  • 我不是想看看什么是最好的工具,我只是想改进我自己的工具,以便更好地理解这门语言。否则我只会使用向量。
  • 这不是真正的 C++。在 C++ 中,会有一个 List 类,其中的函数充当方法。这是 C。
  • @Steven:C++ 并不要求您以面向对象的方式进行编程。这是一个有效的 C++ 问题。
  • 让我用更实际的术语来说明这一点。只会 C 的人可以完全理解这段代码并提供帮助。不需要 C++ 知识,因为没有显着使用区分 C 和 C++ 的特性。这不是 C++ 代码,而是移植的 C 代码。
  • 请停止将C 编译器无法编译的代码标记为C 代码。只懂 C 的人可以理解用 C++、Java、Python 和任何数量的其他语言编写的大部分程序。这也不会神奇地使这些程序成为 C 程序。

标签: c++ data-structures linked-list


【解决方案1】:

嗯,首先,对于一般用途,你应该使用标准库:std:vector,或者,如果你真的需要一个链表,std::list&lt;&gt;. 但是,因为这是一个自学练习,我们将跳过它。

这给我们带来了下一个问题:作为一个教学练习,它真的不是生产代码,那么我们应该抱怨什么? cout 在函数中间??

我看到的一个特殊问题是这样的代码:

    read = new node;    
    read->next = NULL;    
    read->value = val; 

节点对象的构造函数应该处理将其next 成员设置为空。就此而言,它也应该处理设置值,因此这段代码应该是:

    read = new node(val);

另一个问题:在 pop() 中,你有:

node* write = read; 
if(read->next == NULL) 
{ 
    delete read; 
    read = NULL; 
    return write; 
} 

read 设置为null 毫无意义——这是一个即将超出范围的本地变量。而你返回的是write,等于read,已经被删除了。

此外,您在代码中几乎没有使用 C++ 和面向对象编程的特性:如果我们忽略 cout,它基本上只是通过 new 和 delete 分配内存的 C 代码。正如我所指出的,它可以极大地受益于构造函数。析构函数也可能很有用,再加上一个 List 类,它可以保存头节点,并将所有函数作为成员。

【讨论】:

  • 其实,对于一般用途,你应该使用std::vector&lt;t&gt;
  • @Billy,真的,我会改写那句话。
  • 很棒的答案。非常感谢。
  • 在删除指针后立即将指针设置为 null 是一个好习惯。这可能毫无意义,但它仍然不是一个坏主意,就像一个好习惯一样。
  • @Brian:是的,但这是一个学习练习,所以可以假设他认为它在做某事。
【解决方案2】:

如果用户可以删除某个位置的节点,我认为它不能正确地称为“pop”。 Pop 通常是指仅删除顶部节点,例如在堆栈中。 “删除”可能是一个更好的名字。

【讨论】:

  • 虽然不是我要找的答案类型,但这是一个非常合适的建议。谢谢。
【解决方案3】:

只看你的“推送”功能,这里有一些建议:

  • 为清楚起见,第一个参数应命名为“head”,而不是“read”

  • “if(read == NULL)”测试的两个分支之间有很多重复。分解出通用代码(例如,使用@James 建议的构造函数)

  • 将您正在执行的步骤分解为“创建一个值为 'val' 的节点”、“查找列表末尾”、“如果为空列表,则 head = new;否则为 tail->下一个 = 新的"

【讨论】:

    【解决方案4】:

    如果你正在制作一个C风格的链表类,你应该使用void *作为数据项,这样你就可以将链表与其他类型一起使用:

    struct Node
    {
        void * p_data;
        struct Node * next;
    };
    

    另一方面,如果您想使用 C++ 工具,您可以扩展为使用 template 作为数据类型。 template 将允许您对不同类型使用相同的代码。编译器将在模板(模板)的实例化期间解析数据类型:

    template <class Data_Type>
    struct Node
    {
        Data_Type data;
        Node *    next;
    };
    

    正如其他人所说,您应该有一个 List 类或结构。这将帮助用户区分实例化。在 C 风格中,此结构将传递给列表方法(因此列表方法知道要对哪个列表进行操作)。

    struct List_Header
    {
        struct Node * head;
        struct Node * tail;
        unsigned int  size; // Quantity of nodes
    };
    
    void List_Push(List_Header * p_header, void * data)
    {
      if (p_header)
      {
        // Create a node, prepend to list, etc.
      }
      return;
    }
    

    使用列表:

    List_Header    my_list;
    unsigned int *   my_data(new int(1));
    List_Push(&my_list, my_data);
    

    【讨论】:

      【解决方案5】:

      您正在制作 C 样式的列表。在 C++ 中,您将为节点使用一个类,为列表使用另一个类。列表的类应该具有列表中第一个元素的成员。 然后,这里和那里都有很多错误。我没有查看所有代码,但我可以给你一些建议:

      • 始终检查边界,涵盖所有情况。如果我给出一个无效的 pos (>size) 会发生什么? :

         node* insert(node* read, int pos, int val)
          {
             node* write = read;
             for(int i = 0; i < (pos - 1), read <> NULL; i++) 
             {// you should make the for stop if it hits the bottom of the list
                 read = read->next; 
             }
             if (read == NULL) return NULL
             node* ins = new node;
             ins->value = val;
             cout << "Insert: " << ins->value << endl;
             ins->next = read->next;
             read->next = ins;
             return write;
         }
        
      • 小心内存以及如何使用它。尽量避免像 node->next->next 这样的结构,因为它们很容易出错。

          node* pop(node* read)
          {
              node* write = read;
              if(read->next == NULL)
              {
                  delete read;
                  read = NULL;
                  return write;
              }
              else
              {
                  while(read->next != NULL)
                  {
                      if(read->next->next == NULL)
                      {
                          cout << "Pop: " << read->next->value << endl;
                          delete read->next;
                          read->next = NULL;
                      }
                      else
                      {
                          read = read->next;
                      }
                  }
                  return write;
              }
          }
        

      可以这样写:

           node* pop(node* head)
           {
              if (head == NULL) return;
      
              node* previous = NULL;
              node* returnVal = head;
      
              while (head->next != NULL)
              {
                  previous = head;
                  head = head->next;
              }
              if (previous != NULL)
              {
                  previous->next = NULL;
                  delete head;
              }
              else //we need to pop out the head :)
              {
                  delete head;
                  returnVal = null;
              }
              return returnVal;
           }
      

      没有指向列表头部的类的全局成员有点令人困惑。我不习惯那个.. C++ 风格的列表删除函数会这样写:

      void SingleLinkedList::RemoveElement(TInfo value)
      {
          Node* cursor = mHead;
          Node* lastCursor = NULL;
      
          if (cursor != NULL) while (cursor->GetNext() != NULL && cursor->GetInfo() != value) 
          {
              lastCursor = cursor;
              cursor = cursor->GetNext();
          }
          if (cursor->GetInfo() == value)
          {
              lastCursor->SetNext(cursor->GetNext());
              delete cursor;
          }
      }
      

      类的定义如下:

      #define NULL 0
      typedef int TInfo;
      
      class Node
      {
      private:
          TInfo info;
          Node* next;
      public:
      
          Node()                          {info = 0; next = NULL;}
          Node(TInfo iInfo, Node* iNode)  {info = iInfo; next = iNode;}
          ~Node()                         {info = 0; next = NULL;}
      
          TInfo GetInfo() const           {return info;}
          Node* GetNext() const           {return next;}
      
          void SetInfo(TInfo value)       {info = value;}
          void SetNext(Node* pValue)      {next = pValue;}
      };
      
      class SingleLinkedList
      {
      private:
          Node* mHead;
          int mCount;
      
          void RecursiveRemove(Node* iNode);
      
      public:
          SingleLinkedList();
          ~SingleLinkedList() {mHead = NULL;};
      
          int Count() const               {return mCount;}
          Node* const GetHead() const     {return mHead;}
      
          void ClearList();
          void AddHeadElement(TInfo value);
          void AddTailElement(TInfo value);
          void RemoveElement(TInfo value);
          bool SearchElement(TInfo value, int &pos);
      };
      
      void PrintList (SingleLinkedList const& list);
      

      您可以对代码进行许多改进。想的简单:)

      【讨论】:

      • 关于你重写的 pop 函数的一个问题。我的一个朋友注意到,如果你将 pop 函数传递给链表的头部,并且只有一个节点,那么函数就会中断。第一个 if 语句将被传递,因为只有一个节点,while 循环不会做任何事情,考虑到 head->next 已经等于 NULL。 previous->next 会使程序崩溃,因为 previous 已经指向 NULL。你对这件事有什么感想?小心修改?我想看看你是怎么解决的。
      • 真...真..我的错误。感谢您指出。我已经对我的帖子进行了编辑。我希望您会发现添加的信息很有用。祝你有美好的一天!
      猜你喜欢
      • 2011-08-13
      • 2020-07-16
      • 2011-05-15
      • 2022-12-30
      • 1970-01-01
      • 2015-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多