C++ 对向量或链表进行排序

Posted

技术标签:

【中文标题】C++ 对向量或链表进行排序【英文标题】:C++ sorting a vector or linked list 【发布时间】:2008-12-01 19:22:53 【问题描述】:

我有一个输入文件,我想根据时间戳进行排序,时间戳是每条记录的子字符串。我想存储

的多个属性

该列表目前大约有 1000 条记录。但是,我希望它能够扩大一点以防万一。

当我使用链接列表搜索整个列表以进行插入时,大约需要 20 秒。现在,仅填充一个向量并输出到文件需要 4 秒(听起来太长了)?

我想使用归并排序或快速排序(归并排序对我来说似乎更容易一些)。我遇到的问题是我没有看到很多使用对象而不是原始数据类型来实现这些排序的示例。

我可以使用向量或链表。到目前为止,我从这个网站得到的反馈是最有帮助的。我希望有人可以洒在神奇的精灵尘上,让我更轻松:)

任何链接或示例以最简单的方式以相当不错的性能执行此操作将不胜感激。我被困在如何用对象实现这些类型,因为我是 C++ 的新手 :)

这是我的新代码的样子(还没有排序):

class CFileInfo  
  
    public:  
    std::string m_PackLine;  
    std::string m_FileDateTime;  
    int m_NumDownloads;  
;  
void main()  
  
    CFileInfo packInfo;  
    vector<CFileInfo> unsortedFiles;  
    vector<CFileInfo>::iterator Iter;  
    packInfo.m_PackLine = "Sample Line 1";  
    packInfo.m_FileDateTime = "06/22/2008 04:34";  
    packInfo.m_NumDownloads = 0;  
    unsortedFiles.push_back(packInfo);  
    packInfo.m_PackLine = "Sample Line 2";  
    packInfo.m_FileDateTime = "12/05/2007 14:54";  
    packInfo.m_NumDownloads = 1;  
    unsortedFiles.push_back(packInfo);  
    for (Iter = unsortedFiles.begin(); Iter != unsortedFiles.end(); ++Iter )   
      
        cout << " " << (*Iter).m_PackLine;  
      
  

【问题讨论】:

C++ 风格提示:将 Iter 的声明移到更靠近您使用它的第一个位置。它避免了你有一个未初始化的变量的大量代码。 【参考方案1】:

我不确定我是否正确理解了您的问题,您的问题是定义排序函子吗? STL 排序通常实现为内省排序,这在大多数情况下非常适用。

struct sort_functor

    bool operator()(const CFileInfo & a, const CFileInfo & b) const
    

        // may be a little bit more subtle depending on what your strings look like
        return a.m_FileDateTime < b.m_FileDateTime;
    


std::sort(unsortedFiles.begin(), unsortedFile.end(), sort_functor());

或使用 boost::lambda

std::sort(unsortedFiles.begin(), 
    unsortedFile.end(),
    bind(&CFileInfo::m_FileDateTime, _1) < bind(&CFileInfo::m_FileDateTime, _2));

这是需要的信息吗?

【讨论】:

【参考方案2】:

对链表进行排序本质上是 O(N^2) 或涉及外部随机访问存储。

向量具有随机存取存储。数组也是如此。排序可以是 O(NlogN)。

在 1000 个元素时,您将开始看到 O(N^2) 和 O(NlogN) 之间的差异。在 1,000,000 个元素时,您肯定会注意到其中的差异!

在非常特殊的情况下有可能得到 O(N) 排序。 (例如:对一副扑克牌进行排序。我们可以创建一个函数(卡片),将每张卡片映射到其排序位置。)

但总的来说,O(NlogN) 是最好的。所以你不妨使用 STL 的 sort()! 只需添加 #include


您只需要添加一个运算符

但是有一个建议:看在上帝的份上,如果你要对日期进行排序,要么将其编码为表示 seconds-since-epoch(mktime?)的 long int,或者至少使用 “年/月/日-小时:分钟:second.fraction” 格式。 (并确保所有内容都是带前导零的 2(或 4)位数字!)比较“6/22/2008-4:34”和“12/5/2007-14:54”需要解析!比较“2008/06/22-04:34”和“2007/12/05-14:54”要容易得多。 (虽然仍然比比较两个整数效率低得多!)


里奇写道: 其他答案似乎更多地涉及语法,这是我真正缺乏的。

好的。对于基本的“int”类型,我们有:

#define PRINT(DATA,N) for(int i=0; i<N; i++)  cout << (i>0?", ":"") << DATA[i];  cout << endl;

int
main()  

    // Creating and Sorting a stack-based array.
  int d [10] =  1, 4, 0, 2, 8, 6, 3, 5, 9, 7 ;
  PRINT(d,10);
  sort( d, d+10 );
  PRINT(d,10);

  cout << endl;

    // Creating a vector.
  int eData [10] =  1, 4, 0, 2, 8, 6, 3, 5, 9, 7 ;
  vector<int> e;
  for(int i=0; i<10; i++ )
    e.push_back( eData[i] );

    // Sorting a vector.
  PRINT(e,10);
  sort(e.begin(), e.end());
  PRINT(e,10);

我们有您自己的类型:

class Data
  
public:  
  string m_PackLine;  
  string m_FileDateTime;  
  int    m_NumberDownloads;

    /* Lets simplify creating Data elements down below. */
  Data( const string & thePackLine  = "",
        const string & theDateTime  = "",
        int            theDownloads = 0 )
      : m_PackLine        ( thePackLine  ),
        m_FileDateTime    ( theDateTime  ),
        m_NumberDownloads ( theDownloads )
     

    /* Can't use constructor with arrays */
  void set( const string & thePackLine,
            const string & theDateTime,
            int            theDownloads = 0 )
    
      m_PackLine        = thePackLine;
      m_FileDateTime    = theDateTime;
      m_NumberDownloads = theDownloads;
    

    /* Lets simplify printing out down below. */ 
  ostream & operator<<( ostream & theOstream ) const
    
      theOstream << "PackLine=\"" << m_PackLine
                 << "\"   fileDateTime=\"" << m_FileDateTime
                 << "\"   downloads=" << m_NumberDownloads;
      return theOstream;
    


    /*
     * This is IT!  All you need to add to use sort()!
     *  Note:  Sort is just on m_FileDateTime.  Everything else is superfluous.
     *  Note:  Assumes "YEAR/MONTH/DAY HOUR:MINUTE" format.
     */
  bool operator< ( const Data & theOtherData ) const
     return m_FileDateTime < theOtherData.m_FileDateTime; 

;

    /* Rest of simplifying printing out down below. */ 
ostream & operator<<( ostream & theOstream, const Data & theData )
   return theData.operator<<( theOstream ); 


    /* Printing out data set. */
#define PRINT(DATA,N) for(int i=0; i<N; i++)  cout << "[" << i << "]  " << DATA[i] << endl;   cout << endl;

int
main()
  
    // Creating a stack-based array.
  Data d [10];
  d[0].set( "Line 1", "2008/01/01 04:34", 1 );
  d[1].set( "Line 4", "2008/01/04 04:34", 4 );
  d[2].set( "Line 0", "2008/01/00 04:34", 0 );
  d[3].set( "Line 2", "2008/01/02 04:34", 2 );
  d[4].set( "Line 8", "2008/01/08 04:34", 8 );
  d[5].set( "Line 6", "2008/01/06 04:34", 6 );
  d[6].set( "Line 3", "2008/01/03 04:34", 3 );
  d[7].set( "Line 5", "2008/01/05 04:34", 5 );
  d[8].set( "Line 9", "2008/01/09 04:34", 9 );
  d[9].set( "Line 7", "2008/01/07 04:34", 7 );

    // Sorting a stack-based array.
  PRINT(d,10);
  sort( d, d+10 );
  PRINT(d,10);

  cout << endl;

    // Creating a vector.
  vector<Data> e;
  e.push_back( Data( "Line 1", "2008/01/01 04:34", 1 ) );
  e.push_back( Data( "Line 4", "2008/01/04 04:34", 4 ) );
  e.push_back( Data( "Line 0", "2008/01/00 04:34", 0 ) );
  e.push_back( Data( "Line 2", "2008/01/02 04:34", 2 ) );
  e.push_back( Data( "Line 8", "2008/01/08 04:34", 8 ) );
  e.push_back( Data( "Line 6", "2008/01/06 04:34", 6 ) );
  e.push_back( Data( "Line 3", "2008/01/03 04:34", 3 ) );
  e.push_back( Data( "Line 5", "2008/01/05 04:34", 5 ) );
  e.push_back( Data( "Line 9", "2008/01/09 04:34", 9 ) );
  e.push_back( Data( "Line 7", "2008/01/07 04:34", 7 ) );

    // Sorting a vector.
  PRINT(e,10);
  sort(e.begin(), e.end());
  PRINT(e,10);

【讨论】:

从什么时候开始排序喜欢的列表O(n2)?快速排序只需要双向迭代,因此在双重喜欢的列表(std::list 是双重链接的)上与随机访问容器一样有效,而合并排序需要额外的存储空间,它既需要原始存储空间,也需要额外的存储空间可以向前迭代。【参考方案3】:

stl在header中有排序算法

 <algorithm>

Here's SGI 手册的链接。

【讨论】:

【参考方案4】:

在算法头中使用std::sort:

如果您为 CFileInfo 定义运算符

或者,定义一个执行比较的函子,并将其作为单独的参数传递给排序函数。

【讨论】:

【参考方案5】:

Rich -- 要回答您最近的问题(而不是您原来的问题),最好/最简单的方法是使用 sscanf() 解析出日期。理想情况下,您希望一开始就以数字形式存储它。

使用 "YYYY/MM/DD-HH:MM" 字符串,您可以只比较字符串。所有字符串的长度都相同,从左到右阅读时,您从最大的时间增量到最小的时间增量。

但是比较字符串的效率非常低!

日期通常存储为 time_t(整数)值,以自纪元(UTC 时间 00:00:00,1970 年 1 月 1 日)以来的秒数为单位。

mktime() 或 timegm()(如果您有 timegm)将从您提供的 "struct tm" 构造一个 time_t 值。

示例代码:

#define SHOW(X)  cout << # X " = " << (X)

int
main()

  const string s = "2008/12/03 12:48";
  struct tm    datetime;
  time_t       t;

  memset( & datetime, 0, sizeof(datetime) );

  if ( 5 != sscanf( s.c_str(), "%d/%d/%d %d:%d",
                    & datetime.tm_year,
                    & datetime.tm_mon,
                    & datetime.tm_mday,
                    & datetime.tm_hour,
                    & datetime.tm_min  ) )
  
    cout << "FAILED to parse:  \"" << s << "\"" << endl;
    exit(-1);
  

    /* tm_year - The number of years since 1900. */
  datetime.tm_year -= 1900;

    /* tm_mon - The number of months since January, in the range 0 to 11. */
  datetime.tm_mon --;

    /* tm_mday - The day of the month, in the range 1 to 31. */
    /* tm_hour - The number of hours past midnight, in the range 0 to 23. */
    /* tm_min - The number of minutes after the hour, in the range 0 to 59. */
  // No change.

  /* If using mktime, you may need these to force UTC time:
   *   setenv("TZ","",1);
   *   tzset();
   */

  t = mktime( & datetime );

  SHOW( t ) << endl;
  SHOW( asctime( & datetime ) );
  SHOW( ctime( & t ) );

现在给出两个时间(日期)值,例如time_t t1, t2,您可以将它们与 t1 进行比较。

【讨论】:

以上是关于C++ 对向量或链表进行排序的主要内容,如果未能解决你的问题,请参考以下文章

排序顺序管理 - 不满意 SortOrder * 10^n 或链表实现

Python 可以将不同的数据类型存储在列表或链表中

双向链表模板向量中的输入排序(C++)

在 C++ 中使用链表按字母顺序对字符串进行排序

如何在 C++ 中使用一个变量对两部分链表进行排序?

二叉树 vs. 链表 vs. 哈希表