数据结构实验报告-实验四-实现桶式排序和基于桶式排序的基数排序

实验四 实现桶式排序和基于桶式排序的基数排序

并用C语言设计堆栈,实现中缀表达式到后缀表达式的转换

一、实验描述

1.Radix Sort:实现桶式排序和基于桶式排序的基数排序。在基数B,数组长度n和最大元素值m中,对排序时间影响最大的是哪一个?元素在未排序数组中的顺序是否对时间复杂度有影响?设计试验证明你的想法。

2.Stack:用C语言设计堆栈,并实现中缀表达式到后缀表达式的转换。

 

二、实验分析

桶式排序的基本思想:是将阵列分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递回方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的阵列内的数值是均匀分配的时候,桶排序使用线性时间(O(n))。但桶排序并不是比较排序,他不受到O(nlogn)下限的影响。简单来说,就是把数据分组,放在一个个的桶中,然后对每个桶里面的在进行排序。

基于桶式排序的基数排序的基本思想:按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以是稳定的。

中缀表达式转后缀表达式:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或者优先级低于栈顶符号(乘除优先加减),则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。

 

三、实验设计

1.实现桶式排序

例如要对大小为[1..1000]范围内的n个整数A[1..n]排序

首先,可以把桶设为大小为10的范围,具体而言,设集合B[1]存储[1..10]的整数,集合B[2]存储(10..20]的整数,……集合B[i]存储((i-1)*10,i*10]的整数,i=1,2,..100。总共有100个桶。

然后,对A[1..n]从头到尾扫描一遍,把每个A[i]放入对应的桶B[j]中。再对这100个桶中每个桶里的数字排序,这时可用冒泡,选择,乃至快排,一般来说任何排序法都可以。

最后,依次输出每个桶里面的数字,且每个桶中的数字从小到大输出,这样就得到所有数字排好序的一个序列了。

假设有n个数字,有m个桶,如果数字是平均分布的,则每个桶里面平均有n/m个数字。如果

对每个桶中的数字采用快速排序,那么整个算法的复杂度是

O(n+m*n/m*log(n/m))=O(n+nlogn-nlogm)

从上式看出,当m接近n的时候,桶排序复杂度接近O(n)

桶式排序的代码实现:

#include <iostream>  
   
using namespace std;  
   
void BucketSort(int *A, int Max, int Size){  
    int B[Max+1];  
    int i,j,count = 0;  
    memset(B, 0, (Max+1)*sizeof(int));  
    for (i = 0; i < Size; i++) {  
        j = A[i];  
        B[j] += 1;  
    }  
    for (i = 0; i <= Max; i++) {  
        if (B[i] > 0) {  
            for (j = 0; j < B[i]; j++) {  
                A[count] = i;  
                count++;  
            }  
        }  
    }  
}  
   
int main(int argc, const char * argv[])  
{  
    int A[]={1, 2, 2, 7, 4, 9, 3, 5};  
    int Max = 9; 
    int Size = sizeof(A)/sizeof(int);  
    BucketSort(A, Max, Size);  
    for (int i = 0; i < Size; i++) {  
        printf("%d ",A[i]);  
    }  
    printf("\n");  
    return 0;  
}  

2.实现基于桶式排序的基数排序

假如我们有10个乱序的数字,第一趟排序之后:

0

1

512

343

64

125

216

27

8

729

0

1

2

3

4

5

6

7

8

9

表格的第二行是10个桶的列表,第一行是10个数字。

第一趟排序按照个位数对应排序,第二趟按照十位数,一个桶里能放下多个数。所以要二维数组。

基于桶式排序的基数排序的代码实现:

#include <iostream>  
#include <cstdio>  
#include <cstdlib>  
#include <cmath>  
#include <cstring>  
using namespace std;  
struct data  
{  
    int key[2];  
};  
struct linklist  
{  
    linklist *next;  
    data value;  
    linklist(data v,linklist *n):value(v),next(n) {}  
    ~linklist()  
    {  
        if (next)  
            delete next;  
    }  
};  
void BucketSort(data *A,int N,int K,int y)  
{  
    linklist *Bucket[101],*p;//建立桶  
    int i,j,k,M;  
    M=K/100+1;  
    memset(Bucket,0,sizeof(Bucket));  
    for (i=1; i<=N; i++)  
    {  
        k=A[i].key[y]/M; //把A中的每个元素按照的范围值放入对应桶中  
        Bucket[k]=new linklist(A[i],Bucket[k]);  
    }  
    for (k=j=0; k<=100; k++)  
    {  
        for (p=Bucket[k]; p; p=p->next)  
            j++;  
        for (p=Bucket[k],i=1; p; p=p->next,i++)  
            A[j-i+1]=p->value; //把桶中每个元素取出  
        delete Bucket[k];  
    }  
}  
void RadixSort(data *A,int N,int K)  
{  
    for (int j=1; j>=0; j--) //从低优先到高优先 LSD  
        BucketSort(A,N,K,j);  
}  
int main()  
{  
    int N=100,K=1000,i;  
    data *A=new data[N+1];  
    for (i=1; i<=N; i++)  
    {  
        A[i].key[0]=rand()%K+1;  
        A[i].key[1]=rand()%K+1;  
    }  
    RadixSort(A,N,K);  
    for (i=1; i<=N; i++)  
        printf("(%d,%d) ",A[i].key[0],A[i].key[1]);  
    printf("\n");  
    return 0;  
}  

3. 用C语言设计堆栈,并实现中缀表达式到后缀表达式的转换

(1)遇到操作数:直接输出(添加到后缀表达式中)

(2)栈为空时,遇到运算符,直接入栈

(3)遇到左括号:将其入栈

(4)遇到右括号:执行出栈操作,并将出栈的元素输出(添加到后缀表达式中),直到弹出栈的是左括号,左括号不输出(不添加到后缀表达式中)。

(5)遇到其他运算符:加减乘除:弹出所有优先级大于或者等于该运算符的栈顶元素,然后将该运算符入栈

(6)最终将栈中的元素依次出栈,输出。

例如,中缀表达式a+b*c-d转换成后缀表达式之后为abc*+d-。

利用栈实现中缀表达式转后缀表达式的代码实现:

#include <iostream>  
#include <string>  
#include <stack>  
#include <map>  
using namespace std;  
   
void InfixToPostfix(const string infix, string& postfix)  
{  
    stack<char> mark;                       // 符号栈  
   
    std::map<char, int> priority;               // 符号优先级  
    priority['+'] = 0;  
    priority['-'] = 0;  
    priority['*'] = 1;  
    priority['/'] = 1;  
   
    int infix_length = infix.size();            // 中缀表达式的字符长度  
    postfix.reserve(infix_length);                
    for(int i = 0; i < infix_length; ++i) {  
        switch(infix[i]) {  
        case '0':  
        case '1':  
        case '2':  
        case '3':  
        case '4':  
        case '5':  
        case '6':  
        case '7':  
        case '8':  
        case '9':  
            postfix.push_back(infix[i]);  
            break;  
        case '+':  
        case '-':  
        case '*':  
        case '/':  
            if(!mark.empty()) {  
                char markTop = mark.top();  
                while(markTop != '(' && priority[infix[i]] <= priority[markTop]) {  
                    postfix.push_back(markTop);  
                    mark.pop();  
                    if(mark.empty()) {  
                        break;  
                    }  
                    markTop = mark.top();  
                }  
            }  
            mark.push(infix[i]);  
            break;  
        case '(':  
            if(infix[i - 1] >= '0' && infix[i - 1] <= '9') {    // 5(6/2-1) <==> 5*(6/2-1)  
                mark.push('*');  
            }  
            mark.push(infix[i]);  
            break;  
        case ')':  
        {  
            char markTop = mark.top();  
            while(markTop != '(') {  
                postfix.push_back(markTop);  
                mark.pop();  
                markTop = mark.top();  
            }  
            mark.pop();  
        }  
            break;  
        default:  
            break;  
        }  
    }  
   
    // 剩余的全部出栈  
    while(!mark.empty()) {  
        postfix.push_back(mark.top());  
        mark.pop();  
    }  
}  
   
int main(int argc, char const *argv[])  
{  
    std::string infix = "1+2*3+(4*5+6)*7+(1+2)";  
    std::string postfix;  
   
    cout << "infix : " << infix << endl;  
    InfixToPostfix(infix, postfix);  
    cout << "postfix : " << postfix << endl;   
   
    return 0;  
}  

实例:

把1+2*3+(4*5+6)*7转换成123*+45*6+7*+:

infix(中缀表达式):1+2*3+(4*5+6)*7

postfix(后缀表达式):123*+45*6+7*+

 

四、实验总结

基数排序的性能比桶排序要略差。每一次关键字的桶分配都需要O(N)的时间复杂度,而且分配之后得到新的关键字序列又需要O(N)的时间复杂度。假如待排数据可以分为d个关键字,则基数排序的时间复杂度将是O(d*2N) ,当然d要远远小于N,因此基本上还是线性级别的。基数排序的空间复杂度为O(N+M),其中M为桶的数量。一般来说N>>M,因此额外空间需要大概N个左右。

但是,对比桶排序,基数排序每次需要的桶的数量并不多。而且基数排序几乎不需要任何“比较”操作,而桶排序在桶相对较少的情况下,桶内多个数据必须进行基于比较操作的排序。

通过“C语言设计堆栈,实现中缀表达式到后缀表达式的转换”的实验,我对栈的运用有了更进一步的认识。在转化的时候,将该字符与运算符栈顶的运算符的优先关系相比较。如果该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。否则将栈顶的运算符从栈中弹出,直到栈顶运算符的优先级低于当前运算符,将该字符入栈。计算过程则是:从左到右依次进栈遇字母入栈,遇见运算符将前面两个字母弹出,进行运算符运算后,将值再入栈,重复此过程,最终栈里只有一个元素,该元素的值就是计算结果。栈也被广泛应用于各种程序设计中,所以我们也要掌握它的应用,能够熟悉的运用它来解决一些实际问题。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.实验目的 掌握内排序,比较各种排序的优、缺点。 2 需求分析 2.1原理 2.1.1、直接排序 算描述:经过i-1遍处理后,L[1..i-1]己排好序。第i遍处理仅将L[i]插入L[1..i-1]的适当位置,使得L[1..i]又是排好序的序列。要达到这个目的,我们可以用顺序比较的方。首先比较L[i]和L[i-1],如果L[i-1]≤ L[i],则L[1..i]已排好序,第i遍处理就结束了;否则交换L[i]与L[i-1]的位置,继续比较L[i-1]和L[i-2],直到找到某一个位置j(1≤j≤i-1),使得L[j] ≤L[j+1]时为止。 2.1.2、冒泡排序 算描述:核心思想是扫描数据清单,寻找出现乱序的两个相邻的项目。当找到这两个项目后,交换项目的位置然后继续扫描。重复上面的操作直到所有的项目都按顺序排好。 2.1.3、快速排序 算描述:首先检查数据列表中的数据数,如果小于两个,则直接退出程序。如果有超过两个以上的数据,就选择一个分割点将数据分成两个部分,小于分割点的数据放在一组,其余的放在另一组,然后分别对两组数据排序。通常分割点的数据是随机选取的。这样无论你的数据是否已被排列过,你所分割成的两个字列表的大小是差不多的。而只要两个子列表的大小差不多。 2.1.4、选择排序 算描述:首先找到数据清单中的最小的数据,然后将这个数据同第一个数据交换位置;接下来找第二小的数据,再将其同第二个数据交换位置,以此类推。 2.1.5、堆排序 (1) 基本思想:堆排序是一树形选择排序,在排序过程中,将R[1..N]看成是一颗完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。 (2) 堆的定义: N个元素的序列K1,K2,K3,...,Kn.称为堆,当且仅当该序列满足特性: Ki≤K2i Ki ≤K2i+1(1≤ I≤ [N/2]) 2.1.6、希尔排序 算描述:在直接插入排序算中,每次插入一个数,使有序序列只增加1个节点,并且对插入下一个数没有提供任何帮助。如果比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。 2.2要求 1.本程序对以下六种常用内部排序算进行实测比较:冒泡排序,插入排序,选择排序,希尔排序,快速排序,堆排序。 2.排序的元素的关键字为整数。用正序,逆序,不同乱序的数据作测试比较。比较的指标为有关键字参加的比较次数和关键字的移动次数。 3.程序以人机对话的形式进行,每次测试完毕显示各种比较指标值 。 2.3任务 设计一个测试程序比较几种内部排序算的关键字比较次数和移动次数以取得直观感受。 2.4运行环境 (1)WINDOWSXP系统 (2)C++ 编译环境 3.实验实验主要是内排序,通过比较的次数和移动的次数判断排序的好坏。主要子函数的说明如下。 1.简单选择排序XuanzePaixu(); 2.冒泡排序MaopaoPaixu(); 3. 直接插入排序CharuPaixu(); 4. 快速排序KuaisuPaixu(); 5. 堆排序DuiPaixu(); 6. 希尔排序 XierPaixu(); 以上的排序算均采用书中所用的算。程序采用输入的时候仅输入所要的个数,具体的输入数据由程序随机产生个数,并且输出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值