数据结构C语言描述1(图文结合)--顺序表讲解,实现,表达式求值应用,考研可看

前言

  • 这个专栏将会用纯C实现常用的数据结构和简单的算法;
  • C基础即可跟着学习,代码均可运行;
  • 准备考研的也可跟着写,个人感觉,如果时间充裕,手写一遍比看书、刷题管用很多,这也是本人采用纯C语言实现的原因之一
  • 欢迎收藏 + 关注,本人将会持续更新。

顺序表基本概念

什么是线性表?

线性表是一组具有相同特征 元素的有序序列 ,记作:(a~1~, a~2~, ..., a~i-1~, a~!~, a~i+1~, ..., a~n~),当然这有点官方了,但是核心就两个特征:相同特征、有序序列;但我从《大话数据结构中》中看到了另外一个描述,我感觉听通俗易懂的:

  • 线性表的数据对象集合{a~1~, a~2~, ..., a~i-1~, a~!~, a~i+1~, ..., a~n~},每个元素的类型均为DataType。其中,除了第一个元素a~1~ 外,每一个元素有且只有直接前驱元素 ,除了最后一个元素a~n~ 外,每一个元素有且只有一个直接后驱元素。且数据元素之间关系是一一对应的。

线性表相关概念

  • 直接前驱元素:a~i-1~ 领先于a~i~ ,则称a~i-1~ 是a~i~ 的直接前驱元素;
  • 直接后驱元素:a~i+1~ 领先于a~i~ ,则称a~i+1~ 是a~i~ 的直接后驱元素;
  • 前驱元素:a~1~,a~2~,a~3~,......a~i-1~, 都是a~i~ 的前驱元素;
  • 后驱元素:a~i+1~,a~i+2~,a~i+3~,......a~i+n~, 都是a~i~ 的前驱元素;
  • 线性表长度:线性表中包含所有元素的个数;
  • 空线性表:不包含任何元素的线性表;
  • 位序:元素在线性表第几个位置

什么是顺序表?

顺序表就是用一段连续的内存空间依次存储数据,顺序表的存储大概图如下(参考《大话数据结构》):

顺序表特点

  • 存储数据内存地址连续
  • 可以支持下标访问
  • 删除、添加中间位置元素麻烦
  • 数据容量固定

顺序表的抽象设计

把顺序表存储的东西,看成是一个集合中存储,从**抽象数据类型(ADT)**角度来看,这个我们需要数据的实现可以有一下几个基本操作:

  • 创建、初始化循序表
  • 插入元素
  • 删除元素
  • 查找元素
  • 判空操作
  • 判满操作

总之一句话:增删改查外加排序。

顺序表程序实现

封装顺序表

  • 对数据类型进行取别名,这样是代码更具有泛化性;
  • 采用结构体进行封装,可以更好描述顺序表
c 复制代码
typedef int DataType;

typedef struct SeqList {
	DataType* data;     // 储存数据
	size_t size;	   // 当下储存了多少数
	size_t capacity;   // 当下能够储存数据的最大容量
}SeqList;

创建顺序表

利用calloc函数,他会在申请内存的时候自动初始化为空,这里具体他做了一下几件事情:

  • 事情一块内存空间,大小为sizeof(Seqlist)
  • data = NULL
  • size = 0
  • capacity = 0
c 复制代码
SeqList* create_seqlist()
{
	SeqList* new_data = (SeqList*)calloc(1, sizeof(SeqList));
	if (!new_data) {
		printf("顺序表创建失败\n");
		return new_data;
	}

	return new_data;
}

插入元素

这里是向后插入,扩容规则如下(自己定义的,不是一定的):

  • 如果capacity为0,则赋值为10,当作初始化
  • 否则如果容量不够,则按照两倍扩容

向后插入图示如下(也可以有序插入,这个需要按照具体场景):

c 复制代码
void insert_seqlist(SeqList* list, DataType data)
{
	assert(list);   // list为空,则断言

	// 是否需要扩容
	if (list->size >= list->capacity) {
		// 2倍扩容
		if (list->capacity == 0) {
			list->capacity = 10;
		}
		else {
			list->capacity = 2 * list->capacity;
		}
		// 扩容
		DataType* new_data = (DataType*)realloc(list->data, list->capacity * sizeof(DataType));
		assert(new_data);  // 判断内存是否申请失败
		list->data = new_data;
	}

	// 直接在后面插入,但是也可以有序插入,这个要根据业务
	list->data[list->size++] = data;
}

查找--按值查找

  • 目的:通过 找到返回他在顺序表中的存储的下标,规则:如果改顺序表中不存在该元素,则返回-1。
  • 方法:遍历,从第一个遍历到最后,时间复杂度为O(n)
c 复制代码
int search_value_seqlist(SeqList* list, DataType data)
{
	// 防止空表
	assert(list);

	for (size_t i = 0; i < list->size; i++) {
		if (list->data[i] == data) {
			return i;
		}
	}
		
	return -1;  
}

查找--按位查找

  • 目的:通过下标 找到返回他在顺序表中的存储的元素,规则:如果传递位有误,则返回-1。
c 复制代码
DataType search_position_seqlist(SeqList* list, size_t k)
{
	assert(list);

	if (k >= list->size || k < 0) {
		return -1;
	}

	return list->data[k];
}

删除--通过下标

  • 目的:删除下标为k的元素
  • 数组的删除为伪删除,不是真正的删除,将删除的元素移动到最后而已,具体过程图示如下:
c 复制代码
void erase_seqlist_index(SeqList* list, size_t index)
{
	assert(list);

	if (index < 0 || index >= list->size) {
		printf("无效位置\n");
		return;
	}

	for (int i = index; i < list->size - 1; i++) {
		list->data[i] = list->data[i + 1];
	}

	list->size--;
}

删除--通过指定元素

  • 删除第一个符合元素,具体过程如图示(和上图一样):
c 复制代码
void erase_seqlist_data(SeqList* list, DataType data)
{
	assert(list);

	for (size_t i = 0; i < list->size; i++) {
		if (list->data[i] == data) {
			erase_seqlist_index(list, i);   // 原理一样
		}
	}
}
  • 删除全部符合元素删除,采用双指针算法,最快理解就是通过图示,如下:
c 复制代码
void erase_seqlist_all(SeqList* list, DataType data)
{
	assert(list);

	// 第一种方法:暴力,这里不写

	// 第二种方法: 双指针
	size_t slow = 0, fast = 0;
	int num = 0;
	for (; fast < list->size; fast++) {
		if (list->data[fast] == data) {
			num++;
			continue;
		}
		else {
			list->data[slow++] = list->data[fast];
		}
	}
	list->size -= num;
}

判断是否为空

判断链表是不是没有存储元素,但是要注意 ,没有存储元素只是代表SeqList->size==0,但不代表数组里没有储存元素,没有内容空间,具体为什么,可以想一想????

c 复制代码
bool emtpy_seqlist(SeqList* list) {
	return list->size == 0;
}

查询顺序表大小

查找当前顺序表中储存元素的数量

c 复制代码
int size_seqlist(SeqList* list) {
	return list->size;
}

销毁

释放顺序表,将申请的内存空间全部释放:

  • 第一步:释放数据空间
  • 第二步:释放创建的顺序表

注意 :释放完毕后,指针需要赋值为NULL

c 复制代码
void destory_seqlist(SeqList* list)
{
	assert(list);

	// 释放数据
	if (list->data != NULL) {
		free(list->data);
		list->data = NULL;
	}
	// 释放链表
	free(list);
	list = NULL;
}

总代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

// 增删改查外加排序

typedef int DataType;

typedef struct SeqList {
	DataType* data;
	size_t size;
	size_t capacity;
}SeqList;

// 创建顺序表
SeqList* create_seqlist();

// 插入
void insert_seqlist(SeqList* list, DataType data);

// 查找--按值查找,找得到则返回下标(0开始),找不到就返回-1
int search_value_seqlist(SeqList* list, DataType data);
// 查找--按位查找,越界:-1,正常:下标
DataType search_position_seqlist(SeqList* list, size_t k);

// 删除--通过下标(位置), index从0开始
void erase_seqlist_index(SeqList* list, size_t index);
// 删除--通过指定元素
void erase_seqlist_data(SeqList* list, DataType data);  // 删除一个
void erase_seqlist_all(SeqList* list, DataType data);   // 删除全部

// 万金油函数
bool emtpy_seqlist(SeqList* list) {
	return list->size == 0;
}
int size_seqlist(SeqList* list) {
	return list->size;
}

// 打印
void print_seqlist(SeqList* list);

// 销毁
void destory_seqlist(SeqList* list);

int main()
{
	SeqList* list = create_seqlist();

	for (int i = 0; i < 15; i++) {
		insert_seqlist(list, i);
	}
	insert_seqlist(list, 8);
	printf("插入测试:\n");
	print_seqlist(list);
	printf("查找测试:\n");
	DataType value = search_value_seqlist(list, 8);
	int pos = search_position_seqlist(list, 2);
	printf("from_value: %d, from_pos: %d\n", value, pos);
	printf("删除测试: \n");
	erase_seqlist_index(list, 5);  
	print_seqlist(list);
	erase_seqlist_data(list, 9);
	print_seqlist(list);
	erase_seqlist_all(list, 8);
	print_seqlist(list);
	printf("万金油函数: \n");
	printf("是否为空:%d\n", emtpy_seqlist(list));
	printf("大小: %d\n", size_seqlist(list));
	// 销毁
	destory_seqlist(list);

	return 0;
}

SeqList* create_seqlist()
{
	SeqList* new_data = (SeqList*)calloc(1, sizeof(SeqList));
	if (!new_data) {
		printf("顺序表创建失败\n");
		return new_data;
	}

	return new_data;
}

void insert_seqlist(SeqList* list, DataType data)
{
	assert(list);   // list为空,则断言

	// 是否需要扩容
	if (list->size >= list->capacity) {
		// 2倍扩容
		if (list->capacity == 0) {
			list->capacity = 10;
		}
		else {
			list->capacity = 2 * list->capacity;
		}
		// 扩容
		DataType* new_data = (DataType*)realloc(list->data, list->capacity * sizeof(DataType));
		assert(new_data);  // 判断内存是否申请失败
		list->data = new_data;
	}

	// 直接在后面插入,但是也可以有序插入,这个要根据业务
	list->data[list->size++] = data;
}

void print_seqlist(SeqList* list)
{
	for (size_t i = 0; i < list->size; i++) {
		printf("%d ", list->data[i]);
	}
	puts("\n");
}

int search_value_seqlist(SeqList* list, DataType data)
{
	// 防止空表
	assert(list);

	for (size_t i = 0; i < list->size; i++) {
		if (list->data[i] == data) {
			return i;
		}
	}
		
	return -1;  
}

DataType search_position_seqlist(SeqList* list, size_t k)
{
	assert(list);

	if (k >= list->size || k < 0) {
		return -1;
	}

	return list->data[k];
}

void erase_seqlist_index(SeqList* list, size_t index)
{
	assert(list);

	if (index < 0 || index >= list->size) {
		printf("无效位置\n");
		return;
	}

	for (int i = index; i < list->size - 1; i++) {
		list->data[i] = list->data[i + 1];
	}

	list->size--;
}

void erase_seqlist_data(SeqList* list, DataType data)
{
	assert(list);

	for (size_t i = 0; i < list->size; i++) {
		if (list->data[i] == data) {
			erase_seqlist_index(list, i);   // 原理一样
		}
	}
}

void erase_seqlist_all(SeqList* list, DataType data)
{
	assert(list);

	// 第一种方法:暴力,这里不写

	// 第二种方法: 双指针
	size_t slow = 0, fast = 0;
	int num = 0;
	for (; fast < list->size; fast++) {
		if (list->data[fast] == data) {
			num++;
			continue;
		}
		else {
			list->data[slow++] = list->data[fast];
		}
	}
	list->size -= num;
}

void destory_seqlist(SeqList* list)
{
	assert(list);

	// 释放数据
	if (list->data != NULL) {
		free(list->data);
		list->data = NULL;
	}
	// 释放链表
	free(list);
	list = NULL;
}

顺序表案例

顺序表应用常见有很多, 比如说:储存数据(管理系统等),这里我们做一个多项式合并 ,多项式次数存储有序,次数从高到低。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define max_num 100

typedef struct Node {
	int data;   // 底数
	int exp;    // 指数
}Node;

typedef struct SeqList {
	Node data[max_num];
	int count;
}SeqList;

void add(SeqList* A, SeqList* B, SeqList* C)
{
	if (A == NULL || B == NULL || C == NULL) {
		return;
	}

	int i = 0, j = 0, k = 0;
	while (i < A->count && j < B->count) {
		// 次数对比
		if (A->data[i].exp > B->data[j].exp) {
			C->data[k++] = A->data[i++];
		}
		else if (A->data[i].exp < B->data[j].exp) {
			C->data[k++] = B->data[j++];
		}
		else {  // 次数相同
			Node t;
			if (A->data[i].data + B->data[j].data == 0) {
				continue;
			}
			t.exp = A->data[i].exp;
			t.data = A->data[i++].data + B->data[j++].data;
			C->data[k++] = t;
		}
		C->count++;
	}

	while (i < A->count) {
		C->data[k++] = A->data[i++];
	}

	while (j < B->count) {
		C->data[k++] = B->data[j++];
	}

}

int main()
{
	// y = 3 * x^2 + 5 * x + 2
	SeqList A = { {{3, 2},{5,1},{2, 0}}, 3 };
	// y = -1 * x^3 + 4 * x^2 + 4
	SeqList B = { {{-1,3},{4, 2},{4, 0}}, 3 };

	SeqList C = { 0 };  // 答案

	add(&A, &B, &C);

	int i = 0;
	while (i < C.count) {
		if (C.data[i].exp == 0) {
			printf("%d ", C.data[i].data);
		}
		else {
			printf("%d * x^%d ", C.data[i].data, C.data[i].exp);
		}

		if (i != C.count - 1) {
			printf("+ ");
		}
		i++;
	}


	return 0;
}

// 输出:
/*
-1 * x^3 + 7 * x^2 + 5 * x^1 + 6
*/
相关推荐
cgsthtm21 分钟前
Visual studio Debug调试webservice 使用ip代替localhostt配置IIS Express
android studio·visual studio·webservice·真机调试·iis express
云和数据.ChenGuang21 分钟前
《XML》教案 第1章 学习XML基础
xml·java·学习
王·小白攻城狮·不是那么帅的哥·天文29 分钟前
Java操作Xml
xml·java
发飙的蜗牛'39 分钟前
23种设计模式
android·java·设计模式
music0ant1 小时前
Idean 处理一个项目引用另外一个项目jar 但jar版本低的问题
java·pycharm·jar
小王爱吃月亮糖1 小时前
C++进阶-1-单继承、多继承、虚继承
开发语言·c++·笔记·学习·visual studio
陈大爷(有低保)1 小时前
logback日志控制台打印与写入文件
java
繁川1 小时前
深入理解Spring AOP
java·后端·spring
Am心若依旧4091 小时前
[c++进阶(三)]单例模式及特殊类的设计
java·c++·单例模式
小王爱吃月亮糖1 小时前
补充--C++的项目结构和管理
数据结构·c++·笔记·学习