循环链表结构

循环链表是一种特殊的链表结构,其中链表的最后一个节点的 `next` 指针指向链表的第一个节点,从而形成一个环。循环链表可以是单向的(循环单链表)或双向的(循环双链表)。这种结构的一个显著特点是它没有明确的起点和终点,任何一个节点都可以作为起点。

### 循环单链表

**特点:**

- 最后一个节点的 `next` 指针指向头节点(第一个节点),形成环。
- 可以从链表中的任何一个节点开始遍历。

**结构示例:**

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

// 定义节点结构
typedef struct Node {
    int data;
    struct Node* next;
} Node;

// 创建一个循环单链表
Node* createCircularList(int data[], int size) {
    if (size == 0) return NULL;

    Node* head = (Node*)malloc(sizeof(Node));
    head->data = data[0];
    Node* current = head;

    for (int i = 1; i < size; i++) {
        Node* newNode = (Node*)malloc(sizeof(Node));
        newNode->data = data[i];
        current->next = newNode;
        current = newNode;
    }

    current->next = head; // 形成循环
    return head;
}

// 打印循环链表
void printCircularList(Node* head) {
    if (head == NULL) return;

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

### 循环双链表

**特点:**

- 每个节点有两个指针:`next` 和 `prev`。
- `next` 指向后继节点,`prev` 指向前驱节点。
- 最后一个节点的 `next` 指向头节点,第一个节点的 `prev` 指向最后一个节点。

**结构示例:**

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

// 定义节点结构
typedef struct Node {
    int data;
    struct Node* next;
    struct Node* prev;
} Node;

// 创建一个循环双链表
Node* createCircularDoublyList(int data[], int size) {
    if (size == 0) return NULL;

    Node* head = (Node*)malloc(sizeof(Node));
    head->data = data[0];
    Node* current = head;

    for (int i = 1; i < size; i++) {
        Node* newNode = (Node*)malloc(sizeof(Node));
        newNode->data = data[i];
        newNode->prev = current;
        current->next = newNode;
        current = newNode;
    }

    current->next = head; // 形成循环
    head->prev = current; // 形成循环

    return head;
}

// 打印循环双链表
void printCircularDoublyList(Node* head) {
    if (head == NULL) return;

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

### 使用场景

循环链表在需要循环遍历数据结构的情况下非常有用,例如:

- 实现循环队列。
- 需要一个循环访问的缓冲区。
- 游戏中的玩家循环轮流机制。

循环链表的设计可以避免在处理链表尾部和头部时需要特殊处理,因为它们自然连接在一起。选择使用单链表还是双链表取决于具体需求,比如查找效率和内存使用效率等。

循环链表是一种特殊的链表结构,其中链表的最后一个节点指向链表的第一个节点,使链表形成一个环。循环链表可以是单向的(单循环链表)或双向的(双循环链表),这里主要讨论单循环链表的插入和删除操作。

### 单循环链表的插入

**在给定节点后插入新节点**

假设我们要在节点 `p` 后面插入一个新节点 `s`。

**步骤:**

1. **创建新节点 `s`:**
   - 为新节点分配内存并设置数据。

2. **调整指针:**
   - 将 `s` 的 `next` 指针指向 `p->next`。
   - 将 `p` 的 `next` 指针指向 `s`。

**代码示例:**

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

// 定义节点结构
typedef struct Node {
    int data;
    struct Node* next;
} Node;

// 在节点 p 后插入新节点 s
void insertAfter(Node* p, int data) {
    if (p == NULL) return;

    Node* s = (Node*)malloc(sizeof(Node));
    s->data = data;
    s->next = p->next;
    p->next = s;
}
```

**在单循环链表的末尾插入新节点**

1. **创建新节点 `s`:**
   - 为新节点分配内存并设置数据。

2. **调整指针:**
   - 将 `s` 的 `next` 指针指向头节点(如果有头节点)。
   - 找到最后一个节点(使其 `next` 指向头节点),并将其 `next` 指向 `s`。

**代码示例:**

```c
// 在单循环链表的末尾插入新节点
void insertAtEnd(Node** head, int data) {
    Node* s = (Node*)malloc(sizeof(Node));
    s->data = data;

    if (*head == NULL) {
        *head = s;
        s->next = *head;
    } else {
        Node* temp = *head;
        while (temp->next != *head) {
            temp = temp->next;
        }
        temp->next = s;
        s->next = *head;
    }
}
```

### 单循环链表的删除

**删除给定节点后的节点**

假设我们要删除节点 `p` 后的节点。

**步骤:**

1. **检查 `p` 的 `next` 是否不为头节点(或不为 `p` 本身):**
   - 如果 `p->next` 不等于 `p`,则找到要删除的节点。

2. **调整指针和释放内存:**
   - 用临时指针 `temp` 指向 `p->next`。
   - 将 `p->next` 指向 `temp->next`。
   - 释放 `temp` 的内存。

**代码示例:**

```c
// 删除节点 p 后的节点
void deleteAfter(Node* p) {
    if (p == NULL || p->next == p) return;

    Node* temp = p->next;
    p->next = temp->next;
    free(temp);
}
```

**删除头节点**

删除头节点需要找到最后一个节点,并调整其 `next` 指向新的头节点。

**步骤:**

1. **检查链表是否为空或只有一个节点:**
   - 如果为空或只有一个节点,则直接处理。

2. **调整指针和释放内存:**
   - 找到最后一个节点。
   - 将头节点的 `next` 赋给 `head`。
   - 将最后一个节点的 `next` 指向新的头节点。
   - 释放旧头节点的内存。

**代码示例:**

```c
// 删除循环链表的头节点
void deleteHead(Node** head) {
    if (*head == NULL) return;

    Node* temp = *head;
    if (temp->next == *head) { // 只有一个节点
        free(temp);
        *head = NULL;
    } else {
        Node* last = *head;
        while (last->next != *head) {
            last = last->next;
        }
        *head = temp->next;
        last->next = *head;
        free(temp);
    }
}
```

### 注意事项

- **循环结构**:在处理循环链表时,注意循环条件,防止无限循环。
- **边界条件**:特别是在插入和删除操作中处理单节点或空链表的情况。
- **内存管理**:确保在删除节点时正确释放内存,以避免内存泄漏。

循环链表在实现队列等需要循环访问的结构时非常有用。这些操作的实现可以根据具体需求进行调整和优化。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值