數據結構——棧和隊列相關算法實現

數據結構棧和隊列的基本算法實現

限定性線性表——棧

棧的定義

棧做爲一種限定性的線性表,是將線性表的插入和刪除操做限制爲僅在表的一端進行。算法

基本算法演示

/*
棧的常見操做:
	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)%Quesizespa

循環鏈表拓展

頭節點循環鏈表

帶頭結點的循環鏈表表示隊列, 而且只設一個指針指向隊尾元素結點, 試編寫相應的隊列初始化,入隊列和出隊列的算法。.net

/*  數據結構算法題(假設以帶頭結點的循環鏈表表示隊列,
 *  而且只設一個指針指向隊尾元素結點(注意不設頭指針)
 *  試編寫相應的隊列初始化,入隊列和出隊列的算法!) 
 */

#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;
}

參考文獻

相關文章
相關標籤/搜索