线性表是一种基本的数据类型,每一个元素对应了相应的位置,线性表分为顺序结构和链式结构,也就是顺序表和链表。凡是谈到线性表的实现就要实现基本的“增 删 查 改”。
顺序表
顺序表是一种将数据顺序且连续存放的结构,这核心实则一种数组形式,但是顺序表的内存是动态分配的而数组的元素是静态分配。顺序表由结构体定义,结构体中存在三个变量,分别为:指针变量(用于生成顺序表)、size(用于记录有效元素个数)、capacity(用于记录数组有效元素空间)。顺序表既然是一种数组,那么他的访问(通过下标)和空间释放是更加方便的。
typedef int SLdatetype;
typedef struct Seqlist //typedef struct Seqlist SL;
{
SLdatetype* arr;
int size;//有效数位
int capacity;//有效空间容量
}SL;
//C语言中传值传参的话,就不会对节点产生影响,这里就需要传节点参数
void initialize(SL* ps);//初始化顺序表
void destroyed(SL* ps);//顺序表销毁
void headpush(SLdatetype*p,SLdatetype x);//头插
void endpush(SLdatetype* p, SLdatetype x);//尾插
void headdel(SLdatetype* p);//头删
void enddel(SLdatetype* p);//尾删
void print(SL* ps);//打印数组
void posdel(SL* ps, int n);//指定位置删除
void pospush(SL* ps, unsigned int n, SLdatetype x);//指定位置插入元素
void initialize(SL* ps)
{
ps->arr = NULL;
ps->size = 0;
ps->capacity=0;
}
void destroyed(SL* ps)
{
if (ps->arr != NULL)//空值不用多余释放元素
{
free(ps->arr);
ps->arr = NULL;
}
ps->size = 0;
ps->capacity=0;
}
void headpush(SL* ps, SLdatetype x)
{
assert(ps->arr);
assert(ps->capacity);
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLdatetype* new = (SLdatetype*)realloc(ps->arr, newcapacity * sizeof(SLdatetype));
if (new != NULL)
{
ps->capacity = newcapacity;
ps->arr = new;
}
else
return;
}
for (int i = ps->size; i>=0 ; i--)
{
ps->arr[i+1] = ps -> arr[i ];
}
ps->arr[0] = x;
ps->size++;
}
void endpush(SL* ps, SLdatetype x)
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLdatetype* new = (SLdatetype*)realloc(ps->arr, newcapacity * sizeof(SLdatetype));
if (new != NULL)
{
ps->capacity = newcapacity;
ps->arr = new;
}
else
return;
}
ps->arr[ps->size++] = x;
}
void headdel(SL* ps)
{
assert(ps->size);
for (int i =0; i<ps->size ; i++)
{
ps->arr[i] = ps->arr[i+1];
}
ps->size--;
}
void enddel(SL* ps)
{
assert(ps->size);
ps->arr[ps->size-1];
ps->size--;
}
void posdel(SL*ps,unsigned int n)
{
assert(n<ps->size);
assert(ps->size);
for (int i=n-1;i<ps->size-1;i++)
{
ps->arr[i] = ps->arr[i+1];
}
ps->size--;
}
void pospush(SL* ps, unsigned int n,SLdatetype x)
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLdatetype* new = (SLdatetype*)realloc(ps->arr, newcapacity * sizeof(SLdatetype));
if (new != NULL)
{
ps->capacity = newcapacity;
ps->arr = new;
}
else
return;
}
for (int i = ps->size; i >=n; i--)
{
ps->arr[i] = ps->arr[i-1];
}
ps->arr[n - 1] = x;
ps->size++;
}
void print(SL* ps)
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->arr[i]);
}
printf("\n");
}
这里是测试
#include "seqlist.h"
int main()
{
SLdatetype a = 6;
SL s;
initialize(&s);//初始化顺序表
endpush(&s, 1);//尾部加入
endpush(&s, 2);
endpush(&s, 3);
endpush(&s, 4);
headpush(&s,6);//头部加入
print(&s);//打印顺序表
headdel(&s);//头部删除
print(&s);
enddel(&s);//尾部删除
print(&s);
pospush(&s, 2, 7);//指定位置加入
print(&s);
posdel(&s,2);//指定位置删除
print(&s);
return 0;
}
链表
链式结构即将各个结构体节点由自身的结构体指针链接起来,形成类似链条结构,各个节点使用自身结构体指针访问下个节点,访问方式相对于顺序表就不太方便了,链表的各个节点在内存上是不连续的,也就是说最后释放节点的时候就不能直接像顺序表一样直接用free函数释放。
typedef int datetype;
typedef struct linklist
{
datetype head;
struct linklist* next;
}link;
void endpush(link** s, datetype x);
void print(link* pre);
void headpush(link** s, datetype x);
void enddel(link**s);
link* Find(link** s, datetype x);
void ptdel(link** s, datetype x);//指定节点删除
void ptpush(link** s, link* p, datetype x);
void ptbpush(link* p, datetype x);//再指定位置之后插入数据
void ptdel(link** s, link* p);//指定节点删除
void pbdel(link** s, link* p);//删除指定节点之后的节点
void destroy(link** s);//销毁链表
void headdel(link** s);
link* ornode(datetype x)//初始化
{
link* new = (link*)malloc(sizeof(link));
assert(new);
new->head = x;
new->next=NULL;
return new;
}
void endpush(link** s, datetype x)//尾插
{
assert(s);
link* s1 = *s;
link* new1 = ornode(x);
if (s1 == NULL)
{
*s= ornode(x);
}
else
{
while (s1->next)
{
s1 = s1->next;
}
s1->next = new1;
}
}
void print(link* pre)//打印
{
link* ret = pre;
while ( ret)
{
printf("%d->",ret->head);
ret = ret->next;
}
printf("NULL\n");
}
void headpush(link** s, datetype x)//头插
{
link* new = (link*)malloc(sizeof(link));
new=ornode(x);
new->next = *s;
}
void enddel(link** s)//尾删
{
assert(s && *s);
link* s1 = *s;
link* s2=s1;
while (s1->next)
{
s2 = s1;
s1 = s1->next;
}
free(s1);
s1 = NULL;
s2->next= NULL;
}
void headdel(link** s)//头删
{
assert(s && *s);
link* next = (*s)->next;
free(*s);
*s = next;
}
//使用一个新指针指向第一个节点的next值,再释放第一个节点后,再将新指针的值赋给释放过后的头结点,以导致删除头结点
link* Find(link** s, datetype x)
{
link* point = *s;
while (point)
{
if (point->head == x)
return point;
point = point->next;
}
return NULL;
}
//根据head值查找,创建指向头指针的新指针,在进行遍历查找
void ptpush(link** s, link* p,datetype x)//再指定位置之前插入数据
{
assert(s && *s);//s不能为NULL,s指向的地址也不能为空
assert(p);
link* new = ornode(x);//这是要插入的节点
link* pr = *s;
while (pr->next != p)//结果是p的前面的一个节点,不是用pr!=p否则结果会是pr==p
{
pr = pr->next;
}
pr->next = new;
new->next = p;
}
void ptbpush(link* p, datetype x)//再指定位置之后插入数据
{
assert(p);
link* new = ornode(x);
new->next = p->next;
p->next = new;
//
//相同写法
/*p->next = new;
new->next = p->next;*/
}
void ptdel(link** s, link* p)//指定节点删除
{
assert(p);
assert(*s&&s);
if (p == *s)//如果指定节点是第一个节点
{
link* next = (*s)->next;//定义一个新指针
free(*s);
*s = next;
}
else{
link* pr = *s;
link* p1 = p;
while (pr->next!=p)
{
pr = pr->next;
}
pr->next = p1->next;
free(p);
p = NULL;
}
}
void pbdel(link** s, link* p)//删除指定节点之后
{
assert(s && *s);
assert(p);
link* pnext = p->next;//p的下个节点
link* pr = (p->next)->next;//p的下下个节点
p->next= pr;
free(pnext);
pnext = NULL;
}
void destroy(link**s)//销毁链表
{
link* p = *s;
while (*s)
{
p = (*s)->next;
free(*s);
*s = p;
}//需要定义新变量记录该节点的下一个节点,释放该节点后再将新变量的值赋给该节点,以此循环
}
链表测试用例
int main()
{
link* node1= (link*)malloc(sizeof(link));
node1->head = 1;
link* node2 = (link*)malloc(sizeof(link));
node2->head = 2;
link* node3 = (link*)malloc(sizeof(link));
node3->head = 3;
link* node4 = (link*)malloc(sizeof(link));
node4->head = 4;
//这里也可以先创建单个节点,然后用尾插方式创建
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = NULL;
headpush(&node1, 6);
print(node1);
endpush(&node1, 1);
print(node1);
enddel(&node1);
print(node1);
headdel(&node1);
print(node1);
//
//
//
//
link*p=Find(&node1,3);
printf("Find(&node1,3)->head是%d\n",p->head);
ptpush(&node1, p,3);
print(node1);
ptbpush(p, 4);
print(node1);
ptdel(&node1,p);
print(node1);
ptdel(&node1, node1);
print(node1);
pbdel(&node1, Find(&node1, 3));
print(node1);
destroy(&node1);
print(node1);
return 0;
}
链表因为不能像顺序表一样从下标直接访问,就只能整个链表一个一个接着遍历查找,甚至不能直接访问前面一个节点,这样链表的操作就变得很限制了。但相反链表使数据存储变的更加灵活。
同时链表存在双向链表,即将链表尾节点的指针指向第一个节点,整个链式结构就形成了一个环。增删查改的方式就会变得更方便一点,这里就不多说了。
学以上就是我对线性表的一些学习,欢迎指正讨论。