《數據結構與算法分析》學習筆記-第三章-表、棧和隊列


Fork me on Github

我本身實現了一個雙向循環鏈表,發佈在Github上。
QuickList,包含完整的鏈表模塊源碼和測試用例。遵循GPL V2.0協議
你們能夠去github上獲取,若是以爲好用請幫我點個star,謝謝啦嘿嘿~
QuickList傳送門html

3.1 抽象數據類型

程序設計的基本法則之一是例程不該超過一頁,能夠經過把程序模塊化實現。每一個模塊是一個邏輯單位並執行某個特定的任務,它經過調用其餘模塊而使自己保持很小。優勢以下:node

  1. 調試小程序比調試大程序容易得多
  2. 多我的同時對一個模塊式程序編程要更容易
  3. 一個好的模塊化程序把某些依賴關係只侷限在一個歷程中,這樣修改起來會更容易
  4. 全局變量及其反作用是有害的觀念也正是出於模塊化是有益的想法

概念:抽象數據類型ADT是一些操做的集合。這種操做的實現只在程序中編寫一次,而程序中任何其餘部分須要在該ADT上運行起鬨的一種操做時,能夠經過調用合適的函數來進行。若是要改變操做的細節,那麼只須要修改運行這些ADT操做的例程就很容易實現。git

3.2 表ADT

  • 咱們將處理通常的形如 A1, A2, A3, ... , AN的表,咱們說這個表的大小是N,咱們稱大小爲0的表爲空表。對於除空表外的任何表,咱們說Ai+1後繼Ai(i < N),並稱Ai-1前驅Ai(i > 1)。表中的第一個元素是A1,而最後一個元素是AN。咱們將不定義A1的前驅元,也不定義AN的後繼元。元素Ai在表中的位置爲i
  • 與這些「定義」相關的是咱們要在表ADT上進行的操做的集合。PrintList/MakeEmpty/Find/Insert/Delete/FindKth/Next/Previous等

3.2.1 表的簡單數組實現

連續存儲,訪問很快,插入刪除很慢,並且須要定義數組的大小,而這個大小一般要設的大一些浪費內存空間。因此簡單數組通常不用來實現表這種結構github

3.2.2 鏈表

指針變量就是包含存儲另外某個數據地址的變量。所以,若是P被聲明爲指向一個結構的指針,那麼存儲在P中的值就被解釋爲主存中的一個位置,在該位置可以找到一個結構,該結構的一個域能夠經過P->FieldNme訪問。使用鏈表實現表,能夠在線性時間內完成PrintList/Find(L, Key)。FindKth(L, i)須要花費O(i)時間。實踐中這個界是保守的,由於調用FindKth經常以按i排序的方式進行。例如FindKth(L,2), FindKth(L,3)能夠經過對錶的一次掃描同時實現。刪除命令能夠經過一次free操做和一次指針的修改實現;插入命令能夠經過一次malloc操做和兩次指針修改實現算法

3.2.3 程序設計細節

使用HEAD節點實現一些在表的前面實現插入和刪除操做。實現FindPrevious例程,來實現刪除節點的操做。編程

  • IsEmpty
// 書上例程
int 
IsEmpty(List L)
{
    return L->next == NULL;
}
  • IsLast
// 書上例程
int
IsLast(Position P, List L)
{
    return P->next == NULL;
}
  • Find
// 書上例程
Position
Find(Element X, List L)
{
    Position P;
    
    P = L->next;
    while (P != NULL && P->Element != X) {
        P = P->next;
    }
    
    return P;
}
  • Delete
// 書上例程
void
Delete(Element X, List L)
{
    Position P, TmpCell;
    P = FindPrevious(X, L);
    
    if (!IsLast(P, L)) {
        TmpCell = P->next;
        P->next = TmpCell->next;
        free(TmpCell);
    }
}
  • FindPrevious
// 書上例程
Position
FindPrevious(Element X, List L)
{
    Position P;
    P = L;
    
    while (P->Next != NULL && P->next->Element != X) {
        P = P->next;
    }
    return P;
}
  • Insert
// 書上例程
void
Insert(Element X, List L, Postion P)
{
    Position new = malloc(sizeof(Position));
    if (new == NULL) {
        printf("malloc failed!\n");
    }
    memset(new, 0, sizeof(Position));
    new->Element = X;
    new->next = P->next;
    P->next = new;
}

ADT中還有其餘函數,書上未實現,這裏我實現出來分享給你們。小程序

  • MakeEmpty
List
MakeEmpty(List L)
{
    Position P, tmp;
    P = L;
    
    while (P->next != NULL) {
        tmp = P->next;
        P->next = tmp->next;
        free(tmp);
    }
    
    return L; 
}
  • DeleteList
void
DeleteList(List L)
{
    MakeEmpty(L);
    free(L);
}
  • Header
Position
Header(List L)
{
    return L;
}
  • First
Position
First(List L)
{
    return L->next;
}
  • Retrieve
ElementType
Retrieve(Position P)
{
    return P->Element;
}

3.2.4 常見的錯誤

  1. 初始化變量失敗或參數是否超出規定範圍的。應該增長初始化檢查或者參數檢查,再進行相關操做
  2. 聲明指向一個結構的指針,並不建立該結構。使用malloc庫函數。它建立一個新的結構並返回指向該結構的指針。若是想讓指針變量沿着表前進,則不必建立新的結構。
  3. free(P)的結果是:P正在指向的地址沒變,可是該地址處的數據此時已經無定義了
  4. 若是從未對一個鏈表進行過刪除操做,那麼調用malloc的次數應該等於表的大小,如有表頭則再加1
  5. 刪除節點,須要一個臨時變量。由於節點被free以後,沒法訪問到原指針指向的地址
  6. malloc(sizeof(PtrToNode))只是給指針分配一個空間,並不給結構體分配足夠的空間

3.2.5 雙鏈表

須要在每一個節點上增長一個指向前驅節點的指針,增長了空間的需求,使得插入和刪除的空間開銷增長一倍,可是刪除速度加快了,由於前驅節點無需遍歷尋找api

3.2.6 循環鏈表

最後一個節點的next指針指向第一個節點,若是有頭節點則指向頭節點。若是是雙向鏈表,則第一個節點的previous指針指向最後的節點數組

3.2.7 例子

多項式ADT數據結構

  1. 多項式結構體定義
//書上例程
typedef struct {
    int CoeffArray[MaxDegree + 1];
    int HighPower;
}
  1. 初始化多項式爲0
//書上例程
void
ZeroPolynomial(Polynomial Poly)
{
    int i;
    
    for (i = 0; i <= MaxDegree; i++) {
        Poly.CoeffArray[i] = 0;
    }
    Poly.HighPower = 0;
}
  1. 多項式相加
//書上例程,時間複雜度O(N)
void
AddPolynomial(const Polynomial Poly1, const Polynomial Poly2, Polynomial PolySum)
{
    int i;
    ZeroPolynomial(PolySum);
    PolySum->HighPower = Max(Poly1->HighPower, Poly2->HighPower);
    
    for (i = 0; i < PolySum->HighPower; i++) {
        PolySum->CoeffArray[i] = Poly1->CoeffArray[i] + Poly2->CoeffArray[i];
    }
}
  1. 多項式相乘
//書上例程,時間複雜度O(N^2)
void
MultPolynomial(const Polynomial Poly1, const Polynomial Poly2, Polynomial PolyProd)
{
    int i, j;
    ZeroPolynomial(PolyProd);
    
    PolyProd->HighPower = Poly1->HighPower + Poly2->HighPower;
    
    if (PolyProd->HighPower > MaxDegree) {
        printf("Exceeded array size");
    } else {
        for (i = 0; i < Poly1->HighPower; i++) {
            for (j = 0; j < Poly2->HighPower; j++) {
                PolyProd->CoeffArray[i + j] += Poly1->CoeffArray[i] * Poly2->CoeffArray[j];
            }
        }
    }
}

另外一種方法:單鏈表。多項式的每一項含在一個節點中,而且這些節點以次數遞減的順序排序。

  1. 節點結構體定義
//書上例程
struct Node {
    int Cofficient;
    int Exponent;
    PtrToNode Next;
}
typedef struct Node *PtrToNode;
typedef PtrToNode Polynomial;
  1. 建立並初始化節點
//本身寫的
Polynomial
CreatePolynomial()
{
    Polynomial tmp;
    tmp = (Polynomial)malloc(sizeof(struct Node));
    memset(tmp, 0, sizeof(struct Node));
    return tmp;
}
  1. 銷燬並釋放節點
//本身寫的
void
DestroyPolynomial(Polynomial P)
{
    free(P);
}
  1. 多項式相加
//本身寫的。時間複雜度O(N^2)
void
AddPolynomial(const Polynomial Poly1, const Polynomial Poly2, Polynomial PolySum)
{
    int i, j;
    Polynomial P1, P2, Psum, Ptmp;
    
    Psum = PolySum;
    for (P1 = Poly1->next; P1 != NULL; P1 = P1->next) {
        for (P2 = Poly2->next; P2 != NULL; P2 = P2->next) {
            if (P1->Exponent == P2->Exponent) {
                break;
            }
        }
        
        Ptmp = CreatePolynomial();
        Ptmp->Exponent = P1->Exponent;
    
        if (P2 == NULL) {
            Ptmp->Cofficient = P1->Cofficient;
        } else {
            Ptmp->Cofficient = P1->Cofficient + P2->Cofficient;
        }
        
        Psum->next = Ptmp;
        Ptmp->next = NULL;
        Psum = Psum->next;
    }
}
  1. 多項式相乘
//本身寫的。時間複雜度O(N^3)
void
MultPolynomial(const Polynomial Poly1, const Polynomial Poly2, Polynomial PolyProd)
{
    int i, j, exponentTmp;
    Polynomial P1, P2, Prod, Ptmp;
    
    Prod = PolyProd->next;
    for (P1 = Poly1->next; P1 != NULL; P1 = P1->next) {
        for (P2 = Poly2->next; P2 != NULL; P2 = P2->next) {
            exponentTmp = P1->Exponent + P2->Exponent;
            while (Prod != NULL) {
                if (Prod->Exponent == exponentTmp) {
                    Prod->Cofficient += P1->Cofficient * P2->Cofficient;
                    break;
                }
                Prod = Prod->next;
            }
            
            if (Prod == NULL) {
                Ptmp = CreatePolynomial();
                Ptmp->Exponent = exponentTmp;
                Ptmp->Cofficient = P1->Cofficient * P2->Cofficient;
                
                Prod = PolyProd->next;
                PolyProd->next = Ptmp;
                Ptmp->next = Prod;
            }
        }
    }    
}

桶式排序

// 本身寫的,時間複雜度O(N)
void
sortBucket(int *array, int num, int maxSize)
{
    int CountArraySize = maxSize + 1;
    int *Count = (int *)malloc(CountArraySize * sizeof(int));
    memset(Count, 0, CountArraySize * sizeof(int));
    
    int inputCnt;
    for (inputCnt = 0; inputCnt < num; inputCnt++) {
        Count[array[inputCnt]] = array[inputCnt];
    }
    
    for (inputCnt = 1; inputCnt < CountArraySize; inputCnt++) {
        if (Count[inputCnt] != 0) {
            printf("%d", Count[inputCnt]);
        }
    }
}

基數排序,我本身實現了並上傳到了Github上,你們能夠得到源碼。
bucketSort2

// SPDX-License-Identifier: GPL-2.0-only
/*
 *	bucketSort2.c
 *
 * Copyright (C) 2020  xuri
 */

/*
 * This file show how bucket sort running, it based on QuickList module
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "QuickList.h"

#define ARRAY_NUM	10
#define BUCKET_NUM 10 //10 buckets
#define NUMBER_RANGE	3 // 0 - 999

void bucketSort2(int *inputArray, int num)
{
	int divisor = 1;
	int cnt; 
	int *recvdata;
	int times = 0;
	QuickListNode_T *tmp = NULL, *tmp2 = NULL;
	QuickListNode_T *qlArray[BUCKET_NUM];


	/* initialize the qlArray */
	memset(qlArray, 0, BUCKET_NUM); 
	for (cnt = 0; cnt < BUCKET_NUM; cnt++) {
		qlArray[cnt] = QuickListCreateList();	
	}


	/* first time input array and create listnode add to qlArray */
	for (cnt = 0; cnt < num; cnt++) {
		tmp = QuickListCreateNode(&inputArray[cnt]);
		QuickListAddNodetoTail(qlArray[(inputArray[cnt] / divisor % 10)], tmp);
	}
	printf("after first time\n");	

	/* finish bucket sort */
	while (times < NUMBER_RANGE - 1) {
		divisor *= 10;
		for (cnt = 0; cnt < BUCKET_NUM; cnt++) {
			tmp = NULL;
			tmp2 = NULL;
			QuickList_for_each_entry_safe(qlArray[cnt], tmp, tmp2) {
				recvdata = QuickList_entry(int, tmp);
				if ((*recvdata / divisor % 10) != cnt) {
					QuickListDetachNode(qlArray[cnt], tmp);
					QuickListAddNodetoTail(qlArray[(*recvdata / divisor % 10)], tmp);
				}
			}
		}
		times++;
	}

	/* print array after bucket sort */
	printf("Start print array after bucket sort:\n");
	for (cnt = 0; cnt < BUCKET_NUM; cnt++) {
		QuickList_for_each_entry(qlArray[cnt], tmp) {
			recvdata = QuickList_entry(int, tmp);
			printf("%d ", *recvdata);
		}
	}
	printf("\n");
}

多重表:節約空間,花費時間。也能夠在每一個節點中加入鏈表頭指針,花費空間,節約時間。如圖:

鏈表的遊標實現

  • InitializeCursorSpace
struct node
{
    ElementType Element;
    Position Next;
}

typedef int PtrToNode;
typedef PtrToNode List;
typedef PtrToNode Position;
struct Node CursorSpace[SpaceSize];

void InitializeCursorSpace(void)
{
    int cnt;
    for (cnt = 0; cnt < SpaceSize; cnt++) {
        CursorSpace[cnt].Element = 0;
        if (cnt + 1 >= SpaceSize) {
            CursorSpace[cnt].Next = 0;
        } else {
            CursorSpace[cnt].Next = cnt + 1;
        }
    }
}
  • CursorAlloc & CursorFree
static Position
CursorAlloc()
{
    Position P;
    P = CursorSpace[0].Next;
    CursorSpace[0].Next = CursorSpace[P].Next;
    return P;
}

static void
CursorFree(Position X)
{
    CursorSpace[X].Next = CursorSpace[0].Next;
    CursorSpace[0].Next = X;
}
  • IsEmpty
int 
IsEmpty(List L)
{
    return (CursorSpace[L].Next == 0);
}
  • IsLast
int
IsLast(Position P, List L)
{
    return (CursorSpace[P].Next == 0);
}
  • Find
Position
Find(ElementType X, List L)
{
    Position tmp;
    for(tmp = CursorSpace[L].Next; tmp != 0; tmp = CursorSpace[tmp].Next) {
        if (CursorSpace[tmp].Element == X) {
            break;
        }   
    }
    
    return tmp;
}
  • Delete
void
Delete(ElementType X, List L)
{
    Position tmp, tmpNext;
    for (tmp = L; CursorSpace[tmp].Next != 0; tmp = CursorSpace[tmp].Next) {
        tmpNext = CursorSpace[tmp].Next;
        if (CursorSpace[tmpNext].Element == X) {
            CursorSpace[tmp].Next = CursorSpace[tmpNext].Next;
            CursorFree(tmpNext);
        }
    }
}
  • Insert
void
Insert(ElementType X, List L)
{
    Position P;
    P = CursorAlloc();
    if (P == 0) {
        printf("run out of memory\n");
        return;
    }
    CursorSpace[P].Element = X;
    CursorSpace[P].Next = CursorSpace[L].Next;
    CursorSpace[L].Next = P;
}

3.3 棧ADT

1) 單鏈表實現

節點定義

struct node;
typedef struct node *PtrToNode;
typede PtrToNode Stack;

struct node {
    ElementType Element;
    PtrToNode Next;
}
  • CreateStack
Stack
CreateStack(void)
{
    Stack s = NULL;
    s = (Stack)malloc(sizeof(struct node));
    if (s == NULL) {
        printf("stack malloc failed\n");
        return s;
    }
    memset(s, 0, sizeof(struct node));
    s->Next = NULL;
    return s;
}
  • MakeEmpty
void
MakeEmpty(Stack s)
{
    PtrToNode p = NULL, tmp = NULL;
    p = s->Next;
    while (p != NULL) {
        tmp = p->Next;
        free(p);
        p = tmp;
    }
}
  • Push
int
Push(ElementType X, Stack s)
{
    PtrToNode tmp = NULL;
    tmp = (PtrToNode)malloc(sizeof(struct node)); 
    if (tmp == NULL) {
        return -1;
    }
    memset(tmp, 0, sizeof(struct node));
    tmp->Element = X;
    tmp->Next = s->Next;
    s->Next = tmp;
    return 0;
}
  • Pop
void
Pop(Stack s)
{
    if (s == NULL || s->Next == NULL) {
        return NULL;
    }
    PtrToNode tmp = NULL;
    tmp = s->Next;
    s->Next = tmp->Next;
    free(tmp);
}
  • Top
ElementType
Top(Stack s)
{
    return s->Next.Element;
}
  • IsEmtpy
int
IsEmpty(Stack s)
{
    if (s == NULL) {
        return -1;
    }
    return (s->Next == NULL);
}

2) 棧的數組實現

一個影響棧的執行效率的問題是錯誤檢測。除非在錯誤處理極其重要的場合(如在操做系統中),慣用手法是在棧例程中省去錯誤檢測。當編寫程序時,忽略錯誤檢測通常是不妥的,應該隨時編寫錯誤檢測的代碼,若是它們冗長,當它們確實耗費太多時間時,能夠將它們去掉。

  • 棧定義
struct StackRecord;
typedef struct StackRecord *Stack;

#define EmptyTOS (-1)
#define MinStackSize (5)

struct StackRecord
{
    int Capacity;
    int TopOfStack;
    ElementType *Array;
}
  • CreateStack
Stack
CreateStack(int MaxElement)
{
    if (MaxElement < MinStackSize) {
        return NULL;
    }
    Stack s = NULL;
    s = (Stack)malloc(sizeof(struct StackRecord));
    if (s == NULL) {
        return NULL;
    }
    memset(s, 0, sizeof(struct StackRecord));
    s->Capacity = MaxElement;
    s->TopOfStack = EmptyTOS;
    s->Array = (ElementType *)malloc(sizeof(ElementType) * MaxElement);
    if (s->Array == NULL) {
        free(s);
        return NULL;
    }
    memset(s->Array, 0, sizeof(ElementType) * MaxElement);
    return s;
}
  • DisposeStack
void
DisposeStack(Stack s)
{
    if (s->Array != NULL) {
        free(s->Array);
        s->Array = NULL;
    }
    if (s != NULL) {
        free(s);
    }
}
  • IsEmpty
int
IsEmpty(Stack s)
{
    return (s->TopOfStack == EmptyTOS);
}
  • MakeEmpty
void
MakeEmtpy(Stack s)
{
    memset(s->Array, 0, sizeof(ElementType) * s->Capacity);
    s->TopOfStack = EmptyTOS;
}
  • IsFull
int
IsFull(Stack s)
{
    return (s->TopOfStack + 1 == s->Capacity);
}
  • Push
void
Push(Stack s, ElementType X)
{
    if (IsFull(s)) {
        printf("stack s is full\n");
        return;
    }
    s->array[++s->TopOfStack] = X;
}
  • TopAndPop
ElementType
TopAndPop(Stack s)
{
    if (s->TopOfStack == EmptyTOS) {
        return EmptyTOS;
    }
    return s->array[s->TopOfStack--];
}
  • Pop
void
Pop(Stack s)
{
    if (IsEmpty(s)) {
        printf("stack s is empty\n");
    }
    s->TopOfStack--;
}
  • Top
ElementType
Top(Stack s)
{
    return s->Array[s->TopOfStack];
}

3) 應用

平衡符號

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "stackADT.h"

#define BRACKET_TYPE 3
#define TEST_STR	"(abcd}[ef])"
char bracketOpen[BRACKET_TYPE] = {'{', '(', '['};
char bracketClose[BRACKET_TYPE] = {'}', ')', ']'};

int symbolCheck(char *inputStr)
{
	int strLen = strlen(inputStr);
	int cnt, cnt2;
	char tmp;
	int ret = -1;

	Stack s = CreateStack();
	if (s == NULL) {
		return ret;
	}
	
	for (cnt = 0; cnt < strLen; cnt++) {
		for (cnt2 = 0; cnt2 < BRACKET_TYPE; cnt2++) {
			if (inputStr[cnt] == bracketOpen[cnt2]) {
				Push(inputStr[cnt], s);
			}

			if (inputStr[cnt] == bracketClose[cnt2]) {
				tmp = Top(s);
				if (tmp != bracketOpen[cnt2]) {
					goto __exit;
				}
				Pop(s);
			}
		} 
	}
	if (!IsEmpty(s)) {
		goto __exit;
	}

	ret = 0;
	
__exit:
	DistroyStack(s);
	return ret;
}

void main()
{
	char *p = TEST_STR;
	if (0 == symbolCheck(p)) {
		printf("check success\n");
	} else {
		printf("check fail\n");
	}
}
  • 後綴表達式:時間複雜度O(N),由於對輸入中每一個元素的處理都是由一些棧操做組成並花費常熟時間,該算法的計算是很是簡單的。當一個表達式之後綴記號給出時,沒有必要知道任何優先規則,這是一個明顯的優勢
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "stackADT.h"

#define OPERATOR_TYPE 4
char Operator[OPERATOR_TYPE] = {'+', '-', '*', '/'};
char *Expression = "12+3*85-/";

void suffixExpression(char *inputStr)
{
	int cnt, cnt2;
	int operateNum1, operateNum2;
	Stack s = CreateStack();
	for (cnt = 0; inputStr[cnt] != '\0'; cnt++) {
		if ((inputStr[cnt] >= '0') && (inputStr[cnt] <= '9')) {
			printf("Push %d\n", (inputStr[cnt] - '0'));
			Push((inputStr[cnt] - '0'), s);
		}

		for (cnt2 = 0; cnt2 < OPERATOR_TYPE; cnt2++) {
			if (inputStr[cnt] == Operator[cnt2]) {
				operateNum2 = Top(s);
				Pop(s);
				operateNum1 = Top(s);
				Pop(s);
				printf("operator=%c, num1=%d, num2=%d\n", Operator[cnt2], operateNum1, operateNum2);

				switch (cnt2) {
					case 0: {
						Push((operateNum1 + operateNum2), s);
						printf("Push %d\n", operateNum1 + operateNum2);
						break;
					}
					case 1: {
						Push((operateNum1 - operateNum2), s);
						printf("Push %d\n", operateNum1 - operateNum2);
						break;
					}
					case 2: {
						Push((operateNum1 * operateNum2), s);
						printf("Push %d\n", operateNum1 * operateNum2);
						break;
					}
					case 3: {
						Push((operateNum1 / operateNum2), s);
						printf("Push %d\n", operateNum1 / operateNum2);
						break;
					}
					default:
						break;
				}
			}
		}
	}
	
	operateNum1 = Top(s);
	Pop(s);
	DistroyStack(s);
	printf("result=%d\n", operateNum1);
}

void main()
{
	suffixExpression(Expression);
}
  • 中綴到後綴的轉換: 當讀到一個操做數的時候,當即把它放到輸出中,操做符不當即輸出,從而必須先存在某個地方。正確的作法是將已經見到過的操做符放到棧中,而不是放到輸出中。當遇到左圓括號時咱們也要將其推入棧中,從一個空棧開始計算。若是見到一個右括號,那麼就將棧元素彈出,將彈出的符號寫出直到咱們遇到一個左括號,可是這個左括號和右括號只是彈出,並不輸出。若是見到其餘任何操做符號,那麼就從棧中彈出棧元素,直到發現優先級更低的元素則不彈出,將讀取到的操做符入棧(若優先級相同則出棧)。有一個例外,除非是在處理一個)的時候,不然咱們毫不從棧中移走(。所以,當從棧彈出元素的工做完成後,再將操做符壓入棧中。最後,若是讀到輸入的末尾,咱們將棧元素彈出直到該棧變成空棧。將符號寫道輸出中。
  • 函數調用: 函數的嵌套調用過程當中,主調例程的全部局部變量和返回地址要被保存起來(入棧),在被調例程結束後進行恢復(出棧)。所以所有工做都可由一個棧來完成。當前環境是由棧頂描述的。所以,一條返回語句就可給出前面的環境。實際計算機中的棧,經常時從內存分區的高端向下增加。而在許多系統中是不檢測棧溢出的,可是用盡棧空間的狀況老是可能發生的。棧溢出的後果是嚴重的:程序崩潰,數據錯誤。在正常狀況下不該該越出棧空間,發生這種狀況一般是由失控遞歸的指向引發的(忘記基準情形),也有多是某些表面上正確的程序,可是遞歸了N次(數量巨大),致使棧溢出。例如尾遞歸。
  • 尾遞歸徹底可由goto語句改造出來。例如能夠goto到頂部。遞歸總可以被完全除去(編譯器是在轉變成彙編語言時完成的)雖然非遞歸程序通常說來確實比等價的遞歸程序要快,可是代價時使得程序的清晰性變得不足

3.4 隊列

數組實現

  • 節點定義
#define CAPACITY 10
struct QueueRecord {
    int Capacity;
    int Front;
    int Rear;
    int Size;
    ElementType *Array;
}

typedef struct QueueRecord *Queue;
  • CreateQueue
Queue
CreateQueue(int MaxElements)
{
    if (MaxElements <= 0) {
        return NULL;
    }
    Queue q = NULL;
    q = (Queue)malloc(sizeofstruct QueueRecord(struct QueueRecord));
    if (q == NULL) {
        printf("queue malloc failed\n");
        return NULL;
    }
    memset(q, 0, sizeof(struct QueueRecord));
    q->Capacity = CAPACITY;
    q->Front = 1;
    q->Rear = 0;
    q->Size = 0;
    q->Array = (ElementType *)malloc(sizeof(ElementType) * CAPACITY);
    if (q->Array == NULL) {
        return NULL;
    }
    memset(q->Array, 0, sizeof(ElementType) * CAPACITY);
    return q;
}
  • DisposeQueue
void
DisposeQueue(Queue Q)
{
    if (Q == NULL) {
        return;
    }
    if (Q->Array != NULL) {
        free(Q->Array);
    }
    free(Q);
}
  • IsEmpty
int
IsEmpty(Queue Q)
{
    if (Q == NULL) {
        return -1;
    }
    return (Q->Size == 0);
}
  • IsFull
int
IsFull(Queue Q)
{
    if (Q == NULL) {
        return -1;
    }
    return (Q->Size == Q->Capicity);
}
  • Enqueue
int
Enqueue(Queue Q, Element X)
{
    if (Q == NULL) {
        return -1;
    }
    if (IsFull(Q)) {
        printf("Queue Q is full\n");
        return -1;
    }
    
    if (++(Q->Rear) >= Q->Capicity) {
        Q->Rear -= Q->Capicity;
    }
    Q->Array[Q->Rear] = X;
    Q->Size++;
    return 0;
}
  • Dequeue
int
Dequeue(Queue Q)
{
    if (Q == NULL) {
        return -1;
    }
    if (IsEmpty(Q)) {
        printf("Queue Q is empty\n");
        return -1;
    }
    
    if (++(Q->Front) >= Q->Capicity) {
        Q->Front -= Q->Capicity;
    }
    Q->Size--;
    return 0;
}
  • FrontAndDequeue
ElementType
FrontAndDequeue(Queue Q)
{
    if (Q == NULL) {
        return -1;
    }
    if (IsEmpty(Q)) {
        printf("Queue Q is empty\n");
        return -1;
    }
    
    ElementType ret = Q->Array[Q->Front];
    if (++(Q->Front) >= Q->Capicity) {
        Q->Front -= Q->Capicity;
    }
    Q->Size--;
    return ret;    
}

鏈表實現

  • 節點定義
typedef struct Queue {
    ElementType Element;
    struct Queue *Pre;
    struct Queue *Next;
} Queue_T;

typedef Queue_T *QueuePtr
typedef QueuePtr QueueHead
#define MaxSize 10
#define EMPTY -1
  • IsEmpty
int
IsEmpty(QueueHead Q)
{
    if (Q == NULL) {
        return -1;
    }
    return (Q->Next == Q);
}
  • IsFull
int
IsFull(QueueHead Q)
{
    if (Q == NULL) {
        return -1;
    }
    
    int queueCnt = 0;
    QueuePtr tmp = NULL;
    tmp = Q->Next;
    while (tmp != Q) {
        queueCnt++;
        tmp = tmp->Next;
    }
    
    return (queueCnt == MaxSize);
}
  • CreateQueue
QueueHead
CreateQueue()
{
    QueueHead Q = NULL;
    Q = (QueuePtr)malloc(sizeof(struct Queue));
    if (Q == NULL) {
        return NULL;
    }
    memset(Q, 0, sizeof(struct Queue));
    Q->Next = Q;
    Q->Pre = Q;
    return Q;
}
  • DisposeQueue
int
DisposeQueue(QueueHead Q)
{
    if (Q == NULL) {
        return -1;
    }   
    
    MakeEmpty(Q);
    free(Q);
    return 0;
}
  • MakeEmpty
int
MakeEmpty(QueueHead Q)
{
    if (Q == NULL) {
        return -1;
    }   
    
    QueueHead tmp = NULL, tmpNext = NULL;
    tmp = Q->Next;
    while (tmp != Q) {
        if (tmp) {
            tmpNext = tmp->Next;
            free(tmp);
            tmp = tmpNext;
        }
    }
    return 0;
}
  • Enqueue
int
Enqueue(QueueHead Q, ElementType Element)
{
    if (Q == NULL) {
        return -1;
    }
    if (IsFull(Q)) {
        return -1;
    }
    
    QueuePtr qAdd = NULL;
    qAdd = (QueuePtr)malloc(sizeof(struct Queue));
    if (qAdd == NULL) {
        return -1;
    }
    memset(qAdd, 0, sizeof(struct Queue));
    qAdd->Element = Element;
    qAdd->Pre = Q->Pre;
    qAdd->Next = Q;
    Q->Pre->Next = qAdd;
    Q->Pre = qAdd;
    return 0;
}
  • Front
ElementType
Front(QueueHead Q)
{
    if (Q == NULL) {
        return EMPTY;
    }
    if (IsEmpty(Q)) {
        return EMPTY;
    }
    
    return Q->Next->Element;
}
  • Dequeue
int
Dequeue(QueueHead Q)
{
    if (Q == NULL) {
        return -1;
    }
    if (IsEmpty(Q)) {
        return -1;
    }
    
    QueuePtr tmp = NULL;
    tmp = Q->Next;
    Q->Next = tmp->Next;
    tmp->Next->Pre = Q;
    if (tmp != NULL) {
        free(tmp);
    }
    return 0;
}
  • FrontAndDequeue
ElementType
FrontAndDequeue(QueueHead Q)
{   
    if (Q == NULL) {
        return EMPTY;
    }
    if (IsEmpty(Q)) {
        return EMPTY;
    }
    
    ElementType ret;
    QueuePtr tmp = NULL;
    tmp = Q->Next;
    ret = tmp->Element;
    Q->Next = tmp->Next;
    tmp->Next->Pre = Q;
    if (tmp != NULL) {
        free(tmp);
    }
    return ret;    
}

3.4.3 隊列的應用

排隊論:問題取決於用戶加入隊列的頻率,以及一旦用戶獲得服務時處理服務的時間。
遞歸很是強大,但它並非徹底隨意的操做;遞歸的誤用和亂用可能致使程序崩潰。

參考文獻

  1. Mark Allen Weiss.數據結構與算法分析[M].America, 2007

本文做者: CrazyCatJack

本文連接: https://www.cnblogs.com/CrazyCatJack/p/13321878.html

版權聲明:本博客全部文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!

關注博主:若是您以爲該文章對您有幫助,能夠點擊文章右下角推薦一下,您的支持將成爲我最大的動力!

相關文章
相關標籤/搜索