数据结构-堆栈与队列

王道章节内容

知识框架

考纲内容

  1. 栈与队列的基本概念;
  2. 栈与队列的顺序存储结构;
  3. 栈与队列的链式存储结构;
  4. 多维数组的存储;
  5. 特殊矩阵的压缩存储;
  6. 栈、队列和数组的应用。 

 引入 

后缀表达式

后缀表达式转中缀表达式

6 2 / 3 - 4 2 * + = 

“逐一扫描”,首先是 6,存入;(6);NULL

然后是 2,存入;(6 2);NULL

再者是 /,符合条件做运算输出 6 / 2,更新为3;(6 2 /)——(3);6 / 2

接着是 3,存入;(3 3);6 / 2

下来是 -,符合条件做运算输出 - 3,更新为 0;(3 3 -)——(0);6 / 2 - 3

然后是 4,存入;(0 4);6 / 2 - 3

再者是 2,存入;(0 4 2);6 / 2 - 3

接着是 *,根据“顺序存入,倒序输出”,顶上做运算更新为 8;(0 8);6 / 2 - 3 ~ 4 * 2

下来是 +,符合条件做运算输出 + 4 * 2,更新为 8;(8);6 / 2 - 3 + 4 * 2

最后是 =,符合条件输出 = 8,更新为NULL。(NULL);6 / 2 - 3 + 4 * 2 = 8


把以上过程抽象出来,就是我们粗糙的“栈”的出入操作。

定义

栈(stack)

栈是只允许在一端进行插入或删除操作的线性表;

允许插入和删除的一端叫栈顶(top),另一端叫栈底(bottom),不含数据元素的叫空栈;

栈又称为后进先出(Last In First Out)的线性表,简称 LIFO 结构

插入数据:入栈(Push);

删除数据:出栈(Pop)。

抽象数据类型描述(ADT)

  1. Stack CreateStack( int MaxSize ): 生成空堆栈,其最大长度为MaxSize;
  2. int IsFull( Stack S, int MaxSize ):判断堆栈S是否已满;
  3. void Push( Stack S, ElementType item ):将元素item压入堆栈;
  4. int IsEmpty ( Stack S ):判断堆栈S是否为空;
  5. ElementType Pop( Stack S ):删除并返回栈顶元素;

顺序栈

定义

顺序栈

采用顺序存储的栈称为顺序栈,它利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针( top )指示当前栈顶元素的位置。

结构代码实现​​​​​​​

陈越实现:

#define MaxSize <储存数据元素的最大个数>
typedef struct SNode *Stack;
struct SNode{
ElementType Data[MaxSize];
int Top;
};

《大话》实现(与王道类似):

/* 顺序栈结构 */
typedef struct
{
        SElemType data[MAXSIZE];
        int top; /* 用于栈顶指针 */
}SqStack;

名词解释:

typedef struct SNode *Stack

typedef 用于为现有的类型定义一个新的别名;

struct SNode *Stack; 定义了一个名为 Stack 的类型,它是 struct SNode 类型的指针;

通过这种方式,你可以使用 Stack 类型来声明指向 SNode 结构体的指针

struct SNode { ElementType Data[MaxSize]; int Top; }

struct SNode 定义了一个结构体类型,用于表示栈的数据结构;

ElementType Data[MaxSize]; 定义了一个数组 Data,用于存储栈中的元素。ElementType表示栈中元素的数据类型。例如,如果栈中存储的是整数,则 ElementType 可能被定义为 int;

int Top; 定义了一个整型变量 Top,用于表示栈顶元素的位置索引。当栈为空时,Top 的值通常为 -1

操作及实现

初始化

《大话》实现:

/*  构造一个空栈S */
Status InitStack(SqStack *S)
{ 
        /* S.data=(SElemType *)malloc(MAXSIZE*sizeof(SElemType)); */
        S->top=-1;
        return OK;
}

王道实现:

void InitStack(SqStack &S){
    S.top = -1;  //初始化栈顶指针
}

名词解释:

使用引用 &S

  • 当你使用引用 &S 时,函数参数 S 实际上是一个指向 SqStack 对象的引用。这意味着当你在函数内部修改 S 时,实际上是在修改原始的 SqStack 对象;
  • 使用引用可以避免复制整个 SqStack 对象,这对于较大的结构体来说可以节省内存和提高效率;
  • 引用确保你在函数内部所做的修改会反映到调用函数的上下文中。

使用指针 *S

  • 如果你使用指针 *S,那么 S 将是一个指向 SqStack 的指针。这意味着你需要通过指针来访问和修改 SqStack 的成员;
  • 如果你使用指针,那么你需要使用间接访问运算符 -> 而不是成员访问运算符 . 来访问 SqStack 的成员;
  • 使用指针可以让你传递 SqStack 的地址,但在函数内部,你需要确保指针不是 NULL 并且正确地使用指针。

举个例子:

使用引用传递:

int main() {
    SqStack stack;
    InitStack(stack);  // 使用引用传递

    printf("Top index: %d\n", stack.top);  // 输出应为 -1

    return 0;
}

使用指针传递:

int main() {
    SqStack stack;
    InitStack(&stack);  // 使用指针传递

    printf("Top index: %d\n", stack.top);  // 输出应为 -1

    return 0;
}
判空

《大话》实现:

/* 若栈S为空栈,则返回TRUE,否则返回FALSE */
Status StackEmpty(SqStack S)
{ 
        if (S.top==-1)
                return TRUE;
        else
                return FALSE;
}

王道实现:

bool StackEmpty(SqStack S){
    if(S.top == -1 )  //栈空
        return true;
    else    
        return false;
}
求栈长

《大话》实现:

/* 返回S的元素个数,即栈的长度 */
int StackLength(SqStack S)
{ 
        return S.top+1;
}
入栈(Push)

陈越实现:

void Push( Stack PtrS, ElementType item )
{
 if ( PtrS->Top == MaxSize-1 ) {
 printf(“堆栈满”); return;
 }else {
 PtrS->Data[++(PtrS->Top)] = item;
 return;
 }
}

名词解释:

void Push(Stack PtrS, ElementType item)

  • void 表示函数不返回任何值。
  • Push 是函数名。
  • (Stack PtrS, ElementType item) 表示函数接收两个参数:一个 Stack 类型的指针 PtrS 和一个 ElementType 类型的变量 item

《大话》实现:

/* 插入元素e为新的栈顶元素 */
Status Push(SqStack *S,SElemType e)
{
        if(S->top == MAXSIZE -1) /* 栈满 */
        {
                return ERROR;
        }
        S->top++;				/* 栈顶指针增加一 */
        S->data[S->top]=e;  /* 将新插入元素赋值给栈顶空间 */
        return OK;
}

王道实现:

bool Push(SqStack &S, ElemType x){
    if(S.top == Maxsize - 1)
        return false;
    S.data[++S.top]=x;    //指针先+1,后入栈
    return true;
}

名词解释:

bool Push(SqStack &S, ElemType x)

其作用是将一个元素 x 压入基于数组实现的栈 SqStack 中。如果栈已满,函数返回 false,表示压栈操作失败。否则,它将栈顶指针加一并将元素存入栈中,然后返回 true 表示成功。由于函数返回一个布尔值,因此返回类型为 bool

 

出栈(Pop)

陈越实现:

ElementType Pop( Stack PtrS )
{
 if ( PtrS->Top == -1 ) {
 printf(“堆栈空”);
 return ERROR; /* ERROR是ElementType的特殊值,标志错误
*/
 } else 
 return ( PtrS->Data[(PtrS->Top)--] );
}

《大话》实现:

/* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
Status Pop(SqStack *S,SElemType *e)
{ 
        if(S->top==-1)
                return ERROR;
        *e=S->data[S->top];	/* 将要删除的栈顶元素赋值给e */
        S->top--;				/* 栈顶指针减一 */
        return OK;

王道实现:

bool Pop(SqStack &S, ElemType &x){
    if(S.top == - 1)
        return false;
    x=S.data[S.top--];    //指针先-1,后入栈
    return true;
}
读栈

读栈顶

《大话》实现:

/* 从栈底到栈顶依次对栈中每个元素显示 */
Status StackTraverse(SqStack S)
{
        int i;
        i=0;
        while(i<=S.top)
        {
                visit(S.data[i++]);
        }
        printf("\n");
        return OK;
}

共享栈

引子

定义

结构代码实现

陈越实现:

#define MaxSize <存储数据元素的最大个数>
struct DStack {
 ElementType Data[MaxSize]; 
 int Top1; /* 堆栈1的栈顶指针 */ 
 int Top2; /* 堆栈2的栈顶指针 */
} S;

 《大话》实现:

/* 两栈共享空间结构 */
typedef struct 
{
        SElemType data[MAXSIZE];
        int top1;	/* 栈1栈顶指针 */
        int top2;	/* 栈2栈顶指针 */
}SqDoubleStack;
操作及实现
初始化

《大话》实现:

/*  构造一个空栈S */
Status InitStack(SqDoubleStack *S)
{ 
        S->top1=-1;
        S->top2=MAXSIZE;
        return OK;
}
判栈空

《大话》实现:

/* 若栈S为空栈,则返回TRUE,否则返回FALSE */
Status StackEmpty(SqDoubleStack S)
{ 
        if (S.top1==-1 && S.top2==MAXSIZE)
                return TRUE;
        else
                return FALSE;
}
 求栈长

《大话》实现:

/* 返回S的元素个数,即栈的长度 */
int StackLength(SqDoubleStack S)
{ 
        return (S.top1+1)+(MAXSIZE-S.top2);
}
入栈

《大话》实现:

/* 插入元素e为新的栈顶元素 */
Status Push(SqDoubleStack *S,SElemType e,int stackNumber)
{
        if (S->top1+1==S->top2)	/* 栈已满,不能再push新元素了 */
                return ERROR;	
        if (stackNumber==1)			/* 栈1有元素进栈 */
                S->data[++S->top1]=e; /* 若是栈1则先top1+1后给数组元素赋值。 */
        else if (stackNumber==2)	/* 栈2有元素进栈 */
                S->data[--S->top2]=e; /* 若是栈2则先top2-1后给数组元素赋值。 */
        return OK;
}
出栈

《大话》实现:

/* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
Status Pop(SqDoubleStack *S,SElemType *e,int stackNumber)
{ 
        if (stackNumber==1) 
        {
                if (S->top1==-1) 
                        return ERROR; /* 说明栈1已经是空栈,溢出 */
                *e=S->data[S->top1--]; /* 将栈1的栈顶元素出栈 */
        }
        else if (stackNumber==2)
        { 
                if (S->top2==MAXSIZE) 
                        return ERROR; /* 说明栈2已经是空栈,溢出 */
                *e=S->data[S->top2++]; /* 将栈2的栈顶元素出栈 */
        }
        return OK;
}
读栈

《大话》实现:

Status StackTraverse(SqDoubleStack S)
{
        int i;
        i=0;
        while(i<=S.top1)
        {
                visit(S.data[i++]);
        }
        i=S.top2;
        while(i<MAXSIZE)
        {
                visit(S.data[i++]);
        }
        printf("\n");
        return OK;
}

链栈

定义

栈需要有栈顶指针,而单链表又可以有头指针,这启发我们可以将两者合二为一。

此时头结点失去其原有意义。至于为什么不取其他位置的指针,很简单,因为如果涉及插入和删除操作,不方便且有丢失数据逻辑关系的可能。

结构代码实现

陈越实现:

typedef struct SNode *Stack;
struct SNode{
ElementType Data;
struct SNode *Next;
} ;

《大话》实现:

/* 链栈结构 */
typedef struct StackNode
{
        SElemType data;
        struct StackNode *next;
}StackNode,*LinkStackPtr;

typedef struct
{
        LinkStackPtr top;
        int count;
}LinkStack;

名词解释:

StackNode,*LinkStackPtr;

  • StackNode 是 struct StackNode 的类型别名。
  • *LinkStackPtr; 定义了 LinkStackPtr 类型,它是一个指向 StackNode 类型的指针。这意味着 LinkStackPtr 可以用于声明指向栈节点的指针。

王道实现:

typedef struct Linknode{
    ElemType data;    //数据域
    struct Linknode *next;    指针域
}LiStack;    栈类型定义
操作及实现
初始化

《大话》实现:

/*  构造一个空栈S */
Status InitStack(LinkStack *S)
{ 
        S->top = (LinkStackPtr)malloc(sizeof(StackNode));
        if(!S->top)
                return ERROR;
        S->top=NULL;
        S->count=0;
        return OK;
}

    p = (int *)malloc(sizeof(int))

  • 分配足够的内存来存储一个整数;
  • 这里的星号 * 在 malloc 函数调用的结果后面,用于类型转换;
  • (int *) 是类型转换操作,将 malloc 返回的 void * 类型转换为 int * 类型,使得 p 可以安全地指向这块内存。​​​​​​​

​​​​​​​为什么这里不用加  *  ?

  • S->top 是一个指向 StackNode 的指针变量;
  • LinkStackPtr 已经是一个指向 StackNode 类型的指针类型;
  • LinkStackPtr 相当于 struct StackNode *。
判栈空

陈越实现:

int IsEmpty(Stack S) 
{ /*判断堆栈S是否为空,若为空函数返回整数1,否
则返回0 */
return ( S->Next == NULL );
}

《大话》实现:

/* 若栈S为空栈,则返回TRUE,否则返回FALSE */
Status StackEmpty(LinkStack S)
{ 
        if (S.count==0)
                return TRUE;
        else
                return FALSE;
}
求栈长

《大话》实现:

/* 返回S的元素个数,即栈的长度 */
int StackLength(LinkStack S)
{ 
        return S.count;
}
进栈

陈越实现:

void Push( ElementType item, Stack S) 
{ /* 将元素item压入堆栈S */
struct SNode *TmpCell;
TmpCell=(struct SNode *)malloc(sizeof(struct SNode));
TmpCell->Element = item;
TmpCell->Next = S->Next;
S->Next = TmpCell;
}

《大话》实现:

/* 插入元素e为新的栈顶元素 */
Status Push(LinkStack *S,SElemType e)
{
        LinkStackPtr s=(LinkStackPtr)malloc(sizeof(StackNode)); 
        s->data=e; 
        s->next=S->top;	/* 把当前的栈顶元素赋值给新结点的直接后继,见图中① */
        S->top=s;         /* 将新的结点s赋值给栈顶指针,见图中② */
        S->count++;
        return OK;
}
出栈

陈越实现:

ElementType Pop(Stack S)
{ /* 删除并返回堆栈S的栈顶元素 */
 struct SNode *FirstCell;
ElementType TopElem;
if( IsEmpty( S ) ) {
printf(“堆栈空”); return NULL;
} else {
FirstCell = S->Next; 
S->Next = FirstCell->Next;
TopElem = FirstCell ->Element;
free(FirstCell);
return TopElem;
}
}

《大话》实现:

/* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
Status Pop(LinkStack *S,SElemType *e)
{ 
        LinkStackPtr p;
        if(StackEmpty(*S))
                return ERROR;
        *e=S->top->data;
        p=S->top;					/* 将栈顶结点赋值给p,见图中③ */
        S->top=S->top->next;    /* 使得栈顶指针下移一位,指向后一结点,见图中④ */
        free(p);                    /* 释放结点p */        
        S->count--;
        return OK;
}
读栈

《大话》实现:

Status StackTraverse(LinkStack S)
{
        LinkStackPtr p;
        p=S.top;
        while(p)
        {
                 visit(p->data);
                 p=p->next;
        }
        printf("\n");
        return OK;
}

队列

定义

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

允许插入的一头叫队尾,允许删除的一头叫队尾;

队列是一种先进先出(First In First Out)的线性表,简称 FIFO 结构。

抽象数据类型描述

  1. Queue CreatQueue( int MaxSize ):生成长度为MaxSize的空队列;
  2. int IsFullQ( Queue Q, int MaxSize ):判断队列Q是否已满;
  3. void AddQ( Queue Q, ElementType item ): 将数据元素item插入队列Q中;
  4. int IsEmptyQ( Queue Q ): 判断队列Q是否为空;
  5. ElementType DeleteQ( Queue Q ):将队头数据元素从队列中删除并返回。

顺序存储结构

定义

为什么要引入 front 和 rear?

让我们想一下,当执行入列操作时是在队尾,,直接插入,不需要移动其他任何元素,时间复杂度低;但是,在执行出列操作时,按以往案例,似乎需要删除头结点,同时所有节点向前移动,时间复杂度较高。

怎么办呢?

不妨想想,假设对头不一定要在头结点位置,同时为了方便解决当只有一个数据元素时对头队尾重合的问题(难以判断是空还是只有一个),我们在出队时选择直接“原地”消失队头,同时引入 front 指针指向队头元素,rear 指针指向队尾的下一个位置。

这样当然还会有新的问题,比如但我们进行入列和出列,使得前面几个位置空出,后面几个位置占满,此时 rear就会超出数组之外,指向非法位置,但此时空间并没有全部利用,存在“伪溢出”。

如何解决这个问题呢,很简单,头尾相接。

循环队列

定义

循环队列

队列中的头尾相接的顺序存储结构(有点拗口哈哈);

即把出存储队列元素的表从逻辑上视为一个环;

队列空时,front = rear ;队列满时,选择空出一个元素空间,使得( rear + 1 )% QueueSize == front 。

 补充:

通用的计算队列长度的公式:

( rear - front + QueueSize )% QueueSize

 结构代码实现

《大话》实现:

/* 循环队列的顺序存储结构 */
typedef struct
{
	QElemType data[MAXSIZE];
	int front;    	/* 头指针 */
	int rear;		/* 尾指针,若队列不空,指向队列尾元素的下一个位置 */
}SqQueue;

Status visit(QElemType c)
{
	printf("%d ",c);
	return OK;
}
操作及实现
初始化

《大话》实现:

/* 初始化一个空队列Q */
Status InitQueue(SqQueue *Q)
{
	Q->front=0;
	Q->rear=0;
	return  OK;
}

王道实现:

void IniQueue(SqQueue &Q){
    Q.rear = Q.front = 0;    //初始化队头、队尾指针
}
判队空

《大话》实现:

/* 若队列Q为空队列,则返回TRUE,否则返回FALSE */
Status QueueEmpty(SqQueue Q)
{ 
	if(Q.front==Q.rear) /* 队列空的标志 */
		return TRUE;
	else
		return FALSE;
}

王道实现:

bool isEmpty(SqQueue Q){
    if (Q.rear == Q.front)
        return true;
    else
        return false;
}    
入队

《大话》实现:

/* 若队列未满,则插入元素e为Q新的队尾元素 */
Status EnQueue(SqQueue *Q,QElemType e)
{
	if ((Q->rear+1)%MAXSIZE == Q->front)	/* 队列满的判断 */
		return ERROR;
	Q->data[Q->rear]=e;			/* 将元素e赋值给队尾 */
	Q->rear=(Q->rear+1)%MAXSIZE;/* rear指针向后移一位置, */
								/* 若到最后则转到数组头部 */
	return  OK;
}

王道实现:

void EnQueue(LinkQueue &Q,ElemType x){
    LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));
    s->data = x;
    s->next = NULL;
    Q.rear->next = s;    //插入队尾
    Q.rear = s;    //修改队尾指针
}

Q.rear->next = s;

  • 目的:将新节点 s 插入到队列的末尾。

  • 作用:设置当前队尾节点 Q.rear 的 next 指针指向新节点 s

Q.rear = s;

  • 目的:更新队尾指针 Q.rear,使其指向新加入的节点 s

  • 作用:将 Q.rear 设置为新节点 s,确保 Q.rear 指向队列的最后一个节点。

出队

《大话》实现:

/* 若队列不空,则删除Q中队头元素,用e返回其值 */
Status DeQueue(SqQueue *Q,QElemType *e)
{
	if (Q->front == Q->rear)			/* 队列空的判断 */
		return ERROR;
	*e=Q->data[Q->front];				/* 将队头元素赋值给e */
	Q->front=(Q->front+1)%MAXSIZE;	/* front指针向后移一位置, */
									/* 若到最后则转到数组头部 */
	return  OK;
}

王道实现:

bool DeQueue(LinkQueue &Q,ElemType &x){
    if(Q.front == Q.rear)
        return false;
    LinkNode *q = Q.front->next;    //创建一个临时指针 q,用于指向删除后的队首节点。
    x = p->data;
    Q.front->next = p->next;
    if(Q.rear == p)
        Q.rear = Q.front;    //若原队列只有一个结点,删除后变空
    free(p);
    return true;
}
读队

《大话》实现:

/* 从队头到队尾依次对队列Q中每个元素输出 */
Status QueueTraverse(LinkQueue Q)
{
	QueuePtr p;
	p=Q.front->next;
	while(p)
	{
		 visit(p->data);
		 p=p->next;
	}
	printf("\n");
	return OK;
}

链式存储结构

链队列,即使用尾进头出的单链表,插入和删除分别在链表的两头进行。

结构代码实现

陈越实现:

struct Node{
 ElementType Data;
 struct Node *Next;
}; 
struct QNode{ /* 链队列结构 */
 struct Node *rear; /* 指向队尾结点 */
 struct Node *front; /* 指向队头结点 */
}; 
typedef struct QNode *Queue;
Queue PtrQ;

 《大话》实现:

typedef struct QNode	/* 结点结构 */
{
   QElemType data;
   struct QNode *next;
}QNode,*QueuePtr;

typedef struct			/* 队列的链表结构 */
{
   QueuePtr front,rear; /* 队头、队尾指针 */
}LinkQueue;
操作及实现
初始化
/* 构造一个空队列Q */
Status InitQueue(LinkQueue *Q)
{ 
	Q->front=Q->rear=(QueuePtr)malloc(sizeof(QNode));
	if(!Q->front)
		exit(OVERFLOW);
	Q->front->next=NULL;
	return OK;
}
判队空
/* 若Q为空队列,则返回TRUE,否则返回FALSE */
Status QueueEmpty(LinkQueue Q)
{ 
	if(Q.front==Q.rear)
		return TRUE;
	else
		return FALSE;
}
求队长
/* 求队列的长度 */
int QueueLength(LinkQueue Q)
{ 
	int i=0;
	QueuePtr p;
	p=Q.front;
	while(Q.rear!=p)
	{
		 i++;
		 p=p->next;
	}
	return i;
}
入队
/* 插入元素e为Q的新的队尾元素 */
Status EnQueue(LinkQueue *Q,QElemType e)
{ 
	QueuePtr s=(QueuePtr)malloc(sizeof(QNode));
	if(!s) /* 存储分配失败 */
		exit(OVERFLOW);
	s->data=e;
	s->next=NULL;
	Q->rear->next=s;	/* 把拥有元素e的新结点s赋值给原队尾结点的后继,见图中① */
	Q->rear=s;		/* 把当前的s设置为队尾结点,rear指向s,见图中② */
	return OK;
}
出队

陈越实现:

//不带头结点的链式队列出队操作的一个示例

ElementType DeleteQ ( Queue PtrQ )
{ struct Node *FrontCell; 
 ElementType FrontElem;
 if ( PtrQ->front == NULL) {
 printf(“队列空”); return ERROR;
 } 
 FrontCell = PtrQ->front;
 if ( PtrQ->front == PtrQ->rear) /* 若队列只有一个元素 */
 PtrQ->front = PtrQ->rear = NULL; /* 删除后队列置为空 */
 else 
 PtrQ->front = PtrQ->front->Next;
 FrontElem = FrontCell->Data;
 free( FrontCell ); /* 释放被删除结点空间 */
 return FrontElem;
}

《大话》实现:

/* 若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR */
Status DeQueue(LinkQueue *Q,QElemType *e)
{
	QueuePtr p;
	if(Q->front==Q->rear)
		return ERROR;
	p=Q->front->next;		/* 将欲删除的队头结点暂存给p,见图中① */
	*e=p->data;				/* 将欲删除的队头结点的值赋值给e */
	Q->front->next=p->next;/* 将原队头结点的后继p->next赋值给头结点后继,见图中② */
	if(Q->rear==p)		/* 若队头就是队尾,则删除后将rear指向头结点,见图中③ */
		Q->rear=Q->front;
	free(p);
	return OK;
}
读队
/* 从队头到队尾依次对队列Q中每个元素输出 */
Status QueueTraverse(LinkQueue Q)
{
	QueuePtr p;
	p=Q.front->next;
	while(p)
	{
		 visit(p->data);
		 p=p->next;
	}
	printf("\n");
	return OK;
}

双端队列

参考

王道《数据结构》;

程杰《大话数据结构》;

陈越《数据结构》。

  • 14
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王悦雨的向北日记

我找不到很好的原因

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值