线性表定义#

线性表是具有相同数据类型的n(n\geqslant 0)个数据元素的有限序列

核心特点:个数有限、有先后次序、为数据元素、类型相同、抽象数据元素

包含:顺序表、链表

顺序表#

定义:用一组地址连续的存储单元依次存储线性表中的数据元素,使得逻辑上相邻的两个元素在物理位置上也相邻

优点:插入和删除操作简单、随机访问操作快、占用内存小

缺点:插入和删除操作耗时、需要连续存储空间不灵活

静态存储与动态存储#

静态分配:表大小固定

动态分配:表大小可变,另寻存储空间并拷贝原数据

(若定义新指针,realloc会自动处理数据复制)

顺序表动态存储典例#

"动态数组列表" 

typedef struct {
    int *data;      // 数组指针
    int size;       // 当前元素数量
    int capacity;   // 总容量
} DynamicArray;

DynamicArray* createArray(int initialCapacity) {
    DynamicArray *arr = (DynamicArray*)malloc(sizeof(DynamicArray));
    arr->data = (int*)malloc(initialCapacity * sizeof(int)); 
    arr->size = 0;
    arr->capacity = initialCapacity;
    return arr;
}

void add(DynamicArray *arr, int value) {
    if (arr->size == arr->capacity) {
        // 扩展容量
        arr->capacity *= 2;
        int *temp = (int*)realloc(arr->data, arr->capacity * sizeof(int));
        if (temp != NULL) {
            arr->data = temp;
        }
    }
    arr->data[arr->size++] = value; // 满时下标为size-1
}

"malloc(sizeof(type)):分配内存,内容随机,保证函数结束后内存不回收,数据可继续使用"
"calloc(n,sizeof(type)):分配并初始化为0"
"realloc(data,sizeof(type)*n):调整已分配内存块的大小并拷贝原数据,返回新数据的指针"
"free(ptr):传入malloc/calloc/realloc返回的地址,释放对应地址内存(不手动释放可能导致内存泄漏)"

链表#

线性表的链式表示:通过“链”建立元素之间的逻辑关系,不要求逻辑相邻的元素物理位置也相邻

优点:插入和删除操作不需要移动元素,只需修改指针

缺点:无法随机存取,需要遍历链表

单链表:每个节点存放一个指向后继的指针,尾节点指针指向NULL

双链表:每个节点有两个指针(直接前驱prior和直接后继next),头节点priorNULL,尾节点nextNULL

循环单链表:单链表 尾节点的next指针指向头节点

循环双链表:双链表尾节点的next指针指向头节点,头节点的prior指针指向尾节点

静态链表:用数组来描述线性表的链式存储结构,节点包含数据域data和指针域next, 指针为后继节点在数组中的相对地址(数组下标)

基本操作实现(单链表)#

节点定义#

typedef struct Node{
    int data;
    struct Node* next;
}Node;
"内含Node类型,指Node标签,标签不可缺失"

新建节点#

Node* createNode(int data){
    Node* newNode= (Node*)malloc(sizeof(Node)); // malloc保证函数结束后节点继续存在可用
    if(newNode=NULL){
        printf("内存分配失败\n");
        exit(1);
    }
    newNode->data=data;
    newNode->next=NULL;
    return newNode;
}

遍历打印链表数据#

void traverseList(Node* head){
    Node* current=head;
    while(current->next!=NULL){
        printf("%d -> ",current->data);
        current=current->next;
    }
}
"单链表操作以第一个节点head为索引"

头部插入#

Node* insertAtHead(Node* head, int data){
    Node* newNode=createNode(data);
    newNode->next=head;
    return newNode;
}

尾部插入#

Node* insetAtTail(Node* head, int data){
    Node* newNode=createNode(data);

    if (head==NULL){
        return newNode;
    }

    Node* current=head;
    while(current->next!=NULL){
        current=current->next;
    }
    current->next=newNode;
    return head;
}

删除指定值的节点#

Node* deleteNode(Node* head, int key){
    if(head==NULL){
        return NULL;
    }

    //删除头节点
    if(head->data=key){
        Node* temp=head;
        head=head->next;
        free(temp);
        return head;
    }

    Node* prev=NULL;
    Node* current=head;

    while(current!= NULL&& current->data!=key){//必须先判断 current != NULL,再访问 current->data,否则可能访问空指针
        prev=current;
        current=current->next;
    }

    if(current!=NULL){
        prev->next=current->next;
        free(current);
    }else{
        printf("未找到值为%d的节点\n", key);
    }

    return head;
}