数据结构与算法第三章:表栈和队列

Posted Coder的自我修养

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法第三章:表栈和队列相关的知识,希望对你有一定的参考价值。

【数据结构与算法】第三章:表、栈和队列

标签(空格分隔): 【数据结构与算法】


第三章:表、栈和队列

3.1 抽象数据类型

  • 抽象数据类型(abstract data type,ADT) ADT是数学的抽象,在ADT的定义中根本没有设计如何实现操作的集合。可以看作是模块化设计的扩充。例如,对于集合ADT,我们有并(union),交(intersection),测定大小(size),取余(complement),查找(find)等操作. 思想:这些操作的实现只在程序中编写一次,在程序中任何其他部分要在该ADT上运行其中的一种操作时可以通过调用适当的函数来进行.

3.2 表ADT

形如 $ A1 ,A2,...A_N $ 的表,我们说这个表的大小是N.

  • 名词解释

    1. 空表:大小为0的表.(empty list)

    2. 前驱元、后继元:对于非空表以外的任何表,我们说 $ A{i+1} $ 是 $ Ai $ 的后继元,称 $ Ai $ 是 $ A{i+1} $ 的前驱元.对于 $ A1 $ 没有前驱元,对于 $ AN $ 没有后继元.

  • 一些ADT

    1. PrintList:打印链表.

    2. MakeEmpty:创建一个空链表.

    3. Find:返回关键字首次出现的位置.

    4. Insert:从表的某个位置插入某个关键字.

    5. Delete:从表的某个位置删除某个关键字.

    6. FindKth:返回某个位置.

    7. Length:返回链表长度.

3.2.1 表的数组实现

 
   
   
 
  1. \\制作空表

  2. List MakeEmpty(){

  3.    List PtrL;

  4.    PtrL = ( List)malloc( sizeof( struct LNode));

  5.    PtrL = -1;

  6.    return PtrL;

  7. }

  8. \\查找某个元素 返回下标

  9. int Find( ElementType X, List PtrL){

  10.    int i;

  11.    while( i <= PtrL->Last && PtrL->Data[i] != X)

  12.        i++;

  13.    if( i > PtrL->Last) return -1;

  14.    else return i;

  15. }

  16. \\在第i个位置上插入元素

  17. void Inser( ElementType X, int i, List PtrL){

  18.    int j;

  19.    if( PtrL->Last == MAXSIZE - 1){

  20.        printf("表满");

  21.        return;

  22.    }

  23.    if( i < 1 || i > PtrL->Last + 2){

  24.        printf("位置不合法");

  25.        return;

  26.    }

  27.    for( j = PtrL->Last; j > i; j--)

  28.        PtrL->Data[j+1] = PtrL->Data[j];

  29.    PtrL->Data[i] = X;

  30.    PtrL->Last++;

  31. }

  32. \\删除第i个位置的元素

  33. void Delete( int i, List PtrL){

  34.    int j;

  35.    if( i < 1 || i > PtrL->Last + 1){

  36.        printf("位置不合法");

  37.        return;

  38.    }

  39.    for( j = i; j < PtrL->Last; j++)

  40.        PtrL->Data[j] = PtrL->Data[j+1];

  41.    PtrL->Last--;

  42. }

3.2.2 链表

  • 链表由一系列不必再内存中相连的结构组成。每个结构均含有表元素和指向包含该元素后继元的结构的指针.这个指针便是Next指针.

  • ANSI C 规定NULL为零.

  • 表头\哑结点:一个标志结点,约定表头处于位置0处. 

 
   
   
 
  1. //ADT

  2. List MakeEmpty( List L);

  3. //是否是空表

  4. int IsEmpty( List L);                          

  5. //当前位置是否是末尾

  6. int IsLast( Position P, List L);  

  7. //返回X的位置

  8. Position Find( ElementType X, List L);    

  9. //返回X的前一个位置

  10. Position FindPrevious( ElementType X, List L);

  11. //删除某个位置元素

  12. void Delete( ElementType X, List L);          

  13. //插入

  14. void Insert( ElementType X, List L, Position P);

  15. void DeleteList( List L);

  16. Position Header( List L);

  17. Position First( List L);

  18. Position Advance( Position P);

  19. ElementType Retriece( Position P);

 
   
   
 
  1. //具体实现

  2. int IsEmpty( List L){   //Return true if L is empty

  3.    return L->Next == NULL;

  4. }

  5. int IsLast( Position P, List L){

  6.    return P->Next == NULL;

  7. }

  8. Position Find( ElementType X, List L){

  9.    Position P;

  10.    P = L->Next;

  11.    while( P != NULL && P->Element != X)

  12.        P = P->Next;

  13.    return P;

  14. }

  15. Position FindPrevious( ElementType X, List L){

  16.    //假设存在表头

  17.    Position P;

  18.    P = L;

  19.    while( P->Next != NULL && P->Next->Element != X)

  20.        P = P->Next;

  21.    return P;

  22. }

  23. void Delete( ElementType X, List L){

  24.    Position P, TmpCell;

  25.    P = FindPrevious( X, L);        //假设存在表头

  26.    if( !IsLast( P,L)){             //P不是最后一个位置时

  27.        TmpCell = P->Next;          //考虑到P是指向X的钱一个位置

  28.        P->Next = TmpCell->Next;    //若X存在,则P肯定不是最后一个位置

  29.        free( TmpCell);

  30.    }

  31. }

  32. void Insert( ElementType X, List L, Position P){

  33.    Position TmpCell;

  34.    TmpCell = ( Position)malloc( sizeof( struct Node));

  35.    if( TmpCell == NULL)

  36.        FatalError("Out of space!");

  37.    TmpCell->Element = X;

  38.    TmpCell->Next = P->Next;

  39.    P->Next = TmpCell;

  40. }

3.2.3 双链表

在数据结构上附加一个域,使它包含只想钱一个单元的指针. 代价:由于需要更多的指纹定位,因此增加了空间的需求,同时使得删除和插入的开销增加了一倍.

【数据结构与算法】第三章:表、栈和队列

3.2.4 循环链表

最后的单元反过来直接指向第一个单元. 它可以有表头、也可以没有.当表头存在的时候,便让最后一个单元指向它. 还可以是一种双向链表.【数据结构与算法】第三章:表、栈和队列

3.2.5 例题

  • 多项式ADT 定义一种一元(具有非负次幂)多项式的抽象数据类型, $$ F(X) = \sum{i=0}^NAiX^i $$

    1. 对于大部分系数为0的多项式,可以使用一个简单数组来储存这些系数.之后进行加减乘除等操作.

 
   
   
 
  1. //数组实现声明

  2. typedef struct{

  3.    int CoeffArray[ NaxDegree + 1];

  4.    int HighPower;

  5. } *Polynomial;

  6. //初始化为0

  7. void ZeroPolynomial( Polynomial Poly){

  8.    int i;

  9.    for( i=0; i <= MaxDegree; i++)

  10.        Poly->CoeffArray[ i] = 0;

  11.    Poly->HighPower = 0;

  12. }

  13. //相加

  14. void AddPolynomial( const Polynomial Poly1, const Polynomial Poly2,

  15.                   Polynomial PolySum){

  16.    int i;

  17.    ZeroPolynomial( PolySum);

  18.    PolySum->HighPower = Poly1->HighPower > Poly2->HighPower ? Poly1->HighPower : Poly2->HighPower;

  19.    for( i = PolySum->HighPower; i >= 0; i--){

  20.        PolySum->CoeffArray[i] = Poly1->CoeffArray[i] + Poly2->CoeffArray[i];

  21.    }

  22. }

  23. //相乘

  24. void MultPolynomial( const Polynomial Poly1, const Polynomial Poly2,

  25.                   Polynomial PolyProd){

  26.    int i, j;

  27.    ZeroPolynomial( PolyProd);

  28.    PolyProd->HighPower = Poly1->HighPower * Poly2->HighPower;

  29.    if( PolyProd->HighPower > MaxDegree)

  30.        Error("Exceeded array size");

  31.    else

  32.        for( i = 0; i < Poly1->HighPower; i++)

  33.            for( j = 0; j < Poly2->HighPower; j++)

  34.                PolySum->CoeffArray[ i+j] = Poly1->CoeffArray[i] + Poly2->CoeffArray[j];

  35. }

2 . 使用单链表表示,多项式的每一项包含于一个单元中,同时,这些单元以次数递减的顺序排序.如下图:【数据结构与算法】第三章:表、栈和队列

 
   
   
 
  1. //链表实现声明

  2. typedef struct Node *PtrToNode;

  3. struct Node{

  4.    int Coefficient;

  5.    int ExPonent;

  6.    PtrToNode Next;

  7. };

  8. typedef PtrToNode Polynomial;

  9. void Attach( int c, int e, Polynomial *pRear){

  10.    Polynomial P;

  11.    P = ( Polynomial)malloc( sizeof( struct Node));

  12.    P->Coef = c;

  13.    P->Expon = e;

  14.    *pRear -> Next = P;

  15.    *pRear = P;

  16. }

  17. //相加

  18. Polynomial PolyAdd( Polynomial P1, Polynomial P2){

  19.    Polynomial front, rear, temp;

  20.    int sum;

  21.    rear = ( Polynomial)malloc( sizeof( struct Node));

  22.    front = rear;   //front 指向表头

  23.    while( P1 && P2){

  24.        if( P1->Expon > P2->Expon){

  25.            Attach( P1->Coef, P1->Expon, &rear);

  26.            P1 = P1->Next;

  27.        }

  28.        else if( P1->Expon < P2->Expon){

  29.            Attach( P2->Coef, P2->Expon, &rear);

  30.            P2 = P2->Next;

  31.        }

  32.        else if( P1->Expon < P2->Expon){

  33.            sum = P1->Coef + P2->Coef;

  34.            if( sum)    //sum not equals 0

  35.                Attach( sum, P1->Expon, &rear);

  36.            P2 = P2->Next;

  37.            P1 = P1->Next;

  38.        }

  39.    }

  40.    for( ; P1; P1 = P1->Next) Attach( P1->Coef, P1->Expon, &rear);

  41.    for( ; P2; P2 = P2->Next) Attach( P2->Coef, P2->Expon, &rear);

  42.    rear->Next = NULL;

  43.    temp = front;

  44.    front = front->Next;

  45.    free(temp);

  46.    return fornt;

  47. }

  48. //相乘

  49. Polynomial PolyMult( Polynomial P1, Polynomial P2){

  50.    Polynomial P, Rear, t1, t2, t;

  51.    int c, e;

  52.    if( !P1 || !P2) return NULL; //存在空表时

  53.    t1 = P1; t2 = P2;

  54.    P = ( Polynomial)malloc( sizeof( struct Node));

  55.    Rear = P;

  56.    while( t2){ //先用P1的第一项乘P2

  57.        Attach( t1->Coef * t2->Coef, t1->Expon + t2->Expon, &Rear);

  58.        t2 = t2->Next;

  59.    }

  60.    t1 = t1->Next;

  61.    while( t1){

  62.        t2 = P2; Rear = P;

  63.        while( t2){

  64.            c = t1->Coef * t2->Coef;

  65.            e = t1->Expon + t2->Expon;

  66.            while( Rear->Next && Rear->Next->Expon > e)

  67.                Rear = Rear->Next;

  68.            if( Rear->Next && Rear->Next->Expon == e){

  69.                if( Rear->Next->Coef + c)

  70.                    Rear->Next->Coef += c;

  71.                else{

  72.                    t = Rear->Next;

  73.                    Rear->Next = t->Next;

  74.                    free(t);

  75.                }

  76.            }

  77.            else{

  78.                t = ( Polynomial)malloc( sizeof( struct Node));

  79.                t->Coef = c; t->Expon = e;

  80.                t->Next = Rear->Next;

  81.                Rear->Next = t;

  82.                Rear = Rear->Next;

  83.            }

  84.            t2 = t2->Next;

  85.        }

  86.        t1 = t1->Next;

  87.    }

  88.    t2 = P;

  89.    P = P->Next;

  90.    free( t2);

  91.    return P;

  92. }

  • 基数排序 基数排序有时候也成为卡式排序. 桶式排序:假定我们有 $ N $ 个整数,大小范围从 $ 1 $ 到 $ M $ (或者从 $ 0 $ 到 $ M-1 $),我们创建一个数组,名为 $ Count $ ,大小为 $ M $ ,并初始化为0,当 $ Ai $ 被读入时, $ Count[Ai] $ 增 $ 1 $ .当所有的输入被读进后,扫描数组 $ Count $,打印输出排好序的表.该算法时间复杂度为 $ O(M+N) $. 基数排序:上述方法的推广.假设我们有 10 个数字,带下范围从 0 到 999 之间,显然,如果采取桶排序的方法,“桶”便太多了. 于是,我们可以对最低的有效位优先进行排序. 举例: 输入: 64, 8, 216, 512, 27, 729, 0, 1, 343, 125. 第一趟排序:对个位数字

0 1 2 3 4 5 6 7 8 9
0 1 512 343 64 125 216 27 8 729

第二趟排序:对十位数字

0 1 2 3 4 5 6 7 8 9
0 512 125
343
64


1 216 27






8
729






第二趟排序:对百位数字

0 1 2 3 4 5 6 7 8 9
0 125 216 343
512
729

1








8








27








64








3.2.6 多重表

3.2.7 链表的游标实现

3.3 栈ADT

3.3.1 栈模型

  • 栈 被限制插入和删除只能在一个位置上进行的表,这个位置是表的末端,叫做栈顶.

  • 基本操作 进栈 $ Push $ 出栈 $ Pop $ 

  • LIFO: Last In First Out 后进先出

3.3.2 栈的实现

3.3.2.1 单链表

在表的顶端(不可以是尾端,因为尾端无法实现出栈的操作)实现进栈 $ Push $ 和 出栈 $ Pop $.

 
   
   
 
  1. struct Node{

  2.    ElementType Element;

  3.    PtrToNode Next;

  4. };

  5. int IsEmpty( Stack S);       //是否是空栈

  6. Stack CreateStack();         //创建一个栈

  7. void DisposeStack( Stack S);

  8. void MakeEmpty( Stack S);    //变空

  9. void Push( ElementType X, Stack S);

  10. void Pop( Stack S);

  11. ElementType Top( Stack S);   //返回栈顶

 
   
   
 
  1. //检测是否为空栈

  2. int IsEmpty( Stack S){

  3.    return S->Next == NULL;

  4. }

  5. //创建一个空栈

  6. Stack CreateStack(){

  7.    Stack S;

  8.    S = ( Stack)malloc( sizeof( struct Node));

  9.    if( S == NULL)

  10.        FatalError(" Out of space");

  11.    S->Next = NULL;

  12.    MakeEmpty( S);

  13.    return S;

  14. }

  15. void MakeEmpty( Stack S){

  16.    if( S == NULL)

  17.        Error(" Must use CreateStack first");

  18.    else{

  19.        while( IsEmpty( S))

  20.            Pop( S);

  21.    }

  22. }

  23. //进栈

  24. void Push( ElementType X, Stack S){

  25.    PtrToNode TmpCell;

  26.    TmpCell = ( Stack)malloc( sizeof( struct Node));

  27.    if( TmpCell == NULL)

  28.        FatalError(" Out of space");

  29.    else{

  30.        TmpCell->Element = X;

  31.        TmpCell->Next = S->Next;    //存在表头

  32.        S->Next = TmpCell;

  33.    }

  34. }

  35. //出栈

  36. void Pop( Stack S){

  37.    PtrToNode FirstCell;

  38.    if( IsEmpty( S))

  39.        Error(" Empty stack");

  40.    else{

  41.        FirstCell = S->Next;

  42.        S->Next = S->Next->Next

  43.        free( FirstCell);

  44.    }

  45. }

  46. //返回栈顶元素

  47. ElementType Top( Stack S){

  48.    if( !IsEmpty( S))   //当S不空时

  49.        return S->Next->Element;

  50.    Error(" Empty stack");

  51.    return 0;           //避免警告

  52. }

3.3.2.2 数组实现

注意:如果使用数组实现,我们需要提前声明数组大小.

 
   
   
 
  1. struct StackRecord{

  2.    int Capacity;

  3.    int TopOfStack;

  4.    ElementType *Array;

  5. };

  6. int IsEmpty( Stack S);

  7. int IsFull( Stack S);

  8. Stack CreateStack( int MaxElements);

  9. void DisposeStack( Stack S);

  10. void MakeEmpty( Stack S);

  11. void Push( ElementType X, Stack S);

  12. ElementType Top( Stack S);

  13. void Pop( Stack S);

  14. ElementType TopAndPop( Stack S);

 
   
   
 
  1. //栈的创建

  2. int CreateStack( int MaxElements){

  3.    Stack S;

  4.    if( MaxElements < MinStackSize)

  5.        Error(" Stack size is too small");

  6.    S = ( Stack)malloc( sizeof( struct StackRecord));

  7.    if( S == NULL)

  8.        FatalError(" Out of space");

  9.    S->Array = malloc( sizeof( struct ElementType));

  10.    if( S->Array == NULL)

  11.        FatalError(" Out of space");

  12.    S->Capacity = MaxElements;

  13.    MakeEmpty(S);

  14.    return S;

  15. }

  16. //创建一个空栈

  17. int MakeEmpty( Stack S){

  18.     S->TopOfStack = EmptyTOS;

  19. }

  20. //检测一个栈是否为空

  21. int IsEmpty( Stack S){

  22.    return S->TopOfStack == EmptyTOS;

  23. }

  24. //释放栈

  25. void DisposeStack( Stack S){

  26.    if( S != NULL){

  27.        free( S->Array);

  28.        free( S);

  29.    }

  30. }

  31. //返回栈顶

  32. ElementType Top( Stack S){

  33.    if( !IsEmpty( S))   //栈非空

  34.        return S->Array[ S->TopOfStack];

  35.    Error(" Empty Stack");

  36.    return 0;           //防止出现警告

  37. }

  38. //进栈

  39. void Push( ElementType X, Stack S){

  40.    if( IsFull( S))

  41.        Error(" Full Stack");

  42.    else

  43.        S->Array[ ++ S->TopOfStack ] = X;

  44. }

  45. //出栈

  46. void Pop( Stack S){

  47.    if( IsEmpty( S))

  48.        Error(" Empty Stack");

  49.    else

  50.        S->TopOfStack--;

  51. }

  52. //给出栈顶元素并弹出

  53. ElementType TopAndPop( Stack S){

  54.    if( !IsEmpty( S))

  55.        return S->Array[ S->TopOfStack --];

  56.    Error(" Empty Stack");

  57.    return 0;           //防止出现警告

  58. }

3.3.3 应用

  • 平衡符号

  1. 描述: 序列 "[()]" 是合法的,"[(])"是非法的.

  2. 解题思路: 创建一个空栈,读入字符直到文件尾. 如果字符是一个开放的符号,比如左括号,那么就将他推入堆栈中, 如果字符是一个封闭的括号,比如有括号,当栈是空时,就报错,否则就将栈的元素弹出.如果弹出的不是对应的开放符号,就报错.在文件尾,如果栈非空就报错.

  • 中缀表达式转换成后缀表达式

  1. 改写成后缀表达式方法:对于一行表达式,从左到右依次读取,如果是操作数就将它输出.遇到操作符 A 就将它压入堆栈 (此时可以视 A 为栈顶元素) ,直到遇到下一个操作符 B ,如果操作符 B 比栈顶操作符 (A) 的优先级低(或者相同),则弹出 A ,反之则压入堆栈. 遇到括号时,把左括号压入堆栈中,并同时从左括号起视为一个新的堆栈.

  2. 举例: 对于中缀表达式:a + b * c + ( d * e + f) * g

步骤 堆栈 输出
1
a
2 + a
3 + ab
4 +* ab
5 +* abc
6 + abc*+
7 + abc*+
8 +( abc*+
9 +( abc*+d
10 +(* abc*+de
11 +(+ abc+def
12 +(+ abc+def
13 +* abc+def+g
14
abc+def+g*+
  • 递归调用: 系统将整个程序运行时所需的数据空间安排在一个栈中,每当调用一个函数时,就为它在栈顶分配一个存储区,每当从一个函数退出时,就释放这个存储区,因此当前运行的函数的数据必定储存在栈顶.

3.4 队列ADT

  • 队列: 插入在一端进行而删除在另一端进行的一种表.

3.4.1 队列模型

  • 基本操作: $ Enqueue $ 入队:在表的末端( $ rear $ 队尾 )插入一个元素. $ Dequeue $ 入队:删除(或返回)表的开头( $ front $ 队头 )的元素. 

3.4.2 队列实现

3.4.2.1 队列的链表实现

只能在链表头部进行删除(出队)操作,尾部进行插入(入队)操作

 
   
   
 
  1. typedef struct Node *PtrToNode;

  2. struct Node{            //队列中的结点

  3.    ElementType Data;

  4.    PtrToNode Next;

  5. };

  6. typedef struct PtrToNode Position;

  7. struct QNode{

  8.    Position Front, Rear;

  9.    int MaxSize;

  10. };

  11. typedef struct QNode *Queue;

  12. //判断是否为空

  13. int IsEmpty( Queue Q){

  14.    return Q->Front == NULL;

  15. }

  16. //出队

  17. ElementType DeleteQ( Queue Q){

  18.    Position FrontCell;

  19.    ElementType FrontElem;

  20.    if( IsEmpty( Q)){

  21.        printf("队列空");

  22.        return ERROR;

  23.    }

  24.    else{

  25.        FrontCell = Q->Front;

  26.        if( Q->Rear == Q->Front)

  27.            Q->Rear = Q->Front = NULL;

  28.        else

  29.            Q->Front = Q->Front->Next;

  30.        FrontElem = FrontCell->Data;

  31.        return FrontElem;

  32.    }

  33. }

  34. //入队

  35. void Enqueue( ElementType X, Queue Q){  

  36.    Position P;

  37.    P = ( Position)malloc( sizeof( struct Node));

  38.    P->Data = X;

  39.    P->Next = NULL;

  40.    Q->Rear->Next = P;

  41. }

3.4.2.2 队列的数组实现

 
   
   
 
  1. typedef int Position;

  2. struct QNode{

  3.    ElementType *Data;

  4.    Position Front, Rear;

  5.    int MaxSize;

  6. };

  7. typedef QNode *Queue;

  8. //创建队列

  9. Queue CreatQueue( int MaxSize){

  10.    Queue Q = ( Queue)malloc( sizeof( struct QNode));

  11.    Q->Data = ( ElementType *)malloc( MaxSize * sizeof( ElementTYpe));

  12.    Q->Front = Q->Rear = 0;

  13.    Q->MaxSize = MaxSize;

  14.    return Q;

  15. }

  16. //是否满

  17. int IsFull( Queue Q){

  18.    return (Q->Rear + 1) % Q->MaxSize == Q->Front ;

  19. }

  20. //入队

  21. int AddQ( Queue Q, ElementType X){

  22.    if( IsFull( Q)){

  23.        printf("队列满");

  24.        return ERROR;

  25.    }

  26.    else{

  27.       Q->Rear = (Q->Rear+1) % Q->MaxSize;

  28.       Q->Data[Q->Rear] = X;

  29.       return 1;

  30.    }

  31. }

  32. //是否空

  33. int IsEmpty( Queue Q){

  34.    return Q->Front == Q->Rear;

  35. }

  36. //出队

  37. ElementType DeleteQ( Queue Q){

  38.    if( IsEmpty( Q)){

  39.        printf("队列空");

  40.        return ERROR;

  41.    }

  42.    else{

  43.        Q->Front = (Q->Front+1) % Q->MaxSize;

  44.        return Q->Data[ Q->Front];

  45.    }

  46. }

3.4.2 队列的应用

  • 生活中排队购票;

  • 公司的传呼.


以上是关于数据结构与算法第三章:表栈和队列的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法分析表栈和队列

数据结构与算法分析表栈和队列

表栈和队列

数据结构和算法分析 表栈和队列

数据结构和算法分析(10)表栈和队列的实际应用

数据结构和算法 数据结构基础线性表栈和队列数组和字符串