• 数据结构课程的实践方法指导


      有不少人说数据结构课程抽象,学习起来感到困难。有些同学放弃了,有些同学拿出了高中学习时练熟的功夫,强行理解,死记硬背。结果辛苦不少。效果不佳。反倒得到非常多枯燥的感受。


      其实。数据结构课中有不少理论性分析的内容。但对于本科生学习的内容,以及要达到目标,还是以实践性为主的。在学习的方法上做出改变,这门课程就能够展示出生动的实践性味道来。这就要求在学习过程中,将实践学习有效地开展下去。诸多的困境即能够破解。
      学习数据结构和算法的过程中做出的这些实践。也便成了程序设计能力提高中的重要部分。
      本文就以数据结构课程开篇的“线性表的顺序存储”部分为例,给出两种实践路线的演示样例。

    一、在一个程序文件里验证算法

      參考教材中的解说,我们能够将教材中须要掌握的算法合并成一个程序。让程序在计算上跑起来。在输入、调试、測试的过程中。完毕了掌握算法的目的。
      本文演示样例基于李春葆老师的《数据结构教程(第4版)》给出,使用其它教材的做法相似:
      第一步:定义数据的存储结构
      比如。对于线性结构,能够写下:

    #define MaxSize 50    //Maxsize将用于后面定义存储空间的大小
    typedef int ElemType;  //ElemType在不同场合能够依据问题的须要确定,在此取简单的int
    typedef struct
    {
        ElemType data[MaxSize];  //利用了前面MaxSize和ElemType的定义
        int length;
    } SqList;

      第二步:实现各种基本操作
      比如,对于线性表的顺序存储

    //用数组创建线性表
    void CreateList(SqList *&L, ElemType a[], int n)
    {
        int i;
        L=(SqList *)malloc(sizeof(SqList));
        for (i=0; i<n; i++)
            L->data[i]=a[i];
        L->length=n;
    }
    
    //初始化线性表InitList(L)
    void InitList(SqList *&L)   //引用型指针
    {
        L=(SqList *)malloc(sizeof(SqList));
        //分配存放线性表的空间
        L->length=0;
    }
    
    //销毁线性表DestroyList(L)
    void DestroyList(SqList *&L)
    {
        free(L);
    }
    
    //判定是否为空表ListEmpty(L)
    bool ListEmpty(SqList *L)
    {
        return(L->length==0);
    }
    
    //求线性表的长度ListLength(L)
    int ListLength(SqList *L)
    {
        return(L->length);
    }
    
    //输出线性表DispList(L)
    void DispList(SqList *L)
    {
        int i;
        if (ListEmpty(L)) return;
        for (i=0; i<L->length; i++)
            printf("%d ",L->data[i]);
        printf("
    ");
    }
    
    //求某个数据元素值GetElem(L,i,e)
    bool GetElem(SqList *L,int i,ElemType &e)
    {
        if (i<1 || i>L->length)  return false;
        e=L->data[i-1];
        return true;
    }
    
    //按元素值查找LocateElem(L,e)
    int LocateElem(SqList *L, ElemType e)
    {
        int i=0;
        while (i<L->length && L->data[i]!=e) i++;
        if (i>=L->length)  return 0;
        else  return i+1;
    }
    
    //插入数据元素ListInsert(L,i,e)
    bool ListInsert(SqList *&L,int i,ElemType e)
    {
        int j;
        if (i<1 || i>L->length+1)
            return false;   //參数错误时返回false
        i--;            //将顺序表逻辑序号转化为物理序号
        for (j=L->length; j>i; j--) //将data[i..n]元素后移一个位置
            L->data[j]=L->data[j-1];
        L->data[i]=e;           //插入元素e
        L->length++;            //顺序表长度增1
        return true;            //成功插入返回true
    }
    
    //删除数据元素ListDelete(L,i,e)
    bool ListDelete(SqList *&L,int i,ElemType &e)
    {
        int j;
        if (i<1 || i>L->length)  //參数错误时返回false
            return false;
        i--;        //将顺序表逻辑序号转化为物理序号
        e=L->data[i];
        for (j=i; j<L->length-1; j++) //将data[i..n-1]元素前移
            L->data[j]=L->data[j+1];
        L->length--;              //顺序表长度减1
        return true;              //成功删除返回true
    }

       第三步。编写測试函数,对于每个基本运算的实现进行測试
      首先。应该在程序头部加上必要的头文件(假设不加,出现编译错误倒是也会对付)。

    #include <stdio.h>
    #include <malloc.h>

      測试函数一般“宜小不宜大”。也就是说,一次尽可能測试尽可能少的基本操作。只是。在必要的时候。也须要调用别的基本操作,用来完毕辅助性的工作。比如。以下的main函数。目标是測试创建顺序表的函数CreateList。创建中须要的数组x能够在程序中直接初始化,而创建的结果,希望通过输出进行观察,于是,调用DispList成为必要。


      用于測试CreateList的測试函数写作:

    int main()
    {
        SqList *sq;
        ElemType x[6]= {5,8,7,2,4,9};
        CreateList(sq, x, 6);
        DispList(sq);
        return 0;
    }

       再例,測试初始化操作InitList时。能够与插入元素的操作ListInsert一起进行。

    int main()
    {
        SqList *sq;
        InitList(sq);
        ListInsert(sq, 1, 5);
        ListInsert(sq, 2, 3);
        ListInsert(sq, 1, 4);
        DispList(sq);
        return 0;
    }

      接下来,能够更换測试函数,用于測试别的基本操作。

    “宜小不宜大”的原则简化了工作的难度,可是可能须要编制多个測试函数,分多次执行,才干将多个測试函数測试完。

    二、构建自己的“算法库”

      数据结构课程的主线就是各种逻辑结构,在不同的存储结构下,基本运算的实现与复杂度分析。

    这些基本数据结构的定义,以及各种基本运算的实现,能够构成“算法库”。建立起来的算法库。能够作为即将要解决这个问题时,所依托的“基础设施”。其实,在project中用到的系统库,或第三方的算法库,也是将通用的模块集中起来而形成的。


      为有效地组织。程序将用多文件的方式组织。当中的头文件和提供自定函数实现的文件。组成的就是算法库。
      对于线性表的顺序存储,算法库的头文件能够定义为:
    list.h

    #ifndef LIST_H_INCLUDED
    #define LIST_H_INCLUDED
    
    #define MaxSize 50
    typedef int ElemType;
    typedef struct
    {
        ElemType data[MaxSize];
        int length;
    } SqList;
    void CreateList(SqList *&L, ElemType a[], int n);//用数组创建线性表
    void InitList(SqList *&L);//初始化线性表InitList(L)
    void DestroyList(SqList *&L);//销毁线性表DestroyList(L)
    bool ListEmpty(SqList *L);//判定是否为空表ListEmpty(L)
    int ListLength(SqList *L);//求线性表的长度ListLength(L)
    void DispList(SqList *L);//输出线性表DispList(L)
    bool GetElem(SqList *L,int i,ElemType &e);//求某个数据元素值GetElem(L,i,e)
    int LocateElem(SqList *L, ElemType e);//按元素值查找LocateElem(L,e)
    bool ListInsert(SqList *&L,int i,ElemType e);//插入数据元素ListInsert(L,i,e)
    bool ListDelete(SqList *&L,int i,ElemType &e);//删除数据元素ListDelete(L,i,e)#endif // LIST_H_INCLUDED
    #endif

      配套地,在list.cpp中实现这些基本操作。


      
    list.cpp

    #include <stdio.h>
    #include <malloc.h>
    #include "list.h"
    
    //用数组创建线性表
    void CreateList(SqList *&L, ElemType a[], int n)
    {
        int i;
        L=(SqList *)malloc(sizeof(SqList));
        for (i=0; i<n; i++)
            L->data[i]=a[i];
        L->length=n;
    }
    
    //初始化线性表InitList(L)
    void InitList(SqList *&L)   //引用型指针
    {
        L=(SqList *)malloc(sizeof(SqList));
        //分配存放线性表的空间
        L->length=0;
    }
    
    //销毁线性表DestroyList(L)
    void DestroyList(SqList *&L)
    {
        free(L);
    }
    
    //判定是否为空表ListEmpty(L)
    bool ListEmpty(SqList *L)
    {
        return(L->length==0);
    }
    
    //求线性表的长度ListLength(L)
    int ListLength(SqList *L)
    {
        return(L->length);
    }
    
    //输出线性表DispList(L)
    void DispList(SqList *L)
    {
        int i;
        if (ListEmpty(L)) return;
        for (i=0; i<L->length; i++)
            printf("%d ",L->data[i]);
        printf("
    ");
    }
    
    //求某个数据元素值GetElem(L,i,e)
    bool GetElem(SqList *L,int i,ElemType &e)
    {
        if (i<1 || i>L->length)  return false;
        e=L->data[i-1];
        return true;
    }
    
    //按元素值查找LocateElem(L,e)
    int LocateElem(SqList *L, ElemType e)
    {
        int i=0;
        while (i<L->length && L->data[i]!=e) i++;
        if (i>=L->length)  return 0;
        else  return i+1;
    }
    
    //插入数据元素ListInsert(L,i,e)
    bool ListInsert(SqList *&L,int i,ElemType e)
    {
        int j;
        if (i<1 || i>L->length+1)
            return false;   //參数错误时返回false
        i--;            //将顺序表逻辑序号转化为物理序号
        for (j=L->length; j>i; j--) //将data[i..n]元素后移一个位置
            L->data[j]=L->data[j-1];
        L->data[i]=e;           //插入元素e
        L->length++;            //顺序表长度增1
        return true;            //成功插入返回true
    }
    
    //删除数据元素ListDelete(L,i,e)
    bool ListDelete(SqList *&L,int i,ElemType &e)
    {
        int j;
        if (i<1 || i>L->length)  //參数错误时返回false
            return false;
        i--;        //将顺序表逻辑序号转化为物理序号
        e=L->data[i];
        for (j=i; j<L->length-1; j++) //将data[i..n-1]元素前移
            L->data[j]=L->data[j+1];
        L->length--;              //顺序表长度减1
        return true;              //成功删除返回true
    }

      大功告成。假设这些函数的实现还没有经过測试的话。编制main.cpp,在当中写測试函数完毕測试工作。
      比如。为測试CreateList。写
      
    main.cpp

    #include "list.h"
    int main()
    {
        SqList *sq;
        ElemType x[6]= {5,8,7,2,4,9};
        CreateList(sq, x, 6);
        DispList(sq);
        return 0;
    }

    三、应用数据结构求解问题

      要实现的内容必然是要基于特定的存储结构,可能利用各基本操作的组合就能够完毕。也可能须要在数据结构上,自己设计算法完毕。也不排除主要部分是专门设计的算法,但有些环节。也须要基本操作的支持。总之,能够看出。从解决这个问题的角度。须要综合运用知识了,同一时候,前面建的算法库,有了用武之地。
      比如:设顺序表有10个元素。其元素类型为整型。设计一个算法。以第一个元素为分界线。将全部小于它的元素移到该元素的前面。将全部大于它的元素移到该元素的后面。


      设计出的算法是:

    void move1(SqList *&L)
    {
        int i=0,j=L->length-1;
        ElemType pivot=L->data[0];
        ElemType tmp;
        while (i<j) 
        {
            while (i<j && L->data[j]>pivot)
                j--;    
            while (i<j && L->data[i]<=pivot)
                i++;    
            if (i<j)
            {
                tmp=L->data[i];
                L->data[i]=L->data[j];
                L->data[j]=tmp;
            }
        }
        tmp=L->data[0]; 
        L->data[0]=L->data[j];
        L->data[j]=tmp;
    }

      为了将其作为一个程序,通过执行观察程序的执行过程,能够写出例如以下的main.cpp。能够看出。前面编制好的头文件list.h和list.cpp正在支持着这些工作。
      
    main.cpp

    #include "list.h"
    void move1(SqList *&L)  //定义解决这个问题的算法
    {
        int i=0,j=L->length-1;
        ElemType pivot=L->data[0];
        ElemType tmp;
        while (i<j)
        {
            while (i<j && L->data[j]>pivot)
                j--;
            while (i<j && L->data[i]<=pivot)
                i++;
            if (i<j)
            {
                tmp=L->data[i];
                L->data[i]=L->data[j];
                L->data[j]=tmp;
            }
        }
        tmp=L->data[0];
        L->data[0]=L->data[j];
        L->data[j]=tmp;
    }
    int main()   //在main函数中调用。保证程序能执行。解决这个问题
    {
        SqList *sq;
        ElemType x[10]= {3, 8, 2, 7, 1, 5, 3, 4, 6, 0};
        CreateList(sq, x, 10);
        DispList(sq);
        move1(sq);
        DispList(sq);
        return 0;
    }

    程序执行结果:

    3 8 2 7 1 5 3 4 6 0
    1 0 2 3 3 5 7 4 6 8

    四、执行程序,观察算法 法过程

      作为学习算法的过程,建议在须要的时候,通过单步执行的方式,观察程序的执行过程,以此来理解算法的执行细节。比如,下图是上面样例在单步执行过程中的一个截屏。
    这里写图片描写叙述
      非常显然。这样的将执行过程“可视化”的方式,对于算法的学习而言,是非常有效的。

  • 相关阅读:
    设置系统时间
    设置访问URL不要项目名二级目录
    tomcat部署项目报错NoSuchMethodException#addServlet,addFilter
    Java多线程-线程池ThreadPoolExecutor构造方法和规则
    IDEA Debug 快捷键
    收藏网站
    eclipse添加hadoop插件
    Frida Mac环境搭建
    Android UiAutomator 1&2
    Android下monkey命令参数
  • 原文地址:https://www.cnblogs.com/tlnshuju/p/7372880.html
Copyright © 2020-2023  润新知