C/C++数据结构之深入了解线性表:顺序表、单链表、循环链表和双向链表

线性表是一种基本的数据结构,它在计算机科学中起着至关重要的作用。线性表用于存储一系列具有相同数据类型的元素,这些元素之间存在顺序关系。在C/C++中,我们可以使用各种方式来实现线性表,其中包括顺序表、单链表、循环链表和双向链表。本篇博客将深入探讨这些数据结构的概念,并通过示例代码进行详细分析。

什么是线性表?

线性表是一种抽象数据类型,它表示具有相同数据类型的元素的有序集合。线性表中的元素之间存在明确的线性顺序,通常由一个首元素和一个尾元素界定。线性表的常见操作包括插入、删除、查找和遍历。在C/C++中,线性表可以通过数组或链表来实现。

顺序表(Array)

顺序表是一种使用数组来实现的线性表,其中元素在内存中是连续存储的。这使得顺序表具有O(1)时间复杂度的随机访问特性,但在插入和删除操作时可能需要移动大量元素。顺序表的大小通常是固定的,除非重新分配内存。

顺序表的定义

在C/C++中,顺序表可以定义如下:

#include <stdio.h>

#define MAX_SIZE 100  // 顺序表的最大容量

typedef struct {
    int data[MAX_SIZE];  // 数据存储数组
    int length;          // 当前长度
} SeqList;

顺序表的示例

以下是一个简单的C程序,用于初始化、插入和遍历顺序表:

#include <stdio.h>

#define MAX_SIZE 100

typedef struct {
    int data[MAX_SIZE];
    int length;
} SeqList;

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

// 在指定位置插入元素
int insert(SeqList *list, int position, int element) {
    if (position < 0 || position > list->length || list->length >= MAX_SIZE) {
        return 0;  // 插入失败
    }

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

    list->data[position] = element;
    list->length++;
    return 1;  // 插入成功
}

// 遍历顺序表
void traverse(SeqList *list) {
    for (int i = 0; i < list->length; i++) {
        printf("%d ", list->data[i]);
    }
    printf("\n");
}

int main() {
    SeqList list;
    init(&list);
    
    insert(&list, 0, 1);
    insert(&list, 1, 2);
    insert(&list, 2, 3);

    traverse(&list);

    return 0;
}

这段代码演示了如何初始化顺序表、在指定位置插入元素以及遍历顺序表。输出将是 1 2 3

单链表(Singly Linked List)

单链表是一种通过节点之间的指针链接来实现的线性表。每个节点包含数据元素和一个指向下一个节点的指针。单链表不要求内存中的节点是连续的,因此插入和删除操作比顺序表更高效。

单链表的定义

在C/C++中,单链表可以定义如下:

#include <stdio.h>

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

单链表的示例

以下是一个简单的C程序,用于初始化、插入和遍历单链表:

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

typedef struct Node {
    int data;
    struct Node *next;
} ListNode;

// 初始化单链表
ListNode *init() {
    return NULL;
}

// 在链表末尾插入元素
ListNode *insert(ListNode *head, int element) {
    ListNode *newNode = (ListNode *)malloc(sizeof(ListNode));
    if (newNode == NULL) {
        perror("Memory allocation failed");
        exit(1);
    }

    newNode->data = element;
    newNode->next = NULL;

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

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

// 遍历单链表
void traverse(ListNode *head) {
    ListNode *current = head;
    while (current != NULL) {
        printf("%d ", current->data);
        current = current->next;
    }
    printf("\n");
}

// 释放单链表的内存
void destroy(ListNode *head) {
    while (head != NULL) {
        ListNode *temp = head;
        head = head->next;
        free(temp);
    }
}

int main() {
    ListNode *head = init();
    
    head = insert(head, 1);
    head = insert(head, 2);
    head = insert(head, 3);

    traverse(head);

    destroy(head);

    return 0;
}

这段代码演示了如何初始化单链表、在链表末尾插入元素以及遍历单链表。输出将是 1 2 3

循环链表(Circular Linked List)

循环链表是一种单链表的变体,其中最后一个节点的指针指向第一个节点,形成一个循环。循环链表可以用于解决环形问题,如约瑟夫问题(Josephus Problem)。

循环链表的定义

循环链表的定义与单链表类似,唯一不同之处在于最后一个节点的指针指向第一个节点。

#include <stdio.h>

typedef struct Node {
    int data;
    struct Node *next;
} CircularListNode;

循环链表的示例

以下是一个简单的C程序,用于初始化、插入和遍历循环链表:

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

typedef struct Node {
    int data;
    struct Node *next;
} CircularListNode;

// 初始化循环链表
CircularListNode *init() {
    return NULL;
}

// 在链表末尾插入元素
CircularListNode *insert(CircularListNode *head, int element) {
    CircularListNode *newNode = (CircularListNode *)malloc(sizeof(CircularListNode));
    if (newNode == NULL) {
        perror("Memory allocation failed");
        exit(1);
    }

    newNode->data = element;
    newNode->next = NULL;

    if (head == NULL) {
        newNode->next = newNode; // 指向自己形成循环
        return newNode;
    }

    CircularListNode *current = head;
    while (current->next != head) {
        current = current->next;
    }
    current->next = newNode;
    newNode->next = head; // 最后一个节点指向头节点形成循环
    return head;
}

// 遍历循环链表
void traverse(CircularListNode *head) {
    if (head == NULL) {
        return;
    }

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

// 释放循环链表的内存
void destroy(CircularListNode *head) {
    if (head == NULL) {
        return;
    }

    CircularListNode *current = head;
    CircularListNode *temp;
    do {
        temp = current;
        current = current->next;
        free(temp);
    } while (current != head);
}

int main() {
    CircularListNode *head = init();
    
    head = insert(head, 1);
    head = insert(head, 2);
    head = insert(head, 3);

    traverse(head);

    destroy(head);

    return 0;
}

这段代码演示了如何初始化循环链表、在链表末尾插入元素以及遍历循环链表。输出将是 1 2 3,并形成循环。

双向链表(Doubly Linked List)

双向链表是一种链表,每个节点包含两个指针,一个指向前一个节点,一个指向后一个节点。这种结构使得在双向链表中可以轻松地进行双向遍历,但相对于单链表,它需要更多的内存空间来存储额外的指针。

双向链表的定义

在C/C++中,双向链表可以定义如下:

#include <stdio.h>

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

双向链表的示例

以下是一个简单的C程序,用于初始化、插入和遍历双向链表:

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

typedef struct Node {
    int data;
    struct Node *prev;
    struct Node *next;
} DoublyListNode;

// 初始化双向链表
DoublyListNode *init() {
    return NULL;
}

// 在链表末尾插入元素
DoublyListNode *insert(DoublyListNode *head, int element) {
    DoublyListNode *newNode = (DoublyListNode *)malloc(sizeof(DoublyListNode));
    if (newNode == NULL) {
        perror("Memory allocation failed");
        exit(1);
    }

    newNode->data = element;
    newNode->prev = NULL;
    newNode->next = NULL;

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

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

// 遍历双向链表(正向)
void traverseForward(DoublyListNode *head) {
    DoublyListNode *current = head;
    while (current != NULL) {
        printf("%d ", current->data);
        current = current->next;
    }
    printf("\n");
}

// 遍历双向链表(反向)
void traverseBackward(DoublyListNode *head) {
    DoublyListNode *current = head;
    while (current->next != NULL) {
        current = current->next;
    }

    while (current != NULL) {
        printf("%d ", current->data);
        current = current->prev;
    }
    printf("\n");
}

// 释放双向链表的内存
void destroy(DoublyListNode *head) {
    DoublyListNode *current = head;
    DoublyListNode *temp;
    while (current != NULL) {
        temp = current;
        current = current->next;
        free(temp);
    }
}

int main() {
    DoublyListNode *head = init();
    
    head = insert(head, 1);
    head = insert(head, 2);
    head = insert(head, 3);

    traverseForward(head);
    traverseBackward(head);

    destroy(head);

    return 0;
}

这段代码演示了如何初始化双向链表、在链表末尾插入元素以及正向和反向遍历双向链表。输出将是 1 2 33 2 1


了解更多内容获取更多相关资源前往公众号:每日推荐系列


总结

线性表是计算机科学中常见的数据结构,用于存储一系列具有相同数据类型的元素。在C/C++中,我们可以使用不同的数据结构来实现线性表,包括顺序表、单链表、循环链表和双向链表。每种数据结构都有其独特的特点和适用场景,根据具体需求选择合适的数据结构是非常重要的。

猜你喜欢

转载自blog.csdn.net/qq_72290695/article/details/134095048