渐增型算法一:插入排序
一、算法说明
1、渐增型算法:作为算法的主体是一个循环,逐个处理输入数据,已经处理的部分为问题的解;即按顺序处理问题,当输入数据处理完,问题也就处理了。
2、算法特点:直观,时间复杂度不友好
3、个人理解:降低问题维度,将一维的输入数据转化成单个数据,能准确处理单个数据时,遍历整个输入数据即可。
二、插入排序算法
1、输入一个乱序的数组,使用插入排序算法,输出升序或者降序数组;
2、从乱序数组中,逐个取出元素,加入已经排序好的数组,保证新加入的元素不破坏当前数组排序;
3、将数组排序问题,转化成,单个元素有序插入已经排序数组问题;
4、问题初始状态:从原始数组中取出第一个元素,作为已排序的数组初始状态;
5、算法核心:单个元素插入已排序数组,从数组尾部比较,将数据后移,找到该元素对应位置。
三、基础版本代码实现
int insertionSortV1(int *array, int len)
{
int key, j;
for (int i = 1; i < len; i++) {
key = array[i];
j = i;
// 将key插入到已经排好序的array[j - 1]中,使array[j]有序;修改判断条件,可以控制顺序
while (j > 0 && (array[j - 1] > key)) {
array[j] = array[j -1];
j--;
}
array[j] = key;
}
return 0;
}
四、使用指针优化,扩展通用性
1、指针使用说明:输入任意类型的数据,提供对应的比较函数,即可以实现排序,参考qsort。
2、比较函数实现:
// V2: 输入任意类型的数组,使用指针实现
int intGreater(void *x, void *y)
{
// 需要先对指针类型进行强制转换
return *(int *)x - *(int *)y;
}
int charGreater(void *x, void *y)
{
// 需要先对指针类型进行强制转换
return strcmp((char *)x, (char *)y);
}
3、算法优化实现:
int insertionSortV2(void *array, int len, int array_size, int (*cmp)(void *, void *))
{
int j;
// array_size 表示数组元素的大小
void *key = (void *)malloc(array_size);
memset(key, 0, array_size);
for (int i = 1; i < len; i++) {
// 利用指针取数
key = memcpy(key, array + i * array_size, array_size);
j = i;
// 将key插入到已经排好序的array[j - 1]中,使array[j]有序
while ((j > 0) && (cmp(key, array + (j - 1) * array_size) < 0)) {
// 内存赋值,使用指针
memcpy(array + j * array_size, array + (j - 1) * array_size, array_size);
j--;
}
memcpy(array + j * array_size, key, array_size);
}
return 0;
}
五、双链表的插入排序
链表的创建和清除参考:双链表:结构体定义、创建、清除
整体思路与上相同,依旧是逐个处理各节点数据,将其插入已经排好序的链表中。
/*
* descrition:双链表的插入排序
* input:链表头结点指针,结点数据的大小,比较函数
* output:在原链表上进行排序,成功返回 0
*/
int insertionSortV3(DOUBLE_LINKED_NODE *doubleLinkNode, int nodeDataSize, int(*cmp)(void *, void *))
{
DOUBLE_LINKED_NODE *newNode, *preNode, *posNode;
void* tempData = (void*)malloc(nodeDataSize);
// 默认第一个结点有序,逐个处理后续节点
for (newNode = doubleLinkNode->next; newNode != NULL; newNode = newNode->next) {
// 节点数据赋值
memcpy(tempData, newNode->data, nodeDataSize);
preNode = newNode->pre;
// posNode 用于标记新节点带插入的位置
posNode = newNode;
// 利用前向指针preNode,将新增的节点插入到原来已经排好序的部分链表中
// 实际上,是将不各节点后移,找到新节点的正确位置
while ((preNode != NULL) && (cmp(preNode->data, tempData) > 0))
{
// 满足条件时,当前节点后移,直到不能后移时,就是新节点的位置了
memcpy((preNode->next)->data, preNode->data, nodeDataSize);
posNode = preNode;
preNode = preNode->pre;
}
// 已经确定新节点的插入位置
memcpy(posNode->data, tempData, nodeDataSize);
}
return 0;
}
六、测试
// 链表的打印函数
static int printfDoubleList(char *info, DOUBLE_LINKED_NODE *node)
{
DOUBLE_LINKED_NODE *nodeTenp = node;
printf("%s", info);
while (nodeTenp != NULL && nodeTenp->data != NULL) {
printf("%d ", *(int*)(nodeTenp->data));
nodeTenp = nodeTenp->next;
}
printf("\n");
return 0;
}
// 测试代码
int main(void)
{
int array[ARRAY_LEN] = {9, 3, 2, 4, 6, 7, 1, 0, 5, 8};
char array_char[ARRAY_LEN] = {'a', 'h', 'd', 's', 'c', 'W', 'l', 'M', 'Z', 'z'};
int ret;
// ret = insertionSortV1(array, ARRAY_LEN, sizeof(int), intGreater);
ret = insertionSortV2(array_char, ARRAY_LEN, sizeof(char), charGreater);
if (ret != 0) {
printf(" insetion sort array failed.ret=%d\n", ret);
goto out;
}
for (int i = 0; i < ARRAY_LEN; i++) {
// printf("array[%d] = %d\n", i, array[i]);
printf("array_char[%d] = %c\n", i, array_char[i]);
}
// 链表的测试函数:需要添加链表的创建和删除的函数实现,以及链表结构体定义
int array[LIST_LEN] = {0, 5, 9, 2, 4, 1, 7, 6};
DOUBLE_LINKED_NODE *temp;
// 创建双链表
temp = createDoubleLinkedList(array, LIST_LEN, sizeof(int));
printfDoubleList("createList:", temp);
// 插入排序
insertionSortV3(temp, sizeof(int), intGreater);
printfDoubleList("sortList:", temp);
// 清空双链表
clearDoubleLinkedList(temp);
printfDoubleList("clearList.", temp);
out:
while (1);
return 0;
}