优点:1物理空间连续,2.可以实现下标随机访问。
缺点:1.空间不够要扩容,所以有一定空间浪费和空间消耗。
2.头部或中间位置插入删除效率低。
改善方案:1.按需申请空间,需要一个空间开辟一个。
2.头部,中间插入删除不挪动数据。
——————链表
1.2链表#pragma once
#include
#include
#include
#include
typedef int SLTDateType;
struct SListNode
{
SLTDateType date; //数据个数
struct SListNode* next; //指向下一个结点的指针
};
这是在头文件区域定义的,上面的#pragma是防止头文件重复的,下面的结构体是链表的主体。
先写四个节点
#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"
void TestSList1()
{
SLTNode* n1 = (SLTNode*)malloc(sizeof(SLTNode));
assert(n1);
SLTNode* n2 = (SLTNode*)malloc(sizeof(SLTNode));
assert(n2);
SLTNode* n3 = (SLTNode*)malloc(sizeof(SLTNode));
assert(n3);
SLTNode* n4 = (SLTNode*)malloc(sizeof(SLTNode));
assert(n4);
n1->date = 1;
n2->date = 2;
n3->date = 3;
n4->date = 4;
n1->next = n2;
n2->next = n3;
n3->next = n4;
n4->next = NULL;
}
int main()
{
TestSList1();
return 0;
}
next中存的是下一个数据的地址,最后一个数据的next中存的是NULL。
//打印
void SListPrint(SLTNode* phead)
{
//phead存首元素地址
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->date);
cur = cur->next; //指向下一个
}
printf("NULL\n");
}
为了方便,先写一个打印函数。
2.打印//打印
void SListPrint(SLTNode* phead)
{
//phead存首元素地址
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->date);
cur = cur->next; //指向下一个
}
printf("NULL\n");
}
phead中存的是首元素地址,要不然一会打印不能从头开始,所以又创建个新变量cur。
中间这个->是咱自己在printf中加的。
3.尾插实现//尾插
void SListPushBack(SLTNode* phead, SLTDateType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
newnode->date = x;
newnode->next = NULL;
//找节点
SLTNode* tail = phead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
思路:新开辟一个空间,这个空间分两部分,一部分用来存值x,一部分用来存地址,但是在最后所以存NULL。接下来我要去空间中去找最后的地址,所以又创建个tail。
那如果是空指针实现尾插呢?
很明显,程序直接崩掉了,所以尾插代码还不完善。
//尾插
void SListPushBack(SLTNode* phead, SLTDateType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
assert(newnode);
newnode->date = x;
newnode->next = NULL;
//先判断phead是否为空
if (phead == NULL)
{
phead = newnode;
}
else
{
//找节点
SLTNode* tail = phead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
但是打印结果是NULL(不在粘贴图了),为啥呢?
3.2二级指针形参是实参的一份临时拷贝,要改变形参,就要传实参的地址,同类,我要改变指针,那我就要传指针的地址,指针是一份地址,指针的地址又是一份地址,所以用到二级指针。
咱们要做的是把NULL改成要插入元素的地址,所以用到二级指针,而不仅仅改变数值。
//尾插
void SListPushBack(SLTNode** pphead, SLTDateType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
assert(newnode);
newnode->date = x;
newnode->next = NULL;
//先判断phead是否为空
if (*pphead == NULL)
{
*pphead = newnode;
//*pphead代表的是地址,而不是解引用
}
else
{
//找节点
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
print函数可以不传,因为它不需要改变数值。
4.头插 4.1节点函数每次插入都要开辟节点,那不妨创建一个节点函数
//开辟空间
SLTNode* BuySListNode(SLTDateType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
assert(newnode);
newnode->date = x;
newnode->next = NULL;
return newnode;
}
4.2头插
好好理解一下兄弟们。
5.头删//头删
void SListPopFront(SLTNode** pphead)
{
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
这个很好理解,我先找一个寄存器存下一个数的地址,然后free掉,我再把寄存器中的地址传给*pphead然后通过地址直接访问的是原来的一串数的第二个,因为我找的地址就是第二个数的,所以第一个数就被free了。
尾删两次。注意一定要判空。
6.尾删//尾删
void SListPopBack(SLTNode** pphead)
{
SLTNode* tail = *pphead;
SLTNode* tailPrve = NULL;
while (tail->next != NULL)//前提是tail不是空
{
tailPrve = tail;
tail = tail->next;
}
free(tail);
tailPrve->next = NULL;
}
当tail访问最后一个是,就不进入while循环了,所以tailPrve是倒数第二个值。
7.某位置插入://某处添加
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
assert(pos && *pphead!=NULL);
if (*pphead == pos)
{
SListPushFront(pphead, x);
}
else
{
SLTNode* prev = *pphead;
SLTNode* newnode = BuySListNode(x); //开辟节点
while (prev->next != pos);
{
prev = prev->next;
}
prev->next = newnode; //把newnode接在prev上,再把pos和newnode接在一起
newnode->next = pos;
}
}
8.某位置删除
//某处删除
void SListErase(SLTNode** pphead, SLTNode* pos)
{
assert(pos && pphead);
if (*pphead == pos)
{
SListPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != NULL)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}
9.在pos后面添加和删除
//pos后添加
void SListInsertafter(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
assert(pos);
SLTNode* newnode = BuySListNode(x); //开辟节点
SLTNode* next = pos->next;
pos->next = newnode;
newnode->next = next; //不用考虑顺序
}
//pos后删除
void SListEraseafter(SLTNode** pphead, SLTNode* pos)
{
assert(pos);
if (pos->next == NULL) //最后了就不需要删除了
return;
SLTNode* del = pos->next; //正好把中间那个隔离
pos->next = del->next;
free(del);
del = NULL;
}
由于时间有限,最后几个就不在解释了,有不太理解的兄弟们私信我。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)