Сортировка связанного списка вручную на языке программирования C - PullRequest
0 голосов
/ 08 января 2010

Как отсортировать связанный список по имени в функции в 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 ]

2 голосов
/ 08 января 2010

Кажется, проблема в том, что вы просто манипулируете указателями, а не самими объектами.Когда вы сортируете связанный список, вам необходимо сломать ссылки и заново создать их.

Например, в вашем случае есть три ссылки, и они должны быть изменены, как указано в скобках.

prevPtr-> nextRec (Это необходимо изменить, чтобы указывать на nextPtr вместоcurrentPtr) currentPtr-> nextRec (это необходимо изменить, чтобы указать на nextPtr-> nextRec вместо nextPtr) nextPtr-> nextRec (это необходимо изменить, чтобы указать на currentPtr)

Вам обязательно нужно иметьprevPtr и отслеживать это в вашей программе.

        nextRec                   nextRec           nextRec

prevPtr --------------> currentPtr -------------------------> nextPtr ---------------------------> (nextPtr-> nextRec)

Необходимо изменить на

           nextRec           nextRec              nextRec

prevPtr -----------------------> nextPtr ------------------> currentPtr -------------------> (nextPtr-> nextRec)

1 голос
/ 09 января 2010

ПРЕДУПРЕЖДЕНИЕ : Следующее является наиболее вероятным излишним для того, что вы хотите сделать. Но я расстроен, пытаясь отследить тонкий, но неприятный баг и мне нужно отвлечься.

Если я могу предложить несколько предложений ...

Прежде всего, в 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);

Как и Нил, мне не очень нравятся определения типов для типов указателей; опыт показал мне, что они вызывают больше проблем, чем стоят.

1 голос
/ 08 января 2010

Не для ответа на ваш вопрос, но пара замечаний:

  • использование typedef, которые скрывают тот факт, что что-то является указателем, обычно считается плохим стилем
  • если вам нужна сортировка, тогда связанный список - не лучшая структура для использования - вам лучше использовать массив
1 голос
/ 08 января 2010

Ваша проблема в том, что вы манипулируете только переменными RecPtr, что не имеет постоянного эффекта, когда вместо этого вы должны манипулировать полями nextRec структур в списке.

0 голосов
/ 09 января 2010

Я бы предложил сортировку слиянием в списке, так как это O (N Log N), в то время как сортировка пузырьков или сортировка вставками - O (N ^ 2).

Вот хороший пример простой односвязной сортировки слиянием списков .

0 голосов
/ 08 января 2010

Вы можете отсортировать список либо с помощью выбора, либо с помощью пузырьковой сортировки. Вместо данных вам нужно поменять местами указатель. Ниже я приведу оба примера, которые могут помочь вашей проблеме.

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;
}
0 голосов
/ 08 января 2010

вы можете легко отсортировать связанный список с пузырьковой сортировкой (итерация по нему, отслеживание i-1-го элемента в случае замены). Вы также можете довольно быстро выполнить быструю сортировку (на самом деле, я слышал, что некоторые люди предполагают, что быстрая сортировка имеет больше смысла в связанном списке, чем в массиве).

0 голосов
/ 08 января 2010

Если вы собираетесь изменить порядок связанного списка, то на каком-то этапе вам придется записывать указатели nextRec в списке ссылок. Поскольку вы назначаете только локальные переменные-указатели, вы не вносите никаких изменений в список.

Вы не выполняете никакого сброса между циклом i и циклом j, поэтому трудно понять, как ваш алгоритм гарантирует, что он не выйдет за пределы списка, хотя часто он не собирается продвигаться слишком далеко .

Если тест strcmp не запускается в течение двух итераций, он всегда оставляет nextPtr в покое и всегда присваивает currentPtr nextPtr->nextRec, поэтому ни nextPtr, ни currentPtr не изменятся.

currentPtr = nextPtr;
currentPtr = currentPtr->nextRec;

Вы действительно хотите использовать i++ в теле цикла, а также инкрементную часть цикла for?

Какой у вас алгоритм сортировки? Обратите внимание, что если вам нужно поменять местами два элемента в односвязном списке, вам нужно будет сохранить предыдущие узлы в заменяемых элементах, чтобы можно было настроить указатель «next» предыдущих узлов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...