C数据结构

Posted mChenys

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C数据结构相关的知识,希望对你有一定的参考价值。

目录

一、动态数组

所谓动态数组就是可以自动扩容的数组, 例如:

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

//动态数组
struct DynamicArray

    //数组存储元素的空间首地址,存放的都是地址void*
    void **addr;
    //最大容量
    int capacity;
    //实际的元素个数
    int size;
;

//初始化
struct DynamicArray *Init_DynamicArray(int capacity)

    if (capacity < 0)
        return NULL;
    // 创建动态数组的指针并分配空间    
    struct DynamicArray *arr = malloc(sizeof(struct DynamicArray));
    // 初始化数组初始元素的空间
    arr->addr = malloc(capacity * sizeof(void *));
    // 初始容量
    arr->capacity = capacity;
    // 实际的元素个数
    arr->size = 0;
    return arr;


//在指定位置插入元素,如果该位置大于末尾元素的位置,则直接插入到末尾元素之后
void Inseart_DynamicArray(struct DynamicArray *arr, int index, void *value)

    if (NULL == arr)
        return;
    if (index <= 0)
        return;
    // 如果新增的元素位置大于当前元素末尾位置,则直接插入到末尾位置
    if (index > arr->size)
    
        index = arr->size;
    

    if (arr->size >= arr->capacity)
    
        // 如果当前元素个数大于当前容量,需要进行扩容,在之前的容量上扩大一倍
        int new_capacity = arr->capacity * 2;
        void **new_addr = malloc(new_capacity * sizeof(void *));
        //拷贝原来的数据到新地址上
        memcpy(new_addr, arr->addr, arr->capacity * sizeof(void *));
        //释放旧地址
        free(arr->addr);
        //更新新地址
        arr->addr = new_addr;
        //更新容量
        arr->capacity = new_capacity;
    

    //将原生插入到index位置

    //插入前需要将index位置之后的所有元素需要移动,从尾巴开始向后逐个元素移动一位,腾出位置来放置index的元素
    for (int i = arr->size - 1; i >= index; --i)
    
        //让后一个位置等于前一个元素即可
        arr->addr[i + 1] = arr->addr[i];
    
    //最后将value插入到index位置
    arr->addr[index] = value;

    //更新元素个数
    arr->size++;


//遍历
void Foreach_DynamicArray(struct DynamicArray *arr, void (*_callback)(void *))

    if (NULL == arr)
        return;
    if (NULL == _callback)
        return;
    for (int i = 0; i < arr->size; ++i)
    
        _callback(arr->addr[i]);
    


//按位置删除
void Remove_By_Index(struct DynamicArray *arr, int index)

    if (NULL == arr)
        return;
    if (index < 0 || index > arr->size - 1)
        return;
    for (int i = index; i < arr->size; ++i)
    
        //将后一个元素覆盖前一个元素即可
        arr->addr[i] = arr->addr[i + 1];
    
    //更新size
    arr->size--;


//按值删除
void Remove_By_Value(struct DynamicArray *arr, void *value, int (*compare)(void *, void *))

    if (NULL == arr || NULL == value || NULL == compare)
        return;
    for (int i = 0; i < arr->size; ++i)
    
        //因为无法确定数据类型,所以通过回调函数处理比较
        if (compare(arr->addr[i], value))
        
            Remove_By_Index(arr, i);
            break;
        
    


//销毁
void Destory_DynamicArray(struct DynamicArray *arr)

    if (NULL == arr)
        return;
    if (NULL != arr->addr)
    
        free(arr->addr);
        arr->addr = NULL;
    

    free(arr);
    arr = NULL;

使用方式如下:


//定义存放的元素
struct Person

    char name[64];
    int age;
;

//打印值得回调函数
void my_callback(void *data)

    if (NULL == data)
        return;
    struct Person *p = (struct Person *)data;
    printf("name=%s,age=%d\\n", p->name, p->age);


//比较2个值是否相等的回调函数
int value_compare(void *d1, void *d2)

    struct Person *p1 = (struct Person *)d1;
    struct Person *p2 = (struct Person *)d2;
    return strcmp(p1->name, p2->name) == 0 && p1->age == p2->age;


int main()


    struct DynamicArray *arr = Init_DynamicArray(5);
    struct Person p1 = "aaa", 10;
    struct Person p2 = "bbb", 20;
    struct Person p3 = "ccc", 30;
    struct Person p4 = "ddd", 40;
    struct Person p5 = "eee", 50;
    struct Person p6 = "fff", 60;

    Inseart_DynamicArray(arr, 10, &p1);
    Inseart_DynamicArray(arr, 10, &p2);
    Inseart_DynamicArray(arr, 10, &p3);
    Inseart_DynamicArray(arr, 10, &p4);
    Inseart_DynamicArray(arr, 10, &p5);

    printf("扩容前capacity:%d\\n", arr->capacity);
    Inseart_DynamicArray(arr, 10, &p6);
    printf("扩容后capacity:%d\\n", arr->capacity);
    Foreach_DynamicArray(arr, my_callback);

    printf("删除index=2的元素\\n");
    Remove_By_Index(arr, 2);
    printf("删除后的数组为:\\n");
    Foreach_DynamicArray(arr, my_callback);

    printf("按值删除 aaa, 10 \\n");
    struct Person p_del = "aaa", 10;
    Remove_By_Value(arr, &p_del, value_compare);
    printf("删除后的数组为:\\n");
    Foreach_DynamicArray(arr, my_callback);
    system("pause");
    return 0;

输出结果:

扩容前capacity:5
扩容后capacity:10
name=aaa,age=10
name=bbb,age=20
name=ccc,age=30
name=ddd,age=40
name=eee,age=50
name=fff,age=60
删除index=2的元素
删除后的数组为:
name=aaa,age=10
name=bbb,age=20
name=ddd,age=40
name=eee,age=50
name=fff,age=60
按值删除 aaa, 10 
删除后的数组为:
name=bbb,age=20
name=ddd,age=40
name=eee,age=50
name=fff,age=60

二、单向链表

有2种方案,分别是将数据保存到链表节点中和将链表节点保存到数据中,下面会分别介绍

方案一:
先创建链表的头文件LinkList.h

#pragma once

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

#ifdef __cplusplus
extern "C"

#endif
    // 定义类型
    // 用LinkList替代void*,可以将struct LList保户起来(struct LList定义在实现文件中,它代表链表本身),不让外界看到.
    typedef void *LinkList; 
    typedef void(FOREACH_CALLBACK)(void *); //遍历的回调函数
    typedef int(COMPARE_CALLBACK)(void *, void *); // 对比的回调函数
    
    //初始化
    LinkList Init_LinkList();
    //遍历
    void Foreach_LinkList(LinkList list, FOREACH_CALLBACK callback);
    //插入
    void Inseart_LinkList(LinkList list, int index, void *value);
    //根据位置删除
    void Remove_By_Index(LinkList list, int index);
    //根据值删除
    void Remove_By_Value(LinkList list, void *value, COMPARE_CALLBACK callback);
    //清空列表
    void Clear_LinkList(LinkList list);
    //销毁
    void Destory_LinkList(LinkList list);
    //元素个数
    int Size_Of_LinkList(LinkList list);

#ifdef __cplusplus

#endif

创建链表的实现文件LinkList.c

#include "LinkList.h"

// 代表链表内每个元素的节点结构体
struct LinkNode

    void *addr; //存放数据的地址
    struct LinkNode *next;
;

// 链表的结构体
struct LList

    struct LinkNode header; // 头结点
    int size;
;

// 初始化
LinkList Init_LinkList()

    struct LList *list = malloc(sizeof(struct LList));
    struct LinkNode header;
    header.addr = NULL;
    header.next = NULL;
    list->header = header;
    list->size = 0;
    return list;


// 遍历
void Foreach_LinkList(LinkList linkList, FOREACH_CALLBACK callback)

    if (NULL == linkList)
        return;
    struct LList *list = (struct LList *)linkList;

    struct LinkNode *curr_node = list->header.next;
    while (NULL != curr_node)
       // 从头节点的next节点开始遍历
        callback(curr_node->addr);
        // 更新当前node
        curr_node = curr_node->next;
    

// 按位置插入元素
void Inseart_LinkList(LinkList linkList, int index, void *value)

    if (NULL == linkList || NULL == value)
        return;
    // 先强转成内部链表结构体指针类型    
    struct LList *list = (struct LList *)linkList;
    // 得到头结点的结构体指针
    struct LinkNode *curr_node = &(list->header);

    if (index < 0 || index > list->size)
        index = list->size;

    //查找要插入的节点的前一个节点
    for (int i = 0; i < index; ++i)
    
        // 先获取到index的前一个节点
        curr_node = curr_node->next;
    
    // 创建新节点
    struct LinkNode *new_node = malloc(sizeof(struct LinkNode));
    new_node->addr = value;
    new_node->next = NULL;

    // 将当前节点的next节点给新节点的next
    new_node->next = curr_node->next;
    // 修改当前节点的next节点指向新添加的节点
    curr_node->next = new_node;

    // 更新个数
    list->size++;


// 根据位置删除
void Remove_By_Index(LinkList linkList, int index)

    if (NULL == linkList)
        return;

    struct LList *list = (struct LList *)linkList;
    if (index < 0 || index > list->size - 1)
        return;
    //先找到要删除节点的前一个节点
    struct LinkNode *curr_node = &(list->header);
    for (int i = 0; i < index; ++i)
    
        // 先获取到index的前一个节点
        curr_node = curr_node->next;
    
    //修改当前节点的next为要删除节点的next
    struct LinkNode *del_node = curr_node->next;
    curr_node->next = del_node->next;

    //释放删除节点的空间
    free(del_node);
    del_node = NULL;

    //更新个数
    list->size--;

// 根据值删除
void Remove_By_Value(LinkList linkList, void *value, COMPARE_CALLBACK callback)

    if (NULL == linkList || NULL == callback || NULL == value)
        return;

    struct LList *list = (struct LList *)linkList;

    // curr_node的上一个节点
    struct LinkNode *pre_node = &(list->header);
    // curr_node就是要被删除的节点
    struct LinkNode *curr_node = pre_node->next;
    while (curr_node != NULL)
    
        if (callback(curr_node->addr, value))
            break;
        
        pre_node = curr_node;
        // 不断的更新curr_node
        curr_node = curr_node->next;
    
    if (curr_node == NULL) //没有找到
        return;

    //删除当前节点
    pre_node->next = curr_node->next;
    free(curr_node);
    curr_node = NULL;

    //更新个数
    list->size--;

    //还要一种方式是使用for循环找到要删除元素的index,然后调用Remove_By_Index
    /*struct LinkNode* curr_node = &(list->header);
    for (int i = 0; i < list->size; ++i)
    
    curr_node = curr_node->next;
    if (callback(curr_node->addr, value))
    
    Remove_By_Index(linkList, i);
    break;
    
    */

// 清空列表
void Clear_LinkList(LinkList linkList)

    if (NULL == linkList)
        return;

    struct LList *list = (struct LList *)linkList;

    struct LinkNode *curr_node = list->header.next;
    while (NULL != curr_node)
    
        //先保存下一个节点
        struct LinkNode *next_node = curr_node->next;
        //释放当前节点
        free(curr_node);
        curr_node = next_node;
    
    list->size = 0;
    list->header.<

以上是关于C数据结构的主要内容,如果未能解决你的问题,请参考以下文章

[BZOJ2555] SubString

二叉树的定义常见的性质及其存储结构(C语言)

树的子结构

C的抽象数据类型:二叉树

平衡二叉树AVL树定义插入以及调整最小不平衡子树(C语言)

数据结构(C语言版) 树和二叉树 算法设计Demo6