【问题标题】:Improving my mergesort for linked lists. How?改进我的链表合并排序。如何?
【发布时间】:2010-11-27 13:26:40
【问题描述】:

在我深入解释之前,您应该知道以下代码可能非常糟糕。我大约 2 个月前开始编程(在那里和那里几个小时)。所以我缺乏经验,我的编码风格非常可改进,我仍然怀念实践和大量(基本)知识。这也包括我可能用错误的名字称呼事物:)。

我对算法(大学)很感兴趣,想练习一些指针处理(一开始有很多问题),所以我决定用单链表做合并排序,看看它与我的合并排序算法相比如何教授(对数组进行排序)。不,这不是家庭作业(我的大学课程(电气工程)都没有家庭作业)——这是我对算法、C 语言和简单练习的理解方面的进步。


我的代码已经可以工作了。出于测试目的,我总是创建反向排序列表。对于列表为 NULL 的情况,它仍然缺少一些东西。

所以在发布我正在使用的结构的代码之前:

struct list{
  int nbr;
  struct list *next_el;
};
typedef struct list LIST;
typedef LIST *z_LIST;

我有两个函数,mergeSort 和 merge。 mergeSort 返回已排序(子)列表的新头部,merge 返回合并序列的头部。

现在我给 mergeSort 未排序列表的当前头部和元素的数量。 然后它递归地分解列表(显然:))。 我不确定对以下代码要说多少。如果有不清楚的地方,我会尽快回答和解释,但是

z_LIST mergeSort ( z_LIST head, int length ) {

  int steps;
  int m = 0;
  z_LIST head1 = NULL, head2 = NULL, new_head = NULL;

if( length > 1) {

  m = (length+1)/2;


  head2 = head; 
  for(steps = 0; steps<m; steps++) {
    head2 = head2->next_el;
  }

  head1 = mergeSort(head, m);
  head2 = mergeSort(head2, length-m);

  new_head = merge(head1, head2, m, length-m);

  return new_head;

  } else {
    return head;
  }
}

merge 接收两个子列表的头部(可以是一个元素或已经排序的序列)以及第一个和第二个列表的元素。

z_LIST merge (z_LIST head1, z_LIST head2, int l1, int l2)  {

  int i,j;
  z_LIST part1 = head1, part2 = head2;
  z_LIST temp_head = NULL, head = NULL;

/*First I let it check what the head of the new list is going to 
be and thus initiating the merging process with either i=1/j=0
or i=0/j=1.*/

  if(part1->nbr < part2->nbr){
    head = part1;
    if(part1->next_el != NULL)  {
      part1 = part1->next_el;
    }
    i=1;
    j=0;
  } else {
    head = part2;
    if(part2->next_el != NULL)  { //The last element of the original list points
      part2 = part2->next_el;     //to NULL. If I would then make part2 = NULL,
    }                             //then there wouldn't be part2->nbr ->lots
    i=0;
    j=1;
  }

  temp_head = head;

  while( (i<l1) || (j<l2) ) {
    if( ((part1->nbr < part2->nbr) && i<l1)||( j>=l2 ))  {
      temp_head->next_el = part1;
      part1 = part1->next_el;
      temp_head = temp_head->next_el;
      if (j>=l2)  { //If j>=l2 then I let merge add one more item of list1
       break;       //since list 1 is already sorted and linked correctly.
      }             //Same below. Should shave off some operations/time?
      i++;
    } else {
      temp_head->next_el = part2;
      part2 = part2->next_el;
      temp_head = temp_head->next_el;
      if (i>=l1)  {
        break;
      }
      j++;
    }
  }

  return head;
}

所以我欢迎任何关于我所做的愚蠢的事情,我没有考虑可能的问题,一些输入代码破坏代码或如何做得更好,我敢肯定还有相当多的改进可能性。提前致谢。

【问题讨论】:

    标签: c linked-list mergesort


    【解决方案1】:

    排序合并阶段的“正常”结构是:

    set output list to empty
    while (list1 not empty && list2 not empty)
    {
        if (list1->headvalue < list2->headvalue
            move head of list1 to output list
        else
            move head of list2 to output list
    }
    while (list1 not empty)
        move head of list1 to output list
    while (list2 not empty)
        move head of list2 to output list
    

    “移动”操作包括更新指向列表中下一项的指针。

    merge() 函数中的结构有些不同。

    要求对列表进行计数也是一种惯例。通常,您可以使用“next is null”或“next is head”来确定列表的结尾,具体取决于列表是纯线性列表还是循环列表。

    【讨论】:

    • 我不计算列表本身,但我需要的步骤直到我到达它的中间,所以我可以为递归部分提供特定的 mergeSort。如果我不计算步骤,那么我如何确定(子)列表的中间位置用于除法步骤呢?关于我的合并功能的结构。我写了第一部分来决定新函数的头是什么,然后附上以下所有列表。因为这我认为这将是一个“特殊”的步骤。我可以在 for 循环中放置一个 if(head==NULL) 以确保在这种情况下它应该是头部。
    • @yezariael:您可以通过将第一个元素放入一个子列表,将下一个元素放入第二个子列表,将下一个放回第一个子列表,依此类推,交替使用哪个列表获取下一项。您无需提前知道源列表中有多少项。
    • @yezariael:您的列表结构应该易于创建空列表,并且算法应该几乎可以轻松地适应空列表。您不需要特例将节点添加到以前的空列表中。
    • 啊,我明白了。因此,我总是通过使每个最后一个元素指向 NULL 来完成交替过程。所以如果head-> nextel == NULL,我会停下来。但是使用链表计数元素可以快速完成,并且使用您提到的交替方法创建新列表对我来说似乎更多操作。除了不必知道元素之外,为什么那会更好?抱歉问,但我是一个非常新手的程序员。关于您的最后一条评论:您是否有关于此类列表结构应该是什么样子的更详细信息的链接?到目前为止非常感谢!
    • @David:有趣的改名...找出列表中有多少元素涉及遍历整个列表,就像将列表分成两个涉及遍历列表一样,所以你会遍历列表两次来计数和拆分,而不是一次。当然,您会在拆分时进行更多的指针修复,因此实际工作可能会更少;这是由测量决定的。请注意,与完全适合工作集的玩具列表相比,不适合(内存)工作集的大型列表可能从一次遍历中受益更多。 [...]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-24
    • 2020-09-25
    相关资源
    最近更新 更多