数据结构与算法学习简易总结(C)

1、数据结构

1.1 动态数组

用C语言实现动态数组

1. 数组特点
   存储特点: 内存是连续存储
   优点: 查找快
   缺点: 插入、删除低效
   
2. 如何实现动态可扩展数组
   ① 先分配一块内存 malloc() calloc()
   ② 如果内存不够,realloc() 扩容
   ③ 程序结束前,释放内存

代码实现
1.定义动态数组结构体并进行初始化/释放操作

定义结构体

// 定义元素类型
typedef int ele_type;

// 定义结构体 表示动态数组类型
typedef struct
{
    ele_type *data;  // 存储元素的内存区域地址
    size_t size;     // 以存储的元素个数,动态数组的长度
    size_t capacity; // 动态数组的最大容量
} DynamicArray;

初始化数组

/**************************************************************
 * @brief 初始化动态数组
 *
 * @param DynamicArray dyarray 需要进行初始化的动态数组
 * @param size_t initialCapacity 初始化容量
 ***************************************************************/
void initDynamicArray(DynamicArray *dyarray, size_t initialCapacity)
{
    // 动态开辟内存
    dyarray->data = (ele_type *)malloc(initialCapacity * sizeof(ele_type));
    // 设置初始化长度和容量
    dyarray->size = 0;
    dyarray->capacity = initialCapacity;
}

释放动态数组内存

/**************************************************************
 * @brief 释放动态数组内存
 *
 * @param DynamicArray *dyarray 要释放的动态数组
 ***************************************************************/
void destroyDynamicArray(DynamicArray *dyarray)
{
    // 释放动态开辟的内存区域
    free(dyarray->data);
    // 重置成员的值
    dyarray->data = NULL;
    dyarray->size = 0;
    dyarray->capacity = 0;
}
2.动态数组的增删改查

增:调整动态数组的内存大小

/* @brief 调整动态数组内存大小
 * @param  DynamicArray *dyarray  要调整的动态数组
 * @param size_t newCapacity       新的数组容量
 */
void resizeDynamicArray(DynamicArray *dyarray, size_t newCapacity)
{
    // 动态调整数组容量大小
    dyarray->data = (ele_type *)realloc(dyarray->data, newCapacity * sizeof(ele_type));
    // 调整容量值
    dyarray->capacity = newCapacity;
}

删:删除指定/末尾位置的元素

/**************************************************************
 * @brief 删除指定位置的元素
 *
 * @param  DynamicArray *dyarray  动态数组
 * @param  size_t index 位置
 * @return  ele_type  被删除的元素值
 ***************************************************************/
ele_type deleteAt(DynamicArray *dyarray, size_t index)
{
    // 如果位置不在合理范围内
    if (index < 0 || index >= dyarray->size)
    {
        return -1;
    }

    // 保存被删元素
    ele_type deletedElement = dyarray->data[index];

    // 从删除位置开始,依次将后面元素的值赋值到最前面的元素
    for (size_t i = index; i < dyarray->size - 1; i++)
    {
        dyarray->data[i] == dyarray->data[i + 1];
    }

    // 长度递减
    dyarray->size--;
    // 返回
    return deletedElement;
}

/**************************************************************
 * @brief 删除末尾元素
 *
 * @param  DynamicArray *dyarray  动态数组
 * @return  ele_type  被删除的元素值
 ***************************************************************/
ele_type deleteEnd(DynamicArray *dyarray)
{
    return deleteAt(dyarray, dyarray->size - 1);
}

改:在指定/末尾位置插入新元素

/**************************************************************
 * @brief 指定位置插入新元素
 * @param  DynamicArray *dyarray  动态数组
 * @param   size_t index 位置
 * @param   ele_type element 新元素值
 ***************************************************************/
void insertAt(DynamicArray *dyarray, size_t index, ele_type element)
{
    // 如果位置不在合理范围内
    if (index < 0 || index > dyarray->size)
    {
        return;
    }

    // 如果容量已满 需要扩容
    if (dyarray->size == dyarray->capacity)
    {
        resizeDynamicArray(dyarray, dyarray->capacity * 2);
    }

    // 循环 从插入位置起,元素依次赋值给后面的元素
    for (size_t i = dyarray->size; i > index; i--)
    {
        // 将前面元素的值赋值给后面的元素
        dyarray->data[i] = dyarray->data[i - 1];
    }

    // 将新元素赋值到指定位置
    dyarray->data[index] = element;

    // 长度递增
    dyarray->size++;
}

/**************************************************************
 * @brief 在末尾插入新元素
 *
 * @param  DynamicArray *dyarray  动态数组
 * @param   ele_type element 新元素值
 ***************************************************************/
void insertEnd(DynamicArray *dyarray, ele_type element)
{
    insertAt(dyarray, dyarray->size, element);
}

查:遍历动态数组

/**************************************************************
 * @brief 遍历数组所有的元素
 * @param DynamicArray *dyarray 动态数组
 ***************************************************************/
void printDynamicArray(DynamicArray *dyarray)
{
    for (size_t i = 0; i < dyarray->size; i++)
    {
        printf("%d ", dyarray->data[i]);
    }
    printf("\n");
}
3.main()
int main()
{
    // 定义动态数组(结构体变量)
    DynamicArray da;

    // 动态数组的初始化
    initDynamicArray(&da, 4);

    // 添加新元素,依次在最后面添加
    insertEnd(&da, 100);
    insertEnd(&da, 200);
    insertEnd(&da, 300);
    insertEnd(&da, 400);
    insertEnd(&da, 500);

    // 在指定位置添加新元素
    insertAt(&da, 2, 250);

    // 删除最后一个元素
    deleteEnd(&da);

    // 删除指定元素
    printf("被删除的元素是:%d \n", deleteAt(&da, 2));
    printf("被删除的元素是:%d \n", deleteAt(&da, 3));

    // 遍历数组
    printDynamicArray(&da);

    // 获取数组长度
    printf("动态数组长度:%zu \n", getLenth(&da));

    // 释放动态数组的内存
    destroyDynamicArray(&da);

    return 0;
}
 4.总结

难点在于插入和删除操作,如何通过代码来实现。插入操作逻辑是在任意位置插入新元素时,从插入位置起,需要将前面的元素依次赋值给后面的元素。删除相反。

1.2 链表

1. 链表分类
   单向链表、双向链表、循环单向链表、循环双向链表
   
2. 链表特点
   存储特点: 不一定是连续的空间,节点中使用指针指向下一个节点
   优点: 插入、删除节点高效
   缺点: 查找慢
   
3. 如何实现单向链表
   节点的结构体: 数据、下一个节点地址
   如何查找节点
   如何增加节点
   如何删除节点

代码实现
1.定义结构体表示节点/链表,并初始化和释放链表
// 定义结构体 表示节点
typedef struct Node //(节点名,结构体名称Node)
{
    ele_type data;     // 节点中的数据
    struct Node *next; // 指向下一个节点的地址

} Node; // (别名Node,指代struct Node)

// 定义结构体 表示整个链表
typedef struct
{
    Node *head;  // 第一个节点的地址
    size_t size; // 链表中节点的数量(链表长度)
} LinkedList;

初始化/释放链表

/**************************************************************
 * @brief 初始化链表
 *
 * @param LinkedList *list 需要初始化的链表
 ***************************************************************/
void initLinkedList(LinkedList *list)
{
    // 给成员赋初始值
    list->head = NULL;
    list->size = 0;
}






/**************************************************************
 * @brief 释放链表内存
 *
 * @param LinkedList *list 链表
 ***************************************************************/
void destoryLinkedList(LinkedList *list)
{
    // 获取第一个节点
    // nodeELe 用于保存遍历过程中的每一个节点
    Node *nodeELe = list->head;
    // 循环 依次释放每一个节点的内存空间
    while (nodeELe != NULL)
    {
        // 先把当前节点存下来
        Node *tmp = nodeELe;
        // 获取到下个节点
        nodeELe = nodeELe->next;
        free(tmp);
    }
    // 重置链表长度
    list->head = NULL;
    list->size = 0;
}

链表释放需要一个一个释放

2.链表的增加/删除
/**************************************************************
 * @brief 在指定位置插入节点
 *
 * @param LinkedList *list 链表
 * @param size_t index 位置 (下标、索引,从0开始)
 * @param ele_type element 新节点的数据值
 ***************************************************************/
void insertAt(LinkedList *list, size_t index, ele_type element)
{
    // 排除不合理的值
    if (index < 0 || index > list->size)
    {
        return;
    }

    // 为新节点分配内存空间,并使用变量保存新节点
    Node *newNode = (Node *)malloc(sizeof(Node));
    newNode->data = element;
    newNode->next = NULL;

    // 如果在最前面插入节点
    if (index == 0)
    {
        // 新节点的next 先指向原来的第一个节点
        newNode->next = list->head;
        // 再把 链表中的head 指向新节点
        list->head = newNode;
    }
    else // 新节点在中间位置插入
    {
        // 获取到前一个节点
        Node *prevNode = getPrevNode(list, index);
        // 让新节点指向下个节点
        newNode->next = prevNode->next;
        // 让上个节点指向新节点
        prevNode->next = newNode;
    }

    // 链表长度递增
    list->size++;
}

/**************************************************************
 * @brief 在末尾位置插入节点
 *
 * @param LinkedList *list 链表
 * @param ele_type element 新节点的数据值
 ***************************************************************/
void insertEnd(LinkedList *list, ele_type element)
{
    insertAt(list, list->size, element);
}

/**************************************************************
 * @brief 删除指点位置的节点
 *
 * @param LinkedList *list 链表
 * @param size_t index 位置 (下标、索引,从0开始)
 * @return int 被删除节点的数据值
 ***************************************************************/
ele_type deletAt(LinkedList *list, size_t index)
{
    // 如果不在合理范围内
    if (index < 0 || index >= list->size)
    {
        return -1;
    }

    // 定义变量 用于存储即将被删除的节点
    Node *deletedNode = NULL;

    // 如果要删除的节点是 第一个
    if (index == 0)
    {
        // 定义变量 存储即将被删除的元素
        deletedNode = list->head;

        // 让 list->head 指向该节点的下一个节点
        // list->head = list->head->next;
        list->head = deletedNode->next;
    }
    else // 要被删除的节点不是第一个
    {
        // 获取要被删除元素的前一个结点
        Node *prevNode = getPrevNode(list, index);
        // 定义变量 存储即将被删除的元素
        deletedNode = prevNode->next;

        // 让前一个节点指向被删除节点的下一个
        prevNode->next = deletedNode->next;
    }
    // 定义变量 存储被删除节点的数据值
    ele_type deletdElement = deletedNode->data;
    // 释放被删除节点内存的空间
    free(deletedNode);

    // 链表长度递减
    list->size--;
    // 返回被删除节点的元素值
    return deletdElement;
}

/**************************************************************
 * @brief 删除末尾位置的节点
 *
 * @param LinkedList *list 链表
 * @return int 被删除节点的数据值
 ***************************************************************/
ele_type deletEnd(LinkedList *list)
{
    return deletAt(list, list->size - 1);
}
3.链表的修改/遍历
/**************************************************************
 * @brief 修改指定位置元素的值
 *
 * @param LinkedList *list 链表
 * @param size_t index 位置 (下标、索引,从0开始)
 * @return int 节点值
 ***************************************************************/
void modifyAt(LinkedList *list, size_t index, ele_type element)
{
    // 排除不合理的值
    if (index < 0 || index >= list->size)
    {
        return -1;
    }

    // 定义变量存储当前节点到index对应的节点
    Node *currentNode;

    // 如果获取的是第一个节点
    if (index == 0)
    {
        currentNode = list->head;
    }
    else // 获取的不是第一个节点
    {
        // 先获取指定位置的前一个节点
        Node *prevNode = getPrevNode(list, index);
        currentNode = prevNode->next;
    }

    // 修改当前节点的值
    currentNode->data = element;
}

/**************************************************************
 * @brief 循环打印链表中的数据
 * @param LinkedList *list 链表的地址
 ***************************************************************/
void printLinkedList(LinkedList *list)
{
    // 获取第一个节点
    Node *nodeELe = list->head;

    // 循环 继续打印后面节点的内容
    while (nodeELe != NULL)
    {
        // 打印当前节点的数据值
        printf("%d ", nodeELe->data);
        // 让 nodeELe 指向下一个节点
        nodeELe = nodeELe->next;
    }
}

获得指定位置的前一个节点

/**************************************************************
 * @brief 获取指定位置的前一个节点
 *
 * @param LinkedList *list 链表
 * @param size_t index 位置 (下标、索引,从0开始)
 * @return 指定位置的前一个结点
 ***************************************************************/
Node *getPrevNode(LinkedList *list, size_t index)
{
    // 先定义变量保存上一个节点 从第一个节点开始
    Node *prevNode = list->head;
    // 没开始循环前 prveNode 已经指向0节点(第一个节点)
    // 为了循环变量与prevNode指向的节点的下标对应,循环从1开始
    for (size_t i = 1; i < index; i++)
    {
        prevNode = prevNode->next;
    }
    // 返回前一个节点
    return prevNode;
}

1.3 栈

1. 特点
   先进后出、后进先出

2. 名词
   栈顶(增加、删除的一端)
   栈底
   
   进栈(压栈)
   出栈(弹栈)

代码实现
1.定义栈的结构体,进行初始化/释放操作
// 定义栈结构体
typedef struct
{
    element_t *data; // 内存区域地址
    size_t size;     // 已存储元素的个数(长度)
    size_t capacity; // 栈的最大容量
} Stack;

/**************************************************************
 * @brief 初始化栈
 *
 * @param Stack *stack 要初始化的栈
 * @param size_t initalCapacity  起始容量
 ***************************************************************/
void initStack(Stack *stack, size_t initalCapacity)
{
    // 分配内存
    stack->data = (element_t *)malloc(initalCapacity * sizeof(element_t));
    // 初始化容量和长度
    stack->size = 0;
    stack->capacity = initalCapacity;
}



/**************************************************************
 * @brief 释放栈内存
 *
 * @param Stack *stack 要初始化的栈
 ***************************************************************/
void destoryStack(Stack *stack)
{
    free(stack->data);

    // 重置成员值
    stack->data = NULL;
    stack->size = 0;
    stack->capacity = 0;
}
2.进栈/出栈操作
/**************************************************************
 * @brief 进栈(添加元素)
 * 
 * @param Stack *stack 栈
 * @param element_t element  要添加的元素
 * 
 ***************************************************************/
void pushStack(Stack *stack, element_t element)
{
    // 如果容量不够 扩容
    if (stack->size == stack->capacity)
    {
        resizeStack(stack, stack->capacity * 2);
    }


    // 在数组尾部添加元素
    stack->data[stack->size] = element;

    // 修改长度 递增
    stack->size ++;
    
}





/**************************************************************
 * @brief 出栈(删除元素)
 * 
 * @param Stack *stack 栈
 * @param element_t  被删除的元素
 ***************************************************************/
element_t popStack(Stack *stack)
{
    // 如果空栈
    if (stack->size == 0)
    {
        return -1;
    }

    // 长度递减(最后一个元素不纳入管理,出栈)
    stack->size --;

    // 返回被删除的元素
    return stack->data[stack->size];

    
}
3.遍历
/**************************************************************
 * @brief 遍历
 * 
 * @param Stack *stack 要打印的栈
 ***************************************************************/
void printStack(Stack *stack)
{
    for (size_t i = 0; i < stack->size; i++)
    {
        printf("%d ",stack->data[i]);
    }
    printf("\n");
    
}
4.其他
/**************************************************************
 * @brief 调整栈内存容量
 *
 * @param Stack *stack 要调整内存的栈
 * @param size_t newCapacity 新容量
 ***************************************************************/
void resizeStack(Stack *stack, size_t newCapacity)
{
    // 调整内存容量
    stack->data = (element_t *)realloc(stack->data, newCapacity * sizeof(element_t));
    // 设置成员
    stack->capacity = newCapacity;

}


/**************************************************************
 * @brief 获取栈长度(元素个数)
 * 
 * @param Stack *stack 要获取长度的栈
 * @param size_t 栈长度(元素个数)
 ***************************************************************/
size_t getSize(Stack *stack)
{
    return stack->size;
}

1.4 队列(Queue)

1 特点
  先进先出、后进后出
  
2. 名词
   对头(在队头删除)
   队尾(在队尾插入)
   入队
   出队

代码实现
1.定义结构体表示队列,进行初始化赋值和释放操作
// 定义结构体 表示队列
typedef struct
{
    element_t *data; // 指向存储数据的内存
    size_t size;     // 队列长度(已存储元素个数)
    size_t capacity; // 最大容量
    size_t front;    // 队头(删除元素的位置)
    size_t rear;     // 队尾(添加元素的位置)
} Queue;

/**************************************************************
 * @brief 初始化队列
 *
 * @param Queue *queue // 要初始化的队列
 * @param size_t capacity  // 最大容量
 ***************************************************************/
void initQueue(Queue *queue, size_t capacity)
{
    // 分配内存空间
    queue->data = (element_t *)malloc(capacity * sizeof(element_t));
    // 成员赋值
    queue->size = 0;
    queue->capacity = capacity;
    queue->front = 0;
    queue->rear = 0;
}




/**************************************************************
 * @brief 释放队列
 *
 * @param Queue *queue   要释放的队列
 ***************************************************************/
void destoryQueue(Queue *queue)
{
    // 释放
    free(queue->data);

    // 重置成员
    queue->data = NULL;
    queue->size = 0;
    queue->capacity = 0;
    queue->front = 0;
    queue->rear = 0;
}
2.入队/出队
/**************************************************************
 * @brief 入队(添加元素)
 * 
 * @param Queue *queue  队列
 * @param element_t element 需要添加的元素
 ***************************************************************/
void enqueue(Queue *queue, element_t element)
{
    // 如果队列已满, 不再添加元素
    if (queue->size == queue->capacity)
    {
        printf("队列已满! 入队失败!\n");
        return;
    }

    // 在队尾添加元素
    queue->data[queue->rear] = element;
    // 队列递增
    queue->size ++;
    // 队尾递增 队尾永远保存即将添加的位置
    queue->rear = (queue->rear + 1) % queue->capacity;
    // queue->rear ++;
    // if (queue->rear > queue->capacity - 1)
    // {
    //     queue->rear = 0;
    // }
    
    
}



/**************************************************************
 * @brief 出队(删除元素)
 * @param Queue *queue  队列
 * @return element_t  返回被删除的元素
 ***************************************************************/
element_t dequeue(Queue *queue)
{
    // 如果是空队列
    if (queue->size == 0)
    {
        return -1;
    }

    // 返回出队的元素
    element_t dequeuedElement = queue->data[queue->front];
    // 让队头递增 指向下一个
    queue->front = (queue->front + 1) % queue->capacity;
    // 长度递减
    queue->size --;

    // 返回
    return dequeuedElement; 
    
}
3.遍历及其他
/**************************************************************
 * @brief 获取队列长度(元素个数)
 * @param Queue *queue  队列
 * @return size_t  队列长度
 ***************************************************************/
size_t getSize(Queue *queue)
{
    return queue->size;
}





/**************************************************************
 * @brief 打印队列元素
 * @param Queue *queue  队列
 ***************************************************************/
void printQueue(Queue *queue)
{
    // 循环 循环size 次
    for (size_t i = 0; i < queue->size; i++)
    {
        // 获取到元素的下标
        size_t index = (queue->front + i) % queue->capacity;
        printf("%d ",queue->data[index]);
    }
    
}
4.总结

1. 循环队列的初始化:需要初始化队列的头尾指针,并且需要额外一个变量来记录队列中元素的个数。 2. 循环队列的入队操作:需要考虑队列满的情况,需要移动头尾指针来实现循环。 3. 循环队列的出队操作:同样需要考虑队列为空的情况,并且需要移动头尾指针来实现循环。 4. 循环队列的空间复杂度分析:循环队列的空间复杂度为O(n),其中n为队列的最大容量。 5. 循环队列的应用:循环队列常用于需要频繁进行插入和删除操作的场景,如缓冲区、任务调度等。

2、算法

2.1 查找算法

1.顺序查找
 代码实现
#include <stdio.h>

// 顺序查找
// 参数     数组 、长度、元素
// 返回:  位置
int sequenceSearch(int arr[], int len, int element)
{
    // 计算数组长度
    //int len = sizeof(arr) / sizeof(int);
    // 循环
    for (int i = 0; i < len; i++)
    {
        if (arr[i] == element)
        {
            return i;    // 找到元素,返回索引
        }
        
    }
    // 循环结束 还未找到数组
    return -1;
}

int main()
{
    int nums[6] = {10,11,7,89,56,563};

    printf("89在数组中的位置:%d \n", sequenceSearch(nums, 6, 89));

    return 0;
}
2.二分查找
代码实现
#include <stdio.h>

// 二分查找法
int binarySearch(int arr[], int len, int element)
{
    // 定义变量 分别保存第一个元素和最后一个元素的索引
    int left = 0, right = len - 1;

    // 循环 条件:要找的范围有元素的
    while (left <= right)
    {
        // 获取中间元素
        int mid = (left + right) / 2;

        // 判断要查找的元素与中间元素是否相等
        if (element == arr[mid])
        {
            return mid;
        }

        // 如果要查找的元素小于中间元素
        if (element < arr[mid])
        {
            right = mid - 1;
        }
        else
        {
            // 如果要查找的元素大于中间元素
            left = mid + 1;
        }
    }

    //  循环结束 没有找到 返回-1
    return -1;

}

int main()
{
    int nums[] = {10,20,30,40,50,60,70,80,90};

    
    printf("20的位置:%d \n", binarySearch(nums, 9, 90));

    return 0;
}

适用于排好序的数据

2.2 排序算法

1.冒泡排序
代码实现
#include <stdio.h>

// 冒泡排序的函数
void bubbleSort(int nums[], int len)
{
    // 外层循环 控制轮数
    for (int k = 1; k < len; k++)
    {
        // 内层循环 具体比较
        for (int i = 0; i < len - k; i++)
        {
            // 比较相邻两个数的大小 如果前面的元素值大,交换两个元素的值
            if (nums[i] > nums[i + 1])
            {
                int temp = nums[i];
                nums[i] = nums[i + 1];
                nums[i + 1] = temp;
            }
        }
        printf("经过第%d轮比较的数组:", k);
        printArr(nums, len);
    }
}

// 打印数组元素
void printArr(int arr[], int len)
{
    for (int i = 0; i < len; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n\n");
}

int main()
{
    // 定义数组
    int nums[9] = {3000, 500, 869, 666, 1523, 6394, 52, 13, 69};
    int len = 9;
    printArr(nums, len);

    bubbleSort(nums, len);

    // // 第一轮比较 将最大元素放在最后
    // for (int i = 0; i < len - 1; i++)
    // {
    //     // 比较相邻两个数的大小 如果前面的元素值大,交换两个元素的值
    //     if (nums[i] > nums[i + 1])
    //     {
    //         int temp = nums[i];
    //         nums[i] = nums[i + 1];
    //         nums[i + 1] = temp;
    //     }
    // }
    // printf("经过第一轮比较:");
    // printArr(nums, len);

    // // 第二轮比较 将最大元素放在最后
    // for (int i = 0; i < len - 2; i++)
    // {
    //     // 比较相邻两个数的大小 如果前面的元素值大,交换两个元素的值
    //     if (nums[i] > nums[i + 1])
    //     {
    //         int temp = nums[i];
    //         nums[i] = nums[i + 1];
    //         nums[i + 1] = temp;
    //     }
    // }
    // printf("经过第二轮比较:");
    // printArr(nums, len);

    return 0;
}

使用循环嵌套的方式来实现冒泡排序,外层循环控制轮数,内层循环具体实现冒泡排序,由规律len-1,len-2,len-3等可知,内层循环的次数正好是 len 减去外层循环的次数即 len - k 。

2.快速排序
代码实现
#include <stdio.h>

/**
 *  [12, 1, 3, 5, 7, 13, 14, 15, 21];
 *  基准 12
 *  从左到右找第一个比12大的是 13,索引 5    low
 *  从右到左找第一个比12小的是 7,索引 4     high
 *
 *  基准元素与high换值
 *  [7, 1, 3, 5, 12, 13, 14, 15, 21];
 *
 */

// 定义函数 交换两个变量的值
void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

// 快速排序的函数
// 参数1 数组
// 参数2 首元素索引
// 参数3 尾元素索引
void quickSort(int arr[], int start, int end)
{
    // 如果要排序的数组中只有一个元素,结束
    if (start >= end)
    {
        return;
    }

    // 定义从前向后的指针
    int low = start;
    // 定义从后向前的指针
    int high = end + 1;

    // 从前向后查找第一个比基准元素大的元素
    // while (low <high)
    // {
    //     low ++;
    //     if (arr[start] < arr[low])
    //     {
    //         break;
    //     }
    // }

    // 一轮比较,确定基准元素应该待的位置
    while (1)
    {
        // 从前向后查找第一个比基准元素大的元素
        while (low < high && arr[start] >= arr[++low])
            ;
        // 从后向前查找第一个比基准元素小的元素
        while (start < high && arr[start] <= arr[--high])
            ;

        // 判断 如果 low < high ,没有交叉,交换两个元素的值
        if (low < high)
        {
            swap(&arr[low], &arr[high]);
        }
        else
        {
            // 结束本轮的比较
            break;
        }
    }

    // high位置元素与基准元素换值
    swap(&arr[start], &arr[high]);

    // 分裂
    // 递归调用 左半边排序
    quickSort(arr, start, high - 1);
    // 递归调用 右半边排序
    quickSort(arr, high + 1, end);
}

// 定义函数 遍历数组
void printArr(int arr[], int len)
{
    for (int i = 0; i < len; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main()
{
    // 定义数组
    int nums[] = {9, -20, 30, 29, -30, -45, 25, 87, 55};
    int len = sizeof nums / sizeof(int);
    printf("没排序前的数组:");
    printArr(nums, len);

    // 调用函数排序
    quickSort(nums, 0, len - 1);

    printf("\n排序后的数组:");
    printArr(nums, len);

    return 0;
}

1. **Partition操作**:快速排序的核心在于Partition操作,即如何将数组分割成两部分,并确保左边的元素都小于右边的元素。Partition操作通常使用一个基准元素(pivot)来实现,需要理解如何选择合适的基准元素,并进行元素交换,以实现分割。

2. **递归实现**:快速排序通常使用递归来实现,需要理解递归的调用过程和递归终止条件。递归的正确实现是保证算法正确性的关键之一。

3. **边界条件处理**:在实现快速排序时,需要考虑数组为空或只有一个元素的情况,以及如何处理相等元素的情况,避免出现死循环或错误的排序结果。

4. **性能优化**:快速排序的性能高度依赖于基准元素的选择,不同的选择方式可能导致不同的性能表现。需要理解如何选择合适的基准元素,以及如何优化算法以提高排序效率。

5. **稳定性**:快速排序是一种不稳定的排序算法,即相同元素的相对位置在排序后可能发生改变。理解快速排序的稳定性特点对于某些应用场景很重要。

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值