本文为数据结构课程的第一篇实验报告,主要是用c++实现共享栈、循环队列和链栈,仅供参考学习(也没人看),如有错误望及时提出,我及时改正。
实验一:线性结构的实现
一、实验目的
实验目标:
验证C语言环境下,顺序结构和链式结构的实现。并通过测试数据的运行结果来测试结构的正确性。尝试实现如循环队列、共享栈等在特殊条件下使用的数据结构。
预期效果:
能够成功运行循环队列,共享栈和链栈。
二、实验内容
1、实现顺序循环队列
2、实现链式栈
3、实现共享栈
4、使用数据测试
三、实验过程
实现过程:
1.顺序循环队列
循环队列的目的是为了充分利用空间,克服假溢出现象。实现方法是将队列首尾相接,使空间形成环状。首先在顺序结构中建立顺序表,队尾偏移量back,队列长度(元素个数)length。接着将队列初始化置空。建立入队函数InsertBack和出队函数DelFront。
(5为队列最大长度)
初始化:
void InitList(PSeqList L){
L->length=0;
L->back=-1;
}
入队代码:
int InsertBack(PSeqList L,DataType e){
//判断是否队满
if(L->length==5){
printf("队已满\n");
return 0;
//如果队尾偏移量back达到队尾(即跨界)则循环,实现首尾相接
}else if((L->back+1)>=5){
L->back=(L->back+1)%5;
L->data[L->length]=e;
L->length++;
}else{ //back为达到队尾时数据入队
L->back++;
L->data[L->length]=e;
L->length++;
}
return 1;
}
出队代码:
int DelFront(PSeqList L){
//判断队列是否为空
if(EmptyList(L)){
printf("队为空\n");
return 0;
}
int k;
//判断是否循环跨界
//如果尾部偏移量back+1大于等于队列长度则未跨界,正常出队
if((L->back+1)>=L->length){
L->length--;
for(k=L->back-L->length;k<L->back;k++){
L->data[k]=L->data[k+1];
}
//否则,将队尾back+5,找到队头(back+5-length+1),再进行出队。
}else{
L->length--;
for(k=L->back+5-L->length+1;k<L->back+5;k++){
int a=k+1;
if(a>=5){
a=a%5;
L->data[k]=L->data[a];
}else{
L->data[k]=L->data[a];
}
}
}
return 1;
}
2.链式栈
链式栈是使用链式存储结构表示的栈。实现方法:入栈:创建新结点,将元素插入该结点数值域,然后将该点指针域指向p,再修改p指向该结点;出栈:将栈顶元素删除,将栈顶指针指向下一个结点,然后释放该结点空间。
入栈代码:
void InsNode(LinkList PHead,DataType data){
Node *p;
Node *s;
p=PHead;
//判断头结点
if(p==NULL){
printf("插入位置不合法!\n");
return;
}
//将数据插入s数值域,然后将s指针域指向p,再修改p指向s
s=(LinkList)malloc(sizeof(Node));
s->Data=data;
s->Next=p->Next;
p->Next=s;
}
出栈代码:
int DelNode(LinkList PHead,DataType *data) {
Node *p;
Node *s;
p=PHead;
s=p->Next;
*data=s->Data; //将删除元素该data
p->Next=s->Next; //删除栈顶指针,指向下一结点
free(s); //释放空间
return *data;
}
3.共享栈
共享栈指两个栈共用一个存储空间,目的是为了充分利用有限的存储空间。实现方法是建立两个栈顶指针,以此区分共享栈中的两个栈。左栈入栈时栈顶指针右移,右栈入栈时指针左移,两指针重合则栈满;左栈出栈时栈顶指针左移,右栈出栈时栈顶指针右移,空栈时两指针回到初始化状态。
(5为共享栈最大长度)
初始化:
void InitList(PSeqList L){
if(L==NULL)
return;
L->top1=-1; //左栈栈顶指针
L->top2=5; //右栈栈顶指针
}
空栈判断:
//两指针为初始化状态,栈空
int EmptyList(PSeqList L){
if(L->top1==-1 && L->top2==5)
return 1;
else
return 0;
}
入栈代码:
void InsList(PSeqList L,DataType e,int i){
//两指针重合,栈满
if(L->top1+1==L->top2){
printf("栈已满\n");
return;
}
switch(i){
//左栈入栈,栈顶指针右移
case 0:
L->top1++;
L->data[L->top1]=e;
break;
//右栈入栈,栈顶指针左移
case 1:
L->top2--;
L->data[L->top2]=e;
break;
default:
printf("栈头错误\n");
break;
}
}
出栈代码:
int DelList(PSeqList L,int i){
if(EmptyList(L)){
printf("栈为空\n");
return 0;
}
int e;
switch(i){
//左栈出栈,栈顶指针左移
case 0:
if(L->top1==-1){
printf("左栈为空\n");
return 0;
}
e=L->data[L->top1];
L->top1--;
break;
//右栈出栈,栈顶指针右移
case 1:
if(L->top2==5){
printf("左栈为空\n");
return 0;
}
e=L->data[L->top2];
L->top2++;
break;
default:
printf("栈头错误\n");
break;
}
return e;
}
四、实验结果
1.顺序循环队列
测试数据:1 2 3 4 (-1结尾)
输出队:1 2 3 4
插入数据:8 9
输出队:1 2 3 4 8 队已满
出队一次后输出:2 3 4 8
插入数据:9
输出队:2 3 4 8 9
2.链式栈
测试数据:1 2 3 4 (-1结尾)
输出栈:4 3 2 1
插入数据:8
输出栈:8 4 3 2 1
出栈一次后输出:4 3 2 1
3.共享栈
左栈
测试数据:1 2 3(-1结尾)
输出栈:3 2 1
出栈一次后输出:2 1
插入数据:8
输出栈:8 2 1
右栈:
测试数据:4 5(-1结尾)
输出栈:5 4
出栈一次后输出:4
插入数据:8
输出栈:8 4
五、总结与反思
实验过程中遇到许多问题,对各种数据结构的特点不熟悉导致很多时间浪费。就循环队列的实现而言,我先将顺序表改成普通队列,然后再修改成循环队列,浪费了许多时间。共享栈的实现更是经历了许多波折。
由于我最初是用顺序表实现的循环队列,所以我想要用不同的方法——链表的方法来实现共享栈。很快我就发现了普通链表不能实现共享栈,因为链表的尾结点必定为NULL,从而无法形成一个另一个栈。并且共享栈的目的是为了更充分的利用有限的存储空间而建立的,而链表的特点是存储空间动态变化,几乎没有存储空间上的限制,二着相冲突。链表是不断变动的,共享栈是利用两端栈底不动,栈顶移动来实现的,这也就使得双向链表也无法实现共享栈。共享栈的目的和特点和实现的思想都要求其实现的方法应该是顺序表而不是链表。虽然链表无法实现共享栈,但是链栈却因为储存空间的动态分配而具备优势优势。
在这次实验过程中,我深刻认识到了顺序表和链表,循环队列,链栈和共享栈的特点。顺序表的优点是可以通过下标来直接存取;缺点是内存限制大。链表的优点是插入删除方便,存储空间动态变化;缺点是不适合查找元素。顺序表实现的循环队列和共享栈很大程度上提高了存储空间利用效率,链表实现的栈可以动态扩充栈的大小,受到存储空间的限制问题的影响比较少。在实现共享栈过程所出现的问题让我深刻认识到数据结构选择的重要性,对于数据结构的选择,首先要考虑应用场景的特点,再考虑各种数据结构的特点,在进行相应的匹配,否则可能很难达到,甚至无法达到最终目的。