//单链表的节点结构体创建
typedef int SLTDataType;
typedef struct SLTNode
{SLTDataType data;struct SLTNode* next;
}SLTNode;
//单链表的打印
void SLTPrint(SLTNode* phead)
{SLTNode* cur = phead;while (cur != NULL){printf("%d->",cur->data);cur = cur->next;}printf("NULL\n");
}
//单链表的新malloc一个节点
SLTNode* BuySLTNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("BuySLTNode::Malloc");return NULL;}newnode->data = x;newnode->next = NULL;return newnode;
}
//单链表的尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = BuySLTNode(x);if (*pphead == NULL){*pphead = newnode;}else{SLTNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}tail->next = newnode;}
}
//单链表的头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = BuySLTNode(x);newnode->next = *pphead;*pphead = newnode;
}
//单链表的尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead);assert(*pphead);if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SLTNode* tail = *pphead;SLTNode* prev = NULL;while (tail->next != NULL){prev = tail;tail = tail->next;}prev->next = NULL;free(tail);tail = NULL;}
}
//单链表的头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead);assert(*pphead);SLTNode* first = *pphead;*pphead = (*pphead)->next;free(first);first = NULL;
}
1. 查找其实没啥好说的,无非就是遍历一遍,找到符合你需要查找的数值,就返回对应节点的地址,找不到的话就返回NULL。
2. 由于单链表的查找是返回对应节点的指针,如果需要去修改单链表的话,就不用单独去写一个函数,因为查找与修改本身就是密切相关,查找会返回指针,直接外头修改就可以了。
3. find操作主要是以其他操作进行配合
//单链表的根据数值查找节点
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* cur = phead;while (cur != NULL){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}
1. 实际上单链表并不适合这样子在什么什么前面插入,因为单链表之间各个节点的位置记录,从来都是从前往后记录,不会从后往前,因此从后往前的话是找不着的。
2. 为了找到节点pos前面的那个节点的位置,这时候必须得从头往后开始遍历一遍,主要还是因为从后往前是找不到的。
3. 这时候有可能会出现一个问题,就是说你传过来的pos指针是不存在单链表里面的,这个时候不是函数本身的错误,是调用函数的那个人的错误,因此在函数内部时没必要为这个错误买单。当然,如果传过来一个空指针的话,这个还是可以进行断言检查的。
4. 需要注意可以与单链表的查找函数搭配使用。
5. 与此同时,pphead也不能为空指针,这个指针永远都不可能为空指针,就算你这个链表是空链表。因为即使是空链表的话,phead也是存在的,无非就是NULL。但是他的地址仍然是存在的,因此pphead永远不可能为空,因此在函数开头需要断言一下。phead是可以为空的。
//单链表的在pos节点之前插入一个节点
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);if (pos == *pphead){SLTPushFront(pphead, x);}else{SLTNode* newnode = BuySLTNode(x);SLTNode* cur = *pphead;while (cur->next != pos){cur = cur->next;}cur->next = newnode;newnode->next = pos;}
}
1. 与上面那个插入一样,如果这个pos就是第一个节点,那么就演变成为头插或头删。
2. 注意与find查找函数搭配使用
//单链表的删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(pos);assert(*pphead);if (pos == *pphead){SLTPopFront(pphead);}else{SLTNode* cur = *pphead;while (cur->next != pos){cur = cur->next;}cur->next = pos->next;free(pos);pos = NULL;}
}
//单链表的在pos节点后面插入一个新节点
void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);SLTNode* newnode = BuySLTNode(x);newnode->next = pos->next;pos->next = newnode;
}
//单链表的删除pos节点之后的节点
void SLTEraseAfter(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(*pphead);assert(pos);assert(pos->next);SLTNode* del = pos->next;pos->next = pos->next->next;free(del);del = NULL;
}
#define _CRT_SECURE_NO_WARNINGS 1#include
#include
#include typedef int SLTDataType;
typedef struct SLTNode
{SLTDataType data;struct SLTNode* next;
}SLTNode;void SLTPrint(SLTNode* phead)
{SLTNode* cur = phead;while (cur != NULL){printf("%d->",cur->data);cur = cur->next;}printf("NULL\n");
}SLTNode* BuySLTNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("BuySLTNode::Malloc");return NULL;}newnode->data = x;newnode->next = NULL;return newnode;
}void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = BuySLTNode(x);if (*pphead == NULL){*pphead = newnode;}else{SLTNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}tail->next = newnode;}
}void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = BuySLTNode(x);newnode->next = *pphead;*pphead = newnode;
}void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);if (pos == *pphead){SLTPushFront(pphead, x);}else{SLTNode* cur = *pphead;while (cur->next != pos){cur = cur->next;}SLTNode* newnode = BuySLTNode(x);cur->next = newnode;newnode->next = pos;}
}void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);SLTNode* newnode = BuySLTNode(x);newnode->next = pos->next;pos->next = newnode;
}void SLTPopBack(SLTNode** pphead)
{assert(pphead);assert(*pphead);if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SLTNode* prev = NULL;SLTNode* tail = *pphead;while (tail->next != NULL){prev = tail;tail = tail->next;}prev->next = NULL;free(tail);tail = NULL;}
}void SLTPopFront(SLTNode** pphead)
{assert(pphead);assert(*pphead);SLTNode* first = *pphead;*pphead = (*pphead)->next;free(first);first = NULL;
}void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(*pphead);assert(pos);if (pos == *pphead){SLTPopFront(pphead);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}void SLTEraseAfter(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(*pphead);assert(pos);assert(pos->next);SLTNode* del = pos->next;pos->next = pos->next->next;free(del);del = NULL;
}SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* cur = phead;while (cur != NULL){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}void menu()
{printf("************* Single List Table *************\n");printf("1. 单链表的打印(SLTPrint)\n");printf("2. 单链表的头插(SLTPushFront)\n");printf("3. 单链表的尾插(SLTPushBack)\n");printf("4. 单链表的在某节点(输入数值)前面插入(SLTInsert)\n");printf("5. 单链表的在某节点(输入数值)后面插入(SLTInsertAfter)\n");printf("6. 单链表的头删(SLTPopFront)\n");printf("7. 单链表的尾删(SLTPopBack)\n");printf("8. 单链表的删除某节点(输入数值)(SLTErase)\n");printf("9. 单链表的删除某节点(输入数值)后面的节点(SLTEraseAfter)\n");printf("*********************************************\n");printf("请输入(按0退出):");
}void HoldAndClear()
{int num = 0;printf("按任意键继续....\n");getchar();getchar();system("cls");
}int main()
{SLTNode* phead = NULL;int num = 0;int node = 0;int input = 0;do{menu();scanf("%d", &input);switch (input){case 1:SLTPrint(phead);break;case 2:scanf("%d",&num);SLTPushFront(&phead, num);break;case 3:scanf("%d", &num);SLTPushBack(&phead, num);break;case 4:SLTPrint(phead);scanf("%d %d", &node, &num);SLTInsert(&phead, SLTFind(phead, node), num);break;case 5:SLTPrint(phead);scanf("%d %d", &node, &num);SLTInsertAfter(&phead, SLTFind(phead, node), num);break;case 6:SLTPopFront(&phead);break;case 7:SLTPopBack(&phead);break;case 8:SLTPrint(phead);scanf("%d", &node);SLTErase(&phead, SLTFind(phead, node));break;case 9:SLTPrint(phead);scanf("%d", &node);SLTEraseAfter(&phead, SLTFind(phead, node));break;case 0:printf("退出\n");goto A;break;default:printf("重新输入:\n");break;}HoldAndClear();} while (input);A:return 0;
}
很显然,单链表适合头插,头删,然而并不适合尾删和尾插,因为这样子消耗太大,你需要去找之前那个节点,找之前那个节点的话,只有老老实实的从头节点开始向后遍历。
单链表适合在pos节点后面插入或者删除pos节点后面的节点,然而并不适合在pos节点之前插入或者删除pos节点,道理一样,消耗太大。
(pphead永远不可能为空,当phead为空的时候表示单链表为空链表)
(顺序表的话需要用size,但是链表的话不需要,链表的话我只需要知道第一个节点的地址就可以了,因为后面都是串联起来的,那之所以有pphead的这个概念,是因为我需要去改动指针,因此要传地址过去。)
1. 空链表是可以打印的。
2. 空链表也可以插入,头插尾插怎么插都可以(但是pphead永远不可能为空)
3. 空链表不能删除,头删尾删怎么删都不行(但是pphead永远不可能为空)