C语言顺序表和链表总结

193 阅读9分钟

线性表

基础概念

1. 线性表即线性存储结构,使用线性表存储数据的方式,即“把所有数据用一根线儿串起来,再存储到物理空间中”。

2. 是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表、链表、队列

顺序表

概念:顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

c7b3d1e9cfb54d999b7a99dc6ab0062e.png

顺序表一般可以分为:

1.静态顺序表:使用定长数组存储数据。(创建顺序表的时候容量已经确定,不可以更改)

1693902183736.jpg

```静态顺序表-静态顺序表使用固定大小的数组来存储元素,插入和删除操作需要移动元素,但不需要考虑内存的扩容和释放。
#include <stdio.h>
#define MAX_SIZE 100 // 最大容量

typedef struct {
    int data[MAX_SIZE]; // 存储元素的数组
    int length; // 当前长度
} StaticSequentialList;

// 初始化静态顺序表
void init(StaticSequentialList* list) {
    list->length = 0;
}

// 在指定位置插入元素
int insert(StaticSequentialList* list, int pos, int value) {
    if (list->length >= MAX_SIZE || pos < 1 || pos > list->length + 1) {
        return 0; // 插入失败,返回0
    }
    for (int i = list->length; i >= pos; i--) {
        list->data[i] = list->data[i - 1];
    }
    list->data[pos - 1] = value;
    list->length++;
    return 1; // 插入成功,返回1
}

// 删除指定位置的元素
int removeItem(StaticSequentialList* list, int pos) {
    if (pos < 1 || pos > list->length) {
        return 0; // 删除失败,返回0
    }
    for (int i = pos - 1; i < list->length - 1; i++) {
        list->data[i] = list->data[i + 1];
    }
    list->length--;
    return 1; // 删除成功,返回1
}

// 获取指定位置的元素值
int get(StaticSequentialList* list, int pos) {
    if (pos < 1 || pos > list->length) {
        return -1; // 位置无效,返回-1表示失败
    }
    return list->data[pos - 1];
}

// 打印静态顺序表中的元素
void print(StaticSequentialList* list) {
    if (list->length == 0) {
        printf("List is empty.\n");
        return;
    }
    for (int i = 0; i < list->length; i++) {
        printf("%d ", list->data[i]);
    }
    printf("\n");
}

int main() {
    StaticSequentialList list;
    init(&list);
    insert(&list, 1, 10);
    insert(&list, 2, 20);
    insert(&list, 3, 30);
    print(&list); // 输出:10 20 30
    removeItem(&list, 2);
    print(&list); // 输出:10 30
    printf("Element at position 1 is: %d\n", get(&list, 1)); // 输出:10
    return 0;
}

```js

2.动态顺序表:使用动态开辟的数组存储。

1693902260821.jpg

```动态顺序表--动态顺序表使用动态分配的数组来存储元素,可以自动扩容,并需要在程序结束后手动释放内存
#include <stdio.h>
#include <stdlib.h>
``
#define CHUNK_SIZE 10 // 每次扩容的大小

typedef struct {
    int* data; // 存储元素的数组指针
    int length; // 当前长度
    int capacity; // 当前容量
} DynamicSequentialList;

// 初始化动态顺序表
void init(DynamicSequentialList* list) {
    list->data = (int*)malloc(sizeof(int) * CHUNK_SIZE); // 分配初始容量
    list->length = 0;
    list->capacity = CHUNK_SIZE;
}

// 在指定位置插入元素
int insert(DynamicSequentialList* list, int pos, int value) {
    if (pos < 1 || pos > list->length + 1) {
        return 0; // 插入失败,返回0
    }

    if (list->length >= list->capacity) {
        // 扩容
        int* newData = (int*)realloc(list->data, sizeof(int) * (list->capacity + CHUNK_SIZE));
        if (newData == NULL) {
            return 0; // 插入失败,返回0
        }
        list->data = newData;
        list->capacity += CHUNK_SIZE;
    }

    for (int i = list->length; i >= pos; i--) {
        list->data[i] = list->data[i - 1];
    }
    list->data[pos - 1] = value;
    list->length++;

    return 1; // 插入成功,返回1
}

// 删除指定位置的元素
int removeItem(DynamicSequentialList* list, int pos) {
    if (pos < 1 || pos > list->length) {
        return 0; // 删除失败,返回0
    }

    for (int i = pos - 1; i < list->length - 1; i++) {
        list->data[i] = list->data[i + 1];
    }
    list->length--;

    return 1; // 删除成功,返回1
}

// 获取指定位置的元素值
int get(DynamicSequentialList* list, int pos) {
    if (pos < 1 || pos > list->length) {
        return -1; // 位置无效,返回-1表示失败
    }
    return list->data[pos - 1];
}

// 打印动态顺序表中的元素
void print(DynamicSequentialList* list) {
    if (list->length == 0) {
        printf("List is empty.\n");
        return;
    }

    for (int i = 0; i < list->length; i++) {
        printf("%d ", list->data[i]);
    }
    printf("\n");
}

int main() {
    DynamicSequentialList list;
    init(&list);
    insert(&list, 1, 10);
    insert(&list, 2, 20);
    insert(&list, 3, 30);
    print(&list); // 输出:10 20 30
    removeItem(&list, 2);
    print(&list); // 输出:10 30
    printf("Element at position 1 is: %d\n", get(&list, 1)); // 输出:10
    free(list.data); // 释放内存
    return 0;
}

```js

链表

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

链表的分类

c47af427d1798157b3bc67fbba1aa45.png

1693904289301.jpg

1693904338291.jpg

1693904410286.jpg df79e856c19ac2be43b77a68ee2a61e.png

```单链表
#include <stdio.h>
#include <stdlib.h>

//Node** head 表示一个指向指针的指针,通常用于需要修改指针本身的情况。例如,在链表中插入新节点时,可能需要修改头指针的指向。
//&head 表示头指针的地址,可以用于传递指针的指针作为参数,以便在函数内部对头指针进行修改
//head 表示头指针本身的值,即指向链表的第一个节点。可以通过*head来访问或修改头指针所指向的节点的值。

// 定义链表节点结构
typedef struct Node{
	int data;                // 存储节点的数据
	struct Node *next;       // 指向下一个节点的指针
};

Node* createNode(int value){
	Node *newNode = (Node*)malloc(sizeof(Node));   // 分配新节点的内存空间
	if(NULL==newNode){
		printf("分配内存失败\n");
		exit(1);
	}
	newNode->data = value;                        // 将值赋给新节点的 data 成员
	newNode->next = NULL;                         // 将新节点的 next 指针初始化为 NULL
    return newNode;
}

//头部插入节点
void insertAtHead(Node **head,int value){
	Node *newNode = createNode(value);            // 创建新节点
//	头节点一般不存放数据
	newNode->next=*head;
	*head = newNode;                               // 更新头指针指向新节点
}

//头部插入节点
void inserAtTail(Node **head,int value){
	Node *newNode = createNode(value);
	if(*head==NULL){
		*head = newNode;
		return;
	}
	Node *currNode = *head;
	while(currNode->next!=NULL){
		currNode=currNode->next;
	}
	currNode->next = newNode;                    // 将新节点接在链表的尾部
}

// 删除指定值的节点
void deleteNode(Node **head, int value){
	 Node* currNode = *head;                       // 从头节点开始遍历链表
	    Node* prevNode = NULL;                        // 记录当前节点的前一个节点
	    while (currNode != NULL && currNode->data != value) {  // 遍历链表,查找给定值的节点
	        prevNode = currNode;
	        currNode = currNode->next;
	    }
	    if (currNode == NULL) {                       // 如果没找到,打印错误信息
	        printf("节点不存在\n");
	        return;
	    }
	    if (prevNode == NULL) {                       // 如果找到的节点是头节点,更新头指针
	        *head = currNode->next;
	    } else {
	        prevNode->next = currNode->next;          // 否则,直接将前一个节点的 next 指向当前节点的下一个节点
	    }
	    free(currNode);                               // 释放当前节点的内存空间
	}
	
	// 遍历链表并打印节点值
	void printList(Node* head) {
	    Node* currNode = head;                        // 从头节点开始遍历链表
	    while (currNode != NULL) {                    // 遍历至链表末尾
	        printf("%d ", currNode->data);             // 打印节点数据
	        currNode = currNode->next;                 // 移动到下一个节点
	    }
	    printf("\n");                                  // 打印完毕,换行
	}
	
	// 释放链表内存
	void freeList(Node** head) {
	    Node* currNode = *head;                       // 从头节点开始遍历链表
	    Node* nextNode;
	    while (currNode != NULL) {                    // 遍历至链表末尾
	        nextNode = currNode->next;                 // 记录下一个节点的指针
	        free(currNode);                            // 释放当前节点的内存空间
	        currNode = nextNode;                       // 移动到下一个节点
	    }
	    *head = NULL;                                  // 将头指针置为 NULL
	}
	
	int main(){
		Node *head = NULL;       // 定义一个空链表
        //头插节点
		insertAtHead(&head,3);
		insertAtHead(&head,2);
		insertAtHead(&head,1);
		//尾插节点
		inserAtTail(&head,4);
		inserAtTail(&head,5);
		inserAtTail(&head,6);
		//删除指定节点
		deleteNode(&head, 3);
		  // 打印链表
		    printf("链表内容:");
		    printList(head);
		    
		    freeList(&head);
		    printf("链表内容:");
		    printList(head);
	}
     
```js
双向链表
``` 双向链表
#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    int data; // 数据域
    struct Node* prev; // 指向前一个节点的指针
    struct Node* next; // 指向后一个节点的指针
} Node;

// 创建一个新节点
Node* createNode(int value) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) {
        printf("Memory allocation failed.\n");
        exit(1);
    }
    newNode->data = value;
    newNode->prev = NULL;
    newNode->next = NULL;
    return newNode;
}

// 在链表尾部插入节点
void insertEnd(Node** head, int value) {
    Node* newNode = createNode(value);

    if (*head == NULL) {
        *head = newNode;
    } else {
        Node* currentNode = *head;
        while (currentNode->next != NULL) {
            currentNode = currentNode->next;
        }
        currentNode->next = newNode;
        newNode->prev = currentNode;
    }
}

// 在链表指定位置插入节点
void insertAtPosition(Node** head, int position, int value) {
    if (position < 1) {
        printf("Invalid position.\n");
        return;
    }

    Node* newNode = createNode(value);

    if (position == 1) {
        newNode->next = *head;
        if (*head != NULL) {
            (*head)->prev = newNode;
        }
        *head = newNode;
    } else {
        Node* currentNode = *head;
        int currentPosition = 1;

        while (currentNode != NULL && currentPosition < position - 1) {
            currentNode = currentNode->next;
            currentPosition++;
        }

        if (currentNode == NULL) {
            printf("Invalid position.\n");
            return;
        }

        newNode->next = currentNode->next;
        newNode->prev = currentNode;
        currentNode->next = newNode;

        if (newNode->next != NULL) {
            newNode->next->prev = newNode;
        }
    }
}

// 删除链表中指定值的节点
void deleteValue(Node** head, int value) {
    if (*head == NULL) {
        printf("List is empty.\n");
        return;
    }

    Node* currentNode = *head;

    // 遍历链表,找到要删除的节点
    while (currentNode != NULL && currentNode->data != value) {
        currentNode = currentNode->next;
    }

    if (currentNode == NULL) {
        printf("Node not found.\n");
        return;
    }

    if (currentNode->prev == NULL) {
        // 要删除的节点是头节点
        *head = currentNode->next;
    } else {
        currentNode->prev->next = currentNode->next;
    }

    if (currentNode->next != NULL) {
        currentNode->next->prev = currentNode->prev;
    }

    free(currentNode);
}

// 打印链表中的节点值
void printList(Node* head) {
    if (head == NULL) {
        printf("List is empty.\n");
        return;
    }

    Node* currentNode = head;
    while (currentNode != NULL) {
        printf("%d ", currentNode->data);
        currentNode = currentNode->next;
    }
    printf("\n");
}

int main() {
    Node* head = NULL;

    insertEnd(&head, 10);
    insertEnd(&head, 20);
    insertEnd(&head, 30);
    printList(head); // 输出:10 20 30

    insertAtPosition(&head, 2, 15);
    printList(head); // 输出:10 15 20 30

    deleteValue(&head, 20);
    printList(head); // 输出:10 15 30

    return 0;
}

```js
循环链表
```循环链表
#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    int data; // 数据域
    struct Node* next; // 指向下一个节点的指针
} Node;

// 创建一个新节点
Node* createNode(int value) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) { // 内存分配失败
        printf("Memory allocation failed.\n");
        exit(1);
    }
    newNode->data = value;
    newNode->next = NULL;
    return newNode;
}

// 在链表尾部插入节点
void insertEnd(Node** head, int value) {
    Node* newNode = createNode(value);

    if (*head == NULL) { // 链表为空
        *head = newNode;
        newNode->next = *head; // 将尾节点指向头节点,形成循环
    } else {
        Node* currentNode = *head;
        while (currentNode->next != *head) { // 遍历找到尾节点
            currentNode = currentNode->next;
        }
        currentNode->next = newNode;
        newNode->next = *head; // 将尾节点指向头节点,形成循环
    }
}

// 在链表指定位置插入节点
void insertAtPosition(Node** head, int position, int value) {
    if (position < 1) {
        printf("Invalid position.\n"); // 位置无效
        return;
    }

    Node* newNode = createNode(value);

    if (position == 1) {
        if (*head == NULL) { // 链表为空
            *head = newNode;
            newNode->next = *head; // 将尾节点指向头节点,形成循环
        } else {
            Node* currentNode = *head;
            while (currentNode->next != *head) { // 遍历找到尾节点
                currentNode = currentNode->next;
            }
            newNode->next = *head;
            *head = newNode;
            currentNode->next = *head; // 修改尾节点的指针,使其仍然指向头节点
        }
    } else {
        Node* currentNode = *head;
        int currentPosition = 1;

        while (currentNode->next != *head && currentPosition < position - 1) { // 遍历找到插入位置的前一个节点
            currentNode = currentNode->next;
            currentPosition++;
        }

        if (currentPosition != position - 1) {
            printf("Invalid position.\n"); // 位置无效
            return;
        }

        newNode->next = currentNode->next;
        currentNode->next = newNode;
    }
}

// 删除链表中指定值的节点
void deleteValue(Node** head, int value) {
    if (*head == NULL) {
        printf("List is empty.\n"); // 链表为空
        return;
    }

    Node* currentNode = *head;
    Node* prevNode = NULL;
    
    // 遍历链表,找到要删除的节点及其前一个节点
    do {
        if (currentNode->data == value) {
            break;
        }
        prevNode = currentNode;
        currentNode = currentNode->next;
    } while (currentNode != *head);

    if (currentNode == *head && currentNode->data != value) {
        printf("Node not found.\n"); // 节点未找到
        return;
    }

    if (currentNode == *head && currentNode->next == *head) {
        // 只有一个节点
        *head = NULL;
        free(currentNode);
        return;
    }

    if (currentNode == *head) {
        // 要删除的节点是头节点
        *head = currentNode->next;
    }

    prevNode->next = currentNode->next;
    free(currentNode);
}

// 打印链表中的节点值
void printList(Node* head) {
    if (head == NULL) {
        printf("List is empty.\n"); // 链表为空
        return;
    }

    Node* currentNode = head;
    
    do {
        printf("%d ", currentNode->data);
        currentNode = currentNode->next;
    } while (currentNode != head); 
    printf("\n");
}

int main() {
    Node* head = NULL;

    insertEnd(&head, 10);
    insertEnd(&head, 20);
    insertEnd(&head, 30);
    printList(head); // 输出:10 20 30

    insertAtPosition(&head, 2, 15);
    printList(head); // 输出:10 15 20 30

    deleteValue(&head, 20);
    printList(head); // 输出:10 15 30

    return 0;
}

```js

1693905228579.jpg