数据结构概述
Q1.什么是数据结构?
A要想说清楚数据结构,先搞清楚几个更基本的概念:数据、数据对象、数据元素、数据项。
比如:电话本数据对象中,数据元素是每个人的记录;一条记录中的姓名是数据项。
数据结构官方定义:存在一定逻辑关系的数据元素的集合。比如:数组这种数据结构每个元素都只有前后两个邻居(首尾各一个)
Q2 为什么需要学习数据结构?
A 个人观点:数据结构是程序设计发展的必然产物。就像围棋中应该有基本的棋路一样。如果把用程序设计解决一问题比作用积木搭建一座城堡。数据结构就好像是城门,柱子等基本功能模块。
师曰:程序 = 数据结构 + 算法。比如要想查询某人的电话号码。表示和存储电话号码的所有信息就是 数据结构要做的事;如何更方便的查询属于算法设计。
Q3 常用的数据结构有哪些?
A无规律:集合。
一对一:线性表;
一对多:树
多对多:图
Q4 数据的存储结构有哪些?
A 所有的逻辑结构都有顺序存储和链式存储两种。比如有序存储的线性表叫做 有序表;连式存储的线性表叫链表。
Q5 怎样学习数据结构?
A 数据结构由一个四元组表示:Data_Structure = (D, L, S, O)
即:数据元素、逻辑结构、存储结构、操作。所以学习每一种数据结构都可以遵循这样的顺序。
*/
线性表
Q1 线性表的作用?
A 任何涉及到数据的知识都有的结构。
在实际应用中,线性表都是以栈、队列、字符串、数组等特殊线性表的形式来使用的。由于这些特殊线性表都具有各自的特性,
因此,掌握这些特殊线性表的特性,对于数据运算的可靠性和提高操作效率都是至关重要的。
Q2 怎么实现用线性表存储数据?
A 分三步走:定义以明确线性表的基本要素;初始化一个空表;向空表中插入元素
/*
*存储结构定义
*/
typedef struct{
Elemtype* elem; //存储空间基址
int length; //当前长度
int size; //当前分配的存储空间的大小
}SqList;
//自定义了一种结构体类型,名字叫SqList,基本构成有 存储空间基址,用于定位;当前长度(元素个数);当前分配的内存大小.意味着当线性表变化(比如插入元素)时,这三个要素都需要考虑。
/初始化一个空的线性表/
Status InitList(SqList *L)
{
L->elem = (Elemtype *)malloc(INIT_SIZE * sizeof(Elemtype));//动态分配一块内存,返回值是空类型
// void *malloc(size_t size);
//malloc()功能:申请一块连续的动态内存;
//参数:内存的大小;
//返回值:分配的内存区域的地址,void*类型表示未确定类型的指针,可以强制转换为int*等任意类型
if (!L->elem)
{
return ERROR;
}
else
{
L->length = 0;
L->size = INIT_SIZE;
return OK;
}
}
/向空表中插入元素/
Status InsertElem (Elemtype e, int i, SqList *L)
{
if (i < 1 || i > L->length + 1)//异常1:i(插入位置)给值不合适的情况
{return ERROR;}
//检查空间够不够用
if (L->length > = L->size)//之前分配的存储空间不够怎么办
{
Elemtype* new;
new = (Elemtype*)realloc (L->elem, (L->size + INCREMENT_SIZE) * sizeof(Elemtype));
if (!new)//异常2
{return ERROR;}
L->elem = new;
L->size += INCREMENT_SIZE;
}
//腾空
for (int j = i-1; j < L->length; j++)
{
L->elem[j + 1] = L->elem[j];
}
//插入
L->elem[i - 1] = e;
L->length++;
return OK;
}
Q3 还有哪些常用基本操作
A增删改查排序
Q4 应用举例
eg1 有两个线性表LA LB.求A并B,并用线性表LA表示。
SqList merge (SqList* LA, SqList* LB)
{
for (int i = 1; i < LB->length + 1; i++)
{
//判断LB中的元素是否在LA中
for (int j = 1; j < LA->length + 1; j++)
{
if (LA->elem[j-1]==LB->elem[i-1])
{break;}
}
if (j == LA->length + 1)//不在的话插入到LA的末尾
{
InsertElem (Elemtype LB-elem[i-1], LA->length + 1, SqList *LA);
}
}
return LA;
}
线性表的顺序表示和实现(可运行代码)
线性表的顺序表示指的是用物理上的一段连续的地址来存储数据元素,如下图所示。如果第一个元素的在内存上的地址为a1,每个元素占用的空间是l,那么第n个元素的地址就是a1+(n-1) x l。
//线性表的顺序表示、实现和操作
#include
#include
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INIT_SIZE 10 //初始化列表空间
#define INCREMENT_SIZE 5 //分配增量
typedef int Status;
typedef int Elemtype;
/*
*存储结构定义
*/
typedef struct{
Elemtype* elem; //存储空间基址
int length; //当前长度
int size; //当前分配的存储空间的大小
}SqList;
/*初始化一个空的线性表*/
Status InitList(SqList *L)
{
L->elem = (Elemtype *)malloc(INIT_SIZE * sizeof(Elemtype));//动态分配一块内存,返回值是空类型
//malloc()功能:申请一块连续的动态内存;参数:内存的大小;返回值:分配的内存区域的地址,void*类型表示未确定类型的指针,可以强制转换为int*等任意类型
if (!L->elem)
{
return ERROR;
}
L->length = 0;
L->size = INIT_SIZE;
return OK;
}
/*销毁线性表*/
Status DestroyList(SqList *L)
{
free(L->elem);
L->length = 0;
L->size = 0;
return OK;
}
/*清空线性表*/
Status ClearList(SqList *L)
{
L->length = 0;
return OK;
}
/*判断线性表是否为空*/
Status isEmpty(const SqList L)
{
if (L.length == 0)
{
return TRUE;
}
else
{
return FALSE;
}
}
/*获取长度*/
Status getLength(const SqList L)
{
return L.length;
}
/*根据位置获取元素*/
Status GetElem(const SqList L, int i, Elemtype *e)
{
if (i < 1 || i > L.length)
{
return ERROR;
}
else{
*e = L.elem[i - 1];
return OK;
}
}
/*比较两个元素是否相等*/
Status compare(Elemtype e1, Elemtype e2)
{
if (e1 == e2)
{
return 0;
}
else if (e1 < e2)
{
return -1;
}
else
{
return 1;
}
}
/*查找元素*/
Status FindElem(const SqList L, Elemtype e, Status (*compare)(Elemtype, Elemtype))
//(*compare)是函数指针吗?是的。函数指针是指向函数的指针。一般用于函数调用和用作形参。
{
int i;
for (i = 0; i < L.length; i++)
{
if (!(*compare)(L.elem[i], e))//如果相等的话,返回在线性表中的位置。
{
return i + 1;
}
}
if (i >= L.length)
{
return ERROR;
}
}
//TODO查找前驱元素
Status PreElem(const SqList L, Elemtype cur_e, Elemtype* pre_e)
{
int i;
for (i = 0; i < L.length; ++i)
{
if (cur_e == L.elem[i])
{
if (i != 0)
{
*pre_e = L.elem[i - 1];
return OK;
}
else
{
return ERROR;
}
}
}
if (i >= L.length)
{
return ERROR;
}
}
//查找后继元素
Status NextElem(const SqList L, Elemtype cur_e, Elemtype* next_e)
{
int i;
for (i = 0; i < L.length - 1; ++i)
{
if (cur_e == L.elem[i])
{
*next_e = L.elem[i + 1];
return OK;
}
}
if (i == L.length-1)
{
return ERROR;
}
}
//插入元素;变长,给空, 插入
Status InsertElem(SqList *L, int i, Elemtype e)//这个i是线性表的位置索引,不是内存的,所以
{
Elemtype*new;
if (i < 1 || i > L->length + 1)
{
return ERROR;
}
if (L->length >= L->size)//当前空间不足
{
new = (Elemtype*)realloc (L->elem, (L->size + INCREMENT_SIZE) * sizeof(Elemtype));
//动态内存调整函数extern void *realloc(void *men_address, unsigned int newsize);
/*功能:先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有的数据从头到尾拷贝到新分配的内存区域,而后释放原来的(自动释放,不需要free),同时返回新分配的内存区域的首地址。即重新分配存储器快的地址;
参数:新区域的首地址和总大小;
返回值:新区域的首地址;*/
if (!new)
{
return ERROR;
}
L->elem = new;
L->size += INCREMENT_SIZE;
}
//找空
Elemtype*p = &L->elem[i - 1];
Elemtype*q = &L->elem[L->length - 1];
for (; q >= p; q--)
{
*(q + 1) = *q;
}
//插入
*p = e;
++L->length;
return OK;
}
//删除元素并返回其值
Status DeleteElem(SqList *L, int i, Elemtype *e)
{
if (i < 1 || i > L->length)
{
return ERROR;
}
Elemtype *p = &L->elem[i - 1];
*e = *p;
for (; p < &L->elem[L->length]; p++)
{
*(p) = *(p + 1);
}
--L->length;
return OK;
}
//访问元素
void visit(Elemtype e)
{
printf("%d", e);
}
//遍历线性表
Status TraverseList(const SqList L, void (*visit)(Elemtype))
{
int i;
for (i = 0; i < L.length; i++)
{
visit (L.elem[i]);
}
return OK;
}
//主函数测试
int main()
{
SqList L;
if (InitList(&L))
{
Elemtype e;
printf("init_success\n");
int i;
for (i = 0; i < 10; i++)
{
InsertElem(&L, i + 1, i);
}
printf("length is %d\n", getLength(L));
if (GetElem(L, 1, &e))
{
printf ("The first element is %d\n", e);
}
else
{
printf("element is not exist\n");
}
if (isEmpty(L))
{
printf("list is empty\n");
}
else
{
printf("List is not empty\n");
}
printf("The 5 at %d\n", FindElem(L, 5, *compare));
PreElem(L, 6, &e);
printf("The 6's previous element is %d\n", e);
NextElem(L, 6, &e);
printf("The 6's next element is %d\n", e);
printf("list:");
TraverseList(L,visit);
if (DestroyList(&L))
{
printf("\ndestroy_success\n");
}
}
}