线性表(数据结构)

 线性表是一种基本的数据类型,每一个元素对应了相应的位置,线性表分为顺序结构和链式结构,也就是顺序表和链表。凡是谈到线性表的实现就要实现基本的“增 删 查 改”。

顺序表

顺序表是一种将数据顺序且连续存放的结构,这核心实则一种数组形式,但是顺序表的内存是动态分配的而数组的元素是静态分配。顺序表由结构体定义,结构体中存在三个变量,分别为:指针变量(用于生成顺序表)、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;
}

链表因为不能像顺序表一样从下标直接访问,就只能整个链表一个一个接着遍历查找,甚至不能直接访问前面一个节点,这样链表的操作就变得很限制了。但相反链表使数据存储变的更加灵活。

同时链表存在双向链表,即将链表尾节点的指针指向第一个节点,整个链式结构就形成了一个环。增删查改的方式就会变得更方便一点,这里就不多说了。

学以上就是我对线性表的一些学习,欢迎指正讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值