Логическая ошибка - сортировка вставок

Структура каталогов кода,

$ pwd
/home/Personal/../Computing
$ ls -LR
.:
list testList.c testList.exe type.h

./list:
arrayImpl.c  linkedListImpl.c  list.h

/********* type.h ********/
#ifndef TYPE_H
#define TYPE_H /* Header guard */
 #include<stdbool.h>
 #include<stddef.h>
 #include<stdlib.h>
 #include<stdio.h>
 #include<string.h>
#endif /* TYPE_H */

/************ list.h ************/

#ifndef LIST_H /* Header guard */
#define LIST_H
#include"type.h"

/***************** Usage-start ************/

#if defined(ARRAY) || (LINKED_LIST)

  /* To ensure Encapsulation(i.e., maintain invariants of array & linked list)
     So, Just provide the `List` declartion, to avoid mis-use of `List`
  */
  typedef struct List List;

#else
  #error "Wrong list implementation macro name !!!"
#endif



 void listInsertItem(List *, void *newItem);
 void *listDeleteItem(List *, int listIndex);
 void *listDeleteLastItem(List *);
 void *listDeleteFirstItem(List *);
 const void *listGetItem(List *, const int index); /* 'index' is array index */
 int listGetSize(List *);


 /*********** Searching & Sorting algorithm - start*************/

 typedef int (*compareTo)(const void *key, const void *item);
 typedef bool (*isLess)(const void *key, const void *item);
 typedef bool (*isEqual)(const void *key, const void *item);

 void *linearSearch(const void *key, List *, size_t listSize, isEqual);
 /*
   bsearch() function returns a pointer to a matching member
   of the array, or NULL if no match is found
 */
 void *binarySearch(const void *key, List *, size_t listSize, compareTo);


 /*'listSize' elements. First argument points to list.*/
 void insertionSort(List *, size_t listSize, isLess);
 void selectionSort(List *, size_t listSize, compareTo);
     void shellSort(List *, size_t listSize, compareTo);
     void mergeSort(List *, size_t listSize, compareTo);
     void quickSort(List *, size_t listSize, compareTo);
/**************** Sorting algorithm - end*************/

 List* createList();
 void freeList(List *);
#endif /* LIST_H */

/***************** Usage-end ***************/

Ниже приведена реализация массива,

/***************** arrayImpl.c **************/

#include"list/list.h"

#if defined(ARRAY)
typedef enum {DOUBLE_THE_LIST, HALF_THE_LIST}ListResizeOperation;

static List *resizeList(List *, ListResizeOperation);
static void *bSearchRecur(const void *, void**, int, int, compareTo);
static void *bSearchIter(const void *, void **, int, int, compareTo);

static void swap(void **, int i, int j);
static void insSort(void **, size_t, isLess);

/************ Representation - start ************/
typedef struct List{
  void **array;

  /* Housekeeping - Array enhancement/shrink */
  int lastItemPosition;
  int size;
}List;

#define INITIAL_LIST_SIZE 50
#define FIRST_ITEM_INDEX 0
/********************* Representation - end ************/




/************* Usage - start ***************/
List *createList(){

    List *list = malloc(sizeof(List));
    if(list != NULL){

      list->array = malloc(INITIAL_LIST_SIZE*sizeof(void*));
      if(list->array != NULL){

        /* Is it safe to initialise zero to  array of  pointers? */
        list->array = memset(list->array, 0, INITIAL_LIST_SIZE*sizeof(void *));
        list->lastItemPosition = -1;
        list->size = INITIAL_LIST_SIZE;
      }else{
        free(list);
        list = NULL;
      }
    }

    return list;
}

void freeList(List *list){

  if(list != NULL){
    if(list->array != NULL){
      int index = 0;
      while( index < list->size){
        free(list->array[index++]);
      }
      free(list->array);
    }else{
      fprintf(stderr, "Invalid list sent to freeList()\n");
    }
    free(list);
  }
}


int listGetSize(List *list){
  if(list != NULL){
    return list->lastItemPosition + 1;
  }else{
    fprintf(stderr, "List is NULL\n ");
    return -1;
  }
}

const void *listGetItem(List *list, const int index){
  if((index >=0) && (index < list->size)){
    return (const void *)list->array[index];
  }else{
    return NULL;
  }
}

void listInsertItem(List *arrayList, void *newItem){

  if(arrayList == NULL){
    fprintf(stderr, "listInsertItem() -Invalid list \n");
    return;
  }
  /* House keeping - Enhance the array */
  if(arrayList->lastItemPosition + 1 == arrayList->size){
    arrayList = resizeList(arrayList, DOUBLE_THE_LIST);
    if(arrayList == NULL){
      fprintf(stderr, "insertItem() - Unable to allocate memory \n");
      exit(1);
    }
  }


  /* Insert new element - O(1) operation */
  arrayList->array[++(arrayList->lastItemPosition)] = newItem;


}

void *listDeleteItem(List *arrayList, int listIndex){

  if(arrayList == NULL){
    fprintf(stderr, "Invalid list \n");
    return NULL;
  }

  void *returnElement  = arrayList->array[listIndex];

  /* Delete operation - O(n) operation */
  for(int accumulator = listIndex; accumulator <= arrayList->lastItemPosition; accumulator++){
    arrayList->array[accumulator] = arrayList->array[accumulator + 1];
  }

  arrayList->lastItemPosition--;


  /* House keeping - Half the list */
  if(arrayList->size > INITIAL_LIST_SIZE){ /* Minimum size maintained */
    if((arrayList->lastItemPosition + 1) == ((arrayList->size)/2)){
      arrayList = resizeList(arrayList, HALF_THE_LIST);
      if(arrayList == NULL){
        fprintf(stderr, "deleteItem() - Unable to allocate memory \n");
        exit(1);
      }
    }
  }
  return returnElement; /* User must free this element*/
}


void * listDeleteLastItem(List *arrayList){
  return listDeleteItem(arrayList, arrayList->lastItemPosition);
}

void *listDeleteFirstItem(List *arrayList){
  return listDeleteItem(arrayList, FIRST_ITEM_INDEX);
}



/**************Searching & Sorting -start **************/
void *linearSearch(const void *key, List *list, size_t listSize, isEqual equal){

  if(list != NULL && (listSize > 0)){
    void ** array = list->array;
    for(int index =0; index < listSize; index++){
      if(equal(key, (array[index])) ){
        return array[index];
      }
    }
  }
  return NULL;
}

void *binarySearch(const void *key, List *list, size_t listSize, compareTo compare){
  if(list != NULL && (listSize > 0)){
    return bSearchIter(key, list->array, 0, listSize-1, compare);
    //return bSearchRecur(key, list->array, 0, listSize-1, compare);
  }
  return NULL;
}

void insertionSort(List *list, size_t listSize, isLess less){
  if(list!=NULL && (listSize > 0)){
    insSort(list->array, listSize, less);
  }
}
/**************Searching & Sorting -end **************/




/******************** Usage - end *******************/

/************* helper function - start     ********/


static void swap(void **array, int i, int j){
  void *tempPointer = array[i];
  array[i] = array[j];
  array[j] = tempPointer;
}


static void insSort(void **array, size_t listSize, isLess less){
  for(int sortedBoundaryIndex = -1; sortedBoundaryIndex < listSize; sortedBoundaryIndex++){
    /*
      -1 mean sorted pool is yet to form.
       0 mean first element is in sorted pool
    */

    for(int unSortedElementIndex = sortedBoundaryIndex + 1; unSortedElementIndex > 0; unSortedElementIndex--){
      /* Within this loop, sorted pool does not exist, as unsorted new element is being compared*/
      if(less(array[unSortedElementIndex], array[unSortedElementIndex-1])){
        swap(array, unSortedElementIndex, unSortedElementIndex-1);
      }else{
        break; //If the unsorted element is > or ==, no swap, Stable sort
      }
    }
  }

}


static void *bSearchIter(const void *key, void **array, int lowerBound, int upperBound, compareTo compare){

  int mid =0;
  while(lowerBound <= upperBound){

    mid = (lowerBound + upperBound)/2;

    if(compare(key, array[mid]) == 0){

      return array[mid];
    }else if(compare(key, array[mid]) < 0){
      upperBound = mid-1;
    }else if(compare(key, array[mid]) > 0){
      lowerBound = mid + 1;
    }
  }/* end while */

  return NULL;
}

static void *bSearchRecur(const void *key, void **array, int lowerBound, int upperBound, compareTo compare){

  if(lowerBound > upperBound) return NULL;

  int mid = (lowerBound + upperBound)/2;

  if(compare(key, array[mid]) == 0){

    return array[mid];
  }else if(compare(key, array[mid]) < 0){

    return bSearchRecur(key, array, lowerBound, mid-1, compare);
  }else { // compare() > 0

    return bSearchRecur(key, array, mid+1, upperBound, compare);
  }
}



/* resizeList() is not visible to Linker (ld) */

static List *resizeList(List *list, ListResizeOperation opType){

  if(opType == DOUBLE_THE_LIST){

    list->array = realloc(list->array, 2*(list->size)*sizeof(void *));
    if(list->array == NULL){ return NULL; }
    list->lastItemPosition = list->lastItemPosition;;
    list->size = 2*(list->size);
  }else if(opType == HALF_THE_LIST){

    list->array = realloc(list->array, ((list->size)/2)*sizeof(void *));
    if(list->array == NULL){ return NULL; }
    list->lastItemPosition = list->lastItemPosition;
    list->size = (list->size)/2;
  }

  return list;
}

/************* helper function - end     *************/

#endif

который выполняет сортировку вставки, но ниже кода пользователя,

/************************* testList.c *****************/
#include"list/list.h"
#include<time.h>
#include<stdlib.h>


bool equal (const void * key, const void *item){
  if( *((int *)key) == *((int *)item) ){
    return true;
  }else{
    return false;
  }
}

bool less (const void * key, const void *item){
  if( *((int *)key) < *((int *)item) ){
    return true;
  }else{
    return false;
  }
}


int compare(const void *key, const void *item){
  if( *((int *)key) == *((int *)item) ){
    return 0;
  }else if((int *)key < (int *)item){
    return -1;
  }else{
    return 1;
  }
}

int main(void){
  // Comments in the below, how do I formally convey to user ?
  List *arrayList = createList();
  int *item = NULL;
  srand(time(NULL));
  for(int input = 10; input > 0; input--){
    item = malloc(sizeof(int));
    *item = rand();
    listInsertItem(arrayList, item);
  }

  item = (int  *)listGetItem(arrayList, 0); // Returns (const void *)
  printf("First item: %d\n", *item);

  printf("\nSize of list: %d\n", listGetSize(arrayList));

  int *item1 = listDeleteItem(arrayList, 3);
  free(item1); // User's responsibility to avoid leak, after calling deleteItem()
  printf("One item deleted \n");

  printf("\nSize of list: %d\n", listGetSize(arrayList));

  printf("Item found after linear search: %d\n",*(int *)(linearSearch(item, arrayList, listGetSize(arrayList), equal)));
  printf("Item found after Binary search: %d\n",*(int *)(binarySearch(item, arrayList, listGetSize(arrayList), compare)));
  insertionSort(arrayList, listGetSize(arrayList), less);

  printf("Sorted array \n");
  for(int index =0; index < listGetSize(arrayList); index++){
    printf("Element: %d \n", *((const int *)listGetItem(arrayList, index)));
  }


  freeList(arrayList);  // User is responsible to free list, before stop using list
}

не печатать отсортированный массив.

Скомпилировано успешно, с инструкциями ниже,

> pwd
/home/Personal/../Computing
> `gcc -Wall -g -I. -DARRAY ./list/*.c testList.c -o testList`
./list/arrayImpl.c:240:14: warning: ‘bSearchRecur’ defined but not used [-Wunused-function]
 static void *bSearchRecur(const void *key, void **array, int lowerBound, int upperBound, compareTo compare){

но вывод есть,

$ ./testList.exe
First item: 2043943058

Size of list: 10
One item deleted

Size of list: 9
Item found after linear search: 2043943058
Item found after Binary search: 2043943058

Before sorting
Element: 1611124216
Element: 1315139801
Element: 642548661
Element: 1598077511
Element: 792705528
Element: 913581568
Element: 1766294466
Element: 1469344807
Element: 895523447

After sorting
Element: 1611124216
Element: 1315139801
Element: 642548661
Element: 1598077511
Element: 792705528
Element: 913581568
Element: 1766294466
Element: 1469344807
Element: 895523447

Ожидаемый результат после сортировки:

After sorting
Element: 1611124216
Element: 1315139801
Element: 642548661
Element: 1598077511
Element: 792705528
Element: 913581568
Element: 1766294466
Element: 1469344807
Element: 895523447

Вопрос:

Элемент не сортируется после вызова insertionSort(arrayList, listGetSize(arrayList), less);,

Как решить эту проблему? Это как-то связано с swap() указатели?

Примечание: Cygwin не помогает с GDB.

2 ответа

Просто взглянув на ваш код, похоже, вы уже понимаете основы реализации этого алгоритма сортировки. Вы можете написать свою функцию сортировки вставки так:

list_t *insertion_sort(list_t *list) {
    int i, j;

    for (i = 1; i < list->lastItemPosition; i++) {
        for (j = i-1; j >= 0 && int_cmp(list->array[j+1], list->array[j]); j--) {
            swap(&list->array[j], &list->array[j+1]);
        }
    }
    return list;
}

Какой внешний цикл проверяет, если array[0] в array[n-1] имеют действительные значения. Вы также можете установить lastItemPosition в 0, так как имеет смысл начинать здесь, а не -1, Внутренний цикл просто проверяет, если число после j+1 больше текущего числа j и проверяет в диапазоне [i-1,0], а затем меняются оба числа.

Вот некоторые из вспомогательных функций, используемых в этом коде:

/* Just like your function */
int int_cmp(const void *a, const void *b) {
    if(*((int *)a) < *((int *)b) ){
        return 1;
    }
    return 0;
}

void swap(void **a, void **b) {
    void *temp = *a;
    *a = *b;
    *b = temp;
}  

Как видно это void swap() Вы можете просто сравнить ** значения, вместо интеграции индексов i а также j в ваше сравнение.

Я решил проверить это с некоторым кодом, который я написал, и это выглядит так:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define INITIAL_LIST_SIZE 50

#define MALLOC_MSG "Memory allocation"
#define REALLOC_MSG "Memory reallocation"

typedef struct {
    void **array;
    size_t lastItemPosition;
    size_t currsize;
} list_t;

list_t *create_list(void);
list_t *insert_item(list_t *list, void *item);
list_t *insertion_sort(list_t *list);
void print_array(list_t *list);
void free_list(list_t *list); 
void check_ptr(void *ptr, const char *msg);
int int_cmp(const void *a, const void *b);
void swap(void **a, void **b);

int main(void) {
    list_t *list;
    int i, n, *item;

    list = create_list();

    printf("Enter n: ");
    if (scanf("%d", &n) != 1 || n < 1) {
        printf("Invalid n value.\n");
        exit(EXIT_FAILURE);
    }

    srand(time(NULL));
    for (i = 0; i < n; i++) {
        item = malloc(sizeof(*item));
        check_ptr(item, MALLOC_MSG);
        *item = rand();
        list = insert_item(list, item);
    }

    printf("Before: ");
    print_array(list);

    list = insertion_sort(list);

    printf("After: ");
    print_array(list);

    free_list(list);

    return 0;
}

void print_array(list_t *list) {
    size_t i;

    for (i = 0; i < list->lastItemPosition; i++) {
        printf("%d ", *(int*)list->array[i]);
    }
    printf("\n");
}

list_t *insertion_sort(list_t *list) {
    size_t i;
    int j;

    for (i = 1; i < list->lastItemPosition; i++) {
        for (j = i-1; j >= 0 && int_cmp(list->array[j+1], list->array[j]); j--) {
            swap(&list->array[j], &list->array[j+1]);
        }
    }
    return list;
}

list_t *insert_item(list_t *list, void *item) {
    if (!list) {
        printf("Invalid list requested.\n");
        exit(EXIT_FAILURE);
    }

    if (list->currsize == list->lastItemPosition) {
        list->currsize *=2;
        list->array = realloc(list->array, list->currsize * sizeof(*(list->array)));
        check_ptr(list->array, REALLOC_MSG);
    }

    list->array[list->lastItemPosition++] = item;
    return list;
}

list_t *create_list(void) {
    list_t *list = malloc(sizeof(*list));
    check_ptr(list, MALLOC_MSG);

    list->currsize = INITIAL_LIST_SIZE;

    list->array = malloc(list->currsize * sizeof(*(list->array)));
    check_ptr(list->array, MALLOC_MSG);

    list->lastItemPosition = 0;
    return list;
}

void free_list(list_t *list) {
    size_t i;

    for (i = 0; i < list->lastItemPosition; i++) {
        free(list->array[i]);
        list->array[i] = NULL;
    }
    free(list->array);
    list->array = NULL;

    free(list);
    list = NULL;
}

int int_cmp(const void *a, const void *b) {
    if(*((int *)a) < *((int *)b) ){
        return 1;
    }
    return 0;
}

void swap(void **a, void **b) {
    void *temp = *a;
    *a = *b;
    *b = temp;
}

void check_ptr(void *ptr, const char *msg) {
    if (!ptr) {
        printf("Unexpected null pointer found: %s.\n", msg);
        exit(EXIT_FAILURE);
    }
}

Примечание. Этот код, безусловно, можно улучшить, но он показывает, что функция сортировки вставками работает (насколько мне известно).

Кроме того, было бы также лучше использовать qsort, который O(logN) время выполнения в среднем, что лучше, чем вставка сортирует среднее значение O (N ^ 2).

Вот пример вывода:

Enter n: 5
Before: 593517087 518087463 1534155136 922486149 304751363
After: 304751363 518087463 593517087 922486149 1534155136

Внутри insSort функция у вас есть sortedBoundaryIndex типа int (подписано) начинается с -1 тогда вы сравниваете это в for состояние с listSize типа size_t (без подписи). Когда сравниваются два числа, одно со знаком и другое без знака, подписанное число интерпретируется как число без знака (еще хуже, чем меньше будет подписанное число, тем больше будет его интерпретация). Так -1 (поскольку это наименьшее отрицательное число) будет интерпретироваться как максимально возможное значение для unsigned int который 4294967295, Таким образом, цикл никогда не вводится. В любом случае это бессмысленно sortedBoundaryIndex начать с -1 потому что состояние внутреннего цикла потерпит неудачу, так sortedBoundaryIndex должен начинаться с 0,

ПРИМЕЧАНИЕ: состояние внешнего цикла должно быть sortedBoundaryIndex < listSize - 1 и не sortedBoundaryIndex < listSize потому что во внутреннем цикле вы добавляете 1 в sortedBoundaryIndex получить unSortedElementIndex и использовать его как индекс для array, так когда sortedBoundaryIndex достичь listSize - 1 затем unSortedElementIndex будет равен listSize который выходит за пределы и вызовет ошибку выполнения.

Вот моя версия insSort функция (я использовал while вместо for в качестве внутреннего цикла И конечно же, я использовал короткие имена):

static void insSort(void **array, size_t listSize, isLess less){
    for(int sbi = 0; sbi < listSize - 1; sbi++){
        int usei = sbi + 1;
        while(usei > 0 && less(array[usei], array[usei - 1])){
            swap(array, usei, usei - 1);
            usei--;
        }
    }
}
Другие вопросы по тегам