数据结构——栈和队列相关算法实现

数据结构栈和队列的基本算法实现

限定性线性表——栈

栈的定义

栈作为一种限定性的线性表,是将线性表的插入和删除操作限制为仅在表的一端进行。

基本算法演示

/*
栈的常见操作:
    1.初始化栈
    2.元素进栈
    3.元素出栈
    4.栈的遍历
    5.判断栈是否为空栈
    6.清空整个栈 

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

typedef struct Node
{
    int date;
    struct Node * pNext;
}NODE,* PNODE;

typedef struct Stack
{
    PNODE pTop;
    PNODE pBottom;
}STACK, * PSTACK;

void init(PSTACK pS)
{
    pS->pTop = (PNODE)malloc(sizeof(NODE));
    if (NULL == pS->pTop)
    {
        printf("动态内存分配失败");
        exit(-1);

    }
    else
    {
        pS->pBottom = pS->pTop;//如果分配成功的话,这两个节点都指向 同一个节点(头节点)
        pS->pTop->pNext = NULL; //模拟最后的那个“头节点”pS->Bottom->pNext = NUll 也是一样
    }

}

void push(PSTACK pS, int val)
{
    PNODE pNew = (PNODE)malloc(sizeof(NODE));
    pNew->date = val;
    pNew->pNext = pS->pTop;//按照逻辑来的话,进栈时,新的元素会在栈顶,所以要pNext->pTop
    pS->pTop = pNew; //把新的入栈的元素作为栈的top 

} 

void traverse(PSTACK pS)
{
    PNODE p = pS->pTop;
    while (p != pS->pBottom)
    {
        printf("%d ",p->date);
        p = p->pNext;
    }
    printf("\n");

    return;
} 

bool empty(PSTACK pS)
{
    if(pS->pTop == pS->pBottom)
         return true; //如果为空,返回true(证明是空的)
    else
        return false;
} 

//把pS所指向的栈出栈一次,并把出栈的元素存入pVal形参所指向的变量中,
//如果出栈成功,返回true,否则返回false
bool pop(PSTACK pS,int * pVal)
{
    if (empty(pS))//pS 本身存放的就是S的地址,直接返回给empty()函数
    {
        return false;
    }
    else
    {
        //首先需要一个指针r来指向 栈顶元素,但是如果是pS->pTop = pS->pNext 的话
        //内存就没有释放,造成内存泄漏,所以这个方法不可取。 

        PNODE r = pS->pTop;
        *pVal = r->date;
        pS->pTop = r->pNext;//r 指向栈顶,所以把r的next域赋给栈顶
        free(r);
        r = NULL;
        return true;

    }
}
//清空 

void clear(PSTACK pS)
{
    if(empty(pS))
    {
        return;
    }
    else
    {
        PNODE p = pS->pTop;
        PNODE q = NULL;
        while(p!=pS->pBottom)
        {
            q = p->pNext;
            free(p);
            p = q;  

        }
        //清空之后pTop 的值一定要改写
         pS->pTop = pS->pBottom; 

    }

}

int main(void)
{
    STACK S;
    int val;
    init(&S);//对栈进行初始化 ,去地址才会放入元素
    push(&S,1);
    push(&S,2);
    push(&S,3);
    push(&S,4);
    push(&S,5);
    push(&S,6);
    traverse(&S);
    clear(&S); //清空之后就会提示出栈失败 

    if(pop(&S,&val))//需要判断是否为空,如果空了就无法出栈,所以需要一个返回值,但是进栈不会满的。
    {
        printf("出栈成功,出栈的元素是%d\n",val);
    }
    else
    {
        printf("出栈失败!\n");
    }
    traverse(&S);
    return 0;
}

运行演示

算法小结

所有的算法已经给出,值得注意的是在clear()算法中 PNODE p = pS->pTop;PNODE q = NULL; 定义了两个指针,以为一个被free掉后就无法进行操作了,对于pop()函数就没有这个问题,因为它只执行了一次 ,也就是说,只进行了一次出栈操作,然后操作完成之后才把r指针给free掉的,所以一个指针就可以完成这个操作。

限定性线性表——队列

队列是另外一种限定性的线性表,它只允许在表的一端插入元素,在另外一端删除元素。

基本算法演示(链队列)

/*
队列的常见操作:
    1.初始化队列
    2.元素进队列
    3.元素出队列
    4.队列的遍历

*/ 

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

typedef struct Node

{
    int date;
    struct Node * pNext;
}NODE, * PNODE;//LInkQueueNode

typedef struct LinkQueue
{
    PNODE pFront;
    PNODE pRear;
}LINKQUEUE,* PLINKQUEUE;

bool InitQueue(PLINKQUEUE pQ)
{
    pQ->pFront= (PNODE)malloc(sizeof(NODE));
    if (NULL == pQ->pFront)
    {
        printf("动态内存分配失败!");
        exit(-1);

    }
    else if(NULL != pQ->pFront)
    {
        pQ->pRear = pQ->pFront;
        pQ->pFront->pNext = NULL;
        return (true);
    }
    else
        return (false);//溢出 

}

bool EnterQueue(PLINKQUEUE pQ ,int x)
{

    PNODE pNew = (PNODE)malloc(sizeof(NODE));
    if(pNew != NULL)
    {
    pNew->date = x;
    pNew->pNext = NULL;
    pQ->pRear->pNext = pNew;
    pQ->pRear = pNew;
    return (true); 

    }
    else
        return false;

} 

void traverse(PLINKQUEUE pQ)
{
    PNODE p = pQ->pFront->pNext;//注意这个地方队列和栈的不同
                                //PNODE p = pS->pTop;   while (p != pS->pBottom)  这是栈的条件 

    while (p)
    {
        printf("%d ",p->date);
        p = p->pNext;
    }
    printf("\n");

    return;
} 

bool DeleteQueue(PLINKQUEUE pQ,int * x)   //出队
{
    PNODE p;
    if (pQ->pRear==NULL)      //队列为空
        return false;
    p=pQ->pFront;            //p指向第一个数据节点
    if (pQ->pFront==pQ->pRear)  //队列中只有一个节点时
        pQ->pFront=pQ->pRear=NULL;//必须要更改值,不然指针就会指向他处
    else                    //队列中有多个节点时
        pQ->pFront=pQ->pFront->pNext;
    *x = p->date;
    free(p);
    return true;
}

int main()
{
    LINKQUEUE Q;
    int x;
    InitQueue(&Q);
    EnterQueue(&Q,10);
    EnterQueue(&Q,20);
    EnterQueue(&Q,30);
    EnterQueue(&Q,40);
    traverse(&Q);
    DeleteQueue(&Q,&x);
    traverse(&Q);
    return 0;
}

运行演示

算法小结

队列的操作和栈的操作基本原理上是差不多的,值得注意的是再对队列进行遍历的话和栈的遍历稍微有点差别。其中需要注意的地方已经在代码块中进行了说明。

基本算法演示(循环队列)

/*
    1.循环队列初始化
    2.循环队列进队
    3.循环队列出队
    4.循环队列遍历
    5.循环队列长度
*/

// 实现循环队列
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 21
typedef int ElementType;

typedef struct  {
    int data[MaxSize];
    int rear;      // 队尾指针
    int front;     // 队头指针
}Queue,*L;

void InitQueue(Queue * Q )
{
    Q->front = Q->rear = 0;
}
// 元素入队
void AddQ(Queue *PtrQ, int item)
{
    if( (PtrQ->rear+1)%MaxSize == PtrQ->front )
    {
        printf("队列满.\n");
        return;
    }
    PtrQ->rear = (PtrQ->rear+1) % MaxSize;
    PtrQ->data[PtrQ->rear] = item;
}

// 删除队头元素并把队头元素返回
int DeleteQ( Queue *PtrQ )
{
    if( PtrQ->front == PtrQ->rear )
    {
        printf("队列空.\n");
        return -1;
    }
    else {
        PtrQ->front = (PtrQ->front+1) % MaxSize;
        return PtrQ->data[PtrQ->front];
    }
}

// 队列元素的遍历
void print(Queue *PtrQ)
{
    int i = PtrQ->front;
    if( PtrQ->front == PtrQ->rear )
    {
        printf("队列空.");
        return;
    }
    printf("队列存在的元素如下:");
    while( i != PtrQ->rear)
    {
        printf("%d ", PtrQ->data[i+1]);
        i++;
        i = i % MaxSize;
    }
    return;
}

int len(Queue *PtrQ)
{
    return (PtrQ->rear-PtrQ->front+MaxSize)%MaxSize;
}
int main()
{
    Queue  Q;    //注意不是Queue * Q; 因为数组本身就是地址吧~(emmmm,应该是,求大佬解答)
     int length;
     length = len(&Q);             //用Queue * Q 的话会报错
    InitQueue(&Q);
    AddQ(&Q,1);
    AddQ(&Q,2);
    AddQ(&Q,3);
    AddQ(&Q,4);

    print(&Q);
    DeleteQ(&Q);//出队一次
    print(&Q);
    printf("\n循环队列的长度为%d",length);
    return 0;
}

运行演示

算法小结

循环队列和链队列基本是一致的,之所以引入“循环队列”是因为,对于顺序列会存在“假溢出的现象”。相关概念不多做解释,原理主要在数据结构-用C语言描述(第二版)[耿国华] 一书的p101-103。值得注意的是,在main方法中和链队列不同的是Queue Q;个人认为是利用数组模拟的原因,因为数组本身也是利用地址传值嘛。关于循环队列长度计算:当rear大于front时,循环队列的长度:rear-front,当rear小于front时,循环队列的长度:分为两类计算 0+rear和Quesize-front即rear-front+Quesize。总的来说,总长度是(rear-front+Quesize)%Quesize

循环链表拓展

头节点循环链表

带头结点的循环链表表示队列, 并且只设一个指针指向队尾元素结点, 试编写相应的队列初始化,入队列和出队列的算法。

/*  数据结构算法题(假设以带头结点的循环链表表示队列,
 *  并且只设一个指针指向队尾元素结点(注意不设头指针)
 *  试编写相应的队列初始化,入队列和出队列的算法!)
 */

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define OK 1
#define ERROR 0

typedef int QElemType;
typedef int Status;

typedef struct QNode
{
    QElemType data;
    struct QNode * rear;
    struct QNode * next;
}QNode,*LinkQueue;

//链式队列的初始化
Status InitLinkQueue(LinkQueue * L)
{

    (*L)=(LinkQueue)malloc(sizeof(QNode));
    if((*L)==NULL)
    {
        printf("内存分配失败!\n");
        return OK;
    }
    (*L)->rear=(*L);
    return OK;
}

//链式队列的建立
Status Create(LinkQueue * L,int n)
{
    srand(time(0));
    LinkQueue P;
    for(int i=0;i<n;i++)
    {
        P=(LinkQueue)malloc(sizeof(QNode));
        P->data=rand()%100+1;
        (*L)->rear->next=P;
        (*L)->rear=P;
    }
    P->next=(*L);
    return OK;
}

//入队操作
Status EnQueue(LinkQueue * L,QElemType e)
{
    LinkQueue P;
    P=(LinkQueue)malloc(sizeof(QNode));
    P->data=e;
    P->next=(*L);
    (*L)->rear->next=P;
    (*L)->rear=P;
    return OK;
}

//出队操作
Status DeQueue(LinkQueue * L,QElemType * e)
{
    LinkQueue temp;
    *e=(*L)->next->data;
    temp=(*L)->next;
    (*L)->next=(*L)->next->next;
    delete(temp);
    return OK;

}

//输出
void Print(LinkQueue * L)
{
    LinkQueue P;
    P=(*L)->next;
    printf("输出元素:\n");
    while(P!=(*L))
    {
        printf("%d ",P->data);
        P=P->next;
    }
    printf("\n");
}

int main()
{
    LinkQueue L;
    int ElemNumber;
    QElemType EnElem,DeElem;
    InitLinkQueue(&L);
    printf("请输入元素个数:\n");
    scanf("%d",&ElemNumber);
    Create(&L,ElemNumber);
    Print(&L);
    printf("请输入入队元素:\n");
    scanf("%d",&EnElem);
    EnQueue(&L,EnElem);
    Print(&L);
    printf("出队操作,并返回出队元素:\n");
    DeQueue(&L,&DeElem);
    printf("出队元素为:%d\n",DeElem);
    Print(&L);
    return 0;
}

参考文献

原文地址:https://www.cnblogs.com/yjlblog/p/8884751.html

时间: 2024-08-03 07:18:07

数据结构——栈和队列相关算法实现的相关文章

C数据结构-栈和队列,括号匹配举例

1.栈和队列是两种特殊的线性表 运算操作被限定只能在表的一端或两端插入,删除元素,故也称它们为限定的线性表结构 2.栈的基本运算 1).Stackinit(&s) 构造一个空栈 2).Stackempty(s) 判断s是否为空栈,当s为空栈时,函数返回值1 否则 0 3).Push(&s,x)  在栈s 的顶部插入元素x,简称将x入 栈 4).Pop(&s,&x) 在栈s 中删除顶元并将其值保存在x单元中返回,简称将x出栈 5)Gettop(s,&x)  读s栈中的

数据结构-栈与队列

相比于数组这种存储数据的数据,栈(Stock)和队列(Queue)主要作用是在程序中作为构思算法的辅助工具,是一种程序员开发过程中的便利工具.Stock和Queue具有访问受限以及更加抽象的特征. 一.栈 栈只允许访问最后一个插入的元素,即栈是先进后出(FILO)的一种数据结构.栈主要提供的算法包括push,pop和peek.其中push是插入一个元素,pop是弹出最近添加的一个元素,peek是返回最近添加的一个元素. 栈的底层实现可以是数组,也可以是链表,这里采用数组实现一个栈,代码如下: 1

c数据结构 -- 栈与队列

栈和队列 ·栈和队列是两种常用的.重要的数据结构 ·栈和队列是限定插入和删除只能在表的“端点”进行的线性表 栈 只能在队尾插入,只能在队尾删除 -- 后进后出 表尾称为栈顶:表头称为栈底 插入元素到栈顶(即表尾)的操作,称为入栈 从栈顶删除最后一个元素的操作,称为出栈 注意:函数调用的流程就是入栈和出栈的实现,遵循后调用的先返回 队列 只能在队尾插入,只能在对队头删除 -- 先进先出 顺序栈的实现: #include <stdio.h> #include <stdlib.h> #d

栈与队列相关笔试题

1.用两个栈实现队列 分析:队列的特点是"先进先出",而栈的特点是"后进先出",入队列时让所有的元素进到stack1中,出队列时先判断stack2是否为空,不为空直接pop即可,若为空,将stack1中所有的元素"倒入"stack2中,这样就用两个栈实现了队列. 实现代码: class Solution { public: void push(int node) { stack1.push(node); } int pop() { if(stac

C#数据结构—栈和队列

一:栈 栈和队列也是线性结构,线性表.栈和队列这三种数据结构的数据元素以及数据元素间的逻辑关系完全相同,差别是线性表的操作不受限制,而栈和队列的操作受到限制.栈的操作只能在表的一端进行,队列的插入操作在表的一端进行而其它操作在表的另一端进行,所以,把栈和队列称为操作受限的线性表. 1:栈的定义及基本运算 栈(Stack)是操作限定在表的尾端进行的线性表.表尾由于要进行插入.删除等操作,所以,它具有特殊的含义,把表尾称为栈顶(Top),另一端是固定的,叫栈底(Bottom).当栈中没有数据元素时叫

数据结构-栈、队列和链表

一.栈stack 是后进先出的数据结构 栈顶指针指的始终是栈最上方元素的一个标记,即放在最上面的元素.栈顶元素为空时令top为-1. 在使用pop()函数和top()函数时,需要使用empty()判断栈是否为空. 在STL中stack容器来编写代码,STL定义stack的复杂度是O(1). 常见函数: clear() size() empty() push() pop() top() 二.队列queue 是一种先进先出的数据结构 需要一个队首指针front来指向队首元素的前一个位置,而使用一个队

数据结构-栈和队列

栈和队列都是线性表,所以满足-只有一个节点没有前继,只有后继,只有一个节点只有后继没有前继,其他的节点只有一个前继只有一个后继. 栈的定义是先进后出,最典型的例子就是弹夹,最先进去的反而是最后射出来的,在实际的软件开发中会进经常的遇到这种类型的线性表,我们成为LIFO(Last in First out).可以把栈想象成是只有一个出口的容器,最先放进去的东西只能够等其上面的东西呗拿走之后才能够拿出来. 队列则是另外的一种线性表,队列在我们生活中就更常见了,比如排队啊什么的,队列讲的是先进先出,在

浅谈算法和数据结构: 十二 无向图相关算法基础

从这篇文章开始介绍图相关的算法,这也是Algorithms在线课程第二部分的第一次课程笔记. 图的应用很广泛,也有很多非常有用的算法,当然也有很多待解决的问题,根据性质,图可以分为无向图和有向图.本文先介绍无向图,后文再介绍有向图. 之所以要研究图,是因为图在生活中应用比较广泛: 无向图 图是若干个顶点(Vertices)和边(Edges)相互连接组成的.边仅由两个顶点连接,并且没有方向的图称为无向图. 在研究图之前,有一些定义需要明确,下图中表示了图的一些基本属性的含义,这里就不多说明. 图的

数据结构——栈与队列

题目: 1.编写函数,采用链式存储实现栈的初始化.入栈.出栈操作 2.编写函数,采用顺序存储实现栈的初始化.入栈.出栈操作 3.编写函数,采用链式存储实现队列的初始化.入队.出队操作 4.编写函数,采用顺序存储实现队列的初始化.入队.出队操作 5.编写一个主函数,在主函数中设计一个简单的菜单,分别调试上述算法 题目分析: 1.顺序栈的类型定义 #define MAX 100  //栈的最大值 typedef struct {     ElemType *base;     int top; }