线性表工具
一、什么是线性表
1.定义:线性表是最基本、最简单、也是最常用的一种数据结构。
线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。(一般性)增加删除不会改变线性表的性质。
2.分类
二、线性表工具
1.问题与分析
问题导入:
数组也是一种线性表,那么有工具性吗?什么是工具性线性表?
那么需要怎么实现?
需求分析:
1.这个线性表应该具有一定的数据存储空间;
2.这个空间应该存在一系列有关这个空间的管理的数据;
例如:空间的最大空间(capacity),有效使用空间(count);
2.工具性基本实现
根据需求分析,我采用设计一个线性控制头:
typedef struct LINEAR {
USER_TYPE *data;.//用户需要的数据类型
int capacity; //最大空间
int count;//有效使用空间
}LINEAR;
再给用户一个自己输入的数据类型:
#ifndef _STICK_USERTYPE_H_
#define _STICK_USERTYPE_H_
typedef int USER_TYPE;
//这里可以根据用户个人要求修改其类型(任何类型都行,结构体更丰富)。暂定为int型
#endif
编程习惯,使用boolean值,增强代码可读性;
typedef unsigned char boolean;
#define TRUE 1
#define FALSE 0
#define NOT_FOUND -1
有了控制头和存储位置,接着就可以初始化了。注意有初始化,就必须有销毁。boolean返回值来知道是否成功。
//初始化线性表
boolean initLinear(LINEAR **head, int capacity) {
if(NULL != *head ) {
return FALSE;//如果控制头引进初始化过,返回
}
*head = (LINEAR *)calloc(sizeof(LINEAR), 1); //申请一个控制头
(*head)->data = (USER_TYPE *)calloc(sizeof(USER_TYPE), capacity); //申请一个用户自定义存储空间
(*head)->capacity = capacity;
(*head)->count = 0;
return TRUE;//初始化成功
}
//销毁线性表
void destoryLinear(LINEAR **head) {
if (NULL == head || NULL == *head) {
return;
}
free((*head)->data);//释放自定义数据存储空间
free(*head); //释放控制头空间
*head = NULL; //指向控制头的指针销毁
}
3.工具功能性实现
一个工具如果仅仅是使用,那么是不够的。必须的考虑到用户的使用需求。
例:用户想得到这个工具是空间大小和已经使用空间
//得到最大空间量
int getCapacity(const LINEAR *head) {
return head->capacity;
}
//得到有效使用空间量
int getCount(const LINEAR *head) {
return head->count;
}
例:空间是满还是空的?
boolean isLinearFull(const LINEAR *head) {
/*if (head->count >= head->capacity) {
return TRUE;
}
return FALSE;*/
return head->count >= head->capacity;
}
//判断线性表是否为空
boolean isLinearEmpty(const LINEAR *head) {
return head->count <= 0;
}
上面的四个例子都是一个线性表基本的属性。
接下来是功能的实现。面对一串数字,你想干啥?
1.可以修改某个值;
//修改线性表元素
boolean setElementAt (const LINEAR *head, int index, USER_TYPE data) {
if (NULL == head || index < 0 || index >= head->capacity) {
return FALSE;
}
head->data[index] = data; //修改
return TRUE;
}
2.可以指定位置插入某个值。追加就是最后的插入;
//追加线性表元素
boolean appendElementAt(LINEAR *head, USER_TYPE data) {
//追加是插入到最后的插入数据
return insertElementAt(head, head->count, data);
}
//插入线性表元素
boolean insertElementAt(LINEAR *head, int index, USER_TYPE data) {
int i;
if (NULL == head || isLinearFull(head)
|| index < 0 || index > head->count ) {
return FALSE;
}
for (i = head->count; i > index; i--) {
head->data[i] = head->data[i-1]; // 从最后一个元素开始,依次向后移动,空出插入位置空间
}
head->data[index] = data; //实现插入
++head->count; //有效元素增加
return TRUE;//插入成功+
}
3.可以通过值找位置,通过位置找值;
//得到指定位置的线性表元素
boolean getElemenAt(const LINEAR *head, int index, USER_TYPE *data) {
if (NULL == head || index < 0 || index > head->count) {
return FALSE;
}
*data = head->data[index]; //将指定指定位置的线性表元素复制下,可以用来展示
return TRUE;
}
//查找某一元素
int elementIndexOf(const LINEAR *head, USER_TYPE data,
boolean (*eq)(USER_TYPE, USER_TYPE)) {
int index;
if (NULL == head || isLinearEmpty(head)) {
return NOT_FOUND;
}
for (index = 0; index < head->count; index++) {
if (eq(data, head->data[index])) {
return index; //遍历已有的元素,找到返回其位置
}
}
return NOT_FOUND; // 没找到
}
4.可以删除某个值;
//清空线性表
void clearLinear(LINEAR *head) {
head->count = 0;
}
//删除线性表某一元素
boolean removeElementAt(LINEAR *head, int index, USER_TYPE *data) {
if (NULL == head || isLinearEmpty(head)
|| index < 0 || index >= head->count) {
return FALSE;
}
for (; index < head->count - 1; index++) {
head->data[index] = head->data[index + 1]; //从最后一个元素,依次向前移动一位
}
head->count--;//有效元素个数减1
return TRUE;
}
5.如果可以是不是还能排序。。。
等等,不过功能更依赖与用户反馈
4.一个重要思想–“消费未来”
这个线性表的本质是一个工具。一个通用性工具。用户可以自定义数据类型USER_TYPE,但是正是由于是自定义,我们无法控制用户数据类型。所以在通过值查找元素下标时出现一个尴尬现象。
我们暂定的为int型可以相互比较。但如果是结构体,字符串等等,我们无法用同一种比较规则去应对不同的数据类型。
所以说最好,这个比较类型是让用户自己定义一个比较函数。我们来使用这个比较规则。
例如:int型,可以直接比较
boolean equalse(USER_TYPE one, USER_TYPE other);
boolean equalse(USER_TYPE one, USER_TYPE other) {
return one == other;
}
所以你看到
int elementIndexOf(const LINEAR *head, USER_TYPE data,
boolean (*eq)(USER_TYPE, USER_TYPE))
boolean (*eq)(USER_TYPE, USER_TYPE)这个就是调用用户自己数据类型的比较法则
这个是通过指向函数的指针,这个强有力的工具实现的。
关于这个指针后面会说到
所以,如果用户想要如何排序的话,我们也可以运用用户排序法则进行排序。
三、总结
1.人和动物的区别:人会使用工具,少有动物会使用工具(猩猩,乌鸦会)。
再区别:人会根据需求制造工具,人还会携带工具。
制造、携带工具使得先人们从森林法则中脱颖而出。
2.使用工具的人可以被替代,更换。但是能够制造工具的人,少了一个,就是一个领域的崩溃。就像语言就是一个工具。
3.消费未来。
笔者水平有限,目前只能描述以上问题,如果有其他情况,可以留言,有错误,请指教,有继续优化的,请分享,谢谢!
完整代码如下:
userType.h代码:
#ifndef _STICK_USERTYPE_H_
#define _STICK_USERTYPE_H_
typedef int USER_TYPE;
//这里可以根据用户个人要求修改其类型。
#endif
linear.h 代码:
#ifndef _STICK_LINEAR_H_
#define _STICK_LINEAR_H_
#include "userType.h"
typedef struct LINEAR {
USER_TYPE *data;//用户需要的数据类型
int capacity; //最大空间
int count;//有效使用空间
}LINEAR;
typedef unsigned char boolean;
#define TRUE 1
#define FALSE 0
#define NOT_FOUND -1
boolean initLinear(LINEAR **head, int capacity);
void destoryLinear(LINEAR **head);
int getCapacity(const LINEAR *head);
int getCount(const LINEAR *head);
boolean isLinearFull(const LINEAR *head);
boolean isLinearEmpty(const LINEAR *head);
boolean setElementAt (const LINEAR *head, int index, USER_TYPE data);
boolean getElemenAt(const LINEAR *head, int index, USER_TYPE *data);
boolean appendElementAt(LINEAR *head, USER_TYPE data);
boolean insertElementAt(LINEAR *head, int index, USER_TYPE data);
int elementIndexOf(const LINEAR *head, USER_TYPE data,
boolean (*eq)(USER_TYPE, USER_TYPE));
void clearLinear(LINEAR *head);
boolean removeElementAt(LINEAR *head, int index, USER_TYPE *data);
#endif
linear.c代码:
#include <stdio.h>
#include <malloc.h>
#include "linear.h"
//初始化线性表
boolean initLinear(LINEAR **head, int capacity) {
if(NULL != *head ) {
return FALSE;//如果控制头引进初始化过,返回
}
*head = (LINEAR *)calloc(sizeof(LINEAR), 1); //申请一个控制头
(*head)->data = (USER_TYPE *)calloc(sizeof(USER_TYPE), capacity); //申请一个用户自定义存储空间
(*head)->capacity = capacity;
(*head)->count = 0;
return TRUE;//初始化成功
}
//销毁线性表
void destoryLinear(LINEAR **head) {
if (NULL == head || NULL == *head) {
return;
}
free((*head)->data);//释放自定义数据存储空间
free(*head); //释放控制头空间
*head = NULL; //指向控制头的指针销毁
}
//得到最大空间量
int getCapacity(const LINEAR *head) {
return head->capacity;
}
//得到有效使用空间量
int getCount(const LINEAR *head) {
return head->count;
}
//判断线性表是否满了
boolean isLinearFull(const LINEAR *head) {
/*if (head->count >= head->capacity) {
return TRUE;
}
return FALSE;*/
return head->count >= head->capacity;
}
//判断线性表是否为空
boolean isLinearEmpty(const LINEAR *head) {
return head->count <= 0;
}
//修改线性表元素
boolean setElementAt (const LINEAR *head, int index, USER_TYPE data) {
if (NULL == head || index < 0 || index >= head->capacity) {
return FALSE;
}
head->data[index] = data; //修改
return TRUE;
}
//得到指定位置的线性表元素
boolean getElemenAt(const LINEAR *head, int index, USER_TYPE *data) {
if (NULL == head || index < 0 || index > head->count) {
return FALSE;
}
*data = head->data[index]; //将指定指定位置的线性表元素复制下,可以用来展示
return TRUE;
}
//追加线性表元素
boolean appendElementAt(LINEAR *head, USER_TYPE data) {
//追加是插入到最后的插入数据
return insertElementAt(head, head->count, data);
}
//插入线性表元素
boolean insertElementAt(LINEAR *head, int index, USER_TYPE data) {
int i;
if (NULL == head || isLinearFull(head)
|| index < 0 || index > head->count ) {
return FALSE;
}
for (i = head->count; i > index; i--) {
head->data[i] = head->data[i-1]; // 从最后一个元素开始,依次向后移动,空出插入位置空间
}
head->data[index] = data; //实现插入
++head->count; //有效元素增加
return TRUE;//插入成功+
}
//查找某一元素
int elementIndexOf(const LINEAR *head, USER_TYPE data,
boolean (*eq)(USER_TYPE, USER_TYPE)) {
int index;
if (NULL == head || isLinearEmpty(head)) {
return NOT_FOUND;
}
for (index = 0; index < head->count; index++) {
if (eq(data, head->data[index])) {
return index; //遍历已有的元素,找到返回其位置
}
}
return NOT_FOUND; // 没找到
}
//清空线性表
void clearLinear(LINEAR *head) {
head->count = 0;
}
//删除线性表某一元素
boolean removeElementAt(LINEAR *head, int index, USER_TYPE *data) {
if (NULL == head || isLinearEmpty(head)
|| index < 0 || index >= head->count) {
return FALSE;
}
for (; index < head->count - 1; index++) {
head->data[index] = head->data[index + 1]; //从最后一个元素,依次向前移动一位
}
head->count--;//有效元素个数减1
return TRUE;
}
2020年02.18 家