数据结构排序之冒泡、快速、插入、选择、堆、归并等排序及时间,空间复杂度等(超详解,绝对能满足你的需求,并能学到很多有用知识)

在本文章开始之前给大家介绍个网站,可以通过下面动画网址来理解 ,(国外的网站帮助学习数据结构很多知识,可以翻译下来,在搜索框搜索相应的排序算法进行动画演示,非常好用。)​​​https://www.cs.usfca.edu/~galles/visualization/icon-default.png?t=N7T8https://www.cs.usfca.edu/~galles/visualization/ 动画使用方法是,先点play,然后及时点击pause,自己通过Step Forward来查看。

作者QQ:2529702031 备注CSDN 可以问不会的问题,解答疑惑

目录

冒泡排序

快速排序

插入排序

选择排序

堆排序

归并排序

所有排序算法时间与空间复杂度汇总:

冒泡排序

 
  冒泡排序的基本思想是:从后往前(或从前往后)两两比较相邻元素的值,(若A[j-1]>A[j]),则交换它们,直到序列比较完。我们称它为第一趟冒泡,结果是将最小的元素交换到待排序列的第一个位置。关键字最小的元素如气泡一般逐渐往上“漂浮”直至“水面”。下一趟冒泡时, 前一趟确定的最小元素不再参与比较,每趟冒泡的结果是把序列中的最小元素放到了序列的最终位置……这样最多做n - 1趟冒泡就能把所有元素排好序。https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html给大家已经找好了,直接点击就可看排序算法动画演示。

(用的是王道的资料,王道还是比较权威的) 

代码如下: 

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef int ElemType;
typedef  struct{
    ElemType *elem;//存储元素的起始地址
    int TableLen;//元素个数
}SSTable;
void  ST_Init(SSTable &ST,int len)
{
    ST.TableLen=len;
    ST.elem=(ElemType*)malloc(sizeof (ElemType)*ST.TableLen);//申请堆空间,当数组来使用
    srand(time(NULL));
    for (int i = 0; i < ST.TableLen; ++i) {
        ST.elem[i]=rand()%100;//利用的是随机数生成 生成的是0-99之间
    }
}
//打印数组的元素
void ST_print(SSTable ST)
{
    for (int i = 0; i < ST.TableLen; ++i) {
        printf("%3d",ST.elem[i]);
    }
    printf("\n");
}
//交换两个元素
void swap(ElemType &a,ElemType &b)
{
    ElemType temp;
    temp=a;
    a=b;
    b=temp;
}
//冒泡排序
void BubbleSort(ElemType A[],int n)
{
    bool flag;
    for (int i = 0; i < n-1; ++i) //这里可以推出i最多可以访问到8
    {
        flag= false;//元素是否发生交换的标志
        for (int j = n-1; j > i;j--)//把最小值就放在最前面
        {
            if(A[j-1]>A[j])
            {
                swap(A[j-1],A[j]);
                flag=true;
            }
        }
        if(false==flag)//如果一趟比较没有发生任何变换,说明有序,也不再浪费时间,直接用这个哨兵提前结束排序
        {
            return;
        }
    }
}
int main() {
    SSTable ST;
    ST_Init(ST,10);//初始化
    ST_print(ST);
    BubbleSort(ST.elem,10);
    ST_print(ST);
    return 0;
}

时间复杂度O\left ( n^{2} \right ),空间复杂度O(1)

稳定性:冒泡排序是一种稳定的排序算法

适用性:适用于顺序存储和链式存储的线性表

快速排序

  快速排序的核心是分治思想:假设我们的目 标依然是按从小到大的顺序排列,我们找到 数组中的一个分割值,把比分割值小的数都 放在数组的左边,把比分割值大的数都放在 数组的右边,这样分割值的位置就被确定。 数组一分为二,我们只需排前一半数组和后 一半数组,复杂度直接减半。采用这种思想, 不断地进行递归,最终分割得只剩一个元素 时,整个序列自然就是有序的。可通过下面链接进行更加详细理解(注意大致思想一样,但有区别,下面有我的草稿图,呜呜呜,字太丑了,配合教材吧,再配上我的代码来理解)

Comparison Sorting Visualizationicon-default.png?t=N7T8https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html

 

 (重点理解图示)

代码如下: 

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
typedef int ElemType;
typedef struct
{
    ElemType *elem;//存储元素的起始地址
    int TableLen;//元素个数
}SSTable;
void ST_Init(SSTable &ST,int len)
{
    ST.TableLen=len;
    ST.elem=(ElemType*) malloc(sizeof (ElemType)*ST.TableLen);//申请堆空间,当数组来使用
    int i;
    srand(time(NULL));//随机数生成,每一次代码执行就会得到随机的10个元素
    for (int i = 0; i < ST.TableLen; ++i) {
        ST.elem[i]= rand()%100;//生成的是0-99之间
    }
}
void ST_Print(SSTable ST)
{
    for (int i = 0; i < ST.TableLen; ++i) {
        printf("%3d",ST.elem[i]);
    }
    printf("\n");
}
int Partition(ElemType A[],int low,int high)
{
   ElemType pivot=A[low];//首先使用左边变量存储分割值
    while (low < high)
    {
        while (low < high && A[high]>=pivot)//从后往前遍历找到一个比分割值小的
            high--;
        A[low]=A[high];//把比分隔值小的那个元素,放到A[low]
        while (low < high && A[low]<=pivot)//从前往后遍历,找到一个比分隔值大的
            low++;
        A[high]=A[low];//把比分割值大的那个元素,放到A[high],因为刚才high位置的元素已经放到low位置
    }
    A[low]=pivot;
    return low;//返回分隔值所在的下标
}
//递归实现
void  QuickSort(ElemType A[],int low,int high)
{
    if(low < high)
    {
        int pivotpos=Partition(A,low,high);//分割点左边的元素都比分割点要小,右边的比分割点大
        QuickSort(A,low,pivotpos-1);
        QuickSort(A,pivotpos+1,high);
    }
}

int main() {
    SSTable ST;
    ST_Init(ST,10);//初始化
//    ElemType A[10]={64,94,95,79,69,84,18,22,12,78};//内存copy接口,当你copy整型数组,或者浮点型时,要用memcpy,不能用strcpy,初试考memcpy概率很低
//    memcpy(ST.elem,A,sizeof (A));//这是为了降低调试难度,每次数组数据固定而设计的
    ST_Print(ST);
    QuickSort(ST.elem,0,9);//注意这个位置是n-1,也就是9,因为函数里取了high位置的值
    ST_Print(ST);
    return 0;
}

时间复杂度O\left ( n\log_{2}n \right )、空间复杂度O \left ( \log_{2}n \right )

稳定性:快速排序是所有内部排序算法中平均性能最优的排序算法,但这是一种不稳定的排序算法

插入排序

  插入排序分为 1、直接插入排序 2、折半插入排序 3、希尔排序 以上 3 种插入类型的排序,考研都是考选择题,考大题概率很低,因此我们仅讲解直接插 入排序的原理与代码实战,折半插入排序与希尔排序原理可以在后面的课程中进行学习。如果一个序列只有一个数,那么该序列自然是有序的。插入排序首先将第一个数视为有 序序列,然后把后面 9 个数视为要依次插入的序列。首先,我们通过外层循环控制要插入的 数,用 insertVal 保存要插入的值 87,我们比较 arr[0]是否大于 arr[1],即 3 是否大于 87,由 于不大于,因此不发生移动,这时有序序列是 3, 87。接着,将数值 2 插入有序序列,首先将 2 赋给 insertVal,这时判断 87 是否大于 2,因为 87 大于 2,所以将 87 向后移动,将 2 覆盖, 然后判断 3 是否大于 2,因为 3 大于 2,所以 3 移动到 87 所在的位置,内层循环结束,这时 将 2 赋给 arr[0]的位置,得到下表中第 2 次插入后的效果

  继续循环会将数依次插入有序序列,最终使得整个数组有序。插入排序主要用在部分数有序 的场景,例如手机通讯录时时刻刻都是有序的,新增一个电话号码时,以插入排序的方法将 其插入原有的有序序列,这样就降低了复杂度。

接下来我们借助动画网站来理解一下 https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html

代码如下 :

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef int ElemType;
typedef struct {
    ElemType  *elem;
    int TableLen;
}SSTable;
void ST_Init(SSTable &ST,ElemType len)
{
    ST.TableLen=len;//申请10个元素空间
    ST.elem=(ElemType*) malloc(sizeof (ElemType)*ST.TableLen);
    srand(time(NULL));
    for (int i = 0; i < ST.TableLen; ++i) {
        ST.elem[i]=rand()%100;//随机10个数
    }
}
void ST_Print(SSTable ST)
{
    for (int i = 0; i < ST.TableLen; ++i) {
        printf("%3d",ST.elem[i]);
    }
    printf("\n");
}
void InsertSort(ElemType *A,int len)
{
    int i,j,insertVal;
    for ( i = 1; i < len; ++i) //控制要插入的数
    {
        insertVal=A[i];//先保存要插入的数值
        //内层控制比较,j要大于等于0,同时A[j]大于insertVal,A[j]位置元素往后覆盖
        for(j=i-1;j>=0&&A[j]>insertVal;j--)
        {
            A[j+1]=A[j];
        }
        A[j+1]=insertVal;
    }
}
int main() {
    SSTable ST;
    ST_Init(ST,10);//申请10个元素空间
    ST_Print(ST);//排序前打印
    InsertSort(ST.elem,10);
    ST_Print(ST);//排序后打印
    return 0;
}

时间复杂度O\left ( n^{2} \right )、空间复杂度O(1)

稳定性:因为每次插入元素时总是从后往前先比较再移动,所以不会出现相同的元素相对位置发生变化的情况,即直接插入排序是一个稳定的排序算法

适用性:直接插入排序适用于顺序存储和链式存储的线性表,采用链式存储时无需移动元素

选择排序

  简单选择排序原理:假设排序表为 L[1…n],第 i 趟排序即从 L[i…n]中选择关键字最小 的元素与 L(i)交换,每一趟排序可以确定一个元素的最终位置,这样经过 n - 1 趟排序就可 使得整个排序表有序。 首先假定第零个元素是最小的,把下标 0 赋值给 min(min 记录最小的元素的下标),内层比 较时,从 1 号元素一直比较到 9 号元素,谁更小,就把它的下标赋给 min,一轮比较结束后,将 min 对应位置的元素与元素 i 交换,如下表所示。第一轮确认 2 最小,将 2 与数组开头的元素 3 交 换。第二轮我们最初认为 87 最小,经过一轮比较,发现 3 最小,这时将 87 与 3 交换。持续进行, 最终使数组有序。

接下来我们借助动画网站来理解一下 https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html

代码如下: 

#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
typedef int ElemType;
typedef struct {
    ElemType *elem;
    int TableLen;
}SSTable;
void Init_ST(SSTable &ST,int len)//申请空间,并进行随机数生成
{
    ST.TableLen=len;
    ST.elem=(ElemType*) malloc(sizeof (ElemType)*ST.TableLen);
    srand(time(NULL));
    for (int i = 0; i < ST.TableLen; ++i) {
        ST.elem[i]=rand()%100;
    }
}
void Print_ST(SSTable ST)
{
    for (int i = 0; i < ST.TableLen; ++i) {
        printf("%3d",ST.elem[i]);
    }
    printf("\n");
}
void swap(ElemType &a,ElemType &b)
{
    ElemType temp;
    temp=a;
    a=b;
    b=temp;
}
void SelectSort(ElemType *A,int n)
{
    int i,j,min;//min是用来记录每一趟最小元素的下标
    for(i=0;i<n-1;i++)
    {
        min=i;
        for(j=i+1;j<n;j++)
        {
            if(A[j]<A[min])
                min=j;
        }
        if(i!=min)
        {
            swap(A[i],A[min]);
        }
    }
}
int main() {
    SSTable  ST;
    Init_ST(ST,10);
    Print_ST(ST);
    SelectSort(ST.elem,10);
    Print_ST(ST);
    return 0;
}

时间复杂度:O\left ( n^{2} \right )、空间复杂度:O(1)

稳定性:是一种不稳定的排序算法

适用性:简单选择排序适用于顺序存储和链式存储的线性表,以及关键字较少的情况

堆排序

  堆(Heap)是计算机科学中的一种特殊的树状数据结构。 若满足以下特性,则可称为堆:“给定堆中任意结点P和C ,若P是C的父结点,则P的值小于等于(或大于等于)C的 值。”若父结点的值恒小于等于子结点的值,则该堆称为最 小堆(min heap);反之,若父结点的值恒大于等于子结 点的值,则该堆称为最大堆(max heap)。堆中最顶端的 那个结点称为根结点(root node),根结点本身没有父结 点(parent node)。平时在工作中,我们将最小堆称为小 根堆或小顶堆,把最大堆称为大根堆或大顶堆。

  假设我们有3, 87, 2, 93, 78, 56, 61, 38, 12, 40共10个元素 ,我们将这10个元素建成一棵完全二叉树,这里采用层次 建树法,虽然只用一个数组存储元素,但是我们能将二叉树 中任意一个位置的元素对应到数组下标上,我们将二叉树 中每个元素对应到数组下标的这种数据结构称为堆,比如最 后一个父元素的下标是N/2-1,也就是a[4],对应的值为78 。为什么是N/2-1?因为这是层次建立一棵完全二叉树的特 性。可以这样记忆:如果父结点的下标是dad,那么父结点 对应的左子结点的下标值是2*dad+1。接着,依次将每棵子 树都调整为父结点最大,最终将整棵树变为一个大根堆。

(这是建立大根堆的示意图,并不是代表堆排序完成了,第一步大根堆有利于后面抽出最大值)

  通过下面动画网址来理解

https://www.cs.usfca.edu/~galles/visualization/HeapSort.html

#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
typedef int ElemType;
typedef struct {
    ElemType *elem;
    int TableLen;
}SSTable;
void Init_ST(SSTable &ST,int len)//申请空间,并进行随机数生成
{
    ST.TableLen=len;
    ST.elem=(ElemType*) malloc(sizeof (ElemType)*ST.TableLen);
    srand(time(NULL));
    for (int i = 0; i < ST.TableLen; ++i) {
        ST.elem[i]=rand()%100;
    }
}
void Print_ST(SSTable ST)
{
    for (int i = 0; i < ST.TableLen; ++i) {
        printf("%3d",ST.elem[i]);
    }
    printf("\n");
}
void swap(ElemType &a,ElemType &b)
{
    ElemType temp;
    temp=a;
    a=b;
    b=temp;
}
void AdjustDown(ElemType A[],int k,int len)
{
    int dad=k;
    int son=2*dad+1;//左孩子下标
    while (son<=len)
    {
        if(son+1<=len && A[son]<A[son+1])//看下有没有右孩子,比较左右孩子选大的
        {
            son++;
        }
        if(A[son]>A[dad])//比较孩子和父亲,如果孩子大于父亲,那么进行交换
        {
            swap(A[son],A[dad]);
            dad=son;//孩子重新作为父亲,判断下一颗子树是否符合大根堆
            son=2*dad+1;
        } else
        {
            break;
        }
    }
}
void HeapSort(ElemType A[],int len)
{
    int i;
    //建立大根堆
    for(i=len/2;i>=0;i--)
    {
        AdjustDown(A,i,len);
    }
    swap(A[0],A[len]);//交换顶部和数组最后一个元素
    //下面的策略就是,不但调整剩余元素为大根堆,因为根部最大,所以再次与A[i]交换(相当于放到数组后面),循环往复
    for(i=len-1;i>0;i--)
    {
        AdjustDown(A,0,i);//剩下元素调整为大根堆
        swap(A[0],A[i]);
    }
}
int main() {
    SSTable  ST;
    Init_ST(ST,10);//初始化
    ElemType A[10]={3,87,2,93,78,56,61,38,12,40};
    memcpy(ST.elem,A, sizeof(A));//此处使用固定的数组进行分析,不用随机的了
    Print_ST(ST);
    HeapSort(ST.elem,9);//所有元素参与排序
    Print_ST(ST);
    return 0;
}

时间复杂度 O\left ( n\log_{2}n \right )、空间复杂度O(1)

稳定性:不稳定

适用性:堆排序仅适用于顺序存储的线性表

归并排序

  接下来我们借助动画网站来理解一下:

https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html

  归并排序的代码是采用递归思想实现的,考研掌握递归实现即可。首先,最小下标值和最 大下标值相加并除以 2,得到中间下标值 mid,用 MergeSort 对 low 到 mid 排序,然后用 MergeSort 对 mid+1 到 high 排序。当数组的前半部分和后半部分都排好序后,使用 Merge 函 数。Merge 函数的作用是合并两个有序数组。为了提高合并有序数组的效率,在 Merge 函数内 定义了 B[N]。首先,我们通过循环把数组 A 中从 low 到 high 的元素全部复制到 B 中,这时游 标 i(遍历的变量称为游标)从 low 开始,游标 j 从 mid+1 开始,谁小就将谁先放入数组 A,对 其游标加 1,并在每轮循环时对数组 A 的计数游标 k 加 1。  

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#define N 7
typedef int ElemType;
void Merge(ElemType A[],int low,int mid,int high)
{
    static ElemType B[N];//加static的目的是无论多次递归,都只有一个B[N]
    int i,j,k;
    for(k=low;k<=high;k++)//复制元素到B中
        B[k]=A[k];
    //合并数组
    for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++)
    {
        if(B[i]<=B[j])
        A[k]=B[i++];
        else
            A[k]=B[j++];
    }
    while (i<=mid)//如果有剩余元素,接着放入即可
        A[k++]=B[i++];//前一半有剩余的放入
    while (j<=high)//如果有剩余元素,接着放入即可
        A[k++]=B[j++];//后一半有剩余的放入
}
void MergeSort(ElemType A[],int low,int high)//递归分割
{
    if(low<high)
    {
        int mid=(low+high)/2;
        MergeSort(A,low,mid);//排序好前一半
        MergeSort(A,mid+1,high);//排序好后一半
        Merge(A,low,mid,high);//将连个排序好的数组合并
    }

}
void print(int *a)
{
    for (int i = 0; i < N; ++i) {
        printf("%3d",a[i]);
    }
    printf("\n");
}
int main() {
    int A[7]={49,38,65,97,76,13,27};
    MergeSort(A,0,6);
    print(A);
    return 0;
}

 时间复杂度 O\left ( n\log_{2}n \right )、空间复杂度O(n)

稳定性:稳定的算法

适用性:归并排序适用于顺序存储和链式存储的线性表

所有排序算法时间与空间复杂度汇总:

数据结构算法演示(Windows版) 使 用 手 册 一、 功能简介 本课件是一个动态演示数据结构算法执行过程的辅助教学软件, 它可适应读者对算法的输入数据和过程执行的控制方式的不同需求, 在计算机的屏幕上显示算法执行过程中数据的逻辑结构或存储结构的变化状况或递归算法执行过程中栈的变化状况。整个系统使用菜单驱动方式, 每个菜单包括若干菜单项。每个菜单项对应一个动作或一个子菜单。系统一直处于选择菜单项或执行动作状态, 直到选择了退出动作为止。 二、 系统内容 本系统内含84个算法,分属13部分内容,由主菜单显示,与《数据结构》教科书中自第2章至第11章中相对应。各部分演示算法如下: 1. 顺序表 (1)在顺序表中插入一个数据元素(ins_sqlist) (2)删除顺序表中一个数据元素(del_sqlist) (3)合并两个有序顺序表(merge_sqlist) 2. 链表 (1)创建一个单链表(Crt_LinkList) (2)在单链表中插入一个结点(Ins_LinkList) (3)删除单链表中的一个结点(Del_LinkList) (4)两个有序链表求并(Union) (5)归并两个有序链表(MergeList_L) (6)两个有序链表求交(ListIntersection_L) (7)两个有序链表求差(SubList_L) 3. 栈和队列 (1)计算阿克曼函数(AckMan) (2)栈的输出序列(Gen、Perform) (3)递归算法的演示  汉诺塔的算法(Hanoi)  解皇后问题的算法(Queen)  解迷宫的算法(Maze)  解背包问题的算法(Knap) (4)模拟银行(BankSimulation) (5)表达式求值(Exp_reduced) 4. 串的模式匹配 (1)古典算法(Index_BF) (2)求Next 函数值(Get_next)和按Next 函数值进行匹配 (Index_KMP(next)) (3)求 Next 修正值(Get_nextval)和按 Next 修正值进行匹配(Index_KMP(nextval)) 5. 稀疏矩阵 (1)矩阵转置 (Trans_Sparmat) (2)快速矩阵转置 (Fast_Transpos) (3)矩阵乘法 (Multiply_Sparmat) 6. 广义表 (1)求广义表的深度(Ls_Depth) (2)复制广义表(Ls_Copy) (3)创建广义表的存储结构(Crt_Lists) 7. 二叉树 (1)遍历二叉树  二叉树的线索化  先序遍历(Pre_order)  中序遍历(In_order)  后序遍历(Post_order) (2) 按先序建二叉树(CrtBT_PreOdr) (3) 线索二叉树  二叉树的线索化  生成先序线索(前驱或后继) (Pre_thre)  中序线索(前驱或后继) (In_thre)  后序线索(前驱或后继) (Post_thre)  遍历中序线索二叉树(Inorder_thlinked)  中序线索树的插入(ins_lchild_inthr)和删除(del_lchild_inthr)结点 (4)建赫夫曼树和求赫夫曼编码(HuffmanCoding) (5)森林转化成二叉树(Forest2BT) (6)二叉树转化成森林(BT2Forest) (7)按表达式建树(ExpTree)并求值(CalExpTreeByPostOrderTrav) 8. 图 (1)图的遍历  深度优先搜索(Travel_DFS)  广度优先搜索(Travel_BFS) (2)求有向图的强连通分量(Strong_comp) (3)有向无环图的两个算法  拓扑排序(Toposort)  关键路径(Critical_path) (4)求最小生成树  普里姆算法(Prim)  克鲁斯卡尔算法(Kruscal) (5)求关节点和重连通分量(Get_artical) (6)求最短路径  弗洛伊德算法(shortpath_Floyd)  迪杰斯特拉算法(shortpath_DIJ) 9. 存储管理 (1)边界标识法 (Boundary_tag_method) (2)伙伴系统 (Buddy_system) (3)紧缩无用单元 (Storage_compaction) 10. 静态查找 (1)顺序查找(Search_Seq) (2)折半查找 (Serch_Bin) (3)插值查找 (Search_Ins) (4)斐波那契查找 (Search_Fib) (5)次优查找树(BiTree_SOSTree) 11. 动态查找 (1)在二叉排序树上进行查找(bstsrch)、插入结点(ins_bstree)和删除结点(del_bstree) (2)在二叉平衡树上插入结点(ins_AVLtree) 和删除结点(del_AVLtree) (3)在 B-树上插入结点(Ins_BTree) 和 删除结点(Del_BTree) (4)在 B+树上插入结点(Ins_PBTree) 和 删除结点(Del_PBTree) 12. 内部排序 (1)简单排序法  直接插入排序(Insert_sort)  表插入排序(内含插入(Ins_Tsort) 重排(Arrange)两个算法)  起泡排序(BubbleSort)  简单选择排序(SelectSort) (2)复杂排序法  排序(HeapSort)  快速排序(QuickSort)  锦标赛排序(Tournament) (3)其他  快速地址排序(QkAddrst)  基数排序(RadixSort) 13. 外部排序 (1)多路平衡归并排序(K-Merge) (2)置换-选择排序(Repl_Selection) 三、 运行环境 1. 硬件:Pentium100以上PC机。 2. 软件:Windows95及以上版本的操作系统。 四、 运行 本系统的执行文件为DSDEMOW.EXE。 五、 如何使用本课件 读者可以利用鼠标移动光标选择“演示算法”或“菜单命令”来控制课件的运行过程。 1. 课件的演示算法菜单为页式菜单。第一级菜单中的各项与上述“系统内容”中各大项相对应,读者运行“算法演示课件”后, 即进入“算法选择一级菜单”画面,此时可移动光标进行选择,当光标所在菜单项改为红色时,单击鼠标即进入“算法选择二级菜单”,进行相同操作之后,或进入算法选择三级菜单(如图1所示),或进入算法演示的执行状态(如图2所示)。 图1 图2 在算法选择菜单画面中,形如 的图标意为尚有下级菜单,形如 的图标则表示将直接进入算法演示状态。此时也可直接单击一级菜单或二级菜单的标题直接返回之,注意:菜单右侧上方的“退出”按钮意为退出整个演示课件。 2. 算法演示执行状态下的屏幕分为三部分:第一行为“标题行”,第二行为“菜单命令”,以下为算法演示屏上各菜单的说明。 菜单命令中各项自左至右的功能分别为:  数据——设置算法演示的数据(数据结构)。  调用栈——察看当前函数(或过程)嵌套或递归的历程。  说明——察看算法说明。  暂停——中断演示过程。  执行——连续执行算法直至所设断点或至算法执行完毕。  单步——执行一行算法,遇到子程序调用时,连续执行完子程序。  跟踪——执行一行算法,遇到子程序调用时,进入子程序。  执行到——演示算法到当前所设最近的断点或算法窗口中的当前行。  恢复——重置屏幕为当前算法执行前的初始状态。  断点——在算法窗口的当前选择行设置断点或清除断点。  设置——设置连续演示时的速度或开/闭背景音乐(或动作音效)开关。  返回——返回算法选择菜单。 3. 断点的设置方法为:移动光标至“断点语句”所在行,点击鼠标后即出现绿色光条,之后单击“断点”菜单中的“设置断点”命令项即可,此时该断点语句所在行上将出现红色光条。 六、 算法演示屏的详细说明 本系统对屏幕设计的基本原则是集数据结构算法和其他重要信息(如栈等)于同一屏幕。一般情况下演示屏由图示、算法和变量三个窗口组成,特殊情况下将根据演示内容适当增加。一般情况下, 左侧图示窗口显示演示数据的逻辑结构或存储结构,右侧上方窗口显示算法文本,右侧下方窗口显示当前算法中各变量的值或递归工作栈的状态。各窗口的边界大小均可自由调节,且可按需扩大至全屏。 算法窗口显示当前演示的算法文本,并以深蓝色的光条覆盖将要执行的语句。若算法中含有函数或过程调用语句,则当光条覆盖到该过程调用语句时,随即自动隐去原算法文本而显示子过程的文本,而从此过程返回时再重新显示原算法文本。类似地,在演示递归算法执行过程时,每当执行递归调用本过程的语句时,随即隐去当前层次的算法文本而显示下一层的算法文本,并且以不同颜色的算法文本表示递归的不同层次。如第一层的算法文本为深绿色,第二层为紫色,第三层为深红色,第四层为深蓝色,第五层为浅蓝色,第六层为玫瑰红色等。 当演示递归算法执行过程中递归工作栈的变化状态时,递归工作栈显示在右侧下窗口,递归工作栈的状态和算法文本窗口中相应语句执行后的结果相对应,栈顶记录为当前递归层的参量值。每进入一层递归时,就产生一个新的工作记录(包括调用语句行号、变量参数或全程变量、数值参数和局部变量)压入栈顶;每退出一层递归时,先根据栈顶的调用语句行号返回至上层,然后在传递完变量参数的值后退栈。 各个算法演示屏的补充说明如下: 1. 顺序表和链表的插入、删除和链表的生成 算法演示屏由显示顺序表或链表的图示、算法文本及变量等三个窗口组成。在演示算法之前,需先在弹出的小窗口中输入线性表的数据元素及算法参数 i(插入或删除的位置)和 b(被插的数据元素)的值。顺序表的图示窗口在演示屏的上方,链表的图示窗口在左侧。 2. 有序表的操作 算法演示屏的状态和 1 中所述相同。 3. 汉诺塔问题 算法演示屏由4个窗口组成。右侧上方为算法文本,在算法中有4个形式参量,其中值参 n 为圆盘个数,x、y、和 z 分别表示3个塔座;右侧下方为递归工作栈,栈中每个记录包含调用语句行号 adr 及值参 n 和 x、y、z;左侧上方显示汉诺塔图形及移动操作结果;左侧下方显示移动操作的记录。 4. 迷宫问题 左侧窗口显示迷宫的逻辑结构,由 N×N 个方格组成,左上[1,1]为入口,右下[N,N]为出口,并且以红色钉子填充表示障碍,空白表示通路,红色交通灯表示已游历过的路,箭头表示继续游历的方向,演示结束时显示一条通路或迷宫不通的信息。右侧下窗口为递归工作栈,栈中每个记录含6个数据项,其中 adr 指示调用语句行号,k 指示步数,(x,y) 表示当前坐标,i 指示路径方向(起始方向为 1,顺时针方向旋转搜索)。 5. 皇后问题 左侧图示窗口包含棋盘和递归工作栈两部分,栈中记录含3个数据项,其中 adr 指示调用语句行号,k 指示列号,i 指示行号。此算法演示可求得所有可行结果,在求得每一种排布的结果之后,均会弹出一个窗口显示“找到第 j (j=1,2,…) 种排布”,单击“确定”按钮将继续进行,直至找到所有可能构成的排布。 6. 背包问题 右侧图示窗口的上方显示背包、物件及其体积。 若有解,则在求得每一组结果之后,均会弹出一个窗口提示求得一组解,单击提示窗口中的小人将继续进行。该窗口的下方为递归工作栈,栈中的记录含3个数据项,其中 adr 指示调用语句所在行号,n 指示物件个数,t 指示背包总体积。 7. 阿克曼函数 整个演示屏只有显示算法文本和显示算法执行过程中栈的状态两个窗口。在执行算法之前,首先应按照提示输入参数 m 和 n 的值。 8. 栈的输出序列 图示窗口的内容为:由算法 Gen 生成的栈的操作序列(列出在窗口的下方)、算法 Perform 执行时栈的操作过程(该窗口的上方)以及算法 Perform 执行的结果——栈的输出序列(列出在图示窗口的右侧)。 9. 表达式求值 图示窗口的内容主要为显示表达式求值过程中操作数栈和运算符栈的变化情况以及主要操作。上方的小窗口显示在算法演示之前设定的表达式。 10. 离散事件模拟 图示窗口分成3部分:中部分或显示客户流动情况的动画,或显示程序执行过程中事件表和4个队列的数值,上方两个按钮用以切换动画或静态数据,下方则显示客户总人数、客户逗留的累计时以及调节动画中小人移动速度的指针。 11. 串的模式匹配 上窗口显示算法文本,下窗口显示串的匹配过程或求 next 函数的过程。 12. 稀疏矩阵 图示窗口显示矩阵的状态或其三元组的表示。 13. 求广义表的深度 图示窗口显示广义表的存储结构,图中指针 ls 指向当前所求深度的广义表,值为空时不显示。演示结束时弹出窗口显示求得的深度。 14. 复制广义表 图示窗口的上方显示已知广义表的存储结构,图示窗口的下方显示复制求得的广义表的存储结构。递归工作栈中含调用语句行号 adr、变参 nls 和值参 ls。 15. 创建广义表的存储结构 图示窗口显示广义表存储结构的建立过程和算法执行过程中参数Sub的当前值。 16. 遍历二叉树 图示窗口显示二叉树的逻辑结构和遍历结果输出的结点序列,图中指针 bt 指向当前遍历的二叉树的根结点。 17. 线索二叉树 图示窗口显示二叉树的存储结构,但结点中只含标志域,而以结点的黑色连线表示指针,红色连线表示前驱线索,蓝色连线表示后继线索。在二叉树线索化的过程中,图中指针 p 指向当前层二叉树的根结点,指针 pre 指向当前被访问的结点的前驱。在演示线索树的插入和删除过程时,图示窗口的下方还包括“输入行”和“提示行”。 18. 按先序序列建二叉链表 图示窗口显示输入的先序序列和生成二叉链表的过程。 19. 森林和二叉树的相互转换 图示窗口在显示屏的上方,其左侧为森林,右侧为二叉树。 20. 赫夫曼树和赫夫曼编码 图示窗口显示生成的赫夫曼树的逻辑结构和每个叶子结点的编码。 21. 图的深度优先搜索 图示窗口的上半部分显示图的逻辑结构,初始状态用青色圆圈表示顶点,结点的黑色连线表示边或弧(连线上画有箭头)。演示过程中用红色覆盖已访问的顶点和边(或弧)。窗口下方显示图的邻接表,演示过程中以红色框边表示已访问过的弧。图示窗口的下方显示遍历后输出的顶点序列。 22. 图的广度优先搜索 与深度优先不同的是,在窗口的下方增加一个队列,其左端为队头,右端为队尾。 23. 求有向图的强连通分量 图示窗口自上而下分别显示有向图的逻辑结构、存储结构和 Finished 数组在算法执行过程中的变化情况。所求得的各个强连通分量,将以不同颜色的顶点组表示。 24. 求关节点和重连通分量 图示窗口的上半部分显示无向图,下半部分自上而下分别显示 Vexnum、Vexdata、Visited、Low、Squlow(求得 low 值的顺序)和 artpoint(关节点)的信息。 25. 有向图的拓扑排序 图示窗口由5部分组成。其中左上显示有向图的邻接表;左下显示有向图,其中顶点和弧的初始状态分别为绿色和黑色,从栈中退出的顶点(i)用红色表示,分别以蓝色和红色指示当前访问的邻接点(k)和它们之的弧(ik),灰白色表示已经输出的顶点;右下显示顶点的入度;右上显示入度为零的栈。当拓扑排序不成功时,在演示屏的中央将会弹出一个窗口,显示提示信息“网中存在自环!”,此时用户可在左下显示的有向图中由绿色顶点和黑色弧构成的子图中找到这个环。 26. 有向图的关键路径 图示窗口包含5部分信息。左上显示带入度域的邻接表;左下显示有向网的逻辑结构和顶点的入度及各顶点事件的最早发生时和最迟发生时;右下显示拓扑排序过程中入度为零的顶点的栈S,右上显示的栈 T 存放拓扑序列,其入栈顺序和栈 S 的出栈顺序相同,从栈顶到栈底的顶点顺序即为顶点的逆拓扑序列。算法执行结束后将弹出窗口列出全部结果,其中红色字体的弧表示关键活动。 27. 普里姆算法 图示窗口包含3部分内容。右上是邻接矩阵;左上是无向网的逻辑结构,图中顶点的初始状态为黄色,算法执行过程中,红色覆盖的顶点和边则表示已加入生成树的顶点和生成树上的边;窗口的下方则显示 closedge 数组中的值。 28. 克鲁斯卡尔算法 图示窗口的左侧为无向网,以红色标定已落在生成树上的边;右侧自上而下列出各条边的信息以及选择生成树的边的执行过程。 29. 边界标识法 图示窗口的初始状态为 64KB 的模拟存储器,演示过程中,以绿色覆盖占用块。各个存储块的头部左侧所示为该块的起始地址,头部结构或其他信息参见教科书。用户可根据弹出窗口的操作提示信息进行操作,输入请求分配的空大小或释放块的首地址。 30. 伙伴系统 在图示窗口中,左侧为可利用空链表的逻辑结构,右侧为存储结构,其中红色覆盖部分为占用块。弹出窗口为输入窗口,由用户输入请求分配的空大小或释放块的首地址。 31. 紧缩无用单元 右侧显示存储空,空白表示空闲块,其他颜色覆盖表示占用块,在存储空不足分配时将进行空闲块压缩。左侧显示存储映像。弹出窗口为输入窗口,由用户输入请求分配的空大小和分配或释放块的块名。 32. 静态查找 上窗口为图示窗口,演示查找过程;左下和右下分别为算法文本和变量窗口。 33. B-树和B+树 整个屏幕分为上、下两个窗口,上窗口演示插入或删除结点过程中B-树或B+ 树结构的变化状况;下窗口内显示如查找关键字、插入位置、结点分裂等操作信息。下窗口上面左侧的小窗口为编辑窗口,由用户输入待插或待删的关键字,输入之后其右侧的操作命令将由隐式状态改为显式状态。 34. 内部排序 图示窗口演示排序过程以及排序过程中关键字之进行的比较次数和记录移动的次数。 七、 用户自行输入数据指南 算法操作的对象——数据结构,或由用户自行输入,或由系统随机产生,并在获得用户的确认之前,可反复随机产生,直至用户满意,用鼠标点击“OK”按钮确认为止。 多数情况下的输入界面上有足够的提示信息,以指示用户需要进行何种操作。补充说明如下: 1. 表的数据元素为任意的单个字符。 2. 迷宫的输入界面如图3所示。图中砖墙图案表示障碍,连续点击鼠标可将光标所在位置设置成通道或者障碍,建议用户先点击“随机生成”按钮随机生成一个迷宫,然后移动鼠标调整成所需。所设迷宫可以利用“保存数据”按钮生成dat类型文件,并在需要时可以利用“取出数据”按钮读入。 图3 3. 演示背包问题的算法之前,首先需要输入物品个数,之后将出现如图4所示的输入界面,可以利用“随机生成”的按钮或各个相应的小窗口输入物品体积 wi 和背包体积 T 。背包的总体积不得过 30 ,单个物品的体积不得过 10 。 图4 4. “表达式求值”和“建表达式树”时的输入界面如图5所示。用户可在窗口内自行输入,并以“Enter”键为结束符;也可以连续点击左侧蓝色的表达式由系统自动生成,直至用户点击右侧的计算器表示确认为止。“求值”可实现带括弧的四则运算和幂次运算,并支持sin、cos、tan、arcsin 和 arccos 等函数计算,其操作数为实数。“建树”的表达式仅限于带括弧的四则运算,其操作数为单个字母的字符。 图5 5. 稀疏矩阵的输入界面如图6所示。用户可随意进行矩阵中任意位置元素的输入,只要将光标移动至待输入的元素位置,单击鼠标后将弹出计算器,单击数字按钮,可进行随意输入,之后点击“OK”按钮表示确认。 图6 6. 广义表的数据输入方式为自左向右顺序输入广义表的字符串。输入过程中,图7所示输入界面中的“确定”为灰色字体,只有当用户正确输入完毕时,“确定”两字才改为黑色字体,此时用户可单击此按钮表示确认。 图7 7. 图的输入界面如图8所示。之前尚需确认是否为有向图和带权图。在用户自行输入图时,首先按下“创建节点”按钮,之后可移动光标至窗口的任意位置单击鼠标创建顶点;然后单击“创建弧”按钮,可在任意两个顶点之构建弧或边。构建弧(或边)的操作为:先将光标移动至弧尾的顶点,单击一次鼠标,然后移动光标至弧头位置,再单击一次鼠标。对于带权的图,则在构建弧(或边)的同时,在当时弹出的窗口中输入权值,权值的默认值为 1。 图8 8. 内部排序的关键字均为单个字符。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序garbage

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值