【问题标题】:Selection sort with linked list's使用链表的选择排序
【发布时间】:2012-04-12 18:40:25
【问题描述】:

我有以下数据结构:

struct scoreentry_node {
    struct scoreentry_node *next;
    int score;
    char name[1];    
};  
typedef struct scoreentry_node *score_entry;

我正在尝试创建一个函数,该函数按顺序使用我的结构,并根据名称按升序排列它们。我想修改输入而不分配任何内存或释放任何东西:

我已经尝试了您的建议:

void selectionsort(score_entry *a) {
    for (; *a != NULL; *a = (*a)->next) {
        score_entry *minafteri = a;
        // find position of minimal element
        for (score_entry j = (*a)->next; j != NULL; j = j->next) {
            if (strcmp(j->name, (*minafteri)->name) == -1) {
                *minafteri = j;
            }
        }
        // swap minimal element to front
        score_entry tmp = *a;
        a = minafteri;
        *minafteri = tmp;
    }
}

我正在使用以下代码测试上述代码:

score_entry x = add(8, "bob", (add( 8 , "jill", (add (2, "alfred", NULL)))));
iprint("",x);
selectionsort(&x);
iprint("", x);
clear(x); //Frees the whole list

iprint() 打印结构中的分数和名称字段。我的添加函数如下:

score_entry add(int in, char *n, score_entry en) {      
   score_entry r = malloc(sizeof(struct scoreentry_node) + strlen(n));
   r->score = in;
   strcpy(r->name, n);
   r->next = en;  
   return r;   
}

我遇到堆错误,我的第二次打印没有打印排序列表,它什么也没打印。我做错了什么,我能做些什么来解决它?

【问题讨论】:

  • 选择排序对于单链表(或一般的列表)来说是一种糟糕的排序算法。如果您正在寻找一种在运行时最优且不分配任何内存的排序算法,请尝试归并排序。
  • char name[1]; 有点小。对于要以 null 结尾的字符串,唯一有效的字符串是 "",这会使比较名称变得毫无用处。
  • @Philip 进行合并排序我不需要两个列表吗?我只有一个..
  • 现在我看到你使用了“struct hack”。
  • 可能是,可能不是(行为仍未定义)。你可能在某个地方犯了一个小错误。我不会调查它(因为我认为链表上的选择排序是错误的,而且我不喜欢将指针隐藏在 typedef 后面)

标签: c sorting linked-list structure


【解决方案1】:

除了通过地址传递指针(参见下面的 cmets),您还需要修复交换元素的方式

void selectionsort(score_entry *a) {
  for (; *a != NULL; *a = (*a)->next) 
  {
     score_entry *minafteri = a;
     // find position of minimal element
     for (score_entry j = (*a)->next; j != NULL; j = j->next) {
       if (strcmp(j->name, (*minafteri)->name) == -1) {
         *minafteri = j;
       }
      }
     // swap minimal element to front
     score_entry tmp = *a;
     a = minafteri; // put the minimal node to current position
     tmp->next = (*a)->next ; //fix the links
     (*minafteri)->next=tmp; //fix the links
  }
}

【讨论】:

  • 请注意,严格来说,C 中不存在按引用传递。您正在传递一个值(指针)并取消引用指针。 stackoverflow.com/questions/2229498/passing-by-reference-in-c">
  • 此解决方案已损坏:*a = (*a)->next -> a = &(*a)->next 并且列表仍然损坏,因为您没有将上一个节点的链接更新到您移动的节点。
【解决方案2】:

你必须将参数传递给selectionsort通过引用

void selectionsort(score_entry *a) {
    for (; *a != NULL; *a = (*a)->next) 
    {
      score_entry *minafteri = a;
      // find position of minimal element
      for (score_entry j = (*a)->next; j != NULL; j = j->next) {
      if (strcmp(j->name, (*minafteri)->name) == -1) {
         *minafteri = j;
      }
    }
     // swap minimal element to front
      score_entry tmp = *a;
      a = minafteri;
      *minafteri = tmp;
  }
}

【讨论】:

  • 嗯我有 typedef struct scoreentry_node *score_entry;它不是一个指向结构的指针吗?
  • @Beginnernato 这里需要传递对指针的引用,所以是指向指针的指针。
  • 所以它需要像selectionsort(&x)这样的东西; x 是一个结构体的指针在哪里?我在调用选择排序后,x 会被排序?
  • 已编辑问题,我在使用新的selection sort时遇到问题
  • 此解决方案已损坏:*a = (*a)->next -> a = &(*a)->next 并且列表仍然损坏,因为您没有将上一个节点的链接更新到您移动的节点。
【解决方案3】:

这段代码太可怕了!您不仅没有为我们提供重现您的问题的所有必需品(我们无法编译它!),而且您在typedef 后面隐藏了指针抽象(对我们来说也是一场噩梦)。一般来说,人们甚至不应该在 C 中使用链表,更不用说排序它们了...

不过,这里有两个答案。


*minafteri = j; 在您的 find 循环中找到实际上会修改您的列表!为什么你的 find 循环会修改你的列表?

回答:不应该!通过分配minafteri = &j->next,您将不会使用查找循环修改列表...


或者,您可以在该循环内执行交换。

*minafteri = j; 需要按以下顺序交换以下内容:

  • (*minafteri)->nextj->next
  • *minafterij

您认为单行代码能够执行这两个交换吗?好吧,它通过其中一个...并在此过程中从您的列表中删除一堆元素!


以下似乎是错误的尝试交换元素:

score_entry *minafteri = a; // half of assigning `a` to `a`
/* SNIP!
 * Nothing assigns to `minafteri`  in this snippet.
 * To assign to `minafteri` write something like `minafteri = fubar;` */
score_entry tmp = *a;       // half of assigning `*a` to `*a`
a = minafteri;              // rest of assigning `a` to `a`
*minafteri = tmp;           // rest of assigning `*a` to `*a`

实际上只是将*a 分配给*a 并将a 分配给a...您认为您需要这样做吗?

我以为您在创建MCVE 时会注意到...哦,等一下!你真丢脸!


专注于交换列表中的两个节点作为一项较小的任务。完成此操作后,请考虑承担此任务。

【讨论】:

    【解决方案4】:

    您的代码存在多个问题:

    • if (strcmp(j->name, (*minafteri)->name) == -1) { 不正确:strcmp() 在第一个字符串小于第二个字符串时不一定返回-1,它可以返回任何负值。
    • 您调整链接以移动下方条目的方式不正确:您无法将链接从前一个节点更新到您移动到开始的节点。列表已损坏。

    这是一个改进的版本:

    void selectionsort(score_entry *a) {
        for (; *a != NULL; a = &(*a)->next) {
            // find position of minimal element
            score_entry *least = a;
            for (score_entry *b = &(*a)->next; *b != NULL; b = &(*b)->next) {
                if (strcmp((*b)->name, (*least)->name) < 0) {
                    least = b;
                }
            }
            if (least != a) {
                // swap minimal element to front
                score_entry n = *least;
                *least = n->next;   /* unlink node */
                n->next = *a;       /* insert node at start */
                *a = n;
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      这里是链表选择排序的Java实现:

      • 时间复杂度:O(n^2)
      • 空间复杂度:O(1) - 选择排序是就地排序算法
      class Solution 
      {
          public ListNode selectionSortList(ListNode head)
          {
              if(head != null)
              {
                  swap(head, findMinimumNode(head));
                  selectionSortList(head.next);
              }
              return head;
          }
      
          private void swap(ListNode x, ListNode y)
          {
              if(x != y)
              {
                  int temp = x.val;
                  x.val = y.val;
                  y.val = temp;    
              }
          }
      
          private ListNode findMinimumNode(ListNode head)
          {
              if(head.next == null)
                  return head;
      
              ListNode minimumNode = head;
      
              for(ListNode current = head.next; current != null; current = current.next)
              {
                  if(minimumNode.val > current.val)
                      minimumNode = current;
              }
              return minimumNode;
          }
      }
      

      【讨论】:

      • 这个问题被标记为 C,而不是 Java。
      猜你喜欢
      • 1970-01-01
      • 2019-11-23
      • 2016-02-21
      • 2016-01-18
      • 2017-03-19
      • 2016-05-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多