数据结构与算法分析(5)表、栈和队列(一)

      本节讨论最简单和最基本的三种数据结构:表,栈和队列。

每种程序都明晰地至少使用一种这样的数据结构,而栈在程序中总要间接地用到。

内容:

1.介绍抽象数据类型(ADT)的概念;

2.阐述如何对表进行有效的操作;

3.介绍栈ADT及其在实现递归方面的应用;

4.介绍队列ADT及其在操作系统和算法设计上的与应用。

(1)抽象数据类型

程序设计的基本法则之一是:例程不应超过一页。

模块化的优点:

1)调试小程序比调试大程序容易的多;

2)多个人同时对一个模块式程序编程更加容易;

3)一个写得好的模块化程序把某些依赖关系只局限在一个例程中,这样使得修改起来更加容易。

由模块化是有益的可以推出:全局变量和副作用是有害的。

抽象数据类型(abstract data type ADT)是一些操作的集合。对诸如表、集合、图和他们的操作一起可看作是抽象数据类型。对于集合ADT,我们有诸如并,交,测定大小以及取余等操作。或者我们也可以只要两种操作:并和查找,这两种操作又在集合上定义了一种不同的ADT。

(2)表ADT

几个概念:

1)空表:大小为0的表;

2)后继元:当前元素的后一个;

3)前驱元:当前元素的前一个;

与ADT的定义有关的就是在ADT上进行操作的集合了:printList,makeEmpty,find,findKth,findPreviousinsert,delete等。

2.1表的简单数组实现:

  对表的所有操作都可以使用数组来实现。虽然数组是动态指定的,但是还是需要对表的大小的最大值进行估算。这会导致有可能浪费大量的空间。数组实现使得PrintList和Find以线性时间执行,FindKth以常数时间。而插入和删除的代价昂贵。

因为插入和删除的运行时间是如此的慢以及表的大小必须事先已知,所以简单数组一般不用来实现表这种结构。

2.2链表

为了避免插入和删除的线性开销,我们允许表可以不连续存储。

链表由一系列在不必再内存中相连的结构组成。每一个结构均包含表元素和包含指向该元素后继元的Next指针。最后一个单元的Next指针指向NULL,ANSI C规定NULL为0;

图:

一个链表:

从链表中删除一个元素:

向链表中插入:

2.3程序设计细节:

可能遇到的问题:

1)不存在从所给定义出发在表的前面插入元素的真正显示的方法;

2)从表的前面实行删除是一个特殊情况,因为它改变了表的起始段,在编程中对疏忽可能导致表的丢失。

3)在一般的删除中要求我们记住被删除单元前面的表元。

为了解决这个问题,我们通常留出一个标记结点,有时我们称之为表头(header)或者哑结点(dummy node)。同时为了避免删除中的一些问题,我们需要编写例程findPrevious返回我们要删除表元的前驱元的位置。表头的使用能够使我们表达基本的指针操作而又不致使特殊情形的代码含糊不清。

在处理链表的删除操作的相关问题时,我们需要编写例程findPrevious(),它将返回我们要删除的表元的前驱元的位置。如果我们使用头节点,那么当我们删除表的第一个元素时,findPrevious()将返回表头的位置。

2.4单向链表:

单向链表的ADT的例程:

#ifndef_List_H

struct Node;
typedef struct Node *PtrToNode;
typedef PtrToNode List;//另起一个名字
typedef PtrToNode Position;
typedef int ElementType;

List MakeEmpty(List L);
int IsEmtpy(List L);
int IsLast(Position P,List L);
Position Find(ElementType X,List L);
void Delete(ElementType X,List L);
Position FindPrevious(ElementType X,List L);
void Insert(ElementType X,List L,Position P);
void DeleteList(List L);
Position Header(List L);
Position First(List L);
ElementType Retrieve(Position P);

#endif  /* _List_H */

/*Node结点的声明是在.c文件中(Place in the implementation file)*/
struct Node{
  ElementType Element;
  Position Next;
};
//测试一个链表是否为空表
int IsEmtpy(List L){
    return L->Next==NULL;
}
//测试当前位置是不是链表的尾部
int IsLast(Position    P,List L){
  return P->next==NULL;
}
//Find例程
Position Find(ElementType X,List L){
    Position P;
    P=P->Next;
    while(P!=NULL&&P->Element!=X){
        P=P->Next;
    }
    return P;
}
//链表的删除例程
void Delete(ElementType    X,List L){
    Position P,TmpCell;
    P=FindPrevious(X,L);
    if(!IsLast(P,L)){ //这里需要判断是否是链表的最后一个结点,如果是,则表示找的元素未在链表中
        TmpCell=P->Next;
        P->Next=TmpCell->Next;
        free(TmpCell);
    }
}
//找到元素结点的前一个结点,找不到就返回链表的最后的结点
Position FindPrevious(ElementType X,List L){
    Position P;
    P=L;
    while(P->Next!=NULL&&P->Next->Element!=X){
        P=P->Next;
    }
    return P;
}
//链表的插入例程
void Insert(ElementType    X,List L,Position P){
    Position TmpCell;
    TmpCell=malloc(sizeof(struct Node));
    if(TmpCell==NULL){
      printf("Error!\n");
    }else{
      TmpCell->Element=X;
      TmpCell->Next=P->Next;
      P->Next=TmpCell;
    }
}
void DeleteList(List L){
  Position P,Tmp;
  P=L->Next;
  L->Next=NULL;
  while(P!=NULL){
      Tmp=p;
      P=P->Next;
      free(Tmp);
  }
} 

在表的例程运用中的几个提醒:

1.指针变量包含伪地址,系统提醒"memory access vialation"或"segmentation violation"。例如:{free(p);p=p->Next;}

2.声明一个指向结构的指针并不创建该结构,而只是给出足够的空间容纳结构可能使用的地址。创建尚未声明过的记录的一般方法是使用malloc库函数。malloc创建一个新的结构并返回指向该结构体的指针。一些比较老的编译器需要在malloc的前面加上类型转换(type cast)。C语言提供了两种动态分配内存空间的库函数,malloc和calloc。他们都要求包含stdlib头文件。

3.free(p)的作用是:p正在指向的地址没变,但在该地址处的数据此时已经无定义了。

4.malloc(sizeof(PtrToNode))是合法的,但是它并不给结构体分配足够的内存空间,他只给指针分配内存空间。

单向链表的实例:

1)生成100个随机整数并放入一个链表中,要求链表中的元素按从小到大顺序排列:

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

struct Node{
    int Element;
    struct Node *Next;
};
typedef struct Node *List;
typedef struct Node *PtrToNode; 

List create(){
  List L;
  PtrToNode header=malloc(sizeof(struct Node));
  header->Next=NULL;
  L=header;
  return L;
}

//递归的有序的插入
void InsertRecursive(PtrToNode    temp,List L){
    PtrToNode p=L;
    if(p->Next==NULL){
        temp->Next=p->Next;
        p->Next=temp;
    }
    else if(p->Next!=NULL){
        if(temp->Element<=p->Next->Element){
            temp->Next=p->Next;  //temp指向p的指向的下一个元素
            p->Next=temp;        //p指向temp
        }else if(temp->Element>p->Next->Element){
            InsertRecursive(temp,p->Next);
        }
    }
}

//非递归有序的插入
void InsertUnRecursive(PtrToNode temp,List L){
    PtrToNode p=L;
    //一个元素也没插入时
    if(p->Next==NULL){
        temp->Next=p->Next;
        p->Next=temp;
        return;
    }
    //只有一个元素时
    if((p->Next!=NULL)&&(p->Next->Next==NULL)){
        if((temp->Element)<=(p->Next->Element)){
            temp->Next=p->Next;
            p->Next=temp;
        }else if(temp->Element>p->Next->Element){
            temp->Next=p->Next->Next;
            p->Next->Next=temp;
        }
        return;
    }
    while(p->Next!=NULL&&p->Next->Next!=NULL){
        if(temp->Element<=p->Next->Element){
            temp->Next=p->Next;
            p->Next=temp;
            return;
        }else if(temp->Element>p->Next->Element&&temp->Element<=p->Next->Next->Element){
            temp->Next=p->Next->Next;
            p->Next->Next=temp;
            return;
        }else{
            p=p->Next;
        }
        //有两个元素,但执行p=p->Next时有可能碰到p->Next->Next==NULL;
        if(p->Next->Next==NULL){
          if((temp->Element)<=(p->Next->Element)){
            temp->Next=p->Next;
            p->Next=temp;
          }else if(temp->Element>p->Next->Element){
            temp->Next=p->Next->Next;
            p->Next->Next=temp;
          }
          return;
        }
    }
} 

void printAll(List list){
  PtrToNode p=list;
  int flag=1;
  while(p->Next!=NULL){
      printf("a[%2d]=%3d ",flag,p->Next->Element);
      p=p->Next;
      if(flag%10==0){
      printf("\n");
    }
       flag++;
  }
}

int main(){
  srand(time(NULL));
  List list=create();
  int i,x;
  PtrToNode p;
  //InsertRecursive与InsertUnRecursive函数具有一样的功能:
  for(i=0;i<50;i++){
      x=rand()%1000;
      p=malloc(sizeof(struct Node));
      p->Element=x;
      InsertRecursive(p,list);
  }
  for(i=0;i<50;i++){
      x=rand()%1000;
      p=malloc(sizeof(struct Node));
      p->Element=x;
      InsertUnRecursive(p,list);
  }
  //输出插入的结果
  printAll(list);
} 

2)将两个从小到大排列的链表合并为一个新链表,要求:

(1)原来的两个链表不保存(合并时,无需生成新的空间来存放每个结点,直接将所有结点合并为新链表)

(2)原来的两个链表不作改变(合并时,对每一个结点需要复制并产生新链表)

(3)对以上两小题,若链表中遇到有相同的元素,则在合并时去掉重复元素。(当然,你也可以在链表合并完以后再扫描整个链表去掉重复元素,但这需要额外的运行时间,因此并不是一个好的办法)

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

struct Node{
    int Element;
    int flag;    //用来不重复插入相同数据,做个标记
    struct Node *Next;
};

typedef struct Node *List;
typedef struct Node *PtrToNode;
typedef PtrToNode Position;

//创建具有头节点的链表
List create(){
  List L;
  PtrToNode header=malloc(sizeof(struct Node));
  header->Next=NULL;
  L=header;
  return L;
}
//执行插入操作,参数表示,element将插入到posi所指向的元素的后面
void Insert(int element,List list,Position posi){
    PtrToNode p=list;//用作循环遍历链表,找到地址与posi相同的结点
    PtrToNode temp;//用来新建结点
    while(p!=posi){
        p=p->Next;
    }//找到posi所在位置
    if(p==list){//a1.当位置在头节点
      if(p->Next==NULL){//a1.b1在头节点,当头结点的下一个为空时,直接插入
        temp=malloc(sizeof(struct Node));
        temp->Element=element;
        temp->flag=1;
        temp->Next=p->Next;
        p->Next=temp;
      }else{//a1.b2在头节点,当头结点的下一个不为空时
          if(element==p->Next->Element){//a1.b2.c1当要插入的元素与位置指向的下一个元素相同时,不用重复插入
              p->Next->flag++;
          }else{//a1.b2.c2不同时,肯定小于,所以插入
             temp=malloc(sizeof(struct Node));
            temp->Element=element;
            temp->flag=1;
            temp->Next=p->Next;
            p->Next=temp;
          }
      }
    //a2.当位置不在头节点
    }else if(p!=list){
      if(element==posi->Element){//a2.b1当要插入元素与当前位置元素相同时,不插入
           posi->flag++;
           return;
      }else{//a2.b2当不同时,肯定大于当前元素
        temp=malloc(sizeof(struct Node));
        temp->Element=element;
        temp->flag=1;
        temp->Next=p->Next;
        p->Next=temp;
      }
    }
}

//一下这部分的功能是找到要插入的位置
Position FindInsertPosition(List list,int element){
    PtrToNode p=list;
    Position position;
    while(p!=NULL){
        if(p->Next==NULL){//a1如果下一个元素为空,则一定是这个位置
            position=p;
            break;
          }else if(p->Next!=NULL){//a2如果下一个不为空
               if(element<=p->Next->Element){//a2.b1要插入元素小于或等于下一个元素,都可以插在p和p指向的下一个元素之间
              position=p;
              break;
            }else if(element>p->Next->Element){//a2.b2当要插入元素还大于p指向的下一个元素时,肯定插入点在下一个元素的后面,只需循环遍历
                 p=p->Next;
             }
        }
   }
   return position;
} 

void InsertWithNode(PtrToNode temp,List list,Position posi){
       PtrToNode p=list;//用作循环遍历链表,找到地址与posi相同的结点
       int element=temp->Element;
    while(p!=posi){
        p=p->Next;
    }//找到posi所在位置
    if(p==list){//a1.当位置在头节点
      if(p->Next==NULL){//a1.b1在头节点,当头结点的下一个为空时,直接插入
        temp->Next=p->Next;
        p->Next=temp;
      }else{//a1.b2在头节点,当头结点的下一个不为空时
          if(element==p->Next->Element){//a1.b2.c1当要插入的元素与位置指向的下一个元素相同时,不用重复插入
              p->Next->flag+=temp->flag;
              free(temp);
          }else{//a1.b2.c2不同时,肯定小于,所以插入
            temp->Next=p->Next;
            p->Next=temp;
          }
      }
    //a2.当位置不在头节点
    }else if(p!=list){
      if(element==posi->Element){//a2.b1当要插入元素与当前位置元素相同时,不插入
           posi->flag+=temp->flag;
           free(temp);
           return;
      }else{//a2.b2当不同时,肯定大于当前元素
        temp->Next=p->Next;
        p->Next=temp;
      }
    }
}
List UnitTwoList(List list1,List list2){
    //让将要返回的链表先指向第一个,然后将第二个链表的每个元素依次插入
    List list=list1;
    PtrToNode ptr=list2->Next;
    Position position;
    PtrToNode ptrD=ptr;
    while(ptr!=NULL){
      //对于链表2中的每个元素都要依次找出其在链表1中需要插入的位置
      position=FindInsertPosition(list,ptr->Element);
      //Insert(ptr->Element,list,position);
      //ptr=ptr->Next;
      ptrD=ptr->Next;
      InsertWithNode(ptr,list,position);
      ptr=ptrD;
    }
    return list;
}

void printListWithFlag(List list){
    PtrToNode p=list->Next;
    int i,t=1;
    while(p!=NULL){
        for(i=1;i<=p->flag;i++){
            printf("L[%2d]=%3d ",t,p->Element);
            if(t%10==0){
             printf("\n");
            }
               t++;
        }
        p=p->Next;
    }
}

void initList(List list){
    int x,i;
    Position position;
    for(i=0;i<50;i++){
      x=rand()%1000;
      printf("%4d",x);
      if((i+1)%25==0){
        printf("\n");
      }
      position=FindInsertPosition(list,x);
        Insert(x,list,position);
    }
}
int main(){
    srand(time(NULL));
    List list_a=create();
    List list_b=create();
    //初始化第一个链表
    initList(list_a);
    //初始化第二个链表
    initList(list_b);
    printf("\n");
    printf("输出list_a:\n");
    printListWithFlag(list_a);
    printf("输出list_b:\n");
    printListWithFlag(list_b);
    List list=UnitTwoList(list_a,list_b);
    printf("输出合并后的list:\n");
    printListWithFlag(list);
    return 0;
}

2.5双向链表:

双向链表用来倒序扫描链表很方便,而且简化了删除操作,不再迫使使用一个指向前驱元的指针来访问一个关键字。

实例:编写程序,从键盘输入10个整数并放入一个循环双向链表中(不排序),然后按输入顺序输出这些整数和逆序输出这些整数。

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

struct Node{
  struct Node *Pre;
  int Element;
  struct Node *Next;
};
typedef struct Node *List;
typedef struct Node *PtrToNode;

List create(){
    List list;
    PtrToNode header,tailer;
    header=malloc(sizeof(struct Node));
    tailer=malloc(sizeof(struct Node));
    list=header;
    header->Pre=tailer;
    header->Next=tailer;
    tailer->Pre=header;
    tailer->Next=header;
    return list;
}
void Insert(List list,int x){
    PtrToNode temp;
    PtrToNode p=list;
    temp=malloc(sizeof(struct Node));
    temp->Element=x;

    while(p->Next!=list){
         p=p->Next;//找到尾节点
    }
      temp->Next=p;
      temp->Pre=p->Pre;
      p->Pre->Next=temp;
      p->Pre=temp;

}
 void PrintTwoDListOrder(List list){
     printf("依次顺序输出的结果为:\n");
     PtrToNode p=list;
     int i=1;
     while(p->Next!=list){
         if(p->Next!=list->Pre){
           printf("第%d个元素为%2d  ",i,p->Next->Element);
            p=p->Next;
            if(i%5==0){
                 printf("\n");
               }
               i++;
               continue;
         }
         break;
     }
 }
void PrintTwoDListNOrder(List list){
    printf("依次逆序输出的结果为:\n");
    PtrToNode p=list->Pre;
     int i=1;
     while(p->Pre!=list->Pre){
         if(p->Pre!=list){
           printf("第%d个元素为%2d  ",i,p->Pre->Element);
            p=p->Pre;
            if(i%5==0){
                  printf("\n");
               }
               i++;
               continue;
         }
         break;
     }
}
int main(){
    //创建一个有头有尾的双向链表
    srand(time(NULL));
    List list=create();
    int i,x;
    for(i=0;i<10;i++){
      x=rand()%100;
      printf("生成的随机数为%2d  ",x);
      if((i+1)%5==0){
          printf("\n");
      }
      Insert(list,x);
    }
    PrintTwoDListOrder(list);
    PrintTwoDListNOrder(list);
    return 0;
} 

双向链表的另一个特例是循环链表,即让最后的单元反过来指向第一个单元。循环链表也可以不是双向链表。这种结构在某些应用程序中很流行。

2.6十字链表

主链表上的每个结点都是支链表的头结点:

实例:随机抽取20张扑克牌(扑克牌有两项数据,一为花色,二为大小),花色按黑桃、红桃、梅花、方块次序排列,要求建立链表来存放这些扑克牌,最后输出。

(1)先按花色,再按大小排列

(2)先按大小,再按花色排列

注:一副牌共有52张,每张不能重复,请考虑如何随机产生不重复的牌。

先按花色排序,后按大小:

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

struct Poker{
  int Element;
  int Color;
  int Num;
  struct Poker *mainNext;
  struct Poker *BranchNext;
};
typedef struct Poker *List;
typedef struct Poker *PtrToNode;

void InitNode(PtrToNode p){
  if(p->Element%13==0){
      p->Num=13;
      p->Color=p->Element/13-1;
  }else{
      p->Num=p->Element%13;
      p->Color=p->Element/13;
  }
}
//用数组辅助产生不重复的52以内的无序的整数20个包含52
void InitDisOrder(int a[]){
  int s[52];
  int i,x;
  for(i=0;i<52;i++){
    s[i]=1;
  }
  for(i=0;i<20;i++){
L:  x=rand()%52;//产生1到52的随机数
    if(s[x]==0){
      goto L;
    }else{
      a[i]=x+1;
      s[x]=0;
    }
  }
}
//用数组辅助产生不重复的有序的整数20个包括52
void InitOrder(int a[]){
    int s[52];
    int i,j,x;
    for(i=0;i<52;i++){
      s[i]=1;
    }
    for(i=0;i<20;i++){
L:        x=rand()%52;
        if(s[x]==0){
            goto L;
        }else
        s[x]=0;
    }
    for(i=0;i<52;i++){
      if(s[i]==0){
        a[j]=i+1;
        j++;
      }else
      continue;
    }
}
PtrToNode InsertBranch(List list,int x){
  PtrToNode p1,p2,temp;
  p1=p2=list;
  temp=malloc(sizeof(struct Poker));
  temp->Element=x;
  temp->mainNext=NULL;
  temp->BranchNext=NULL;
  InitNode(temp);
  while(p1!=NULL){
      if(temp->Num<p1->Num){
      temp->BranchNext=p1;
      if(p2==list&&p1==p2){
          list=temp;
        return list;
        }else
          p2->BranchNext=temp;
          return list;
    }
    else if(temp->Num>p1->Num){
         p2=p1;
         p1=p1->BranchNext;
         //当p1==NULL,下一步会跳出while循环,也可以放在while后
         if(p1==NULL){
           temp->BranchNext=NULL;
           p2->BranchNext=temp;
        }
    }
  }
  return list;
}
List InsertMain(List list,int x){ //(x/13代表花色,x%13代表大小)
  PtrToNode p1,p2,temp;
  p1=p2=list;
  temp=malloc(sizeof(struct Poker));
  temp->Element=x;
  InitNode(temp);
  temp->mainNext=NULL;
  temp->BranchNext=NULL;
  //printf("\n%d=%d,%d",temp->Element,temp->Color,temp->Num);
  if(list==NULL){
    list=temp;
  }
  else
    while(p1!=NULL){
       if(temp->Color<p1->Color){
            if(p2==list&&p1==p2){
          temp->mainNext=p1;
          list=temp;
        }else{
             temp->mainNext=p1;
          p2->mainNext=temp;
        }
        return list;
       }else if(temp->Color==p1->Color){
           PtrToNode p3=p1->mainNext;
           PtrToNode p4=p1;
           PtrToNode p=InsertBranch(p1,x);
           p->mainNext=p3;
           if(p2==list&&p2==p4){
           list=p;
         }else{
          p2->mainNext=p;
           }
        return list;
        }else{
           p2=p1;
           p1=p1->mainNext;
           if(p1==NULL){
               temp->mainNext=NULL;
               temp->BranchNext=NULL;
               p2->mainNext=temp;
           }
       }
    }//while结束
  return list;
}
void printSingleList(List list){
    PtrToNode p1;
    p1=list;
    while(p1!=NULL){
        switch(p1->Color){
            case 0:printf("黑桃");break;
            case 1:printf("红桃");break;
            case 2:printf("梅花");break;
            case 3:printf("方块");break;
        }
        printf("%2d  ",p1->Num);
        p1=p1->BranchNext;
    }
}
void printDoubleList(List list){
    PtrToNode p=list;
    while(p!=NULL){
        switch(p->Color){
            case 0:printf("\n下面输出黑桃的数字排序:\n\t");break;
            case 1:printf("\n下面输出红桃的数字排序:\n\t");break;
            case 2:printf("\n下面输出梅花的数字排序:\n\t");break;
            case 3:printf("\n下面输出方块的数字排序:\n\t");break;
            default:printf("没有这个花色:\n");exit(0);
        }
        printSingleList(p);
        p=p->mainNext;
    }
}
void Warn(){
    printf("本程序的规定:\n");
    printf("\t1到13代表黑桃 14到26代表红桃 27到39代表梅花 40到52代表方块。\n\n" );
}
int main(){
    Warn();
    PtrToNode temp;
    int i;
    srand(time(NULL));
      int select[20];
      InitDisOrder(select);
    for(i=0;i<20;i++){
      temp=malloc(sizeof(struct Poker));
      temp->Element=select[i];
      temp->BranchNext=NULL;
      InitNode(temp);
      printSingleList(temp);
      if((i+1)%10==0){
          printf("\n");
        }
    }
    List list=NULL;
    for(i=0;i<20;i++){
     list=InsertMain(list,select[i]);
    }
    printDoubleList(list);
    printf("\n");
    return 0;
} 

再先按大小后按花色排序:

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

struct Poker{
  int Element;
  int Color;
  int Num;
  struct Poker *mainNext;
  struct Poker *BranchNext;
};
typedef struct Poker *List;
typedef struct Poker *PtrToNode;

void InitNode(PtrToNode p){
  if(p->Element%13==0){
      p->Num=13;
      p->Color=p->Element/13-1;
  }else{
      p->Num=p->Element%13;
      p->Color=p->Element/13;
  }
}
//用数组辅助产生不重复的52以内的无序的整数20个包含52
void InitDisOrder(int a[]){
  int s[52];
  int i,x;
  for(i=0;i<52;i++){
    s[i]=1;
  }
  for(i=0;i<20;i++){
L:  x=rand()%52;//产生1到52的随机数
    if(s[x]==0){
      goto L;
    }else{
      a[i]=x+1;
      s[x]=0;
    }
  }
}
//用数组辅助产生不重复的有序的整数20个包括52
void InitOrder(int a[]){
    int s[52];
    int i,j,x;
    for(i=0;i<52;i++){
      s[i]=1;
    }
    for(i=0;i<20;i++){
L:        x=rand()%52;
        if(s[x]==0){
            goto L;
        }else
        s[x]=0;
    }
    for(i=0;i<52;i++){
      if(s[i]==0){
        a[j]=i+1;
        j++;
      }else
      continue;
    }
}
PtrToNode InsertBranch(List list,int x){
  PtrToNode p1,p2,temp;
  p1=p2=list;
  temp=malloc(sizeof(struct Poker));
  temp->Element=x;
  temp->mainNext=NULL;
  temp->BranchNext=NULL;
  InitNode(temp);
  while(p1!=NULL){
      if(temp->Color<p1->Color){
      temp->BranchNext=p1;
      if(p2==list&&p1==p2){
          list=temp;
        return list;
        }else
          p2->BranchNext=temp;
          return list;
    }
    else if(temp->Color>p1->Color){
         p2=p1;
         p1=p1->BranchNext;
         //当p1==NULL,下一步会跳出while循环,也可以放在while后
         if(p1==NULL){
           temp->BranchNext=NULL;
           p2->BranchNext=temp;
        }
    }
  }
  return list;
}
List InsertMain(List list,int x){ //(x/13代表花色,x%13代表大小)
  PtrToNode p1,p2,temp;
  p1=p2=list;
  temp=malloc(sizeof(struct Poker));
  temp->Element=x;
  InitNode(temp);
  temp->mainNext=NULL;
  temp->BranchNext=NULL;
  //printf("\n%d=%d,%d",temp->Element,temp->Color,temp->Num);
  if(list==NULL){
    list=temp;
  }
  else
    while(p1!=NULL){
       if(temp->Num<p1->Num){
            if(p2==list&&p1==p2){
          temp->mainNext=p1;
          list=temp;
        }else{
             temp->mainNext=p1;
          p2->mainNext=temp;
        }
        return list;
       }else if(temp->Num==p1->Num){
           PtrToNode p3=p1->mainNext;
           PtrToNode p4=p1;
           PtrToNode p=InsertBranch(p1,x);
           p->mainNext=p3;
           if(p2==list&&p2==p4){
           list=p;
         }else{
          p2->mainNext=p;
           }
        return list;
        }else{
           p2=p1;
           p1=p1->mainNext;
           if(p1==NULL){
               temp->mainNext=NULL;
               temp->BranchNext=NULL;
               p2->mainNext=temp;
           }
       }
    }//while结束
  return list;
}
void printSingleList(List list){
    PtrToNode p1;
    p1=list;
    while(p1!=NULL){
        switch(p1->Color){
            case 0:printf("黑桃");break;
            case 1:printf("红桃");break;
            case 2:printf("梅花");break;
            case 3:printf("方块");break;
        }
        printf("%2d  ",p1->Num);
        p1=p1->BranchNext;
    }
}
void printDoubleList(List list){
    PtrToNode p=list;
    while(p!=NULL){
        printf("\n数字%d:\n\t",p->Num);
        printSingleList(p);
        p=p->mainNext;
    }
}
void Warn(){
    printf("本程序的规定:\n");
    printf("\t1到13代表黑桃 14到26代表红桃 27到39代表梅花 40到52代表方块。\n" );
}

int main(){
    Warn();
    int i;
    srand(time(NULL));
      int select[20];
      PtrToNode temp;
      InitDisOrder(select);
    for(i=0;i<20;i++){
      printf("%3d=",select[i]);
      temp=malloc(sizeof(struct Poker));
      temp->Element=select[i];
      temp->BranchNext=NULL;
      InitNode(temp);
      printSingleList(temp);
      if((i+1)%10==0){
          printf("\n");
        }
    }
    List list=NULL;
    for(i=0;i<20;i++){
     list=InsertMain(list,select[i]);
    }
    printDoubleList(list);
    return 0;
} 

其实第二个程序只是第一个程序在条件上,顺序改了改,在做第一个时我考虑到了扩展性的问题,于是第二个程序只用了5分钟就完成了。而第一个程序用了几个小时。

时间: 2024-08-02 16:39:38

数据结构与算法分析(5)表、栈和队列(一)的相关文章

小猪的数据结构辅助教程——3.1 栈与队列中的顺序栈

小猪的数据结构辅助教程--3.1 栈与队列中的顺序栈 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点 1.栈与队列的介绍,栈顶,栈底,入栈,出栈的概念 2.熟悉顺序栈的特点以及存储结构 3.掌握顺序栈的基本操作的实现逻辑 4.掌握顺序栈的经典例子:进制变换的实现逻辑 1.栈与队列的概念: 嗯,本节要进行讲解的就是栈 + 顺序结构 = 顺序栈! 可能大家对栈的概念还是很模糊,我们找个常见的东西来拟物化~ 不知道大家喜欢吃零食不--"桶装薯片"就可以用来演示栈! 生产的时

浅谈算法和数据结构(1):栈和队列

浅谈算法和数据结构(1):栈和队列 2014/11/03 ·  IT技术                                         · 2 评论                                      ·  数据结构, 栈, 算法, 队列 分享到: 60 SegmentFault D-Day 2015 北京:iOS 站 JDBC之“对岸的女孩走过来” CSS深入理解之relative HTML5+CSS3实现春节贺卡 原文出处: 寒江独钓   欢迎分享原创

小猪的数据结构辅助教程——3.2 栈与队列中的链栈

小猪的数据结构辅助教程--3.2 栈与队列中的链栈 标签(空格分隔): 数据结构 1.本节引言: 嗯,本节没有学习路线图哈,因为栈我们一般都用的是顺序栈,链栈还是顺带提一提吧, 栈因为只是栈顶来做插入和删除操作,所以较好的方法是将栈顶放在单链表的头部,栈顶 指针与单链表的头指针合二为一~所以本节只是讲下链栈的存储结构和基本操作! 2.链栈的存储结构与示意图 存储结构: typedef struct StackNode { SElemType data; //存放的数据 struct StackN

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

    在接下来的几篇博文中,将介绍表.栈.队列在编程实践中的应用.     (1)表达式求值:     输入一个中缀表达式,操作符包括(+ - * / ^).转化为后缀表达式之后并计算表达式的值: 要求: 1.输入的中缀表达式必须是一个完整的字符串: 2.不限制数字的位数和正负,负数用()括起来: 代码如下: 1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 5 #define EmptyT

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

    本节继续介绍表.栈.队列在编程实践中的应用.     (1)行编辑程序:(允许用户输入出差错,并在发现错误时可以及时更正.)     功能:接受用户从终端输入的字符型的数据,并存入用户的数据区.由于不能保证不出差错,因此“每接受一个字符即存入用户数据区”的做法不是最恰当的:较好的做法是,设立一个输入的缓冲区,用以接受用户输入的一行字符,然后逐行存入用户数据区. 算法原理:当用户发现刚刚键入的一个字符是错的时,可补进一个退格符“#”,以表示前一个字符无效:如果发现当前键入的行内差错较多或者

《数据结构》第3章-栈与队列的学习总结

我之前有接触过栈和队列,当时就觉得很好奇,那是以怎样的存储结构存储数据的呢?拨开重重迷雾,终于学到基础知识了. 学习<栈和队列>有两个星期了,有了前面两个章节的思维基础,我觉得栈和队列学习起来还是很好理解的,通过一些实际应用例子,让我有了更进一步的理解.现在我梳理一下知识,下面总结这一章我所学习到的东西. 一.栈(后进先出:LIFO) 1.顺序栈 这是顺序栈的存储结构: typedef struct { int *base;//栈底指针 int *top; //栈顶指针 int size; /

数据结构 数组,链表,栈,队列理解

数据结构 数据结构是指相互之间存在一种或多种特定关系的数据元素的集合.再简单描述一下:数据结构就是描述对象间逻辑关系的学科. 数据存储结构 常用的数据存储方式有两种:顺序存储,非顺序存储.顺序存储就是把数据存储在一块联系的存储介质(硬盘或内存等)中.反之则是非顺序存储. Java中的数组就是典型的顺序存储,链表就是非顺序存储.数组存储数据时会开辟出一块联系内存,按顺序存储.链表先不会开辟出一块内存来,而是只需要知道下一个节点存储的位置,就能把所以的数据连起来了.所以单向链表的最后一个节点是指向N

纯数据结构Java实现(2/11)(栈与队列)

栈和队列的应用非常多,但其起实现嘛,其实很少人关心. 虽然苹果一直宣传什么最小年龄的编程者,它试图把编程大众化,弱智化,但真正的复杂问题,需要抽丝剥茧的时候,还是要 PRO 人士出场,所以知根知底,实在是必要之举(而非无奈之举). 大门敞开,越往里走越窄,竞争会越激烈. 栈 基本特性 就一条,FILO.但是用在其他复杂数据结构,比如树,或者用在其他应用场景的时候,比如记录调用过程中的变量及其状态等,超有用. 应用举例 比如 撤销操作: 用户每次的录入都会入栈,被系统记录,然后写入文件:但是用户撤

2 限定性线性表——栈与队列

1 栈与队列     1.1 包含min函数的栈 定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数 在该栈中,调用min.push和pop方法 要求时间复杂度均为O(1) 算法思想: 要求时间复杂度均为 O(1),增加辅助空间实现,即增加一个辅助栈存储min值 例如:data 中依次入栈 5, 4, 3, 8, 10, 11, 12, 1, 则 min 中依次入栈 5, 4, 3,no,no, no, no, 1. no 代表此次不如栈,如果入栈的元素小于等于 min 中的栈

【自考】数据结构第三章,栈、队列、数组,期末不挂科指南,第3篇

学习目标 自考重点.期末考试必过指南,这篇文章让你理解什么是栈.什么是队列.什么是数组 掌握栈.队列的顺序存储结构和链式存储结构 掌握栈.队列的基本操作在顺序存储结构和链式存储结构上的实现 掌握矩阵的压缩存储 今天核心咱们先把栈搞清楚 栈和队列可以看做是特殊的线性表 .它们的特殊性表现在它们的基本运算是线性表运算的子集,它们是运算受限的线性表 栈 栈(Stack)是运算受限的线性表,这种线性表上的插入和删除操作限定在表的一端进行 基本概念 栈顶:允许插入和删除的一端 栈尾:另一端 空栈:不含任何