我需要使用Quicksort排序算法对链表的元素进行排序。需要注意的是,我不能只交换键值,还必须交换元素本身。我的代码根本不起作用;我相信我在递归中犯了一个错误。我想要一个解决方案。
我创建了以下函数来实现我的目标:
///ELEMENTO TEM DUAS CHAVES E SEU PRÓXIMO (ENCADEAMENTO SIMPLES)///
struct elemento {
int chave[2];
struct elemento *prox;
};
///MOSTRAR A LISTA GERADA///
void mostrar_chaves(struct elemento *lista){
int i;
struct elemento *aux;
aux = lista;
printf("\n\n\n");
while(lista){
printf("%d - ", lista->chave[0]);
lista = lista->prox;
}
printf("\n\n\n");
while(aux){
printf("%d - ", aux->chave[1]);
aux = aux->prox;
}
}
///QUICK SORT///
struct elemento *particiona(struct elemento *ini, struct elemento *fim){
struct elemento *pivo, *aux, *t1, *t2, *t3, *teste;
t1 = ini;
t2 = ini;
teste = ini;
int flag = 0;
/*printf("\nini = %d\n", ini->chave[0]);
printf("\nfim = %d\n", fim->chave[0]);*/
for(pivo = aux = ini; aux && aux != fim; ) {
if(aux->chave[0] < fim->chave[0]){
if(ini != aux){
pivo = aux;
flag = 1;
if(t1 == ini){
if(t1 != t2){
teste = aux;
ini = ini->prox;
t1->prox = aux->prox;
aux->prox = ini;
t2->prox = t1;
t1 = aux;
aux = t2->prox->prox;
}
else if(t1 == t2){
teste = aux;
aux = aux->prox;
t1 = t1->prox;
t1->prox = ini;
ini->prox = aux;
}
}
else{
t1->prox = aux; //10 liga no 5
aux = aux->prox; //aux 20
t1->prox->prox = ini->prox; //5 liga no 40
t2->prox = ini; //10 liga no 50
ini = ini->prox; //ini 40
t2->prox->prox = aux;
}
}
else{
ini = ini->prox;
}
}
if(flag == 0)
aux = aux->prox;
if(t1 != ini && t1->prox != ini){
t1 = t1->prox;
}
if(t2 != aux && t2->prox != aux)
t2 = t2->prox;
flag = 0;
}
t1->prox = fim; //50 liga no 20
t3 = fim->prox; //NULL
t1->prox->prox = ini->prox; //5 liga no 40
t2->prox = ini; //10 liga no 50
ini->prox = t3;
fim = ini;
ini = aux;
printf("\npivo = %d\n", pivo->chave[0]);*/
mostrar_chaves(teste);
return pivo;
}
void quick_sort(struct elemento *ini, struct elemento *fim){
struct elemento *pivo;
printf("\nINI_Q - %d FIM_Q - %d\n",ini->chave[0], fim->chave[0]);
if (ini == fim) {
printf("\nreturn estado anterior\n");
return;
}
pivo = particiona(ini, fim);
if (pivo && pivo->prox) {
quick_sort(pivo->prox, fim);
}
if (pivo && ini != pivo) {
quick_sort(ini, pivo);
}
}
struct elemento *fim(struct elemento *ini){
struct elemento *fim;
for(fim = ini; fim && fim->prox; fim = fim->prox);
return fim;
}
void main(){
setlocale(LC_ALL, "portuguese");
struct elemento *lista_um, *lista_dois;
lista_um = NULL;
lista_um = (struct elemento *) malloc(sizeof(struct elemento)*6);
lista_dois = lista_um;
lista_dois->chave[0] = 30;
lista_dois = lista_dois->prox;
lista_dois->chave[0] = 50;
lista_dois = lista_dois->prox;
lista_dois->chave[0] = 40;
lista_dois = lista_dois->prox;
lista_dois->chave[0] = 10;
lista_dois = lista_dois->prox;
lista_dois->chave[0] = 5;
lista_dois = lista_dois->prox;
lista_dois->chave[0] = 20;
我正在按“chave[0]”对元素进行排序。在代码的最后,输入 6 个元素的顺序为:30, 50, 40, 10, 5, 20,我需要它们的顺序为 5, 10, 20, 30, 40, 50。
对您的代码的一些评论:
您假设分区后,第一个节点仍然是左分区中的第一个节点,最后一个节点仍然是右分区中的最后一个节点,但这不能保证。因此,分区完成后,您需要更多信息:哪些是左分区中的第一个和最后一个节点,哪些是右分区中的第一个和最后一个节点。
类似的注释适用于
quick_sort
:它接收链表(第一个/最后一个),但调用者在排序完成后将不知道第一个和最后一个节点是什么——回想一下传递了函数参数按值,因此调用者不会知道第一个节点仍然是第一个节点。他们没有其他线索。
t1
、
t2
、flag
、teste
都代表什么——他们的名字不是很能说明问题。
linkedlist
结构,该结构具有这两个指针(第一个/最后一个)以及操作此类链接的函数列表(向其中添加一个节点、从中提取一个节点、连接两个列表、显示一个列表等)。这将有助于抽象出繁琐的操作并使代码更具可读性。
到这三个列表之一,因此输入列表在分区结束时将为空。然后,调用者可以决定如何处理这三个列表:对左列表、右列表应用递归排序,然后连接结果。
元素(Node)
struct element {
int key[2];
struct element *next;
};
struct element *create_element(int key, int payload) {
struct element *el = malloc(sizeof *el);
el->key[0] = key;
el->key[1] = payload;
el->next = NULL;
return el;
}
void display_element(struct element *el) {
printf("%d(%d)", el->key[0], el->key[1]);
}
链表
struct linkedlist {
struct element *first;
struct element *last;
};
struct linkedlist *create_linkedlist() {
struct linkedlist *list = malloc(sizeof(*list));
list->first = list->last = NULL;
return list;
}
void linkedlist_append(struct linkedlist *list, struct element *el) {
if (!list->first) {
list->first = el;
} else {
list->last->next = el;
}
list->last = el;
el->next = NULL;
}
void linkedlist_extend(struct linkedlist *list, struct linkedlist *other) {
if (!other->first) {
return;
}
if (!list->first) {
list->first = other->first;
} else {
list->last->next = other->first;
}
list->last = other->last;
other->first = other->last = NULL;
}
struct element *linkedlist_extract_first(struct linkedlist *list) {
if (!list->first) {
return NULL;
}
struct element* el = list->first;
list->first = el->next;
if (!list->first) {
list->last = NULL;
}
el->next = NULL;
return el;
}
void linkedlist_display(struct linkedlist *list) {
struct element *el = list->first;
while (el) {
display_element(el);
printf(" -> ");
el = el->next;
}
printf("null");
}
快速排序:
void partition(struct linkedlist *list, struct linkedlist *left,
struct linkedlist *mid, struct linkedlist *right) {
// Guaranteed that list is not empty
struct element *el = linkedlist_extract_first(list);
int pivotKey = el->key[0];
while (el)
// Decide where to move this element to:
struct linkedlist *target = el->key[0] < pivotKey ? left
: el->key[0] > pivotKey ? right
: mid;
linkedlist_append(target, el); // Move it
el = linkedlist_extract_first(list); // Take next
}
}
void quick_sort(struct linkedlist *list) {
if (list->first == list->last) { // Empty or one element only
return; // Nothing to do
}
struct linkedlist *left = create_linkedlist();
struct linkedlist *mid = create_linkedlist();
struct linkedlist *right = create_linkedlist();
partition(list, left, mid, right); // Will populate left, mid, right
// At this point list is empty
quick_sort(left);
quick_sort(right);
// Concatenate the sorted partitions
linkedlist_extend(list, left);
linkedlist_extend(list, mid);
linkedlist_extend(list, right);
}
运行示例
int main() {
struct linkedlist *list = create_linkedlist();
linkedlist_append(list, create_element(30, 0));
linkedlist_append(list, create_element(50, 0));
linkedlist_append(list, create_element(40, 0));
linkedlist_append(list, create_element(10, 0));
linkedlist_append(list, create_element( 5, 0));
linkedlist_append(list, create_element(20, 0));
printf("Input:\n");
linkedlist_display(list);
quick_sort(list);
printf("Sorted:\n");
linkedlist_display(list);
return 0;
}
输出将是:
Input:
30(0) -> 50(0) -> 40(0) -> 10(0) -> 5(0) -> 20(0) -> null
Sorted:
5(0) -> 10(0) -> 20(0) -> 30(0) -> 40(0) -> 50(0) -> null
最后的评论
当输入列表已经用不同的值排序时,将第一个节点作为枢轴将导致最坏情况的时间复杂度(因为这样
left
将保持为空,而
right
将只少一个元素)。但是采用另一个枢轴节点将意味着更多的工作,因为您只能从左到右遍历元素 - 没有随机访问。