Я ранее реализовал сортировку слиянием для массивов, поэтому после исправления моего кода для сортировки слиянием на основе массивов я теперь реализовал сортировку слиянием для базовой структуры данных односвязного списка, которая содержит только указатель на данные и указатель на следующий «Узел». Я попытался воспользоваться советом, данным в предыдущем посте (/ q / 260010), чтобы помочь мне на этот раз. Есть три вещи, которые я не изменил ни в моем текущем, ни в предыдущем коде; позвольте мне объяснить мои рассуждения:
ЗамедлениеОбъявление и инициализация переменных делаются отдельно, так как я использую C90, по привычке.- Я привык использовать
<stdint.h>
заголовок для написания всего моего кода с использованием типов фиксированной ширины, поэтому я обычно использую его для таких функций, какmerge_sort
чтобы он оставался неизменным на разных платформах. В то время как код пользователя написан с использованием «int», «short», «char», чтобы имитировать некое подобие пользовательского кода. - Я научился использовать пробел для программирования вместо табуляции, и нам сказали сделать 3 пробела, сколько места вы рекомендуете 2, 3, 4 или X?
Любые советы о том, как улучшить мой код, помогут; Надеюсь, эта реализация намного лучше, поскольку я читал онлайн и был проинформирован, что сортировка слиянием обычно предпочтительнее для связанного списка и больших наборов данных.
TL; DR — посоветуйте, пожалуйста, как улучшить мою реализацию сортировки слиянием для версии связанного списка. А сколько пробелов вы используете для отступов?
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
struct Node{
void *data;
struct Node* next;
};
/*triple ref pointer are really cool as you don't need two pointers*/
int32_t split_list(struct Node *current, struct Node **front, struct Node **middle)
{
uint32_t length, length_of_middle;
struct Node *current_temp = current;
struct Node **triple_ref = ¤t;
if(current == NULL)
{
*front = *middle = NULL;
return -1;
}
*front = current;
*middle = current;
length = length_of_middle = 0;
while(current_temp != NULL)
{
current_temp = current_temp->next;
length++;
}
while(length_of_middle < length / 2)
{
triple_ref = &((*triple_ref)->next);
length_of_middle++;
}
*middle = *triple_ref;
*triple_ref = NULL;
return 0;
}
int32_t merge_sort(struct Node **head, int compare(const void *, const void *))
{
struct Node *front, *middle;
struct Node temp;
struct Node *current = &temp;
if(head == NULL)
{
return -1;
}
else if((*head)->next == NULL)
{
return 0;
}
split_list(*head, &front, &middle);
merge_sort(&front, compare);
merge_sort(&middle, compare);
while(front != NULL && middle != NULL)
{
if(compare(front, middle) <= 0)
{
current->next = front;
front = front->next;
current = current->next;
}
else
{
current->next = middle;
middle = middle->next;
current = current->next;
}
}
if(front != NULL)
{
while(front != NULL)
{
current->next = front;
front = front->next;
current = current->next;
}
}
else
{
while(middle != NULL)
{
current->next = middle;
middle = middle->next;
current = current->next;
}
}
*head = temp.next;
return 0;
}
/*user code*/
int compare(const void *ptr1, const void *ptr2)
{
const struct Node *node1, *node2;
node1 = ptr1;
node2 = ptr2;
return *(int *)(node1->data) - *(int *)(node2->data);
}
void print_list(const struct Node *node)
{
printf("n");
while(node != NULL)
{
printf("%d ", *(int *)(node->data));
node = node->next;
}
printf("n");
}
int32_t push_list(struct Node **head, void *val, const size_t size_of_element)
{
struct Node *node;
if(head == NULL)
{
return -1;
}
node = malloc(sizeof(*node));
if(node == NULL)
{
return -1;
}
/*copy data*/
node->data = malloc(size_of_element);
if(node->data == NULL)
{
free(node);
return -1;
}
memcpy(node->data, val, size_of_element);
node->next = NULL;
/*insertion*/
if(*head != NULL)
{
struct Node *current;
current = *head;
while(current->next != NULL)
{
current = current->next;
}
current->next = node;
return 0;
}
*head = node;
return 0;
}
void free_list(struct Node *head)
{
struct Node *temp;
while(head != NULL)
{
temp = head;
head = head->next;
free(temp->data);
free(temp);
}
}
int main(void)
{
struct Node *head;
struct Node *front, *middle;
int i;
i = 10;
/* i = 666;*/
/*add values*/
head = front = middle = NULL;
for(; i > -1; i--)
{
push_list(&head, &i, sizeof(i));
}
i = 10;
push_list(&head, &i, sizeof(i));
i = -1;
push_list(&head, &i, sizeof(i));
/*print sort print*/
print_list(head);
merge_sort(&head, &compare);
print_list(head);
free_list(head);
return 0;
}
2 ответа
Я научился использовать пробел для программирования вместо табуляции, и нам сказали сделать 3 пробела, сколько места вы рекомендуете 2, 3, 4 или X?
А сколько места вы используете при программировании?
Это проблема стиля, и лучше всего следовать стандарту кодирования вашей группы, который, по-видимому, равен 3. Я использую 2.
Хорошая среда кодирования позволяет вам быстро изменить отступ всего файла с помощью параметра настройки и вызова этого автоматического форматирования.
Если вы вручную редактируете правильный отступ, вы неэффективны. Используйте автоформатер.
Замедление ….
Эй, замедлять есть 😉. Вы имели в виду Декларация?
Пространство имен
Скорее, чем
struct Node{
int32_t split_list()
int32_t merge_sort()
void print_list()
int32_t push_list()
void free_list()
Рассмотрите единообразное именование
struct DList {
int32_t DList_split()
int32_t DList_merge_sort()
void DList_print()
int32_t DList_push()
void DList_free()
Сформировать DList.h
заголовочный файл и отделите свой DList
функции в DList.c
файл.
Слабое сравнение
*(int *)(node1->data) - *(int *)(node2->data)
рискует UB и неверным результатом, когда вычитание приводит к переполнению.
Вместо:
int i1 = *(int *)(node1->data);
int i2 = *(int *)(node2->data);
return (i1 > i2) - (i1 < i2);
int
против. int32_t
против ….
Я не вижу смысла в использовании int32_t
здесь. Рекомендуем переделать код на size_t
для типа, который относится к индексированию массивов и int/bool
для простого флага успеха.
Незначительный: ()
не требуется
Вопрос стиля:
// node = malloc(sizeof(*node));
node = malloc(sizeof *node);
Никаких голых петель. Каждый цикл представляет собой важный алгоритм и как таковой заслуживает названия. Например, петли в
split_list
действительноuint32_t list_length(struct Node *);
а также
struct Node * advance(struct Node *, uint32_t);
split_list
немного сложнее, чем необходимо. Дело в том, чтоfront
становитсяcurrent
известно заранее. Неужели нам нужно его сдавать?Точно не вижу причин для
triple_ref
быть**
(Кстати, название действительно сбивает с толку — почему тройной?).Все, что сказал, считайте
struct Node * split_list(struct Node * current) { if (current == NULL) { return MULL; } uint32_t half_length = list_length(current) / 2; struct Node * pre_middle = advance(current, half_length); struct Node * middle = pre_middle->next; pre_middle->next = 0; return middle; }
Я не вижу причин для
merge_sort
чтобы вернуть целое число. В любом случае это значение никогда не проверяется. Гораздо естественнее вернутьсяhead
:struct Node * merge_sort(struct Node * head, int compare(const void *, const void *));
Необъединенные остатки списков перебирать не нужно. Они уже хороши, а нас не волнует
current
больше.if (front != NULL) { current->next = front; } else { current->next = middle; }
current = current->next
выполняется в обоихif
а такжеelse
ветви слияния. Поднимите это.
Причина, по которой я использую «triple_ref» в качестве имени, связана с этим видео. youtube.com/… пользователя Computerphile. И это плохо, так как это больше концепция по сравнению с фактическим указателем тройной ссылки.
— Дней
Извините, я исправил опечатку в вопросе, испортил ваш каламбур …
— Тоби Спейт
Понятно. Мне нужно изменить имя моей функции для связанного списка, чтобы он был более согласованным! Кстати Хороший каламбур, мне нужно притормозить.
— Дней