【数据结构 - 栈和队列】自学笔记记录(更新中……)

本文详细介绍了栈和队列这两种基本数据结构。栈是一种后进先出(LIFO)的数据结构,顺序栈和链栈是其两种实现方式;队列则是一种先进先出(FIFO)的数据结构,包括循环队列和链队的实现。文中通过实例分析了二进制转十进制问题,并对比了栈和队列的特点。同时,提供了创建、插入、删除等操作的具体代码实现。

目录

一、栈

1、栈的定义及特点

2、顺序栈

1、顺序栈的存储结构

2、创建一个空顺序栈

3、顺序栈入栈

4、顺序栈出栈 ​

5、顺序栈取栈顶元素

6、清空一个顺序栈

7、销毁一个顺序栈

8、计算顺序栈中元素个数

9、实例分析——二进制转十进制

3、链栈

1、链栈的存储结构

2、创建一个空链栈 

3、链栈入栈 

4、链栈出栈

5、链栈取栈顶元素 

6、 计算链栈元素个数

7、销毁一个链栈 

二、队列 

1、队列的定义及特点

2、循环队列(队列的顺序表示)

1、循环队列的存储结构

2、构造一个空循环队列

3、求队列长度

4、循环队列入队

5、循环队列出队

6、循环队列取队头元素

3、链队(队列的链式表示)

 1、链队的存储结构

2、创建一个空链队

3、链队的入队

4、链队的出队

5、 取链队的队头元素

6、链队实例——舞伴问题

6-1 舞伴问题 (8 分)

4、栈与队列比较

5、课后习题


一、栈

1、栈的定义及特点

受特殊限制的线性表,先进后出后进先出

表尾端称为栈顶,表头端称为栈底,不含元素的空表称为空栈

栈的操作只在线性表表尾进行

top指向栈顶元素的后一位置

 

2、顺序栈

1、顺序栈的存储结构

#define MAXSIZE 100 		//顺序栈存储空间

typedef struct
{
    ElemType *base;     //栈底指针
	ElemType *top; 		//栈顶指针
	int stacksize; 			//栈当前可用的最大容量
}SqStack;

2、创建一个空顺序栈

Status InitStack( SqStack &S)
{
    S.base=(ElemType*)malloc(MAXSIZE*sizeof(ElemType));
    //S.base=new ElemType[MAXSIZE];
    if(!S.base) exit(OVERFLOW);
    S.top = S.base;  //一开始栈顶就是栈底
    S.stacksize = MAXSIZE;
    return OK;
}

3、顺序栈入栈

#define add 10 //追加空间

Status Push( SqStack &S, ElemType e)
{
    //如果栈满,追加空间
    if( S.top-S.base >= S.stacksize)
    {    
        S.base=(ElemType*)realloc(S.base,(s.stacksize + add)*sizeof(ElemType));
        if(!S.base) exit(OVERFLOW);
        
        S.top = S.base + S.stacksize;
        S.stacksize += add;
    }
    *(S.top ++) = e; //元素入栈,栈顶指针加一
}

4、顺序栈出栈 

Status Pop( SqStack &S,ElemType &e)
{
    if(S.top == S.base) return ERROR; //空栈
    e =*(--S.top);  //栈顶指针减一,将栈顶元素赋值给e
    return OK;
}

5、顺序栈取栈顶元素

Status GetTop(SqStack S)
{
    if(S.top != S.base) 
        return *(S.top-1);  //返回栈顶元素,栈顶指针不变
}

6、清空一个顺序栈

清空栈不是把栈的存储空间销毁,而是把栈中元素作废

Status ClearStack(SqStack &S)
{
    S.top = S.base;
}

7、销毁一个顺序栈

Status DestroyStack( SqStack &S )
{
    int i,len;
    len = S.stacksize;
    for(i=0;i<len;i++)
    {
        free(S.base);
        S.base++;
    }
    S.base = S.top = NULL;  //不让变成野指针
    S.stacksize = 0;
}

8、计算顺序栈中元素个数

Status Stacklen( SqStack S )
{
    return (S.top - S.base);  //这里系统自动除以sizeof,所以返回的是元素个数
}

9、实例分析——二进制转十进制

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

//顺序栈定义
#define OK 1
#define ERROR 0
#define MAXSIZE  100    //顺序栈存储空间的初始分配量

typedef int Status;
typedef char ElemType;

typedef struct
{
	ElemType *base; //栈底指针
	ElemType *top;  //栈顶指针
	int stacksize;  //栈当前可用的最大容量
}SqStack;

//创建空栈
Status InitStack(SqStack *S)
{
	S->base=(ElemType*)malloc(MAXSIZE*sizeof(ElemType));
	if (!S->base) exit(0);
	S->top = S->base;
	S->stacksize = MAXSIZE;
	return OK;
}

//顺序栈的入栈
Status Push(SqStack *S, ElemType e)
{
	// 插入元素e为新的栈顶元素
	if (S->top - S->base == S->stacksize)  return ERROR; //栈满
	*(S->top ++) = e; //元素e压入栈顶,栈顶指针加1
	return OK;
}

//顺序栈的出栈
Status Pop(SqStack *S, ElemType *e)
{
	//删除S的栈顶元素,用e返回其值
	if (S->base == S->top) return ERROR;  //栈空
	*e = *(--S->top); //栈顶指针减1,将栈顶元素赋给e
	return OK;
}

//计算栈中元素个数
int Stacklen(SqStack S)
{
	return (S.top - S.base);
}

int main()
{
	SqStack s; //定义栈
    ElemType c;
    Status len,i,sum=0;

    InitStack(&s);

    printf("请输入二进制数,输入#符号表示结束!\n");
    scanf("%c",&c);
    while(c != '#')
    {
        Push(&s,c);
        scanf("%c",&c);
    }
    getchar(); //接收空格

    len = Stacklen(s);

    for( i=0; i<len ;i++)
    {
        Pop(&s,&c);
        sum+=(c-'0')*pow(2,i);
    }

    printf("转换的十进制数是:%d",sum);

	return 0;
}

3、链栈

1、链栈的存储结构

typedef struct LNode
{
    ElemType data;
    struct LNode *next;
}LStackNode, *LinkStack;

2、创建一个空链栈 

void InitStack(LinkStack &Top)
{
    Top = (LinkStack)malloc(sizeof(LStackNode));
    if(!Top) return;
    Top->next = NULL;
}

3、链栈入栈 


void PushStack(LinkStack &Top, SElemType e)
{
    LinkStack newbase = (LinkStack)malloc(sizeof(LStackNode));
    if(!newbase)
    {
        printf("内存空间分配失败!\n");
        return;
    }
    newbase->data = e;
    newbase->next = Top->next;
    Top->next = newbase;
    return;
}

4、链栈出栈

void PopStack(LinkStack &Top, SElemType &e)
{
    LinkStack p = Top->next;
    if(!p)
    {
        printf("栈为空!\n");
        return;
    }
    Top->next = p->next;
    e = p->data;
    free(p);
    return;
}

5、链栈取栈顶元素 

void GetTopStack(LinkStack &Top, SElemType &e)
{
    LinkStack p = Top->next;
    if(!p)
    {
        printf("栈为空!\n");
        return;
    }
    e = p->data;
    return;
}

6、 计算链栈元素个数

int LengthStack(LinkStack &Top)
{
    int len=0;
    LinkStack p=Top;
    while(p->next!=NULL)
    {
        len++;
        p=p->next;
    }
    return len;
}

7、销毁一个链栈 

void FreeStack(LinkStack &Top)
{
    LinkStack p=Top,q;
    while(p->next!=NULL)
    {
        q = p;
        p = p->next;
        free(q);
    }
    printf("栈已销毁!\n");
    return;
}

 

二、队列 

1、队列的定义及特点

只允许在一端进行插入操作,而在另一端进行删除操作的线性表。

先进先出后进后出

 

2、循环队列(队列的顺序表示)

 头指针head始终指向队列头元素,尾指针tail始终指向队尾元素的下一个位置

1、循环队列的存储结构

#define MAXSIZE 100  //最大队列长度

typedef struct
{
    ElemType *base; //类似顺序表中的elem数组
    int front;  //头指针,指向队列头元素
    int rear;   //尾指针,指向队列尾元素的下一个位置
}SqQueue;

2、构造一个空循环队列

Status InitQueue( SqQueue &Q )
{
    Q.base=(ElemType*)malloc(MAXSIZE*sizeof(ElemType));
    if(!Q.base) exit(OVERFLOW);
    Q.front = Q.rear = 0;
    return OK;
}

3、求队列长度

对于循环队列,头尾指针的差值可能是负数,因此要加上MAXSIZE,再与MAXSIZE取余

int Queuelength( SqQueue Q )
{
    return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}

4、循环队列入队

Status EnQueue( SqQueue &Q, ElemType e )
{
    if((Q.rear + 1) % MAXSIZE == Q.front)  return ERROR; //队满
                
    Q.base[Q.rear] = e; //队尾加新元素
    Q.rear = (Q.rear + 1)% MAXSIZE;
    return OK;
}

5、循环队列出队

Status DeQueue( SqQueue &Q, ElemType &e)
{
    if(Q.front = Q.rear)  return ERROR;  //队空
    e = Q.base[Q.front];    //保存表头元素
    Q.front = (Q.front+1) % MAXSIZE;
    return OK;
}

6、循环队列取队头元素

Status GetHead( SqQueue Q)
{
    if(Q.front != Q.rear)    //队非空
        return Q.base[Q.front];   //返回队头元素的值,队头指针不变
}

3、链队(队列的链式表示)

 1、链队的存储结构

typedef struct QNode
{
    ElemType data;
    struct QNode *next;
} QNode,*QueuePtr;

typedef struct
{
    QueuePtr front;    //队头指针
    QueuePtr rear;     //队尾指针
}LinkQueue;

2、创建一个空链队

Status InitQueue( LinkQueue &Q )
{
    Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
    //Q.front = Q.rear = new QNode;
    if(!Q.front) exit(OVERFLOW);
    Q.front->next = NULL;  //头指针置空
    return OK;
}

3、链队的入队

Status EnQueue(LinkQueue &Q, ElemType e)
{
    QueuePtr p=(QueuePtr)malloc(sizeof(QNode));
	if(!p) exit(OVERFLOW);
	p->data=e;
	p->next=NULL;
	Q.rear->next=p;  //尾指针指向的尾结点连接新结点
	Q.rear=p;  //尾指针指向新结点
}

4、链队的出队

Status DeQueue(LinkQueue &Q, ElemType &e)
{

	if(Q.front==Q.rear)//空队列
	    return ERROR;
	QueuePtr p = Q.front->next; //p指向首元结点
    e = p->data;
	Q.front->next=p->next;
	free(p);
	if(p==Q.rear)//此时删除最后一个结点(尾结点)
	    Q.rear = Q.front;
    return OK;
}

在链队出队操作时还要考虑当队列中最后一个元素被删后,队列尾指针也丢失了,

因此需对队尾指针重新赋值(指向头结点)

5、 取链队的队头元素

ElemType GetHead( LinkQueue Q )
{
    if(Q.front != Q.rear)  //队列非空
        return Q.front->next->data;  
}

6、链队实例——舞伴问题

6-1 舞伴问题 (8 分)

函数接口定义:

void DancePartner(DataType dancer[], int num) ;

其中 dancer[]是存放男士和女士信息的数组,num是数组大小。

裁判测试程序样例:

#include<stdio.h>
#include<stdlib.h>

typedef struct {
    char name[20]; 
    char sex; 
} DataType;

struct Node {
    DataType      data;
    struct Node*  next;
};
typedef struct Node  *PNode;
struct Queue
{
    PNode        f;
    PNode        r;
};
typedef struct Queue *LinkQueue;
LinkQueue  SetNullQueue_Link()
{
    LinkQueue lqueue;
    lqueue = (LinkQueue)malloc(sizeof(struct Queue));
    if (lqueue != NULL)
    {
        lqueue->f = NULL;
        lqueue->r = NULL;
    }
    else
        printf("Alloc failure! \n");
    return  lqueue;
}

int IsNullQueue_link(LinkQueue lqueue)
{
    return (lqueue->f == NULL);
}

void EnQueue_link(LinkQueue lqueue, DataType x)
{
    PNode  p;
    p = (PNode)malloc(sizeof(struct Node));
    if (p == NULL)
        printf("Alloc failure!");
    else {
        p->data = x;
        p->next = NULL;
        if (lqueue->f == NULL)
        {
            lqueue->f = p;
            lqueue->r = p;
        }
        else
        {
            lqueue->r->next = p;
            lqueue->r = p;
        }
    }
}
void DeQueue_link(LinkQueue lqueue)
{
    struct Node  * p;
    if (lqueue->f == NULL)
        printf("It is empty queue!\n ");
    else
    {
        p = lqueue->f;
        lqueue->f = lqueue->f->next;
        free(p);
    }
}
DataType  FrontQueue_link(LinkQueue lqueue)
{
    if (lqueue->f == NULL)
    {
        printf("It is empty queue!\n");
    }
    else
        return (lqueue->f->data);
}

void DancePartner(DataType dancer[], int num) 
{
            /* 请在这里填写答案 */
}

int main()
{
    DataType dancer[9];
    for (int i = 0; i < 9; i++)
    scanf("%s %c", dancer[i].name, &dancer[i].sex);
    DancePartner(dancer, 9);
    return 0;
}

输入样例:

李敏浩 M
李钟硕 M
高欣雅 F
吴彦祖 M
王思聪 M
张甜源 F
张智霖 M
许丹丹 F
马小云 F

输出样例:

高欣雅 李敏浩
张甜源 李钟硕
许丹丹 吴彦祖
马小云 王思聪

张智霖

男女各一队,然后按队列顺序出队配对,最后输出没有配对的名字

void DancePartner(DataType dancer[], int num)
{
    LinkQueue fdancer = SetNullQueue_Link();
    LinkQueue mdancer = SetNullQueue_Link();
    
    for(int i=0;i<num;i++)
    {
        if(dancer[i].sex=='M') EnQueue_link(mdancer,dancer[i]);
        else EnQueue_link(fdancer,dancer[i]);
    }
    
    for(int i=0;i<num/2;i++)
        if(!IsNullQueue_link(fdancer)&&!IsNullQueue_link(mdancer))
        {
            printf("%s %s\n",FrontQueue_link(fdancer).name,FrontQueue_link(mdancer).name);
            DeQueue_link(fdancer);
            DeQueue_link(mdancer);
        }
  
    printf("\n");
    if(!IsNullQueue_link(fdancer)) printf("%s\n",FrontQueue_link(fdancer).name);
    if(!IsNullQueue_link(mdancer)) printf("%s\n",FrontQueue_link(mdancer).name);
     
}

4、栈与队列比较

5、课后习题

1、设循环队列的元素存放在一维数组Q[0..29](下标为0到29)中,队列非空时,front指示队头元素位置,rear指示队尾元素的后一个位置。如果队列中元素的个数为11,front的值为25,则rear的值是( B

A.5

B.6

C.35

D.36

(Q.rear-Q.front+MAXSIZE)%MAXSIZE=队列长度

(x-25+30)%30 = 11 ,即x-25+30=11 ,则x=6.

2、将一个10×10对称矩阵M的上三角部分的元素mi,j(1≤i≤j≤10)按列优先存入C语言的一维数组N中,元素m7,2在N中下标是( C

A.15

B.16

C.22

D.23

 因为对称矩阵,所以m7,2跟m2,7一样,因为列优先,则第一列1个,第二列2个……第七列2个

1+2+3+4+5+6+2=23,由于数组从0开始,则位置应是22

3、对n阶对称矩阵压缩存储时,需要表长为( C )的顺序表

A.n/2

B.n*n/2

C.n(n+1)/2

D.n(n-1)/2

对称矩阵的存储只需要存储其上三角或下三角部分(含对角线),元素个数为n +(n-1)+ (n -2) +..*+ 1 =n(n+1)/2

1. 经过以下运算后,x的值是( )。 InitStack(s); Push(s,'a'); Push(s,'b'); Pop(s,x); Gettop(s,x); A. a B. b C. 1 D. 0 2.循环队列存储在数组A[0..m]中,则入的操作为( )。 A.rear=rear+1 B. rear=(rear+1) mod(m-1) C. rear=(rear+1)mod m D. rear=(rear+1) mod(m+1) 3. 队列的共同点是( )。 A.都是先进先出 B.都是先进后出 C.只允许在端点处插入删除元素 D.没有共同点 4. 若用一个大小为6的数组来实现循环队列,且当 rear front 的值分别为 0 3。当从队列中删除一个元素,再插入两个元素后,rear front 的值分别为:( )。 A.1 5 B.2 4 C.4 2 D.5 1 5. 程序填顺序循环队列的类型定义如下: typedef int ET; typedef struct{ ET *base; int Front; int Rear; int Size; }Queue; Queue Q; 队列 Q 是否“满”的条件判断为( C )。 A.(Q.Front+1)=Q.Rear B.Q.Front=(Q.Rear+1) C.Q.Front=(Q.Rear+1)% Q.size D.(Q.Front+1) % Q.Size=(Q.Rear+1)% Q.size 6. 若进序列为1,2,3,4,进过程中可以出,则( )不可能是一个出序列。 A.3,4,2,1 B.2,4,3,1 C.1,4,2,3 D.3,2,1,4 7. 向顺序存储的循环队列 Q 中插入新元素的过程分为三步: ( )。 A.进行队列是否的判断,存入新元素,移动尾指针 B.进行队列是否满的判断,移动尾指针,存入新元素 C.进行队列是否的判断,移动尾指针,存入新元素 D.进行队列是否满的判断,存入新元素,移动尾指针 8. 关于队列,( )说法不妥。 A. 是后进先出表 B. 队列是先进先出表 C. 递归函数在执行用到 D. 队列常适用于表达式求值的算符优先法 9. 若用数组S[0..m]作为两个S1S2的共同存储结构,对任何一个,只有当S全满才不能作入操作。为这两个分配间的最佳方案是( )。 A.S1的底位置为0,S2的底位置为m B.S1的底位置为0,S2的底位置为m/2 C.S1的底位置为1,S2的底位置为m D.S1的底位置为1,S2的底位置为m/2 二、程序填题(没特别标注分数的的为3分,共 23 分)。 1.下面的算法是将一个整数e压入堆S,请在格处填上适当的语句实现该操作。 typedef struct{ int *base; int *top; int stacksize; }SqStack; int Push(SqStack S,int e) { if ( S.top- S.base>= S.stacksize ) { S.base=(int *) realloc(S.base,(S.stacksize+1)*sizeof(int)); if( !S.base ) { printf(“Not Enough Memory!\n”); return(0); } S.top= S.base+ S.stacksize ; S.stacksize= S.stacksize+1 ; } * S.top++=e ; return 1; } 2. 在表达式:6+5+3*7/(4+9/3-2)求值过程中,处理到2刻,运算符的状态为: + / ( - (4分),操作数的内容为11,21,7,2(4分)。 3. 递调用,处理参数及返回地址,要用一种称为 数据结构。 4. 循环队列中数组的下标范围是1-n,其头尾指针分别为fr,则其元素个数为_(r-f+n) mod n。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值