无头单向链表的数据元素之间逻辑上的线性关系通过在节点中专用的指针来体现,每个节点包含两部分一部分为数据,另一部分为指针。存储数据元素信息值的为数据域,存放其后继节点地址指针的为指针域
链表优点:
链表缺点:
单纯单链表增删查改的意义不大,它更多的是去做更复杂结构的子结构,链表存储数据还得看双向链表
单链表不适合在pos前插入数据,它适合在pos后插入数据
案例通过c语言来创建一个无头单向链表
目录
- typedef struct SListNode {
- SLTDataType data;
- struct SListNode* next;
- }SLTNode;
- //接口函数
- void SListPrint(SLTNode* phead);//打印链表
- void SListPushBack(SLTNode** pphead, SLTDataType x);//尾插
- void SListPushFront(SLTNode** pphead, SLTDataType x);//头插
- void SListPopBack(SLTNode** pphead);//尾删
- void SListPopFront(SLTNode** pphead);//头删
-
- //查找
- SLTNode* SListFind(SLTNode* pphead, SLTDataType x);
-
- //在pos位置前插入一个节点
- void SListInsert(SLTNode** phead, SLTNode* pos, SLTDataType x);
-
- //在pos位置后插入一个节点
- void SListInsertAfter(SLTNode* pos, SLTDataType x);
-
- //删除当前位置
- void SListErase(SLTNode** pphead, SLTNode* pos);
-
- //删除pos后一个位置
- void SListEraseAfter(SLTNode* pos);
-
- //销毁链表
- void SListDestroy(SLTNode** pphead);
为了提高创建新节点的代码的复用率
- //创建新节点
- SLTNode* BuyNode(SLTDataType x) {
- SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
- if (newnode == NULL) {
- printf("malloc fail\n");
- exit(-1);
- }
- newnode->data = x;
- newnode->next = NULL;
- return newnode;
- }
- //打印链表
- void SListPrint(SLTNode* phead)
- {
- SLTNode* cur = phead;
- while (cur != NULL) {
- printf("%d ->", cur->data);
- cur = cur -> next;
- }
- printf("NULL\n");
- }
- //尾插
- void SListPushBack(SLTNode** pphead, SLTDataType x)
- {
- SLTNode* newnode = BuyNode(x);
- newnode->next = *pphead;
- *pphead = newnode;
- }
需要考虑链表是否为空链表
- //头插
- void SListPushFront(SLTNode** pphead, SLTDataType x)
- {
- SLTNode* newnode = BuyNode(x);
- if (*pphead == NULL) {
- *pphead = newnode;
- }
- else {
- SLTNode* tail = *pphead;
- //找到尾结点
- while (tail->next != NULL) {
- tail = tail->next;
- }
- tail->next = newnode;
- }
- }
需要考虑三种情况:1、链表为空 2、链表只有一个节点 3、链表有两个及以上节点
- //尾删
- void SListPopBack(SLTNode** pphead)
- {
- //此表为空
- if (*pphead == NULL) {
- return;
- }
-
- //只有一个节点
- if (( * pphead)->next == NULL) {
- free(*pphead);
- *pphead = NULL;
- }
- else {
- //两个及两个以上节点
- //第一种方式
- SLTNode* prev = NULL;
- SLTNode* tail = *pphead;
- while (tail->next) {
- prev = tail;
- tail = tail->next;
- }
- free(tail);
- tail = NULL;
- prev->next = NULL;
-
- //第二种方式
- /* 少定义一个变量
- SLTNode* tail = *pphead;
- while (tail->next->next) {
- tail = tail->next;
- }
- free(tail->next);
- tail->next = NULL;
- */
- }
-
- }
- //头删
- void SListPopFront(SLTNode** pphead)
- {
- if (*pphead == NULL) {
- return;
- }
- SLTNode* next = (*pphead)->next;
- free(*pphead);
- *pphead = next;
- }
- //查找
- SLTNode* SListFind(SLTNode* phead, SLTDataType x)
- {
- SListNode* cur = phead;
- while (cur) {
- if (cur->data == x) {
- return cur;
- }
- else {
- cur = cur->next;
- }
- }
- return NULL;
- }
前插会有一定的效率损耗,时间复杂度为O(n)
- //在pos位置前插入一个节点
- void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
- {
- SLTNode* newnode = BuyNode(x);
- if (*pphead == pos) {
- newnode->next = *pphead;
- *pphead = newnode;
- }
- else {
- SLTNode* posPrev = *pphead;
- while (posPrev->next != pos) {
- posPrev = posPrev->next;
- }
- posPrev->next = newnode;
- newnode->next = pos;
- }
- }
后插效率比前插高,时间复杂度为O(1)
- //在pos位置后插入一个节点
- void SListInsertAfter(SLTNode* pos, SLTDataType x) {
- SLTNode* newnode = BuyNode(x);
- newnode->next = pos->next;
- pos->next = newnode;
- }
- //删除当前位置
- void SListErase(SLTNode** pphead, SLTNode* pos)
- {
- if (*pphead == pos)
- {
- *pphead = pos->next;
- free(pos);
- }
- else {
- SLTNode* prev = *pphead;
- while (prev->next != pos) {
- prev = prev->next;
- }
-
- prev->next = pos->next;
- free(pos);
- pos = NULL;
- }
- }
- //删除pos后一个位置
- void SListEraseAfter(SLTNode* pos)
- {
- assert(pos->next);
- SLTNode* next = pos->next;
- pos->next = next->next;
- free(next);
- }
- //销毁链表
- void SListDestroy(SLTNode** pphead)
- {
- assert(pphead);
- SLTNode* cur = *pphead;
- while (cur) {
- SLTNode* next = cur->next;
- free(cur);
- cur = next;
- }
- *pphead = NULL;
- }
需注意释放内存后指针需要指NULL,不然就是典型的野指针问题
在接口函数前可加入断言assert(pphead),如*phead=NULL,但是&phead永远不为空
- #pragma once
- #include<stdio.h>
- #include<stdlib.h>
- #include<assert.h>
- typedef int SLTDataType;
-
- typedef struct SListNode {
- SLTDataType data;
- struct SListNode* next;
- }SLTNode;
-
- //接口函数
- void SListPrint(SLTNode* phead);//打印链表
- void SListPushBack(SLTNode** pphead, SLTDataType x);//尾插
- void SListPushFront(SLTNode** pphead, SLTDataType x);//头插
- void SListPopBack(SLTNode** pphead);//尾删
- void SListPopFront(SLTNode** pphead);//头删
-
- //查找
- SLTNode* SListFind(SLTNode* pphead, SLTDataType x);
-
- //在pos位置前插入一个节点
- void SListInsert(SLTNode** phead, SLTNode* pos, SLTDataType x);
-
- //在pos位置后插入一个节点
- void SListInsertAfter(SLTNode* pos, SLTDataType x);
-
- //删除当前位置
- void SListErase(SLTNode** pphead, SLTNode* pos);
-
- //删除pos后一个位置
- void SListEraseAfter(SLTNode* pos);
-
- //销毁链表
- void SListDestroy(SLTNode** pphead);
-
- #include "SList.h"
-
- //创建新节点
- SLTNode* BuyNode(SLTDataType x) {
- SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
- if (newnode == NULL) {
- printf("malloc fail\n");
- exit(-1);
- }
- newnode->data = x;
- newnode->next = NULL;
- return newnode;
- }
-
- //打印链表
- void SListPrint(SLTNode* phead)
- {
- SLTNode* cur = phead;
- while (cur != NULL) {
- printf("%d ->", cur->data);
- cur = cur -> next;
- }
- printf("NULL\n");
- }
-
- //尾插
- void SListPushBack(SLTNode** pphead, SLTDataType x)
- {
- SLTNode* newnode = BuyNode(x);
- newnode->next = *pphead;
- *pphead = newnode;
- }
-
- //头插
- void SListPushFront(SLTNode** pphead, SLTDataType x)
- {
- SLTNode* newnode = BuyNode(x);
- if (*pphead == NULL) {
- *pphead = newnode;
- }
- else {
- SLTNode* tail = *pphead;
- //找到尾结点
- while (tail->next != NULL) {
- tail = tail->next;
- }
- tail->next = newnode;
- }
- }
-
- //尾删
- void SListPopBack(SLTNode** pphead)
- {
- //此表为空
- if (*pphead == NULL) {
- return;
- }
-
- //只有一个节点
- if (( * pphead)->next == NULL) {
- free(*pphead);
- *pphead = NULL;
- }
- else {
- //两个及两个以上节点
- //第一种方式
- SLTNode* prev = NULL;
- SLTNode* tail = *pphead;
- while (tail->next) {
- prev = tail;
- tail = tail->next;
- }
- free(tail);
- tail = NULL;
- prev->next = NULL;
-
- //第二种方式
- /* 少定义一个变量
- SLTNode* tail = *pphead;
- while (tail->next->next) {
- tail = tail->next;
- }
- free(tail->next);
- tail->next = NULL;
- */
- }
-
- }
-
- //头删
- void SListPopFront(SLTNode** pphead)
- {
- if (*pphead == NULL) {
- return;
- }
- SLTNode* next = (*pphead)->next;
- free(*pphead);
- *pphead = next;
- }
-
- //查找
- SLTNode* SListFind(SLTNode* phead, SLTDataType x)
- {
- SListNode* cur = phead;
- while (cur) {
- if (cur->data == x) {
- return cur;
- }
- else {
- cur = cur->next;
- }
- }
- return NULL;
- }
-
- //在pos位置前插入一个节点
- void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
- {
- SLTNode* newnode = BuyNode(x);
- if (*pphead == pos) {
- newnode->next = *pphead;
- *pphead = newnode;
- }
- else {
- SLTNode* posPrev = *pphead;
- while (posPrev->next != pos) {
- posPrev = posPrev->next;
- }
- posPrev->next = newnode;
- newnode->next = pos;
- }
- }
-
- //在pos位置后插入一个节点
- void SListInsertAfter(SLTNode* pos, SLTDataType x) {
- SLTNode* newnode = BuyNode(x);
- newnode->next = pos->next;
- pos->next = newnode;
- }
-
- //删除当前位置
- void SListErase(SLTNode** pphead, SLTNode* pos)
- {
- if (*pphead == pos)
- {
- *pphead = pos->next;
- free(pos);
- }
- else {
- SLTNode* prev = *pphead;
- while (prev->next != pos) {
- prev = prev->next;
- }
-
- prev->next = pos->next;
- free(pos);
- pos = NULL;
- }
- }
-
- //删除pos后一个位置
- void SListEraseAfter(SLTNode* pos)
- {
- assert(pos->next);
- SLTNode* next = pos->next;
- pos->next = next->next;
- free(next);
- }
-
- //销毁链表
- void SListDestroy(SLTNode** pphead)
- {
- assert(pphead);
- SLTNode* cur = *pphead;
- while (cur) {
- SLTNode* next = cur->next;
- free(cur);
- cur = next;
- }
- *pphead = NULL;
- }
- #include "SList.h"
- int main()
- {
- SListNode* plist = NULL;
-
- SListPushBack(&plist, 1);
- SListPushBack(&plist, 2);
- SListPushBack(&plist, 3);
- SListPushBack(&plist, 4);
- SListPrint(plist);
-
- SLTNode* pos = SListFind(plist,3);
- SListPrint(plist);
- SListInsert(&plist, pos, 333);
- SListPrint(plist);
- SListInsertAfter(pos, 33333333);
- SListPrint(plist);
-
- SListErase(&plist, pos);
- SListPrint(plist);
- pos = SListFind(plist, 333);
- SListEraseAfter(pos);
- SListPrint(plist);
- SListDestroy(&plist);
- SListPrint(plist);
- }