前提
- 不探讨头结点
- 空链表可以插入和查找,不可删除
- 一般不选择
phead
移动,定义一个新结点把phead
赋给他,移动新结点即可- 单链表不适合在前面和后面插入或删除,适合在后面插入删除
头插
void SLPushFront(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = BuySLTNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
尾插
//给新结点开辟空间并传入值
SLTNode* BuySLTNode(SLTDataType x)
{
assert(pphead);
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
//不为空的链表的尾插
//尾插的本质是尾结点中要存放新的尾结点的地址
//void SLPushBack(SLTNode* phead, SLTDataType x)
void SLPushBack(SLTNode** pphead, SLTDataType x)
{
给新结点开辟空间
//SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
//if (newnode == NULL)
//{
// perror("malloc fail");
//}
//newnode->data = x;
//newnode->next = NULL;
SLTNode* newnode = BuySLTNode(x);
if (*pphead == NULL)
{
*pphead= newnode;
}
else
{
//找尾
//此时不需要用二级指针
//要改变的是结构体,与前不同,前面要改变的是结构体的指针
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
尾删
- 要判断链表是否为空
- 注意剩一个结点和多个结点的删除方法不同
- 要找到前一个结点的指针
//第一种方法
void SLPopBack1(SLTNode** pphead)
{
//链表本身就为空的情况
// 方法一:
/*if (*pphead == NULL)
return;*/
//方法二:
//顺序反则没有意义
assert(pphead);
assert(*pphead);
//注意优先级
//一个结点
if ((*pphead)->next == NULL)
{
//这里说明,尾删不能用一级指针
free(*pphead);
*pphead = NULL;
}
//多个结点
else
{
SLTNode* tail = *pphead;
//定义prev指针指向最后一个结点的前一个结点
SLTNode* prev = NULL;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
free(tail);
//不加一个结点的判断,在这里会出问题,不进入循环
prev->next = NULL;
}
}
//第二种方法
void SLPopBack2(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);
//一个结点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
//多个结点
else
{
SLTNode* tail = *pphead;
//不加一个结点的判断,在这里会出问题,不进入循环
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
头删
不需要判断一个结点的状况
void SLPopFront(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);
SLTNode* first = *pphead;
*pphead = first->next;
free(first);
first = NULL;
}
插入/删除数据
1.在pos指针之前插入
- 首先要考虑头插,其次要找到pos的前一个位置
- 类似于尾删
//pos结点前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pos);
assert(pphead);
//头插
if (pos == *pphead)
{
SLPushFront(pphead, x);
}
else
{
//找到pos结点的前一个结点
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
//要添加的结点以及值
SLTNode* newnode = BuySLTNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
2. 在pos位置删除
//pos位置删除
void SLErase(SLTNode** pphead, SLTNode* pos)
{
assert(pos);
assert(pphead);
assert(*pphead);
//头删
if (pos == *pphead)
{
SLPopFront(pos);
}
else
{
//找到pos结点的前一个结点
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
//滞空无用,形参改变不影响实参
//pos = NULL;
}
}
3. pos后面插入
void SLInsertAfter(SLTNode* pos,SLTDataType x)
{
assert(pos);
SLTNode* newnode = BuySLTNode(x);
//顺序不可返
//否则数据丢失
newnode->next = pos->next;
pos->next = newnode;
}
4. pos后面删除
void SLTEraseAfter(SLTNode* pos)
{
assert(pos);
assert(pos->next);
//ok
//SLTNode* del = pos->next;
//pos->next = pos->next->next;
//free(del);
//del = NULL;
SLTNode* del = pos->next;
pos->next = del->next;
free(del);
del = NULL;
}
测试
void TestLinkList1()
{
SLTNode* plist = NULL;
//形参的地址不改变实参
SLPushBack(&plist, 2);
SLPushBack(&plist, 3);
SLPushBack(&plist, 4);
SLPushBack(&plist, 5);
printf("尾插法:");
SLTPrint(plist);
printf("头删法:");
SLPopFront(&plist);
SLTPrint(plist);
SLPopFront(&plist);
SLTPrint(plist);
SLPopFront(&plist);
SLTPrint(plist);
SLPopFront(&plist);
SLTPrint(plist);
//空链表 -- 报错
/*SLPopFront(&plist);
SLTPrint(plist);*/
}
void TestLinkList2()
{
SLTNode* plist = NULL;
SLPushFront(&plist, 1);
SLPushFront(&plist, 2);
SLPushFront(&plist, 3);
SLPushFront(&plist, 4);
printf("头插法:");
SLTPrint(plist);
printf("尾删法:");
SLPopBack1(&plist);
SLTPrint(plist);
SLPopBack2(&plist);
SLTPrint(plist);
SLPopBack1(&plist);
SLTPrint(plist);
SLPopBack2(&plist);
SLTPrint(plist);
//空了再删 -- assert警告
/*SLPopBack2(&plist);
SLTPrint(plist);*/
}
TestLinkList3()
{
SLTNode* plist = NULL;
SLPushFront(&plist, 2);
SLPushFront(&plist, 5);
SLPushFront(&plist, 7);
SLPushFront(&plist, 9);
//值为2的结点的值*2
printf("查找值为2的结点的值乘2:");
SLTNode* ret = SLFind(plist, 2);
ret->data *= 2;
SLTPrint(plist);
//pos结点前插入
//找到结点位置
SLTNode* ret2 = SLFind(plist, 5);
printf("在值为%d前插入:",ret2->data);
SLInsert(&plist,ret2, 100);
SLTPrint(plist);
//在pos位置删除
SLTNode* ret3 = SLFind(plist, 7);
printf("删除值为%d的数:", ret3->data);
SLErase(&plist,ret3);
SLTPrint(plist);
//在pos后插入
SLTNode* ret4 = SLFind(plist,5 );
printf("在%d后插入数据:", ret4->data);
SLInsertAfter(ret4, 299);
SLTPrint(plist);
//在pos后删除
printf("在%d后删除数据:", ret4->data);
SLTEraseAfter(ret4);
SLTPrint(plist);
}
int main()
{
TestLinkList1();
TestLinkList2();
TestLinkList3();
return 0;
}
补充
区分两种断言,一定不能为空才断言