数据结构刷题日常

实验一:线性表

  1. 实验目的
    (1)了解线性表的逻辑结构特性是数据元素之间存在着线性关系,在计算机中 表示这种关系有顺序存储结构和链式存储结构;
    (2)掌握这两种存储结构的描述方法;
    (3)掌握线性表的基本操作(查找、插入、删除) ;
    (4)考虑时间和空间复杂度设计算法。
  2. 实验内容(实验题目与说明)
    (1)创建一个顺序表,存放在数组A[N]中,元素的类型为整型,设计算法调整A,使其左边的所有元素小于0,右边的所有元素大于0(要求算法的时间复杂度和空 间复杂度均为 O(n)) 。
    (2)建立一个循环单链表,其节点有prior,data 和 next三个域,其中data为数据域,存放元素的有效信息,next域为指针域,指向后继节点,prior 为指针域,它的值为NULL。编写一个算法将此表改为循环双链表。
  3. 算法设计(核心代码或全部代码)

void quickSwapList(int a[],int n)
 {
    
     
    int i = 0, j = n - 1 ; // n 为顺序表的长度,即数的个数 
    while( i < j ) {
    
     
    while( a[i] < 0 && i < j ) 
    i ++ ; // 找到左边大于等于 0 的数
    while( a[j] >= 0 && i < j )
    j -- ; // 找到右边小于 0 的数 
    if(i < j ) // 交换 
    {
    
     
        int t = a[i] ; 
        a[i] = a[j] ; 
        a[j] = t ;
    } 
    i++; 
    j--; } }
void singleCircleToDoubleCircleLinkList(struct Node *first) {
    
     
      struct Node *p,*q; p=first; //工作指针 p 指向头结点 
      q=first->next; //工作指针 q 指向第 1 个结点 
      while(q!=first) {
    
     
         q->prior=p; //置结点 q 的前驱为指针 p 指向的结点 
         p=p->next; //移动工作指针 p 
         q=q->next; //移动工作指针 q 
       }
       q->prior=p; //置头结点的前驱为最后一个结点 
   }
  1. 运行与测试(测试数据和实验结果分析)
    图一

  2. 总结与心得
    以上代码虽然书本上都有,但是经过我的学习,我觉得我都已经慢慢掌握了,可是涉及到双链表和循环链表的还不是很理解,后面的就更不用说了,还需要在下把力气了。

实验二:栈

  1. 实验目的
    (1)理解栈的定义、特点及与线性表的异同;
    (2)熟悉顺序栈的组织方法,栈满、栈空的判断条件及其描述;
    (3)掌握栈的基本操作(进栈、退栈等) 。

  2. 实验内容(实验题目与说明)
    (1)设计一个算法,将一般算术表达式转化为逆波兰表达式,并求逆波兰表达 式的值。
    (2)设计两个栈 S1、 S2 都采用顺序栈方式, 并且共享一个存储区[0, MaxLen-1], 为了尽量利用空间,减少溢出的可能,可采用栈顶相向、迎面增长的存储方式,设计一个有关栈的入栈和出栈算法。

  3. 算法设计(核心代码或全部代码)


/*计算后缀表达式的值*/
float ComputeExpress(char a[])
{
    
    
    OpStack S;                 /*定义一个操作数栈*/
    int i=0,value;
    float x1,x2;
    float result;
    S.top=-1;                     /*初始化栈*/
    while(a[i]!='\0')                 /*依次扫描后缀表达式中的每个字符*/
    {
    
    
        if(a[i]!=' '&&a[i]>='0'&&a[i]<='9')     /*如果当前字符是数字字符*/
        {
    
    
            value=0;
            while(a[i]!=' ')                 /*如果不是空格,说明数字字符是两位数以上的数字字符*/
            {
    
    
                value=10*value+a[i]-'0';
                i++;
            }
            S.top++;
            S.data[S.top]=value;     /*处理之后将数字进栈*/
        }        
        else                    /*如果当前字符是运算符*/
        {
    
    
            switch(a[i])         /*将栈中的数字出栈两次,然后用当前的运算符进行运算,再将结果入栈*/
            {
    
            
            case '+':
                x1=S.data[S.top];
                S.top--;
                x2=S.data[S.top];
                S.top--;
                result=x1+x2;
                S.top++;
                S.data[S.top]=result;
                break;
            case '-':
                x1=S.data[S.top];
                S.top--;
                x2=S.data[S.top];
                S.top--;
                result=x2-x1;
                S.top++;
                S.data[S.top]=result;
                break;
            case '*':
                x1=S.data[S.top];
                S.top--;
                x2=S.data[S.top];
                S.top--;
                result=x1*x2;
                S.top++;
                S.data[S.top]=result;
                break;
            case '/':
                x1=S.data[S.top];
                S.top--;
                x2=S.data[S.top];
                S.top--;
                result=x2/x1;
                S.top++;
                S.data[S.top]=result;
                break;
            }
            i++;
        }
    }
    if(!S.top!=-1)                        /*如果栈不空,将结果出栈,并返回*/
    {
    
    
        result=S.data[S.top];
        S.top--;
        if(S.top==-1)
            return result;
        else
        {
    
    
            printf("表达式错误");
            exit(-1);
        }
    }
}

int PushStack(SequenStack *S,DataType e,int flag)
/*将元素e入共享栈。进栈成功返回1,否则返回0*/
{
    
    
    if(S->top[0]==S->top[1]+1)        /*如果共享栈已满*/
        return 0;                 /*返回0,进栈失败*/
    switch(flag)
    {
    
    
    case 1:                /*当flag为1,表示将元素进左端的栈*/
        S->stack[S->top[0]]=e;    /*元素进栈*/
        S->top[0]++;            /*修改栈顶指针*/
        break;
    case 2:            /*当flag为2,表示将元素要进右端的栈*/
        S->stack[S->top[1]]=e;    /*元素进栈*/
        S->top[1]--;            /*修改栈顶指针*/
        break;
    default:
        return 0;
    }
    return 1;                     /*返回1,进栈成功*/
}
int PopStack(SequenStack *S,DataType *e,int flag)
{
    
    
    switch(flag)             /*在出栈操作之前,判断哪个栈要进行出栈操作*/
    {
    
    
    case 1:             /*为1,表示左端的栈需要出栈操作*/
        if(S->top[0]==0)    /*左端的栈为空*/
            return 0;     /*返回0,出栈操作失败*/
        S->top[0]--;        /*修改栈顶指针,元素出栈操作*/
        *e=S->stack[S->top[0]];    /*将出栈的元素赋给e*/
        break;
    case 2:             /*为2,表示右端的栈需要出栈操作*/
        if(S->top[1]==MaxLen-1)    /*右端的栈为空*/
            return 0;             /*返回0,出栈操作失败*/
        S->top[1]++;                /*修改栈顶指针,元素出栈操作*/
        *e=S->stack[S->top[1]];    /*将出栈的元素赋给e*/
        break;
    default:
        return 0;
    }
    return 1; /*返回1,出栈操作成功*/
}

  1. 运行与测试(测试数据和实验结果分析)
    图一

  2. 总结与心得
    这是数据结构的第二个实验,主要涉及到了栈的构造构造及应用,注意一下代码中注释的地方,这边的*S.top++ = e;等于 *S.top = e;S.top++;这是Push的关键。同时注意一下malloc和realloc的用法。

实验三:队列

  1. 实验目的
    (1) 理解队列的定义、特点及与线性表的异同;
    (2) 熟悉队列的组织方法,队列满、队列空的判断条件及其描述;
    (3) 掌握队列的基本操作(入队、出队等) 。

  2. 实验内容(实验题目与说明)
    (1)假设以数组 sequ[MaxSize]存放环形队列的元素, 同时 Rear 和 Len 分别指示 环形队列中队尾元素的位置和内含元素的个数。设计相应的入队和出队算法。
    (2)某汽车轮渡口,过江渡船每次能载 10 辆车过江。过江车辆分别为客车类和 货车类,上船有如下规定:同类车先到先上船,客车先于货车上渡船,且每上 4 辆客 车,才允许上一辆货车;若等待客车不足 4 辆则以货车代替;若无货车等待则允许客 车都上船。设计一个算法模拟渡口管理。

  3. 算法设计(核心代码或全部代码)


//入队 
void entryQueue(int x) {
    
     
   if(Len==MaxSize)//队满 { 
      printf("队列已满,不能入队\n");
      return; } 
   Rear=(Rear+1)%MaxSize;//移动队尾指针 
   sequ[Rear]=x;//入队 
   Len++;//长度加 1 
return; }

//出队,返回出队元素 
int exitQueue(void) {
    
     
   int x,First;//队头元素的下标 
   if(Len==0)//队空 { 
      printf("队列已空,不能出队\n"); 
      return -1; } 
   First=((Rear+MaxSize)-Len+1)%MaxSize;//计算队头元素的下标
   x=sequ[First];//获得队头元素 
   Len--;//长度减 1 
   return x;//返回队头元素 }

  1. 运行与测试(测试数据和实验结果分析)
    图一

  2. 总结与心得
    通过这一次的实验,我不仅实现了栈和队列的算法,还解决了上次作业遗留下来的问题。通过百度,我知道了“ ? : ”操作符是需要返回值的,所以问号后面的和冒号后面的都是需要一个表达式,而不是一个完整的语句。那么 top==NULL?return 1:return 0; 语句就应该改为 return (top == -1)?1:0; 语句,程序才得以运行成功。

实验四:二叉树

  1. 实验目的

    (1)掌握二叉树的结构特性和二叉链表存储结构;

    (2)理解二叉树、完全二叉树、满二叉树的概念和存储特点;

    (3)掌握二叉树遍历的递归和非递归方法。

  2. 实验内容(实验题目与说明)

    (1)假设二叉树采用链接存储方式存储,分别编写一个二叉树先序遍历的递归算法和非递归算法。

    (2)一棵完全二叉树以顺序方式存储,设计一个递归算法,对该完全二叉树进行中序遍历。

  3. 算法设计(核心代码或全部代码)


struct biTreeNode *creatBiTree(struct biTreeNode *T) {
    
     
     char ch;
     static int i=0;
     ch=node[i]; //数组 node 存放了要创建的二叉树的扩展先序遍历序列
     i++; 
     if(ch=='#') {
    
     
        T=NULL; //#代表空子树; 
     } 
     else {
    
     
        T=(struct biTreeNode*)malloc(sizeof(struct biTreeNode)); 
        if(!T)
        exit(0); 
        T->data = ch; //数据域为 ch 
        T->leftChild=creatBiTree(T->leftChild); //递归建立左子树 
        T->rightChild=creatBiTree(T->rightChild); //递归建立右子树 }
        return T; }

void seqPreOrder(int i) {
    
     
      if(i==0) //递归调用的结束条件 
      return; 
      else {
    
     
         printf("%2c",node[i]);//输出根结点 
         if(2*i<=node[0]) 
         seqPreOrder(2*i);//先序遍历 i 的左子树 
         else 
         seqPreOrder(0);
      if(2*i+1<=node[0])
      seqPreOrder(2*i+1);//先序遍历 i 的右子树 
      else 
      seqPreOrder(0); } }

  1. 运行与测试(测试数据和实验结果分析)
    在这里插入图片描述
    在这里插入图片描述
  2. 总结与心得

    通过本次作业,加深了对递归的理解,掌握了递归的用法。

    对于实验四:判断两棵二叉树是否相等。当判断到字符不同时,由于是递归函数,不能只用简单的return 0;退出函数,因为这样只会退到上一层函数。解决方法:定义一个全局变量flag,初始值为1,当判断到字符不同时令flag=0;在判断函数Equal2中加上判断:if(flag==0) return 0;由于flag是全局变量,每一层Equal2函数中的flag都会变为0,所以可以完全退出递归。

实验五:哈夫曼树

  1. 实验目的

    (1)理解哈夫曼树的概念、结构特性和哈夫曼编码原理;

    (2)掌握构造哈夫曼树的基本方法;

    (3)掌握运用哈夫曼树进行哈夫曼编码的方法;

  2. 实验内容(实验题目与说明)

    根据哈夫曼(Huffman)编码的原理,编写一个程序,在用户输入节点权重的基础 上建立它的哈夫曼编码。

  3. 算法设计(核心代码或全部代码)


void huffmanTreeCode(int n) {
    
     
     char hc[MaxSize]; 
     int hcLen; 
     int i,j,k,parent,p; 
     for(i=0;i<n;i++) {
    
     
        hcLen=0; 
        parent=huffmanTree[i].parent;//待编码字符的双亲结点下标 
        p=i; 
        while(parent!=-1)//未到达根结点 
        {
    
     if(huffmanTree[parent].lChild==p)//是左孩子
         hc[hcLen]='0',hcLen++;
         else if(huffmanTree[parent].rChild==p)//是右孩子 
         hc[hcLen]='1',hcLen++; 
         p=parent;
         parent=huffmanTree[parent].parent;//继续向根结点查找
        } 
        for(j=0,k=hcLen-1;j<hcLen;j++,k--)//将编码写入相应字符数组 
           huffmanTree[i].hCode[j]=hc[k];
           huffmanTree[i].hCode[j]='\0';//加上字符串结束符 
     } return; }

  1. 运行与测试(测试数据和实验结果分析)
    在这里插入图片描述

  2. 总结与心得

    哈弗曼编码是一种常用的编码形式。甚至是WinRAR、ZIP、7z等压缩软件虽然不是是用哈弗曼编码进行压缩解压,毕竟纯哈弗曼编码的压缩率还是不足,但是至少它们多多少少都有点哈弗曼编码的思想。所以这是一种很不错的算法。

实验八:排序

  1. 实验目的

    (1)理解各类内部排序的基本思想;

    (2)掌握各类内部排序的基本方法;

    (3)了解各类内部排序的优缺点。

  2. 实验内容(实验题目与说明)

    (1)编写一个双向冒泡排序算法,即在排序过程中以交替的正、反两个方向进 行遍历。若第一趟把关键字最大的记录放到最末尾,则第二趟把关键字最小的记录放 到最前端,如此反复进行之。

    (2)设计一个进行堆排序的算法。

  3. 算法设计(核心代码或全部代码)


void doubleBubble(int r[],int n) {
    
    
     int flag=1; 
     int i=0,j;
     int temp; 
     while (flag==1) {
    
     
        flag=0; 
        for(j=n-i-1;j>=i+1;j--)//反向遍历找最小值 
        if(r[j]<r[j-1]) {
    
     
          flag=1; //能交换说明没有排好序,要继续 
          temp=r[j]; 
          r[j]=r[j-1]; 
          r[j-1]=temp; } 
        for(j=i+1;j<n-1;j++)//正向遍历找最大值
        if(r[j]>r[j+1]) {
    
     
        flag=1; //能交换说明没有排好序,要继续 
        temp=r[j]; 
        r[j]=r[j+1]; 
        r[j+1]=temp; } 
        i++; } return; }


void sift(int r[],int k,int m) {
    
    
     int i,j;
     i=k;//i 指向被筛选结点 
     j=2*i;// j 指向结点 i 的左孩子
     while(j<=m) {
    
     
     if(j<m && r[j]<r[j+1])//比较 i 的左右孩子, j 指向较大者 
     j++; if(r[i]>r[j])//根结点已经大于左右孩子中的较大者 
     break;
     else {
    
     
        r[0]=r[i];//交换根结点与结点 j
        r[i]=r[j]; 
        r[j]=r[0]; 
        i=j;//被筛选结点位于原来结点的 j 的位置 
        j=2*i; }
    } return; }


  1. 运行与测试(测试数据和实验结果分析)
    在这里插入图片描述

  2. 总结与心得

  • 在堆排序中,我们在做的是:
    • 先将序列进行m/2次调整为大顶堆(m为结点个数,自底第一个非叶子结点开始向上);
    • 再将头结点与尾结点作交换,然后当前序列的结点个数减1,再作一次从的调整(即将当前的头结点尽量的往下沉,直到它的孩子结点的值都比它小);
      *重复操作2,直到当前的元素个数为1即可结束!

猜你喜欢

转载自blog.csdn.net/qq_36987489/article/details/104946146