有更好的方法进行插入排序吗?

问题描述

要插入视频的YouTube视频-https://www.youtube.com/watch?v=JU767SDMDvA

这是我在C语言中的实现

void insertionSort(void *data,uint32_t length,int8_t compareto(const void * const,const 
  void * const),const size_t bytes){
  uint32_t i;
  for(i = 0; i < length; i++){
    uint8_t isSorted;
    int32_t j;
    isSorted = 0;
    for(j = i - 1; j > -1 && !isSorted; j--){
        isSorted = 1;
        if(compareto((int8_t *)data + j * bytes,(int8_t *)data + (j + 1) * bytes) > 0){
            uint32_t byteIndex;
            void *valcopy;
            valcopy = malloc(bytes);
            memcpy(valcopy,(int8_t *)data + j * bytes,bytes);

            for(byteIndex = 0; byteIndex < bytes; byteIndex++){
                *((int8_t *)data + (j * bytes + byteIndex)) = *((int8_t *)data + ((j + 1) * bytes + byteIndex));
                *((int8_t *)data + ((j + 1) * bytes + byteIndex)) = *((int8_t *)valcopy + byteIndex);
            }

            /**
            instead of the for loop you can replace it with this to make it look more clean
            memcpy((int8_t *)data + j * bytes,(int8_t *)data + (j + 1) * bytes,bytes);
            memcpy((int8_t *)data + (j + 1) * bytes,valcopy,bytes);
            **/

            free(valcopy);
            isSorted = 0;
      }
    }
  }
}


int8_t compareto(const void * const val1,const void * const val2){
   if(*(const int32_t * const)val1 > *(const int32_t * const)val2)return 1;
   else if(*(const int32_t * const)val1 < *(const int32_t * const)val2)return -1;
   return 0;
}

int main(void){
   int32_t i;
   int32_t data[] = {2,6,5,3,8,7,1,0};
   int32_t dataLength = sizeof(data) / sizeof(*data);

   insertionSort(data,dataLength,&compareto,sizeof(int32_t));

   for(i = 0; i < dataLength; i++){
       printf("%d ",data[i]);
   }

   return 0;
}

我想知道是否有比每次使用memcpy或for循环复制值更有效的方法

解决方法

正如另一个答案所观察到的,不需要为每个交换调用{todos.map((todo,isComplete) => ( <> <div key={todo.id} onClick={completeTodo} className={isComplete ? 'complete' : ''} > {todo.text} </div> <FaWindowClose onClick={() => removeTodo(todo.id)} /> </> ))} malloc()。所有这些额外的电话确实似乎是效率低下的最大根源。您最多需要一个free()和一个malloc通话,但是您可以在没有 any 的情况下摆脱困境,因为商品大小不超过所选限制。例如,

free

作为一个小问题,不需要使用变量#define SMALL_ITEM_LIMIT 1024 /* for example */ // ... void insertionSort(void *data,uint32_t length,int8_t compareTo(const void * const,const void * const),const size_t bytes) { char auto_buffer[SMALL_ITEM_LIMIT]; char *temp; if (bytes > SMALL_ITEM_LIMIT) { temp = malloc(bytes); if (temp == NULL) { // ... handle allocation failure ... } } else { temp = auto_buffer; } // ... main part of the sort ... if (temp != auto_buffer) { free(temp); } } ,而且有点笨拙。您可以避免发生这种情况,只需在当前元素到达其插入位置时从isSorted循环break进行循环,就可以节省几个周期。

您问:

我想知道是否有比复制值更有效的方法 每次使用memcpy或for循环?

对于这样的通用排序,在这种情况下您不知道要排序的项目的类型,没有替代大容量存储器操作的元素。我更倾向于从j和/或memcpy()开始,这很清楚。为此,请不要使用内部循环,而无需在各种情况下进行测试以确定它是否确实可以提供任何改进。

但是,您不一定需要一次将元素移动一个位置。相反,在每个外循环迭代中,您都可以定位插入位置而不移动任何内容,然后通过单个 n 元素旋转执行插入。对于随机数据,将倾向于执行较少的读取和写入。这种变化可能看起来像这样(有些名称已更改,以使其更清楚):

memmove()

或者,在实践中将旋转与比较更并行地执行以更好地利用高速缓存和数据局部性可能会更有效。这就好比每次交换只执行一半(或三分之一)的交换。排序循环如下所示:

void insertionSort(void *data,uint32_t item_count,int compare(const void *,const void *),size_t item_size) {
    char auto_buffer[SMALL_ITEM_LIMIT];
    char *temp = (item_size > SMALL_ITEM_LIMIT) ? malloc(item_size) : auto_buffer;

    if (temp) {
        char *char_data = data;  // for clarity; avoids having to cast all the time

        for (uint32_t i = 1; i < count; i++) { // no point in starting at 0
            // Find the insertion position
            for (uint32_t j = i; j > 0; j--) {
                if (compare(char_data +  j      * item_size,char_data + (j - 1) * item_size) >= 0) {
                    break;
                }
            }
            // Rotate the current item into position
            if (j != i) {
                memcpy(temp,char_data + i * item_size,item_size);
                memmove(char_data +  j      * item_size,char_data + (j + 1) * item_size,(i - j) * item_size);
                memcpy(char_data + j * item_size,temp,item_size);
            }
        }

        if (temp != auto_buffer) {
            free(temp);
        }
    } // else memory allocation failed
}

在任何特定情况下,其中哪些实际上在实践中会表现得更好,这是通过测试来回答的问题。

,

实现中最无效的部分是对sqllines=dflines.createOrReplaceTempView('lines') spark.sql("""select word,count(word) count from (select explode(split(value,' ')) word from lines) words group by word order by count desc""").show(2) malloc()的不必要的调用,这些调用只需要被调用一次,然后就可以在算法中的每次交换中重复使用。这是一个可能的实现:

free()

Try it on godbolt

,

插入排序不适用于数组,因为必须执行昂贵的数组重新排列。插入排序应在linkedLists上使用。在数组上,类似但更好的算法是选择排序。当然,快速排序或合并排序之类的分而治之算法也很好。