数据结构实验四 Radix Sort And Stack

Posted fanlumaster

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构实验四 Radix Sort And Stack相关的知识,希望对你有一定的参考价值。

一、实验描述

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

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

二、问题分析与算法设计

1、Radix Sort

  • 对于桶排序,其算法实现为:使用一个大小为 (M) 的名为 (Count) 的数组,它被初始化为全部为 0.于是,(Count)(M) 个单元(即桶),这些桶初始化为空。当读入 (A_i) ((A_1, A_2..., A_N) 为小于 (M) 的输入数据,且为正整数),(Count[A_i]) 增 1。在所有的输入数据读入后,扫描数组 (Count),打印出排序后的数组。

  • 对于基数排序,其算法实现为:将整数按位数切割成不同的数字,然后按每个位数分别比较。具体操作是将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

2、中缀表达式转后缀表达式

算法主要利用堆栈,算法实现为:

从头到尾读取中缀表达式中的每个对象,对不同对象按不同的情况处理。

  • Ⅰ 运算数:直接输出
  • Ⅱ 左括号:压入堆栈
  • Ⅲ 右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不输出)
  • Ⅳ 运算符:
    • 若优先级大于栈顶运算符时,则将它压栈
    • 若优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出;再比较新的栈顶运算符,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈
  • Ⅴ 若各对象处理完毕,则把堆栈中存留的运算符一并输出。

三、算法实现

1、Radix Sort

1.1、桶式排序

/**
 * 桶式排序
 * @param A 待排序数组
 * @param m 最大元素
 * @param n 数组长度
 */
void BucketSort(int *A, int m, int n)
{
    int B[m + 1];
    int i, j, count = 0;
    memset(B, 0, (m + 1) * sizeof(int)); // 数组初始化
    for (i = 0; i < n; i++)
    {
        j = A[i];
        B[j] += 1; // 记录该桶中元素个数
    }

    // 把桶中的元素按顺序依次放入原数组中
    for (i = 0; i <= m; i++)
    {
        if (B[i] > 0) // 该桶不为空
        {
            for (j = 0; j < B[i]; j++)
            {
                A[count] = i;
                count++;
            }
        }
    }
}

1.2、基数排序

/**
 * 利用计数排序的基数排序
 * @param A 待排数组
 * @param n 数组元素个数
 */
void RadixSort(int *A, int n)
{
    int exp; // 指数,当对数组按照个位数进行排序时,exp = 1,按十位数排序时,exp = 10...
    int max = 0;

    // 获取数组最大值
    for (int i = 0; i < n; i++)
    {
        if (A[i] > max)
            max = A[i];
    }

    // 从个位数开始,对数组进行按位排序
    for (exp = 1; max / exp > 0; exp *= 10)
    {
        int tmp[n]; // 存储“被排序数据”的临时数组
        int i, buckets[10];
        memset(buckets, 0, 10 * sizeof(int)); // 数组初始化为 0

        // 存储数据出现的次数
        for (i = 0; i < n; i++)
            buckets[(A[i] / exp) % 10]++;

        // 将 buckets[i] 更新为前面所有元素的个数之和,即,buckets[i] 中存储的是其对应的数据在 tmp[] 中的位置,即最终此趟排序下来后数据在 A[] 中的位置
        for (i = 1; i < 10; i++)
            buckets[i] += buckets[i - 1];

        // 将数据存储到临时数组 tmp[] 中
        for (i = n - 1; i >= 0; i--)
            tmp[--buckets[(A[i] / exp) % 10]] = A[i];

        // 将排好的数据赋值给 A[]
        for (i = 0; i < n; i++)
            A[i] = tmp[i];
    }
}

2、中缀表达式转后缀表达式

// 获取字符的优先值
int Priority(char op)
{
    int priority;
    if (op == ‘*‘ || op == ‘/‘)
        priority = 2;
    if (op == ‘+‘ || op == ‘-‘)
        priority = 1;
    if (op == ‘(‘)
        priority = 0;
    return priority;
}

bool TransEX(char *str, char * str1)
{
    Stack S = CreateStack();
    int i, j = 0;
    for (i = 0; str[i] != ‘#‘; i++)
    {
        if ((str[i] >= ‘0‘ && str[i] <= ‘9‘) || (str[i] >= ‘a‘ && str[i] <= ‘z‘)) // 数字或者字母直接入栈
            str1[j++] = str[i];
        else // 不是数字或者字母
        {
            if (IsEmpty(S)) // 栈空则入栈
                Push(str[i], S);
            else if (str[i] == ‘(‘) // 左括号入栈
                Push(str[i], S);
            else if (str[i] == ‘)‘) // 如果是右括号,只要栈顶不是左括号,就弹出并输出
            {
                while (Top(S) != ‘(‘)
                {
                    str1[j++] = Top(S);
                    Pop(S);
                }
                Pop(S); // 弹出左括号
            }
            else
            {
                while (Priority(str[i]) <= Priority(Top(S))) // 栈顶优先级大于等于当前运算符,则输出
                {
                    str1[j++] = Top(S);
                    Pop(S);
                    if (IsEmpty(S))
                        break;
                }
                Push(str[i], S); // 把当前运算符压栈
            }
        }
    }

    while (!IsEmpty(S))
    {
        str1[j++] = Top(S);
        Pop(S);
    }
    str1[j] = ‘#‘;
    DisposeStack(S);
    return true;
}

四、实验结果

1、Radix Sort

1.1、桶式排序

技术图片

技术图片

1.2、基数排序

技术图片

2、中缀表达式转后缀表达式

技术图片

技术图片

五、复杂度分析

1、Radix Sort

1.1、桶式排序

原数组的长度为 (N),所以需要扫描 (N) 次,之后在重新赋值给原数组时,又对 (M) 只桶进行了扫描,因此,总的时间复杂度为 (O(M + N))

1.2、基数排序

基数排序中主要为一个二重循环,其中,外层循环的次数是待排序数字中最大值的位数,内层有四个时间复杂度为 (O(n)) 的一重循环,其总体可以看成 (O(n)),因此,基数排序的时间复杂度为 (O(k · n)),其中 (n) 是待排序元素的个数,(k) 是数字的位数。

2、中缀表达式转后缀表达式

从算法的实现中,我们可以看出其时间复杂度是线性的,每一次循环处理一个字符,因此其时间复杂度为 (O(N))

六、实验结论

  • Ⅰ因为最大数的位数是指数级的,基数 (B) 的选择对结果影响不是太大,因此数组的长度 (n) 对排序的时间影响最大。
  • Ⅱ基数排序并非基于比较的排序,因此改变元素次序不会对时间复杂度产生影响。

根据实验结果,可以证明上面的分析的正确性。

七、附:完整代码

1、Radix Sort

#include <stdio.h>
#include "string.h"

/**
 * 桶式排序
 * @param A 待排序数组
 * @param m 最大元素
 * @param n 数组长度
 */
void BucketSort(int *A, int m, int n)
{
    int B[m + 1];
    int i, j, count = 0;
    memset(B, 0, (m + 1) * sizeof(int)); // 数组初始化
    for (i = 0; i < n; i++)
    {
        j = A[i];
        B[j] += 1; // 记录该桶中元素个数
    }

    // 把桶中的元素按顺序依次放入原数组中
    for (i = 0; i <= m; i++)
    {
        if (B[i] > 0) // 该桶不为空
        {
            for (j = 0; j < B[i]; j++)
            {
                A[count] = i;
                count++;
            }
        }
    }
}

/**
 * 利用计数排序的基数排序
 * @param A 待排数组
 * @param n 数组元素个数
 */
void RadixSort(int *A, int n)
{
    int exp; // 指数,当对数组按照个位数进行排序时,exp = 1,按十位数排序时,exp = 10...
    int max = 0;

    // 获取数组最大值
    for (int i = 0; i < n; i++)
    {
        if (A[i] > max)
            max = A[i];
    }

    // 从个位数开始,对数组进行按位排序
    for (exp = 1; max / exp > 0; exp *= 10)
    {
        int tmp[n]; // 存储“被排序数据”的临时数组
        int i, buckets[10];
        memset(buckets, 0, 10 * sizeof(int)); // 数组初始化为 0

        // 存储数据出现的次数
        for (i = 0; i < n; i++)
            buckets[(A[i] / exp) % 10]++;

        // 将 buckets[i] 更新为前面所有元素的个数之和,即,buckets[i] 中存储的是其对应的数据在 tmp[] 中的位置,即最终此趟排序下来后数据在 A[] 中的位置
        for (i = 1; i < 10; i++)
            buckets[i] += buckets[i - 1];

        // 将数据存储到临时数组 tmp[] 中
        for (i = n - 1; i >= 0; i--)
            tmp[--buckets[(A[i] / exp) % 10]] = A[i];

        // 将排好的数据赋值给 A[]
        for (i = 0; i < n; i++)
            A[i] = tmp[i];
    }
}

int main()
{
    int i;
    // 桶式排序测试
    int a0[10] = {7,8,5,4,1,6,12,14,10,11};
    printf("桶式排序 Before sort: 
");
    for (i = 0; i < 10; i++)
        printf("%d ", a0[i]);
    printf("
");

    BucketSort(a0, 54, 10);

    printf("桶式排序 After sort: 
");
    for (i = 0; i < 10; i++)
        printf("%d ", a0[i]);
    printf("
");

    printf("-----------------------
");

    // 基数排序测试
    int a[10] = {64,8,216,512,27,729,0,1,343,125};
    printf("基数排序 Before sort: 
");
    for (i = 0; i < 10; i++)
        printf("%d ", a[i]);
    printf("
");

    RadixSort(a, 10);

    printf("基数排序 After sort: 
");
    for (i = 0; i < 10; i++)
        printf("%d ", a[i]);
    printf("
");

    return 0;
}

2、中缀表达式转后缀表达式

2.1、main.c

#include <stdio.h>
#include <stdlib.h>
#include "stack.h"
#include "stdbool.h"

// 获取字符的优先值
int Priority(char op)
{
    int priority;
    if (op == ‘*‘ || op == ‘/‘)
        priority = 2;
    if (op == ‘+‘ || op == ‘-‘)
        priority = 1;
    if (op == ‘(‘)
        priority = 0;
    return priority;
}

bool TransEX(char *str, char * str1)
{
    Stack S = CreateStack();
    int i, j = 0;
    for (i = 0; str[i] != ‘#‘; i++)
    {
        if ((str[i] >= ‘0‘ && str[i] <= ‘9‘) || (str[i] >= ‘a‘ && str[i] <= ‘z‘)) // 数字或者字母直接入栈
            str1[j++] = str[i];
        else // 不是数字或者字母
        {
            if (IsEmpty(S)) // 栈空则入栈
                Push(str[i], S);
            else if (str[i] == ‘(‘) // 左括号入栈
                Push(str[i], S);
            else if (str[i] == ‘)‘) // 如果是右括号,只要栈顶不是左括号,就弹出并输出
            {
                while (Top(S) != ‘(‘)
                {
                    str1[j++] = Top(S);
                    Pop(S);
                }
                Pop(S); // 弹出左括号
            }
            else
            {
                while (Priority(str[i]) <= Priority(Top(S))) // 栈顶优先级大于等于当前运算符,则输出
                {
                    str1[j++] = Top(S);
                    Pop(S);
                    if (IsEmpty(S))
                        break;
                }
                Push(str[i], S); // 把当前运算符压栈
            }
        }
    }

    while (!IsEmpty(S))
    {
        str1[j++] = Top(S);
        Pop(S);
    }
    str1[j] = ‘#‘;
    DisposeStack(S);
    return true;
}

int main()
{
    char *infix = malloc(20 * sizeof(char));
    char *postfix = malloc(20 * sizeof(char));

    printf("请输入中缀表达式(以 “#” 结束):
");
    fflush(stdout);
    scanf("%s", infix);
    TransEX(infix, postfix);
    printf("后缀表达式为:
");
    // 打印转换好的字符串
    for (int i = 0; postfix[i] != ‘#‘ ; i++)
    {
        printf("%c", postfix[i]);
    }

    return 0;
}

2.2、stack.h

typedef char ElementType;

#ifndef _Stack_h
#define _Stack_h

struct Node;
typedef struct Node *PtrToNode;
typedef PtrToNode Stack;

int IsEmpty(Stack S);

Stack CreateStack(void);

void DisposeStack(Stack S);

void MakeEmpty(Stack S);

void Push(ElementType X, Stack S);

ElementType Top(Stack S);

void Pop(Stack S);

ElementType TopAndPop(Stack S);

#endif

2.3、stack.c

#include "stack.h"
#include "fatal.h"
#include <stdlib.h>

// 节点
struct Node
{
    ElementType Element;
    PtrToNode Next;
};

// 判断栈是否为空,判断方法为判断头节点S指向的下一个节点是否为空
int
IsEmpty(Stack S)
{
    return S->Next == NULL;
}

// 创建一个栈,主要是创建一个头节点
Stack
CreateStack(void)
{
    Stack S;

    S = malloc(sizeof(struct Node));
    if (S == NULL)
        FatalError("Out of space!!!"); // 空间用尽警告
    S->Next = NULL;
    MakeEmpty(S);
    return S;
}

// 创建一个空栈
void
MakeEmpty(Stack S)
{
    if (S == NULL)
        Error("Must use CreateStack first");
    else
        while (!IsEmpty(S))
            Pop(S);
}

// 处理掉这个栈,即清空所有元素并释放头节点
void
DisposeStack(Stack S)
{
    MakeEmpty(S);
    free(S);
}

// 进栈操作
void
Push(ElementType X, Stack S)
{
    PtrToNode TmpCell;

    TmpCell = malloc(sizeof(struct Node));
    if (TmpCell == NULL)
        FatalError("Out of space!!!");
    else
    {
        TmpCell->Element = X;
        TmpCell->Next = S->Next;
        S->Next = TmpCell;
    }
}

// 返回栈顶元素
ElementType
Top(Stack S)
{
    if (!IsEmpty(S))
        return S->Next->Element;
    Error("Empty stack");
    return 0;
}

// 弹出栈顶元素
void
Pop(Stack S)
{
    PtrToNode FirstCell; // 第一个单元,即栈顶

    if (IsEmpty(S))
        Error("Empty stack");
    else
    {
        FirstCell = S->Next;
        S->Next = S->Next->Next;
        free(FirstCell);
    }
}

ElementType TopAndPop(Stack S)
{
    ElementType res = Top(S);
    Pop(S);
    return res;
}

2.4、fatal.h

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

#define Error( Str )        FatalError( Str )
#define FatalError( Str )   fprintf( stderr, "%s
", Str ), exit( 1 )

以上是关于数据结构实验四 Radix Sort And Stack的主要内容,如果未能解决你的问题,请参考以下文章

实验四

Radix Sort

不基于比较的排序算法:Counting-sort和Radix-sort

基数排序(radix sort)

排序算法:Radix Sort 基数排序

桶排序/基数排序(Radix Sort)