因为假期家里有写事,假期没有写一点什么。笔者大一这个下学期开学后加了线性,离散(还有可能物理)这对于我这个读工科的文科生来说简直就是太****了,又因为学校的组织不做要求就很长一段时间没动,现在内心感到愧疚,决定自己挖的坑,自己一定要填完的心理来填坑了。
这篇文章本来是用来学习数据结构的,希望能对别人有参考作用
在数据结构课本中线性表的概念无非两种
1.顺序表下文中定义为SQList
2.链表 下文中定义为linkedlist
1.顺序表的概念顺序表是内存中连续的一块,和我们基础数据类型的数组概念是差不多的,只不过它是通过我们主动去申请内存(堆),而数组是编译器来分配(栈),
顺序表应该具有几种基本的操作函数
1.创表函数(对标进行一些初始化的操作)
2.数据插入函数(对位置下标的操作)
3.数据删除函数(删除指定位置的数据,可以用一个指针保存数据)
4.删表函数(释放顺序标所占用的内存)
<PS>:为了在编程过程中便于查看效果,可以加入显示数据函数
现在以C语言的方式来思考
1.用结构体来表示顺序表
2.每一个操作都需要一个独立的函数完成
下面用一个思维导图来看怎么构造这个顺序表结构
这里用Elementype来代表具体的数据类型(Elementype也可以是一些结构体类型)
typedef int Elementype
typedef struct{
Elementype *elem; //用来指向申请的内存空间
unsigned length; //记录有元素的内存的长度
int listsize; //一张表的容量
}SQlist;
接下来是具体的操作函数(这个具体因人而异)
关于形参类型和形参个数可以不同,返回值可以是sqlist类型(但是不推荐,因为返回一个顺序表的开销太大了)
每个函数都应该传入一个顺序表结构体变量的地址,再考虑具体需求
下面是第一个函数
创表函数
void creatsqlist(SQlist*p,int long1)
{
p->listsize = long1; //传给listsize规定表的容量
p->elem = (Elementype*)malloc(sizeof(Elementype)*p->listsize);//开辟listsize个Elementype的内存空间
p->length = 0
}
这样通过创表函数,我们可以构造一张空表。
接下来,我们要能给这张空表插入数据,
这就需要插入函数来完成
int listinsert(SQlist*p1,int pos,Elementpye e)
{
Elementype*newbase = NULL;
Elementype*p = NULL,*q = NULL;
if(pos>p->length||pos<0)return -1; //避免错误的插入位置
if(p->length>p->listsize){ //当空间不够时,用realloc函数再分配空间
newbase = (Elementype*)realloc(p1->elem,(p1->listsize+HOWMUCH)*sizeof(Elementype));
p1->elem = newbase;
p1->listsize+=HOWMUCH; //容量跟新
}
q = &(p1->elem[pos-1]); //找到要插入的位置和表末尾的,将它们向后移一个位置
for(p = &(p1->elem[p1->length-1];p>=q;p--)*(p+1) = *(p);
*q = e; //插入数据元素e
++p->length; //长度增加1位
return 1;
}
其中HOWMUCH是一个常量,是空间不够时多分配的内存空间的量
接下来就是要来实现删除的功能
int deletelist(SQlist*p1,int pos)
{
if(pos>p1->length||pos<0)return -1; //避免错误的插入位置
Elementype *p = NULL,*q = NULL;
q = &(p1->elem[pos-1]); //得到要删除元素得位置
p = &(p1->elem[p1->length-1]); //得到表得末尾元素位置
for(++q;p>=q;++q)
{
*(q-1) = *q; //将每一位元素向前
}
p1->length--;
return 1;
}
接下来就是最后一个基本操作函数,释放表得函数
void Destroylist(SQlist*p1)
{
free(p1->elem);//释放指针指向得数据空间
p1->=NULL;
}
(PS这些申请内存得操作全都要包含stdlib头文件)
为了在编写中方便看到效果,我加入了批量写入和显示元素得函数
它们如下
void writedata(SQlist*p,int how) //从表头写how个数据入表
{
Elementype*temp = p->elem;
int i = 0;
for(i = 0;i<how;i++,temp++)
{
scanf("%d",temp);
p->length++; //长度增加
}
}
void showdata(SQlist*p)
{
int i = 0,count = 0;
Elementype*temp = p->elem;
for(i;i<p->length;i++,temp++)
{
printf("| %d ",*temp);
count++;
if(count==4){
count = 0;printf("\n");
}
}
printf("\n/\n");
}
下面写了一个主函数
int main()
{
SQlist s1;
creatsqlist(&s1,10);
writedata(&s1,10);
showdata(&s1);
deletelist(&s1,9);
deletelist(&s1,6);
deletelist(&s1,3);
showdata(&s1);
listinsert(&s1,3,3);
showdata(&s1);
}
下面是运行截图
补充一个合表函数如下
void combinelist(SQlist*s1,SQlist*s2,SQlist*s3) //这个S3是没有容量的,请不要给S3创表
{
Elementype*p1 = s1->elem;
Elementype*p2 = s2->elem;
Elementype*p1_last = s1->elem+s1->length-1;
Elementype*p2_last = s2->elem+s2->length-1;
int newlength = s2->length+s1->length;
s3->elem = (Elementype*)malloc(newlength*sizeof(Elementype));
Elementype*p3 = s3->elem;
s3->length = newlength;
while(p1<=p1_last)*p3++=*p1++;
while(p2<=p2_last)*p3++=*p2++;
}
下面将上述封装到一个头文件中,下面给出头文件源码
#ifndef _SQLIST_H_
#define _SQLIST_H_
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define HOWMUCH 10
void creatsqlist(SQlist*p,int long1); //创表函数
int listinsert(SQlist*p1,int pos,Elementype e);//插入函数
int deletelist(SQlist*p1,int pos); //删除函数
void writedata(SQlist*p,int how); //批量数据写入
void showdata(SQlist*p); //显示函数
void combinelist(SQlist*s1,SQlist*s2,SQlist*s3);//合并函数
void Destroylist(SQlist*p1);
typedef int Elementype;
typedef struct{
Elementype *elem; //用来指向申请的内存空间
unsigned length; //记录有元素的内存的长度
int listsize; //一张表的容量
}SQlist;
void creatsqlist(SQlist*p,int long1)
{
p->listsize = long1; //传给listsize规定表的容量
p->elem = (Elementype*)malloc(sizeof(Elementype)*p->listsize);//开辟listsize个Elementype的内存空间
p->length = 0;
}
int listinsert(SQlist*p1,int pos,Elementype e)
{
Elementype*newbase = NULL;
Elementype*p ,*q;
if(pos>p1->length||pos<0)return -1; //避免错误的插入位置
if(p1->length>p1->listsize){ //当空间不够时,用realloc函数再分配空间
newbase = (Elementype*)realloc(p1->elem,(p1->listsize+HOWMUCH)*sizeof(Elementype));
p1->elem = newbase;
p1->listsize+=HOWMUCH; //容量跟新
}
q=&(p1->elem[pos-1]);
for(p=&(p1->elem[p1->length-1]);p>=q;--p)*(p+1)=*p;
*q = e;
++p1->length;
return 1;
}
int deletelist(SQlist*p1,int pos)
{
if(pos>p1->length||pos<0)return -1; //避免错误的插入位置
Elementype *p = NULL,*q = NULL;
q = &(p1->elem[pos-1]);
p = &(p1->elem[p1->length-1]);
for(++q;p>=q;++q)
{
*(q-1) = *q;
}
p1->length--;
return 1;
}
void writedata(SQlist*p,int how)
{
Elementype*temp = p->elem;
int i = 0;
for(i = 0;i<how;i++,temp++)
{
scanf("%d",temp);
p->length++;
}
}
void showdata(SQlist*p)
{
int i = 0,count = 0;
Elementype*temp = p->elem;
for(i;i<p->length;i++,temp++)
{
printf("| %d ",*temp);
count++;
if(count==4){
count = 0;printf("\n");
}
}
printf("\n/\n");
}
void combinelist(SQlist*s1,SQlist*s2,SQlist*s3)
{
Elementype*p1 = s1->elem;
Elementype*p2 = s2->elem;
Elementype*p1_last = s1->elem+s1->length-1;
Elementype*p2_last = s2->elem+s2->length-1;
int newlength = s2->length+s1->length;
s3->elem = (Elementype*)malloc(newlength*sizeof(Elementype));
Elementype*p3 = s3->elem;
s3->length = newlength;
while(p1<=p1_last)*p3++=*p1++;
while(p2<=p2_last)*p3++=*p2++;
}
void Destroylist(SQlist*p1)
{
free(p1->elem);//释放指针指向得数据空间
p1->=NULL;
}
/*int main()
{
SQlist s1;
creatsqlist(&s1,10);
writedata(&s1,10);
showdata(&s1);
deletelist(&s1,9);
deletelist(&s1,6);
deletelist(&s1,3);
showdata(&s1);
listinsert(&s1,3,3);
showdata(&s1);
SQlist s1,s2,s3;
creatsqlist(&s1,5);
creatsqlist(&s2,6);
writedata(&s1,5);
writedata(&s2,6);
showdata(&s1);
showdata(&s2);
combinelist(&s1,&s2,&s3);
showdata(&s3);
}*/
#endif
C语言实现链表
相比于顺序表,链表在内存空间上是不连续的,是一块一块独立的内存空间,由彼此之间的指针来建立相互之间的各种关系
相比于顺序表,链表对于对数据的修改所需要的内存开销跟小,适合频繁的数据插入和更改。
链表的基本元素是节点,一个节点中可以有若干个指针域和一个数据域
下面画出链表的结构图
以单链表为例
他在内存中的结构如图
为了详细展示链表结构和具体操作得实现,参考下图
现在给出Node和Linklist结构体的代码
typedef struct{
Elementype*elem; //存放数据的数据域
struct Node*next; //存放下一个节点地址的指针域
}Node;
typedef struct{
Node*Head; //头指针用来开辟头节点
int length; //链表的长度
}Linkedlist;
先在完成了结构体的构建,下面要对齐分配内存空间进行创表操作
void Creatlist(Linkedlist*s1) //初始化链表
{
s1->Head = (Node*)malloc(sizeof(Node)); //开辟头节点
s1->length = 0; //设置长度为0
Head->next = NULL; //指针指向空
}
这样我们就有了一个初始化的链表
下面我面就要能够加入节点,
分别为头插和尾插,分别是在表头加入和在表尾加入,下面给出具体的实现代码
void backnode(Linkedlist*s1,Elementype e) //尾部插入
{
Node*p1 = s1->Head; //得到头指针地址
Node*temp = (Node*)malloc(sizeof(Node)); //开辟新的节点
temp->elem = e; //保存传入的Elementype类型的参数
temp->next = NULL; //设置尾指针指向空
while(p1->next!=NULL) //得到尾指针地址PS注意这个条件是p1->next!=NULL
{
p1 = p1->next;
}
p1->next = temp; //把尾指针连上新节点得地址,完成链接
++s1->length; //长度加一
}
void frontnode(Linkedlist*s1,Elementype e)
{
Node*weitou = s1->Head;
Node*temp = (Node*)malloc(sizeof(Node));
temp->elem = e; //保存数据
temp->next = weitou->next; //建立和Head节点next指针域得链接
weitou->next = temp; //链接头节点,完成头插
++s1->length; //长度加一
}
下面实现删除和释放链表得函数
void delete_linkl(Linkedlist*s1,int pos) //定位删除函数
{
Node*temp = s1->Head->next;
int i = 0;
while(i<pos-2)
{
temp = temp->next; //得到尾要删除节点的前一个节点的地址
i++;
}
Node*tofree = temp->next; //得到要删除节点得地址
Node*temp1 = tofree->next; //得到要删除节点地址得下一个节点得地址
temp->next = temp1; //链接前一个和后一个节点
s1->length--; //长度减一
free(tofree); //释放节点
tofree = NULL;
}
释放表空间函数
void Destroylist(Linkedlist*a) //释放链表空间
{
Node *tmp = NULL; //定义一个临时得节点指针
Node *p = a->head->next;
while(p!=NULL)
{
tmp = p->next; //交替释放空间
free(p);
p = tmp;
}
free(tmp); //尾节点空间释放
tmp = NULL;
free(a->head); //头文件得释放
a->head = NULL;
printf("空间已释放");
}
下面编写一个主函数来调用看一看效果
基本得操作已经完成我再把我写得一些东西封装成一个头文件,源码如下
#ifndef _LINKIST2_H_
#define _LINKIST2_H_
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
typedef int Elementype;
typedef struct{
Elementype*elem;
struct Node*next;
}Node;
typedef struct{
Node*Head;
int length;
}Linkedlist;
void Createlink(Linkedlist*s1)
{
s1->Head = (Node*)malloc(sizeof(Node));
s1->length = 0;
s1->Head->next = NULL;
}
void backnode(Linkedlist*s1,Elementype e)
{
Node*p1 = s1->Head;
Node*temp = (Node*)malloc(sizeof(Node));
temp->elem = e;
temp->next = NULL;
while(p1->next!=NULL)
{
p1 = p1->next;
}
p1->next = temp;
++s1->length;
}
void frontnode(Linkedlist*s1,Elementype e)
{
Node*weitou = s1->Head;
Node*temp = (Node*)malloc(sizeof(Node));
temp->elem = e;
temp->next = weitou->next;
weitou->next = temp;
++s1->length;
}
void showlinklist(Linkedlist*s1) //根据链表得长度显示链表数据
{
int i = 0,count = 0;
Node*t1 = s1->Head;
Node*p = t1->next;
for(i;i<s1->length;i++)
{
printf("| %d ",p->elem);
p = p->next;
count++;
if(count==5){ //5个数据一行就回车换行
printf("\n");count = 0;
}
}
printf("\n//\n");
}
void Destroylist(Linkedlist*a) //释放链表空间
{
Node *tmp = NULL;
Node *p = a->Head->next;
while(p!=NULL)
{
tmp = p->next;
free(p);
p = tmp;
printf("!");
}
free(tmp);
tmp = NULL;
free(a->Head);
a->Head = NULL;
printf("空间已释放");
}
void piliangweicha(Linkedlist*s1,int towhere) //批量重键盘上输入链表数据
{
int i = 0;
for(i;i<towhere;i++)
{
Node*p1 = s1->Head;
Node*temp = (Node*)malloc(sizeof(Node));
Elementype i1;
scanf("%d",&i1);
temp->elem = i1;
temp->next = NULL;
while(p1->next!=NULL)
{
p1 = p1->next;
}
p1->next = temp;
s1->length++;
}
}
void insertlinklist(Linkedlist*s1,int pos,Elementype e) //定位插入节点函数
{
Node*temp = (Node*)malloc(sizeof(Node)); //开辟新得节点
temp->elem = e; //保存传入得数据
Node*t1 = s1->Head->next; //得到链表元素开始地址
int i = 0;
while(i<pos-2) //得到要插入节点得前一个节点
{
t1 = t1->next;
i++;
}
Node*t2 = t1->next; //得到要插入得位置节点得地址
temp->next = t2; //完成链接
t1->next = temp;
s1->length++; //长度增加
}
void delete_linkl(Linkedlist*s1,int pos)
{
Node*temp = s1->Head->next;
int i = 0;
while(i<pos-2)
{
temp = temp->next;
i++;
}
Node*tofree = temp->next;
Node*temp1 = tofree->next;
temp->next = temp1;
s1->length--;
free(tofree);
tofree = NULL;
}
void linklistcombin(Linkedlist*s1,Linkedlist*s2) //链表合并函数
{
Node*p = s1->Head; //得到并入链表得头节点
Node*q = s2->Head;
Node*q1 = q->next; //得到要被并入链表得头节点得下一个有数据节点得地址
while(p->next!=NULL)
{
p = p->next; //得到并入链表得尾节点
}
p->next = q1; //链接
s1->length+=s2->length; //加上表长
}
#endif
下篇,我将用C++的泛型实现通用的顺序表和链表
。。。。再见。