数据结构
文章目录
1、顺序表
1.1 顺序表的结构定义
#define LIST_INIT_SIZE 100 //线性表存储空间的初始分配量
#define LISTINCREMENT 10 //线性表存储空间的分配增量
typedef struct {
ElemType *elem; //存储空间基地址
int length; //当前长度
int listsize; //当前分配的存储容量(以sizeof(ElemType)为单位)
} SqList;
SqList L;
1.2 顺序表基本操作的实现
1.2.1 初始化操作
status InitList_Sq(SqList &L) {
//构造一个空的线性表L
L.elem = (ElemType *)malloc(LIST_INIT_SIZE * sizeof(ElemType));
if(!L.elem)
exit(OVERFLOW); //分配失败
L.length = 0; //空表长度为0
L.listsize = LIST_INIT_SIZE; //初始存储容量
return OK;
} //InitList_Sq
时间复杂度: ◯ ( 1 ) \bigcirc(1) ◯(1)
1.2.2 取值
status GetElem(SqList L,int i,ElemType &e) {
//若i的值小于1或大于表长,则返回ERROR;否则用e返回第i个元素的值,并返回OK
if(i < 1 || i > L.length)
return ERROR;
e = L.elem[i - 1];
return OK;
} //GetElem
时间复杂度: ◯ ( 1 ) \bigcirc (1) ◯(1)
1.2.3 查找操作
int LocateElem_Sq(SqList L,ElemType e) {
//在顺序表L中查找第i个值与e相等的元素的位序,若查找到,返回其在L中的位序,否则返回-1
for(int i = 0;i < L.length;i++){
if(e == L.elem[i])
return i + 1;
}
return -1;
} //LocateElem_Sq
时间复杂度: ◯ ( n ) \bigcirc(n) ◯(n)
1.2.4 插入操作
status ListInsert_Sq(SqList &L,int i,ElemType e) {
//在顺序表L的第i个位置之前插入新的元素e
//i的合法值为 1 <= i <= Listlength(L) + 1
if(i < 1 || i > L.length + 1)
//i的值不合法
return ERROR;
if(L.length >= L.listsize) {
//当前存储空间已满,追加分配
newbase = (ElemType *) recall(L.elem,(L.listsize + LISTINCREMENT) * sizeof(ElemType));
if(!newbase)
//分配失败
exit(OVERFLOW);
L.elem = newbase; //新基地址
L.listsize += LISTINCREMENT; //增加存储容量
}
p = &L.elem[i-1]; //p为插入位置
for(q = &L.elem[L.length - 1];q >= p;q--){
*(q+1) = *q; //将插入位置及其后的元素后移一个位置
}
*p = e; //插入元素e
L.length ++; //表长加一
return OK;
} //ListInsert_Sq
时间复杂度: ◯ ( n ) \bigcirc(n) ◯(n)
1.2.5 删除操作
status ListDelete_Sq(SqList &L,int i,ElemType &e) {
//在顺序表L中删除第i个元素,并用e返回其值
//i的合法值为 1 <= i <= Listlength(L)
if(i < 1 || i > L.length)
//i的值不合法
return ERROR;
p = L.elem + i - 1; //p为删除元素的位置
e = *p; //用e返回被删除元素的值
q = L.elem + L.length - 1; //q为最后一个元素的位置
for(p ++;p <= q;p++){
*(p - 1) = *p; //被删元素之后的元素向前移动一个位置
}
-- L.length; //表长减一
return OK;
} //ListDelete_Sq
时间复杂度: ◯ ( n ) \bigcirc (n) ◯(n)
1.2.6 删除操作(下标实现法)
status ListDelete_Sq(SqList &L,int i,ElemType &e) {
if(i < 1 || i > L.length)
//i值不合法
return ERROR;
e = L.elem[i - 1]; //用e返回被删元素的值
for(j = i;j < L.length - 1;j++){
L.elem[j -1] = L.elem[j]; //被删元素之后的元素向前移动一个位置
}
--L.length; //表长减一
return OK;
} //ListDelete_Sq
时间复杂度: ◯ ( n ) \bigcirc (n) ◯(n)
1.2.7 顺序表的合并
void MergeList_Sq(SqList La,SqList Lb,SqList &Lc){
//已知顺序表La和Lb中的数据元素按照非递减排列,归并La和Lb所得到新的顺序表Lc,Lc的元素也按照非递减排列
pa = La.elem;
pb = Lb.elem;
Lc.listsize = Lc.length = La.length + Lb.length;
pc = Lc.elem = (ElemType *)malloc(Lc.listsize * sizeof(ElemType));
if(!Lc.elem)
exit(OVERFLOW); //空间分配失败
pa_last = La.elem + La.length + 1; //La中最后一个元素的位置
pb_last = Lb.elem + Lb.length + 1; //Lb中最后一个元素的位置
while(pa <= pa_last && pb <= pb_last)
{
if(*pa <= *pb)
*pc++ = *pa++; //将La中的元素插入Lc
else
*pc++ = *pb++; //将Lb中的元素插入Lc
}
while(pa <= pa_last)
*pc++ = *pa++; //将La中的剩余元素插入Lc
while(pb <= pb_last)
*pc++ = *pb++; //将Lb中的剩余元素插入Lc
}//MergeList_Sq
时间复杂度: ◯ ( L a . l e n g t h + L b . l e n g t h ) \bigcirc (La.length + Lb.length) ◯(La.length+Lb.length)
1.3 顺序表存储实现总结
- 用一组地址连续的存储单元,需要预分配空间,会导致空间的闲置或溢出
- 按逻辑次序依次存储元素,逻辑上相邻的元素物理上也相邻,即物理次序和逻辑次序一致
- 元素之间的关系不占存储单元,用元素物理位置相邻表示,存储密度等于1,空间利用率高
- 支持随机访问,又称直接访问
- 插入元素需要移动大量元素
- 适合表长变化不大,经常按位存取元素的场合
2、单链表
2.1 单链表的结构定义
typedef struct LNode{
ElemType data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList;
LinkList L; //L为单链表的头指针
注: LNode*和LinkList类型实际是一样的
2.2 单链表基本操作的实现
2.2.1 取值操作
status GetELem_L(LinkList L,int i,ElemType &e){
//L为带头结点的单链表的头指针,当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
p = L->next; //p指向首元结点
j=1; //j为计数器
while(p && j < i){
p = p->next;
++j;
} //沿指针向后查找,直至第i个元素或 到表尾
if(!p || j > i)
return OK; //第i个元素不存在
e = p->data;
return OK;
}//GetELem_L
时间复杂度: ◯ ( n ) \bigcirc(n) ◯(n)
2.2.2 查找操作
LNode LocateElem_L(LinkList L,ElemType e){
//从单链表头节点开始查找第一个数据域等于e的结点
LNode *p=L->next;
while(p != NULL && p->data != e)
p = p->next; //从第一个结点开始查找数据域为e的结点
return p; //找到后返回该结点指针,否则返回NULL
}//LocateElem_L
时间复杂度: ◯ ( n ) \bigcirc(n) ◯(n)
2.2.3 插入操作
status ListInsert_L(LinkList &L,int i,ElemType e){
//在带头结点的单链表L中第i个位置之前插入元素e
p = L;
j = 0;
while(p && j < i-1){
p = p->next;
j++;
} //顺指针找到第i- 1个结点
if(!p || j > i-1)
return ERROR; //i小于1或i大于表长加一
s = (LinkList)malloc(sizeof(LNode)); //生成新结点
s->data = e;
s->next = p->next;
p->next = s; //插入L中
return OK;
}//ListInsert_L
时间复杂度: ◯ ( n ) \bigcirc(n) ◯(n)
2.2.4 删除操作
statues ListDelete_L(LinkList &L,int i,ElemType &e){
//在带头结点的单链表L中删除第i个元素,并用e返回其值
p = L;
j = 0;
while(p->next && j < i-1){
p = p->next;
j++;
} //顺指针找到第i-1个结点
if(!(p->next) || j > i-1)
return ERROR; //i小于1或者i大于表长
q = p->next; //q指向第i个结点
p->next = q->next;
e = q->data; //取值
free(q); //释放所删结点的空间
return OK;
}//ListDelete_L
时间复杂度: ◯ ( n ) \bigcirc(n) ◯(n)
2.3 单链表存储实现总结
- 用一组地址任意的存储单元,不需要预分配空间,空间不会闲置或溢出
- 逻辑上相邻的元素物理上不一定相邻,即物理次序和逻辑次序不一定一致
- 除了存放元素值,还要存放表示元素之间关系的指针,存储密度<1
- 不支持随机访问(又称直接访问)
- 插入元素不需要移动大量元素
- 适合表长变化大,经常插入删除的场合