手动对C编程语言中的链表进行排序

时间:2010-01-08 11:28:51

标签: c sorting linked-list

如何在C中的函数中按名称对链接列表进行排序?

struct rec{
    char name[20];
    int nr;
    struct rec *nextRec;
};
typedef struct rec Rec; /* synonym for struct rec */
typedef Rec *RecPtr; /* synonym for pointer */

void SortLinkedList(RecPtr *sPtr, int p_size);/* prototype */
int main() {
    RecPtr startPtr = NULL;
    /* filling upp the linked list... size = nr of nodes in list */
    SortLinkedList(&startPtr, size);
}

void SortLinkedList(RecPtr *sPtr, int p_size){
    int i, j;
    RecPtr tempPtr;
    RecPtr currentPtr;
    RecPtr nextPtr;
    currentPtr = *sPtr;
    nextPtr = currentPtr->nextRec;
    for( j = 0; j <= p_size; j++) { /* loop for nr of nodes */
        for(i = 0; i <= p_size -1 ; i++) { /* loop for one less than nr of nodes */
            if(strcmp(currentPtr->name, nextPtr->name) < 0) { /* it less than ...*/
                tempPtr = currentPtr;/* ...swap with temp */
                currentPtr = nextPtr; /* but this sorting doesn'nt work */
                nextPtr = tempPtr;
            }
           currentPtr = nextPtr;
           currentPtr = currentPtr->nextRec;
        }
    }
}

8 个答案:

答案 0 :(得分:2)

问题似乎是你只是操纵指针而不是对象本身。对链表进行排序时,必须断开链接并重新制作链接。

例如,在您的情况下,有三个链接,它们必须按括号中的指定进行修改。

prevPtr-&gt; nextRec(需要将其更改为指向nextPtr而不是currentPtr) currentPtr-&gt; nextRec(需要将其更改为指向nextPtr-&gt; nextRec而不是nextPtr) nextPtr-&gt; nextRec(需要将其更改为指向currentPtr)

您必须拥有prevPtr并在程序中跟踪它。

        nextRec                   nextRec           nextRec

prevPtr --------------&gt; currentPtr -------------------------&gt; nextPtr- --------------------------&GT; (nextPtr-&GT; NEXTREC)

需要改为

           nextRec           nextRec              nextRec

prevPtr -----------------------&gt; nextPtr ------------------&gt; currentPtr -------------------&GT; (nextPtr-&GT; NEXTREC)

答案 1 :(得分:1)

您的问题是,当您应该操纵列表中结构的nextRec字段时,您只是操纵RecPtr变量,这些变量没有永久效果。

答案 2 :(得分:1)

不是回答你的问题,而是回答几个问题:

  • 使用typedef来隐藏某事物是指针的事实通常被视为坏样式
  • 如果你需要排序,那么链表不是最好的结构 - 你可能更适合使用数组

答案 3 :(得分:1)

警告以下内容很可能是您想要做的事情。但是,我很沮丧地试图找出一个微妙但令人讨厌的错误并需要分心。

如果我可以提供一些建议......

首先,IMO更容易按顺序将项目插入列表,而不是对无序列表进行排序;我要做的是创建一个insertInOrder函数,它接受你的列表头,要添加的新元素,以及一个比较记录的谓词(指向函数的指针),并返回列表的新头部:

Rec *insertInOrder(Rec *head, Rec *new, int (*cmp)(Rec *, Rec *))
{
  if (head == NULL)
  {
    return new;
  }
  else if (cmp(new, head) < 0) // new is "less than" head
  {
    new->nextRec = head;
    return new;                // new becomes the new head of the list
  }
  else
  {
    Rec *tmp = head;
    /**
     * Find the first element in the list that is not "less than" new
     */
    while (tmp->nextRec != NULL && cmp(new, tmp->nextRec) > 0)
    {
      tmp = tmp->nextRec;
    }
    if (tmp->nextRec == NULL)
    {
      // insert new at end of list
      tmp->nextRec = new;
      new->nextRec = NULL;
    }
    else
    {
      // insert new before tmp->nextRec
      new->nextRec = tmp->nextRec;
      tmp->nextRec = new;
    }
    // keep the current list head
    return head;
  }
}

现在您可以根据不同的标准订购列表。 cmp参数指向一个函数,它接受两个记录指针并比较它们,如果第一个参数是“小于”第二个,则返回-1,如果第一个参数是“大于”第二个,则返回1,0如果他们比较平等。例如,如果要按名称排序,请定义类似

的函数
int compareNames(Rec *e1, Rec *e2)
{
  int r = strcmp(e1->name, e2->name);
  if (r < 0) return -1;
  if (r > 0) return 1;
  return 0;
}

并将insertInOrder调用为

head = insertInOrder(head, new, compareNames);

在给定特定谓词的情况下对列表进行排序:从头开始,从现有列表中一次删除一个元素,并使用指示的谓词将其添加到新列表中:

Rec *sortLinkedList(Rec *head, int (*cmp)(Rec *, Rec *))
{
  Rec *newList = NULL;

  while (head)
  {
    Rec *tmp = head;
    head = head->nextRec;
    tmp->nextRec = NULL;
    newList = insertInOrder(newList, tmp, cmp);
  }
  return newList;
}
...
head = sortLinkedList(head, compareNr);
...
head = sortLinkdeList(head, compareNames);
...
head = sortLinkedList(head, compareSomethingElse);

像Neil一样,我并不十分喜欢指针类型的typedef;经验告诉我,他们造成的问题多于他们的价值。

答案 4 :(得分:0)

如果您要更改链接列表的顺序,那么在某个阶段您将不得不写入链接列表中的nextRec指针。由于您所做的唯一分配是本地指针变量,因此您不会对列表进行任何更改。

你没有在i循环和j循环之间进行任何重置,因此很难看出你的算法如何保证不会超出列表的末尾,尽管它通常不会移动很远。

如果strcmp测试没有触发两次迭代,那么它将始终单独nextPtr并始终将currentPtr分配给nextPtr->nextRec,因此nextPtr都不会并且currentPtr也会改变。

currentPtr = nextPtr;
currentPtr = currentPtr->nextRec;

你真的是要在循环体中使用i++以及for循环的增量部分吗?

您的排序算法是什么?请注意,如果您需要在单个链接列表中交换两个元素位置,则需要将先前的节点保留到要交换的元素,以便您可以调整先前节点的“下一个”指针。

答案 5 :(得分:0)

您可以轻松地对带有冒泡排序的链表进行排序(迭代它,在交换的情况下跟踪第i-1个元素)。你也可以很容易地做一个快速排序(事实上,我听说有些人认为quicksort在链表上比在数组上更有意义。)

答案 6 :(得分:0)

您可以通过选择排序或冒泡排序对列表进行排序。您需要交换指针而不是数据。下面我给出了两个可能对您的问题有帮助的例子。

struct link
{
    int data;
    struct link *next;
};
selection(struct link **first)
{
    struct link *p,*q,*pp,*qq,*temp;
    pp=p=*first;
    while(p->next!=NULL)
    {
    q=p->next;
        while(q!=NULL)
        {
            if(p->data > q->data)
            {
                if(p==*first)
                {
                    if(q==p->next)
                    {
                        p->next=q->next;
                        q->next=p;
                    }
                    else
                    {
                        temp=q->next;
                        q->next=p->next;
                        p->next=temp;
                        qq->next=p;
                    }
                    *first=q;
                    pp=*first;
                }
                else
                {
                    if(q==p->next)
                    {
                        p->next=q->next;
                        q->next=p;
                        pp->next=q;
                    }
                    else
                    {
                        temp=q->next;
                        q->next=p->next;
                        p->next=temp;
                        qq->next=p;
                        pp->next=q;
                    }
                 }
                temp=p;
                p=q;
                q=temp;
            }
            qq=q;
            q=q->next;
        }
        pp=p;
        p=p->next;
    }
    return 0;
}

bable(struct link **first)
{
    struct link *p,*q,*lt,*pp,*temp;
    lt=NULL;
    p=*first;
    while((*first)->next!=lt)
    {
        pp=p=*first;
        while(p->next!=lt)
        {
            q=p->next;
            if((p->data)>(q->data))
            {
                if(p==*first)
                {
                    p->next=q->next;
                    q->next=p;
                    *first=q;
                    pp=*first;
                }
                else
                {
                    p->next=q->next;
                    q->next=p;
                    pp->next=q;
                }
                temp=p;
                p=q;
                q=temp;

            }
            pp=p;
            p=p->next;
        }
        lt=p;
    }
    return 0;
}

答案 7 :(得分:0)

我建议在列表上进行merge-sort,因为它是O(N Log N),而bubble-sort或inserted-sort都是O(N ^ 2)。

Here's good example of a simple singly linked list merge sort