8.初始单链表

用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的,甚至是零散的分布在内存的任意位置,链表中元素的逻辑次序与物理次序不一定相同)

img

那怎么表示数据元素之间的逻辑关系呢?

在存储自己内容的同时也存储下一个元素的地址。存储数据元素的域称为数据域,存储直接后继位置的域称为指针域。指针域中存储的信息称作指针或链。这两部分信息组成ai的存储映象称为结点(Node)。n个结点(ai(1≤i≤n)的存储映象链结成一个链表,即为线性表。把链表中第1个结点的存储位置叫头指针。最后一个元素意味着没有直接后继规定最后一个结点指针为空(通常用NULL或^表示)

单链表由头指针唯一确定,因此单链表可用头指针名字来命名。

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

结点只有一个指针域的链表称为单链表或线性链表

img

结点有两个指针域的链表称为双链表

img

首尾相接的链表叫循环链表

img

为了更加方便对链表进行操作,会在单链表的第1个结点前附设一个头结点.头结点的数据域可以不存储任何信息,也可以存储如线性表的长度等附加信息,头结点的指针域存储指向线性表第1个元素的结点。

img

  • 头指针:
    指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针;
    头指针具有标识作用,所以常用头指针冠以链表的名字;
    无论链表是否为空,头指针均不为空。头指针是链表的必要元素

  • 头结点:
    头结点是为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据域一般无意义(也可存放链表的长度)
    有了头结点,对在第一元素结点前插入结点和删除第一结点,其操作与其它结点的操作就统一了
    头结点不一定是链表必须要素

  • 首元结点:是指链表中存储第一个数据元素a1的结点

讨论1:表示空表

img

讨论2:有头结点有什么好处?

①有了头结点,对在第一元素结点前插入结点和删除第一结点,其操作与其它结点的操作就统一了

②便于空表和非空表的统一处理
当链表不设头结点时,假设L为单链表的头指针,它应该指向首元结点,则当单链表为长度n为0的空表时,L指针为空(判定空表的条件可记为:LNULL)。增加头结点后,无论链表是否为空,头指针都是指向头结点的非空指针。头指针指向头结点。若为空表,则头结点的指针域为空(判定空表的条件可记为:L ->next NULL)

顺序表每个元素的存储位置都可从线性表的起始位置计算得到。而在单链表中,各个元素的存储位置都是随意的。取得第i个数据元素必须从头指针出发顺链进行寻找,也称为顺序存取的存取结构。之前说的顺序表是随机存取而链表是顺序存储

单链表是由若干个结点构成,所以先定义一下结点。每一个结点都是有两部分组成,一部分是数据元素本身(数据域data)其数据类型根据实际问题的需要确定。另一部分是指向下一个元素(结点)的指针(指针域next)存放下一个元素的地址,结点可以用C语言中的结构体实现当中包含两个成员。

img

img

img
img

首先,我们需要定义单链表的数据结构类型,包含一个指向下一个节点的指针和存储数据的域。这里我们将创建一个名为`ListNode`的结构体: ```c // 定义单链表节点 typedef struct ListNode { int data; // 存储整数值 struct ListNode* next; // 指向下一个节点的指针 } ListNode; ``` 接下来,我们可以实现单链表的基本操作: 1. 创建单链表初始化为NULL): ```c ListNode* createList() { return (ListNode*)malloc(sizeof(ListNode)); // 分配内存 } ``` 2. 首部插入元素(假设链表已存在且非空): ```c void insertAtHead(ListNode** head, int value) { ListNode* newNode = (ListNode*)malloc(sizeof(ListNode)); newNode->data = value; newNode->next = *head; *head = newNode; } ``` 3. 尾部插入元素(假设链表已存在且非空): ```c void insertAtTail(ListNode** head, int value) { ListNode* newNode = (ListNode*)malloc(sizeof(ListNode)); newNode->data = value; if (*head == NULL) { *head = newNode; return; } ListNode* temp = *head; while (temp->next != NULL) { temp = temp->next; } temp->next = newNode; } ``` 4. 输出链表: ```c void printList(ListNode* head) { ListNode* temp = head; while (temp != NULL) { printf("%d ", temp->data); temp = temp->next; } printf("\n"); } ``` 5. 判断链表是否为空: ```c int isEmpty(ListNode* head) { return head == NULL; } ``` 6. 获取链表长度(使用迭代或递归,这里用迭代): ```c int getLength(ListNode* head) { int count = 0; ListNode* temp = head; while (temp != NULL) { count++; temp = temp->next; } return count; } ``` 7. 删除第i个位置的元素(假设链表长度大于等于i): ```c void deleteElement(ListNode** head, int i) { if (isEmpty(*head)) return; if (i == 0) { // 如果要删除的是头结点 ListNode* temp = *head; *head = (*head)->next; free(temp); } else { ListNode* current = *head; for (int j = 0; j < i - 1 && current != NULL; j++) { current = current->next; } if (current == NULL || current->next == NULL) { printf("Invalid index.\n"); return; } ListNode* toDelete = current->next; current->next = current->next->next; free(toDelete); } } ``` 8. 删除单链表(释放所有节点内存): ```c void deleteList(ListNode** head) { ListNode* temp = *head; while (temp != NULL) { ListNode* nextTemp = temp->next; free(temp); temp = nextTemp; } *head = NULL; } ``` 以上就是单链表的各种基本运算及其相关功能的实现。你可以根据这些函数进行组合使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值