学习一下顺序表吧

本文详细介绍了动态顺序表的实现,包括结构体定义、初始化、扩容策略(成倍扩容)、插入(尾插、头插、任意位置)、删除(尾删、头删)以及销毁操作。重点强调了使用指针和assert进行错误处理的重要性。
摘要由CSDN通过智能技术生成

    先说一下什么是顺序表吧:顺序表的底层结构是数组,对数组的封装,实现了常⽤的增删改查等接⼝ 。顺序表也有静态顺序表,就是利用数组进行实现,这样的内存空间上相对不便控制,让我们来实现一下动态顺序表吧。

1.我们先建立一下动态顺序表的动态数组结构

typedef int innt;//因为我们用的是int的数据进行存储,但是又别的情况,所以我们给int 重命名一下,会方便以后别的类型的使用更改。
typedef struct list
{
	innt* arr;
	int capcity;//这是顺序表的容量
	int sz;//这是顺序表实际的有效数据
}list;

2.让我们给顺序表初始化一下吧

void chushihua(list* ps)
{
	ps->arr = NULL;//将一开始的动态数组设为空指针
	ps->capcity = ps->sz = 0;//一开始没有空间和数据都是零
}

3.初始化之后,我们来说说要为动态数组开辟空间。也就是扩容,怎么扩。

扩容有三种扩容的方式<1>往进写一个元素,就扩容一个元素的大小。想想是不是有点麻烦

<2>.第二种扩容方式是固定一个值的扩容,假如每次扩容一百,那么我存了101,那是不是就浪费了99的空间,这样做是有一点浪费的

<3>.第三种就是我们要用的一种扩容原则,成倍扩容,我们以二倍为例,也就是2,4,8,16,32。。这样的扩容方式是最常见的扩容方式,相比起来,最能节省空间。让我们实现一下。

void checkcap(list* ps)
{
	if (ps->capcity == ps->sz)//如果有效数据堆满了空间,我们就得扩容
	{
		int c=ps->capcity == 0 ? 2 : 2 * sizeof(ps->capcity);
		innt* ret = (innt*)realloc(ps->arr, 2 * sizeof(innt));
		if (ret == NULL)
		{
			perror("realloc false");
			exit(1);
		}
		ps->arr = ret;
		ps->capcity = c;
		
	}				
}

我们用三目操作符判断一下容量是不是为零,不为零就正常扩二倍。接着我们给动态数组扩容,并且把地址传给了它。

<1>return 1;和exit(1)的区别

return 只会结束当前的程序,其他的子程序可能会继续走,而exit(1),会立即结束整个程序。

<2>每次的断言ps是防止是空指针

4.扩容之后我们就要,进行第一个操作尾插。

void pushback(list* ps,innt x)
{
	assert(ps);
	checkcap(ps);
	ps->arr[ps->sz] = x;
	ps->sz++;
}

有了扩容函数,操作很方便。我们只需要让有效数据的个数作为数组的下标,我们就可以进行存储这个数字。让我们调试一下,看看成没成功。看起来还是不错的

void print(list* ps)
{
	for (int i = 0; i < ps->sz; i++)
	{
		printf("%d->", ps->arr[i]);
	}
}

,让我们打印出来看一下。

5,写完了尾插,我们写一下头插。头插就是arr[0]=我们输入的值,让原来的数据只要往后挪一位就可以了。

void pushfront(list* ps, innt x)
{
	assert(ps);
	
	checkcap(ps);
	for (int i = ps->sz; i > 0; i--)
	{
		ps->arr[i ] = ps->arr[i-1];
	}
	ps->arr[0] = x;
	ps->sz++;
}

6 前面的插入和后面的插入我们已经完成了,那么我们完成一下头删和尾删。

尾删相对简单,我们只需要把有效数据的个数减一个就可以了,下次存储都不会影响。

头删相对于来说,和头插也差不多,一个往后挪,一个往前挪。

void popback(list* ps)
{
	assert(ps);
	assert(ps->sz);
	ps->sz--;
}
void popfront(list* ps)
{
	assert(ps);
	assert(ps->arr);
	for (int i = 0; i < ps->sz-1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->sz--;
}

 宝贝是不是越写越轻松呢。

7.任意位置的插入和删除

插入:我们可以想象一下排列好的数组,我们想要在哪里插入,就传入位置和数据就可以了。把该位置的数据之后的数据往后挪一位,把该数据插入进去,是不是也很简单呢,简直和头插一样,只不过一个头不固定,一个头固定。

void insert(list* ps, int pos, innt x)//pos是要更改的位置

{
	assert(pos > 0 && pos <= ps->sz);
	checkcap(ps);
	for (int i = ps->sz - 1; i >= pos-1; i--)
	{
		ps->arr[i + 1] = ps->arr[i];
	}
	ps->arr[pos - 1] = x;
	ps->sz++;
}

<1>请大家注意每次的插入一定要把ps->size进行加一

<2>对pos的范围进行断言很重要

void shanchu(list* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->sz);
	for (int i = pos - 1; i < ps->sz; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->sz--;
}

8再坚持坚持,我们来到最后一步,销毁,要把我们申请的空间还给系统

void destory(list* ps)
{
	assert(ps);
	
	list* ret = ps;
	free(ps->arr);
	ps ->arr= NULL;
	ret->capcity = ret->sz = 0;

}

9 让我总结一下,对顺序表要掌握指针,结构体,熟练掌握才可以运用的很彻底。

第二个就是运用assert的断言,我们目的就是为了防止空间的乱用,避免传过来的是空指针从而导致数据的存储失败。

10代码呈上

//list.h


#pragma once
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>

typedef int innt;
typedef struct list
{
	innt* arr;
	int capcity;
	int sz;
}list;

void chushihua(list* ps);
void pushback(list* ps,innt x);
void print(list* ps);
void pushfront(list* ps, innt x);
void popback(list* ps);
void popfront(list* ps);
void insert(list* ps, int pos, innt x);
void shanchu(list* ps, int pos);
void destory(list* ps);
//list.c
#include"list.h"

void chushihua(list* ps)
{
	ps->arr = NULL;//将一开始的动态数组设为空指针
	ps->capcity = ps->sz = 0;//一开始没有空间和数据都是零
}
void checkcap(list* ps)
{
	if (ps->capcity == ps->sz)//如果有效数据堆满了空间,我们就得扩容
	{
		int c=ps->capcity == 0 ? 2 : 2 * sizeof(ps->capcity);
		list* ret = (list*)realloc(ps->arr, 2 * sizeof(innt));
		if (ret == NULL)
		{
			perror("realloc false");
			exit(1);
		}
		ps->arr = ret;
		ps->capcity = c;
		
	}				
}
void pushback(list* ps,innt x)
{
	assert(ps);
	checkcap(ps);
	ps->arr[ps->sz] = x;
	ps->sz++;
}
void print(list* ps)
{
	for (int i = 0; i < ps->sz; i++)
	{
		printf("%d->", ps->arr[i]);
	}
	printf("\n");
}
void pushfront(list* ps, innt x)
{
	assert(ps);
	
	checkcap(ps);
	for (int i = ps->sz; i > 0; i--)
	{
		ps->arr[i ] = ps->arr[i-1];
	}
	ps->arr[0] = x;
	ps->sz++;
}
void popback(list* ps)
{
	assert(ps);
	assert(ps->sz);
	ps->sz--;
}
void popfront(list* ps)
{
	assert(ps);
	assert(ps->arr);
	for (int i = 0; i < ps->sz-1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->sz--;
}
void insert(list* ps, int pos, innt x)
{
	assert(pos > 0 && pos <= ps->sz);
	checkcap(ps);
	for (int i = ps->sz - 1; i >= pos-1; i--)
	{
		ps->arr[i + 1] = ps->arr[i];
	}
	ps->arr[pos - 1] = x;
	ps->sz++;
}
void shanchu(list* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->sz);
	for (int i = pos - 1; i < ps->sz; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->sz--;
}
void destory(list* ps)
{
	assert(ps);
	if (ps->arr == NULL)
	{
		return;
	}
	list* ret = ps;
	free(ps->arr);
	ps ->arr= NULL;
	ret->capcity = ret->sz = 0;

}
//test.c
#include"list.h"
void test()
{
	list ps ;
	chushihua(&ps);
	pushback(&ps, 1);
	pushback(&ps, 2);
	pushback(&ps, 3);
	pushback(&ps, 4);
	print(&ps);
	insert(&ps, 2, 40);
	print(&ps);
	shanchu(&ps, 2);
	print(&ps);
}
int main()
{
	test();
	return 0;
}

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值