zoukankan      html  css  js  c++  java
  • 数据结构——栈和队列相关算法实现

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

    限定性线性表——栈

    栈的定义

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

    基本算法演示

    /*
    栈的常见操作:
    	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("
    ");
    		
    	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
    ",val);
    	}
    	else
    	{
    		printf("出栈失败!
    "); 
    	}
    	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("
    ");
    		
    	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("队列满.
    ");
            return;
        }
        PtrQ->rear = (PtrQ->rear+1) % MaxSize;
        PtrQ->data[PtrQ->rear] = item; 
    }
    
    
    // 删除队头元素并把队头元素返回
    int DeleteQ( Queue *PtrQ )
    {    
        if( PtrQ->front == PtrQ->rear )
        {
            printf("队列空.
    ");
            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("
    循环队列的长度为%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("内存分配失败!
    ");
            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("输出元素:
    ");
        while(P!=(*L))
        {
            printf("%d ",P->data);
            P=P->next;
        }
        printf("
    ");
    }
    
    
    int main()
    {
        LinkQueue L;
        int ElemNumber;
        QElemType EnElem,DeElem;
        InitLinkQueue(&L);
        printf("请输入元素个数:
    ");
        scanf("%d",&ElemNumber);
        Create(&L,ElemNumber);
        Print(&L);
        printf("请输入入队元素:
    ");
        scanf("%d",&EnElem);
        EnQueue(&L,EnElem);
        Print(&L);
        printf("出队操作,并返回出队元素:
    ");
        DeQueue(&L,&DeElem);
        printf("出队元素为:%d
    ",DeElem);
        Print(&L);
        return 0;
    }
    
    

    参考文献

  • 相关阅读:
    .ascx和.ashx文件说明
    零基础学习JavaScript(1)1.2JavaScript功能简介
    零基础学习JavaScript(1)1.1什么是JavaScript
    小实验3:实现haproxy的增、删、查
    小实验2:三级菜单
    python enumerate
    小实验1:购物车记录
    对西部数据硬盘自带的加密进行修改密码和取消密码保护
    CloudFoundry忘记密码?
    获取网页上没有下载链接的视频音频资源
  • 原文地址:https://www.cnblogs.com/yjlaugus/p/8884751.html
Copyright © 2011-2022 走看看