前言
数据结构在两年前就学过了但以前都是走马观花,现在重拾起来,总结归纳,最后把各种结构和算法用c重写一遍。
线性表的定义
抽象数据类型定义
ADT 线性表(List)
Data
在数据元素的非空有限集中,存在唯一一个首元素(有唯一的后继)和唯一一个末尾元素(有唯一的前驱),其他中间元素(有唯一前驱和后继)。数据元素之间的关系是一对一的关系。
Operation
InitList(*L):初始化操作,建立一个空的线性表。
DestryList(*L);销毁线性表
ClearList(*L):线性表清空。
ListEmpty(L):若线性表为空,返回true,否则返回false。
ListLength(L):返回线性表L的元素个数。
GetElem(L,i,*e):将线性表L中第i个位置元素返回给e。
LocateElem(L,e):在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中的序列号;否则,返回0表示失败。
PriorElem(L,cur_e,&pre_e);若cur_e是L的元素且不是第一个元素则用per_e返回它的前驱
NextElem(L,cur_e,&next_e);若cur_e是L的元素,且不是最后一个元素,则用next_e返回它的后继
ListInsert(*L,i,e):在线性表的第i个位置插入元素e。
ListDelete(*L,i,*e):删除线性表L中的第i个元素,并用e返回其值
ListTranverse(L,visit());依次对L的每个元素调用函数visit(),一旦失败则操作失败
线性表的顺序存储结构
优点:
无需为表中元素之间的逻辑关系增加额外的存储空间,可以快速读取表中的某个位置的元素,时间复杂度O(1)。
缺点:
插入和删除需要移动大量的元素,当线性表的变化很大时候,很难确定线性表的存储空间。容易造成存储空间的碎片化
静态分配
用一组连续地址空间依次存储线性表的数据元素。
typedef struct s_list
{
ElemTpye data[MAXSIZE]; /* 数据元素 */
int length; /* 线性表当前长度 */
}S_List;
第a个元素和第a+1个元素地址关系: LOC(a+1)=LOC(a)+sizeof(ElemTpye );
第a个元素和第1个元素地址关系: LOC(a)=LOC(1)+sizeof(ElemTpye )*(i-1);
代码实现
#include<stdio.h>
#define MAXSIZE 100
#if MAXSIZE<=0 || MAXSIZE>100
#error error maxsize
#endif
typedef int ElemTpye;
typedef struct s_list
{
ElemTpye data[MAXSIZE]; /* 数据元素 */
int length; /* 线性表当前长度 */
}S_List;
typedef enum MyEnum
{
OK = 0,//操作成功
ERROR,
EMPTY,//空
NO_EMPTY,//非空
OVER_FLOW,//溢出
USE_UNDEFOP//使用未定义操作
}RESULT;
/*
@breaf 建立一个空的线性表
@param 线性表指针
@return RESULT
*/
RESULT InitList(S_List *L)
{
L->length = 0;
return OK;
}
/*
@breaf 销毁线性表
@param L[in]线性表指针
@return RESULT
*/
RESULT DestryList(S_List *L)
{
return USE_UNDEFOP;
}
/*
@breaf 清空线性表
@param L[in]线性表指针
@return RESULT
*/
RESULT ClearList(S_List *L)
{
return InitList(L);
}
/*
@breaf 判断线性表是否为空
@param L[in]线性表
@return RESULT
*/
RESULT ListEmpty(S_List L)
{
if (L.length == 0)
return OK;
return NO_EMPTY;
}
/*
@breaf 获得线性表长度
@param L[in]线性表
@return 线性表长度
*/
int ListLength(S_List L)
{
return L.length;
}
/*
@breaf 将线性表L中的第i个位置元素返回给e
@param L[in]线性表
@param i[in]位置
@param e[out]查找的元素
*/
RESULT GetElem(S_List L, int i, int *e)
{
if (ListEmpty(L) == OK)
return EMPTY;
if (L.length>MAXSIZE|| i<1 || i>L.length)
return OVER_FLOW;
*e = L.data[i - 1];
return OK;
}
/*
@breaf 在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中的序列号。
@param L[in]线性表
@param e[in]查找的元素
@return 序列号,-1为失败
*/
int LocateElem(S_List L, ElemTpye e)
{
if (ListEmpty(L) == OK)
return -1;
while (L.length--)
{
if (L.data[L.length] == e)
return L.length+1;
}
return -1;
}
/*
@breaf 若cur_e是L的元素且不是第一个元素则用per_e返回它的前驱
@param L[in]线性表
@param cur_e[in]查找的元素
@param pre_e[out]返回前驱的元素
@return RESULT
*/
RESULT PriorElem(S_List L, ElemTpye cur_e, ElemTpye *pre_e)
{
int i = LocateElem(L, cur_e);
if (i <= 1)
return ERROR;
*pre_e = L.data[i - 1-1];
return OK;
}
/*
@breaf 若cur_e是L的元素且不是最后一个元素则用per_e返回它的后继
@param L[in]线性表
@param cur_e[in]查找的元素
@param pre_e[out]返回后继的元素
@return RESULT
*/
RESULT NextElem(S_List L, ElemTpye cur_e, ElemTpye *pre_e)
{
int i = LocateElem(L, cur_e);
if (i <= 0 || i==L.length)
return ERROR;
*pre_e = L.data[i];
return OK;
}
/*
@breaf 在线性表的第i个位置插入元素e
@param L[in]线性表
@param i[in]序列号
@param e[in]插入的元素
@return RESULT
*/
RESULT ListInsert(S_List *L, int i, ElemTpye e)
{
int k;
if (ListEmpty(*L) == OK)
return EMPTY;
if (L->length+1>MAXSIZE || i<1 || i>L->length+1)/*插入后没有超出最大长度,插入位置无溢出*/
return OVER_FLOW;
if (i <= L->length) /* 若插入数据位置不在表尾 */
{
for (k = L->length - 1; k >= i - 1; k--) /* 将要插入位置之后的数据元素向后移动一位 */
L->data[k + 1] = L->data[k];
}
L->data[i - 1] = e; /* 将新元素插入 */
L->length++;
return OK;
}
/*
@breaf 删除线性表L中的第i个元素,并用e返回其值
@param L[in]线性表
@param i[in]序列号
@param e[out]删除的元素
@return RESULT
*/
RESULT ListDelete(S_List *L, int i, ElemTpye *e)
{
int k;
if (ListEmpty(*L) == OK)
return EMPTY;
if (L->length >MAXSIZE || i<1 || i>L->length )
return OVER_FLOW;
*e = L->data[i - 1];
if (i<L->length) /* 如果删除不是最后位置 */
{
for (k = i; k<L->length; k++)/* 将删除位置后继元素前移 */
L->data[k - 1] = L->data[k];
}
L->length--;
return OK;
}
/*
@breaf 依次对L的每个元素调用函数visit(),一旦失败则操作失败
@param L[in]线性表
@param visit[in]需要操作的函数指针
@return RESULT
*/
RESULT ListTranverse(S_List L, RESULT(* visit)(ElemTpye e))
{
int i = 0;
while (i != L.length)
{
if (visit(L.data[i++]) != OK)
return ERROR;
}
return OK;
}
动态分配
使用堆区动态分配线性表
typedef struct a_list
{
ElemTpye *data; //数据元素指针
int length; //当前长度
int maxsize; //存储容量
}A_List;
/*
@breaf 建立一个空的线性表
@param 线性表指针
@return RESULT
*/
RESULT InitList(A_List *L)
{
L->data = (ElemTpye *)malloc(sizeof(ElemTpye)*MAXSIZE);
if (!L->data)
return MALL_ERR;
L->length = 0;
L->maxsize = MAXSIZE;
return OK;
}
。。。。。。。。
总结
1,插入一个元素所需移动元素次数: n/2
2,删除一个元素所需移动元素次数:(n-1)/2
3,对于两个无序线性表合并,其时间复杂度为O(a.length * b.length)
4,对于两个有序线性表合并,其时间复杂度为O(a.length + b.length)
5,查找时间复杂度O(1)
6,插入删除的时间复杂度O(n)