数据结构——线性结构总结
写在前面
数据元素的存储(顺序存储和链式存储)和数据元素的操作(插入和删除)是数据结构的重要部分。
数据结构中的线性结构可分为线性表,栈和队列。对于这三种结构,有两种存储方式顺序存储和链式存储以两种主要操作,插入和删除。不同线性结构的插入和删除操作的方式不同,而且不同存储方式在插入和删除的效率上也有所不同。本文就这三种结构x两种存储x两种操作来进行总结,并附上几道相关习题。
线性结构的特点
线性结构的特点是:在数据元素的非空有限集中,
(1)存在惟一的一个被称作“第一个”的数据元素和惟一的一个被称作“最后一个”的数据元素;
(2)除第一个之外,集合中的每个数据元素均只有一个前驱;除最后一个之外,集合中的每一个数据元素均只有一个后继。
线性表
线性表简单来说就是数据元素的非空有限序列,其特点是可以从表中的任何位置进行插入([0 , n])和删除([0 , n-1])操作。本文利用C++实现了线性表的初始化,查找,插入和删除操作,直接附代码如下:
- 顺序存储
#define MAX_SIZE 100
typedef int ElementType;
typedef struct sqlist{
ElementType *elem; //存储空间的基地址
int length; //当前长度
int listsize; //当前存储空间的大小
}Sqlist;
int init(Sqlist &L) // 初始化
{
L.elem = (ElementType*)malloc(MAX_SIZE*sizeof(ElementType));
if(!L.elem) exit(0);
L.length=0;
L.listsize = MAX_SIZE;
return 1;
}
int inser(Sqlist &L, int i, ElementType e) //插入操作
{
//i的合法值是[0,length]
//i的意思表示在第i个位置之前插入新元素,i=length时表示在list末尾插入新元素
if(i<0||i>L.length)return 0;
if(L.listsize<=L.length)
{
ElementType *newbase = (ElementType*)realloc(L.elem,((MAX_SIZE+L.listsize)*sizeof(ElementType)));
if(!newbase)exit(0);
L.elem = newbase;
L.listsize +=MAX_SIZE;
}
for(int j=L.length;j>i;j--)L.elem[j]=L.elem[j-1];
L.elem[i] = e;
L.length++;
return 1;
}
int delet(Sqlist &L,int i,ElementType &e)
{
//删除第i个位置上的元素并用e返回其值
//i的合法值是[0,length-1]
if(i<0||i>L.length-1)return 0;
e = L.elem[i];
for(int j=i;j<L.length-1;j++)L.elem[j]=L.elem[j+1];
L.length--;
return 1;
}
int cmpEqual(ElementType a, ElementType b)
{
return a==b;
}
int cmpGreat(ElementType a, ElementType b)
{
return a>b;
}
int cmpLess(ElementType a, ElementType b)
{
return a<b;
}
int fin(Sqlist &L, ElementType e,int (*cmp)(ElementType, ElementType))
{
//查找L中第一个与e满足cmp关系的元素的位序[0,length-1],如果没有则返回-1
//cmp是比较函数,如果满足cmp的比较条件则返回1,否则返回0
int i=0;
for(i=0;i<L.length;i++)
{
if((*cmp)(L.elem[i],e))return i;
}
return -1;
}
- 链式存储
typedef int ElementType;
typedef struct Lnode{
ElementType data;
Lnode* next;
}Lnode, *Lnodelist;
void createList(Lnodelist &l,int n)
{
/*通过控制台的输入,创建大小为n的链表
两种创建方式,头插法创建的链表和输入顺序相反,尾插法创建的链表和输入顺序相同*/
l = (Lnode*)malloc(sizeof(Lnode));
l->next=NULL;
Lnode *p;
/*
Lnode *q = l;
for(int i=0;i<n;i++)//尾插法
{
p = (Lnode*)malloc(sizeof(Lnode));
cin>>p->data;
p->next = q->next;
q->next = p;
q = p;
}
*/
for(int i=n;i>0;i--) //头插法
{
p=(Lnode*)malloc(sizeof(Lnode));
cin>>p->data;
p->next=l->next;
l->next = p;
}
}
int getElem(Lnodelist &l,int i,ElementType&e)
{
/*返回单链表中第i个元素,i的取值范围是[1,n];*/
Lnode *p= l->next;
int j=1;
while(p&&j<i)
{
p=p->next;
j++;
}
if(!p||j>i)return 0; /*此处判断j>i的原因是,如果传入的i小于1时能够及时报错。*/
e = p->data;
return 1;
}
int listInser(Lnodelist &l,int i,ElementType e)
{
/*在链表第i个位置之前插入元素e,i的取值范围[1, n]*/
Lnode *p = l,*newP;
int j=0;
while(p&&j<i-1)
{
p=p->next;
j++;
}
if(!p||j>i-1)return 0; /*此处判断j>i-1的原因是,如果传入的i小于1时能够及时报错。*/
newP = (Lnode*)malloc(sizeof(Lnode));
newP->data = e;
newP->next = p->next;
p->next = newP;
return 1;
}
int listDel(Lnodelist &l,int i,ElementType &e)
{
/*删除链表中第i个元素,并用e返回其值,i的取值范围是[1, n]*/
Lnode *p= l,*q;
int j=0;
while(p->next&&j<i-1)
{
p = p->next;
j++;
}
if(!p->next||j>i-1)return 0;/*此处判断j>i-1的原因同上*/
q = p->next;
p->next = q->next;
e =q->data;
free(q);
return 1;
}
栈
栈的结构特点是,仅在栈顶(表尾)进行插入和删除操作。栈可以说是操作受限的线性表。栈中元素的修改是按后进先出(LIFO)的原则进行的,所以又称后进先出的线性表。
- 顺序栈
#define STACK_SIZE 100
typedef int SelemType;
typedef struct Stack{
SelemType *base; //存放数据的数组
SelemType *top; //栈顶指针,指向栈顶元素上面的地址
int stacksize; //栈的大小
}Sqstack;
int initS(Sqstack &s)
{
s.base = (SelemType*)malloc(STACK_SIZE*sizeof(SelemType));
if(!s.base)exit(0);
s.top=s.base;
s.stacksize = STACK_SIZE;
return 1;
}
int getTop(Sqstack &s,SelemType &e)
{
//如果不是空栈,则将栈顶元素返回到e,并返回1,否则返回0
if(s.top==s.base)return 0;
e = *(s.top-1);
return 1;
}
int push(Sqstack &s,SelemType e)
{
//元素e入栈,成功则返回1