线性表之链表

本文介绍了链表作为线性表的一种,其非连续、非顺序的存储特性。详细讨论了单向链表,包括头结点的概念及操作,循环链表的结构优势,以及双向链表的双指针特性和应用,如约瑟夫环问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链表

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

链表
单向链表
双向链表
循环链表

链表常见操作有:

函数功能
insert(node,index)将node插入到链表中下标为index的位置
output()输出整个链表
delete_node(index)将链表下标为index的元素删除
reverse()翻转整个链表
单向链表

在第一个结点(有效元素)之前附设一个结点,称之为头结点;指向头结点的指针,称之为头指针
对单链表的存取必须头指针开始进行,由于单链表的最后一个元素没有直接后继,则指针为NULL。
对于头结点,数据域可以不存储任何信息,也可存储如链表长度等附加信息。
小结

  • 无论链表是否为空,头指针均不为空。头指针是链表的必要条件;
  • 链表可以没有结点,但不能没有头指针,头指针是链表的必要条。
//定义结点的结构体
typedef struct Node{
	int data;
	struct Node *next;
}Node, *LinkList; 

则定义LinkList L;时,L为链表的头指针

L=(LinkList)malloc(sizeof(Node)); //创建一个结点

此处返回给L的是一个指针,并且赋给了头指针。

L->next=null; //这里说明我创建了一个头结点,即同时运用了头指针和头结点。

示例如下:

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

typedef struct Node{
    int data;
    struct Node *next;
}Node, *LinkedList;

LinkedList insert(LinkedList head, Node *node, int index) {
    if (head == NULL) {
        if (index != 0) {
            printf("failed\n");
            return head;
        }
        head = node;
        printf("success\n");
        return head;
    }
    if (index == 0) {
        node->next = head;
        head = node;
        printf("success\n");
        return head;
    }
    Node *current_node = head;
    int count = 0;
    while (current_node->next != NULL && count < index - 1) {
        current_node = current_node->next;
        count++;
    }
    if (count == index - 1) {
        node->next = current_node->next;
        current_node->next = node;
        printf("success\n");
        return head;
    }
    printf("failed\n");
    return head;
}

void output(LinkedList head) {
    if (head == NULL) {
        return;
    }
    Node *current_node = head;
    while (current_node != NULL) {
        printf("%d ", current_node->data);
        current_node = current_node->next;
    }
    printf("\n");
}

LinkedList delete_node(LinkedList head, int index) {
    if (head == NULL) {
        printf("failed\n");
        return head;
    }
    Node *current_node = head;
    int count = 0;
    if (index == 0) {
        head = current_node -> next;
        free(current_node);
        printf("success\n");
        return head;
    }
    while (current_node -> next != NULL && count < index - 1) {
        current_node = current_node -> next;
        count++;
    }
    if (count == index - 1 && current_node -> next != NULL) {
        Node *delete_node = current_node -> next;
        current_node -> next = delete_node -> next;
        free(delete_node);
            
        printf("success\n");
        return head;
    }
    
    printf("failed\n");
    return head;
}

LinkedList reverse(LinkedList head) {
    if (head == NULL) {
        return head;
    }
    Node *current_node, *next_node;
    current_node = head -> next;
    head -> next = NULL;
    while (current_node != NULL) {
        next_node = current_node -> next;
        current_node -> next = head;
        
        head = current_node;
        current_node = next_node;
    }
    return head;
}

void clear(LinkedList head) {
    Node *current_node = head;
    while (current_node != NULL) {
        Node *delete_node = current_node;
        current_node = current_node->next;
        free(delete_node);
    }
}

int main() {
    LinkedList linkedlist = NULL;

    int count, caseNum, a, b;
    int i = 0;
    scanf("%d", &count);
    while (i++ < count) {
        scanf("%d", &caseNum);
        switch (caseNum) {
            case 1 :
                scanf("%d %d", &a, &b);
                Node *node = (Node *)malloc(sizeof(Node));
                node -> data = b;
                node -> next = NULL;
                linkedlist = insert(linkedlist, node, a);
                break;
            case 2 :
                output(linkedlist);
                break;
            case 3 :
                scanf("%d", &a);
                linkedlist = delete_node(linkedlist, a);
                break;
            case 4 :
                linkedlist = reverse(linkedlist);
                break;
        }
    }
    
    return 0;
}
循环链表

循环链表与单链表不同的是,其将最后一个结点的指针指向了头结点,这样的结构使得链表更加灵活方便。
循环链表里没有空指针,故判断结束条件时,不再判断指针是否为空,而是判断指针是否等于某固定指针
avatar
约瑟夫环

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

typedef struct Node{
    int data;
    struct Node *next;
}Node, *LinkedList;

LinkedList insert(LinkedList head, Node *node, int index) {
    if (head == NULL) {
        if (index != 0) {
            return head;
        }
        head = node;
        head->next = head;
        return head;
    }
    if (index == 0) {
        node->next = head->next;
        head->next = node;
        return head;
    }
    Node *current_node = head->next;
    int count = 0;
    while (current_node != head && count < index - 1) {
        current_node = current_node->next;
        count++;
    }
    if (count == index - 1) {
        node->next = current_node->next;
        current_node->next = node;
    }
    if (node == head->next) {
        head = node;
    }
    return head;
}

// 删除结点并输出   output_josephus
void output_josephus(LinkedList head, int m) {
    Node *current_node = head;
    head = NULL;
    
    while (current_node -> next != current_node) {//判断其是否是最后一个人
        for (int i = 1; i < m; ++i) {
            current_node = current_node -> next;
        }
        printf("%d ", current_node -> next -> data);
        Node *delete_node = current_node -> next;
        current_node -> next = current_node -> next -> next;
        free(delete_node);
    }
    printf("%d\n", current_node -> data);
    free(current_node);
}

int main() {
    LinkedList linkedlist = NULL;
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) {
        Node *node = (Node *)malloc(sizeof(Node));
        node->data = i;
        node->next = NULL;
        linkedlist = insert(linkedlist, node, i - 1);
    }
    output_josephus(linkedlist, m);
    return 0;
}
双向链表

双向链表又叫双链表,相对于单链表,双链表的指针域有两个指针,分别指向前驱结点和后继结点。有了这样的结构,则可以从头结点遍历到尾结点,也可以从尾结点遍历到头结点。
avatar

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值