填一个大坑之C/C++实现线性表(利用泛型)上篇

本文介绍如何使用C语言实现顺序表和链表的数据结构。包括创建、插入、删除等基本操作,并提供完整的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

因为假期家里有写事,假期没有写一点什么。笔者大一这个下学期开学后加了线性,离散(还有可能物理)这对于我这个读工科的文科生来说简直就是太****了,又因为学校的组织不做要求就很长一段时间没动,现在内心感到愧疚,决定自己挖的坑,自己一定要填完的心理来填坑了。
这篇文章本来是用来学习数据结构的,希望能对别人有参考作用

在数据结构课本中线性表的概念无非两种
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++的泛型实现通用的顺序表和链表
。。。。再见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗马苏丹默罕默德

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值