1. 数据结构的基础
首先需要明确几个名词,明确数据结构研究的是什么?
要知道的名词:数据对象,数据元素,数据项。
struct _MyTeacher //一种数据类型
{
char name[32];
char tile[32];
int age;
char addr[128];
};
int main21()
{
struct _MyTeacher t1; //数据元素
struct _MyTeacher tArray[30]; //数据对象
memset(&t1, 0, sizeof(t1));
strcpy(t1.name, "name"); //数据项
strcpy(t1.addr, "addr"); //数据项
strcpy(t1.tile, "addr"); //数据项
t1.age = 1;
}
数据结构研究:数据对象中数据元素的关系。
这些关系包括:点对点,点对多,多对多,无联系,索引。
因为学过STL,所以喜欢将数据对象直接当成容器思考,所以接下来就使用容器和节点来描述数据对象和数据元素。
2. 线性表顺序存储结构的设计
(1)容器的设计
线性表:说明节点间的关系是点对点的。
顺序表:说明节点间在物理上是一个接一个存储的。
所以使用数组作为容器, SeqListNode node[?];
数组是确定大小的,那么应该设计数组为多大呢?
由于我们写的数据结构是用于调用,只有在调用时才知道应该多大,
所以这个数组应该是动态分配的, SeqListNode *node;
接下来的问题是 SeqListNode 应该是什么类型呢?
我们希望这个库可以用于任何类型,所以代码需要和具体类型解耦和,
因此数组上存储的不是具体类型,而是指针。
所以 这个数组是 unsigned int *node;
然后使用数组作为就需要两个重要的属性,capacity和length
所以,线性表顺序存储的容器应该是:
typedef struct _tag_SeqList{
unsigned int *node;(32位编译器适用,考虑64位就使用int **,或unsigned long *)
int length;
int capacity;
}TSeqList;
(2)对外封装
我们不希望这个容器的具体被调用者知道,所以对外的类型应该是:
typedef void SeqList;
typedef void SeqListNode;
接下来是对外的声明:
SeqList* SeqList_Create(int capacity);
void SeqList_Destroy(SeqList* list);
void SeqList_Clear(SeqList* list);
int SeqList_Length(SeqList* list);
int SeqList_Capacity(SeqList* list);
int SeqList_Insert(SeqList* list, SeqListNode* node, int pos);
SeqListNode* SeqList_Get(SeqList* list, int pos);
SeqListNode* SeqList_Delete(SeqList* list, int pos);
void *的指针就是封装和解耦和的关键。
(3)函数实现
#include
#include
#include
#include "seqlist.h"
typedef struct _tag_seqlist
{
unsigned int *node;
int length;
int capacity;
}TSeqList;
SeqList* SeqList_Create(int capacity)
{
TSeqList *tlist;
if(capacity <= 0)
{
printf("(capacity <= 0) err\n");
return NULL;
}
tlist = (TSeqList *)malloc(sizeof(TSeqList));
if(tlist == NULL)
{
printf("(tlist == NULL) err\n");
return NULL;
}
memset(tlist, 0, sizeof(TSeqList));
tlist->node = (unsigned int *)malloc(sizeof(unsigned int)*capacity);
if(tlist->node == NULL)
{
printf("tlist->node == NULL err\n");
free(tlist);
return NULL;
}
memset(tlist->node, 0, sizeof(unsigned int)*capacity);
tlist->length = 0;
tlist->capacity = capacity;
return tlist;
}
void SeqList_Destroy(SeqList *list)
{
TSeqList *tlist;
if(NULL == list)
{
printf("(NULL == list) err\n");
return;
}
tlist = (TSeqList *)list;
if(NULL == tlist->node)
{
free(tlist); tlist = NULL;
return;
}
free(tlist->node);
free(tlist);
tlist = NULL;
return ;
}
void SeqList_Clear(SeqList *list)
{
TSeqList *tlist;
if(NULL == list)
{
printf("(NULL == list) err\n");
return;
}
tlist = (TSeqList *)list;
tlist->length = 0;
return;
}
int SeqList_Length(SeqList *list)
{
TSeqList *tlist;
if(NULL == list)
{
printf("(NULL == list) err\n");
return -1;
}
tlist = (TSeqList *)list;
return tlist->length;
}
int SeqList_Capacity(SeqList *list)
{
TSeqList *tlist;
if(NULL == list)
{
printf("(NULL == list) err\n");
return -1;
}
tlist = (TSeqList *)list;
return tlist->capacity;
}
int SeqList_Insert(SeqList *list, SeqListNode *node, int pos)
{
TSeqList *tlist;
int ret, i;
if(NULL == list || NULL == node || pos < 0)
{
ret = -1;
printf("(NULL == list || NULL == node || pos < 0) err\n");
return ret;
}
tlist = (TSeqList *)list;
if(tlist->length >= tlist->capacity)
{
ret = -1;
printf("(tlist->length >= tlist->capacity) err\n");
return ret;
}
if(pos > tlist->length)
pos = tlist->length;
for(i = tlist->length; i > pos; i--)
tlist->node[i] = tlist->node[i-1];
tlist->node[pos] = (unsigned int)node;
tlist->length++;
return 0;
}
SeqListNode* SeqList_Delete(SeqList *list, int pos)
{
TSeqList *tlist;
SeqListNode *node;
int i;
if(NULL == list || pos < 0)
{
printf("(NULL == list || pos < 0) err\n");
return NULL;
}
tlist = (TSeqList *)list;
if(tlist->length <= 0)
{
printf("(tlist->length <= 0) err\n");
return NULL;
}
if(pos >= tlist->length)
pos = tlist->length -1;
node = (SeqListNode *)tlist->node[pos];
for(i = pos; i < tlist->length -1; i++)
tlist->node[i] = tlist->node[i+1];
tlist->length--;
return node;
}
SeqListNode* SeqList_Get(SeqList *list, int pos)
{
TSeqList *tlist;
SeqListNode *node;
if(NULL == list || pos < 0)
{
printf("(NULL == list || pos < 0) err\n");
return NULL;
}
tlist = (TSeqList *)list;
if(pos >= tlist->length)
{
printf("(pos >= tlist->length) err\n");
return NULL;
}
node = (SeqListNode *)tlist->node[pos];
return node;
}
通过强制转换指针类型实现解封装和解耦合就是代码的精华了。
指针不愧为C的最强利刃。
接下来做个测试:
#include
#include
#include
#include "seqlist.h"
typedef struct _Teacher
{
int age;
char name[64];
}Teacher;
typedef struct _Teacher2
{
int age;
char name[64];
}Teacher2;
typedef struct _Teacher3
{
int age;
char name[64];
int age3;
}Teacher3;
void main()
{
int ret = 0, i = 0;
SeqList* list = NULL;
Teacher t1, t2, t3, t4,t5;
t1.age = 31;
t2.age = 32;
t3.age = 33;
t4.age = 34;
t5.age = 35;
list = SeqList_Create(10);
if (list == NULL)
{
printf("func SeqList_Create() ret :%d \n", ret);
return ;
}
ret = SeqList_Insert(list, (SeqListNode*) &t1, 0); //头插法
ret = SeqList_Insert(list, (SeqListNode*) &t2, 0); //头插法
ret = SeqList_Insert(list, (SeqListNode*) &t3, 0); //头插法
ret = SeqList_Insert(list, (SeqListNode*) &t4, 0); //头插法
ret = SeqList_Insert(list, (SeqListNode*) &t5, 0); //头插法
//遍历
for (i=0; iage:%d ", tmp->age);
}
//删除链表中的节点
while( SeqList_Length(list) > 0 )
{
SeqList_Delete(list, 0);
}
system("pause");
return ;
}
牛逼吧,C实现了C++模板的效果,相对于C++借助编译器多编译几份代码的原理,C更加简洁高效。
想起老师的话,永远不要轻易说掌握了C语言。
3.总结
线性表顺序存储的业务逻辑简单,但对于理解什么是数据结构很有帮助,同时C实现封装和业务逻辑与具体类型的解耦和很值得学习。