数据结构和算法 <字符串>(二、字符串排序 上)

不看《算法4》都不知道字符串排序也能讲这么多,之前还以为就是简单的strcmp就完事了,不过看了发现没这么简单,没这么简单的话,就赶紧来学一学吧。

2.0 字符串排序思路

之前是不打算写这个的,直到我看到后面了之后,才发现需要写一个对比,才能更好的理解后面的字符排序法,所以就起了一个0的序号。

从排序说起,我们学过的排序有冒泡,快排等,但是仔细想了一下,我们之前用的排序都是直接用值比较,不过字符串我们也有函数,strcmp,但是这样的话效率就很低下,所以有必要来研究研究字符串排序的方法。

2.1 键索引计数法

这篇文章本来想看懂再写的,结果发现,这个建索引计数法,怎么都看不懂,还拖慢了进度,那就干脆一遍写一遍看,这样效率反而搞了点。

2.1.1 题目

我们就按《算法4》的题目自己实现一遍,说实话,我现在还没看懂,不过可以先实现、

老师在统计学生分数时会遇到以下数据处理问题。学生分为若干组,标号为1 、2 、3等。在某些情况下,我们希望全部同学按组分类。因为组的编号是较小的整数,使用键索引计数法来排序很合适。

假设数组stu[]中的每个元素都保存了一个名字和一个组号,其中组号在0-R-1之间,如下:

struct student
{
    char name[50];
    int groud;
};

student stu[] = {
    {"Anderson", 2},
    {"Brown", 3},
    {"Davis", 3},
    {"Carcia", 4},
    {"Harris", 1},
    {"Jackson", 3},
    {"Johnson", 4},
    {"Martin", 3},
    {"Martinez", 2},
    {"Miller", 2},
    {"Moore", 1},
    {"Robinson", 2},
    {"Smith", 4},
    {"Taylor", 3},
    {"Thomas", 4},
    {"Thompson", 4},
    {"White", 2},
    {"Williams", 3},
    {"Wilson", 4},
};

//strsort的构造函数,就是把数组准备好
StrSort::StrSort(int count_size)
{
    m_count = new int[count_size];
    memset(m_count, 0, count_size*sizeof(int));
    m_count_size = count_size;
}    

2.1.2 频率统计

频率统计其实就是统计一共有多少个共同的key。

  1. 使用int数组count[]计算每个键出现的频率
  2. 对于数组中的每个元素,都使用它的键访问count[]中的相应元素并将其加一

代码如下:

/*
    使用int数组count[]计算每个键出现的频率
    对于数组中的每个元素,都使用它的键访问count[]中的相应元素并将其加一
*/
int *StrSort::FreqCount(student *stu, int stu_len)
{
    for(int i = 0; i < stu_len; i++) {
        int key = stu[i].groud;
        m_count[key + 1]++; 
        printf("key %d count %d\n", key+1, m_count[key + 1]);
    }

    return m_count;
}

统计之后的图示:
在这里插入图片描述

2.1.3 将频率转换为索引

使用count[]来计算每个键在排序结果中的起始索引位置
例:第一组有3个人,第二组有5个人,因此第三组中的同学在排序结果数组中的起始位置为8。
白话:其实就是把,每个组的基数算出来,然后每个组就在这个基数上操作。

代码如下:

/*
    使用count[]来计算每个键在排序结果中的起始索引位置
    例:第一组有3个人,第二组有5个人,因此第三组中的同学在排序结果数组中的起始位置为8
*/
int StrSort::ConvIndex()
{
    for(int r=0; r < R; r++) {
        m_count[r+1] += m_count[r];        //把前面的个数一直叠加
        printf("ConvIndex r+1=%d = %d r=%d =%d\n", r+1, 		 m_count[r+1], r, m_count[r]);
    }

    return 0;
}

图示:
在这里插入图片描述

其实我很不理解,一个一维数组干啥用二维的方式表示,我之前的看的懵逼了,你们如果也懵逼的话,就要把这个图当做一维数组来看。

ConvIndex count[0]=0
ConvIndex count[1]=0
ConvIndex count[2]=3
ConvIndex count[3]=8
ConvIndex count[4]=14

这样画不香么

2.1.4 数据分类

在将count[]数组转换成一张索引表之后,将所有元素移动到一个辅助数组aux[]中进行排序。
每个元素在aux[]中的位置是由它的键对应的count[]值决定。

代码如下:

/*
    在将count[]数组转换成一张索引表之后,将所有元素移动到一个辅助数组aux[]中进行排序。
    每个元素在aux[]中的位置是由它的键对应的count[]值决定。
*/
int StrSort::Datafen(student *stu, int stu_len)
{
    student *aux = new student[stu_len];

    for(int i=0; i<stu_len; i++) {
        int key = stu[i].groud;     //找到key
        int index = m_count[key]++;   //然后通过key找到索引
        aux[index] = stu[i];      //然后填进对应的坑位中
    }

    for (int i=0; i<stu_len; i++)   //回写
    {
        stu[i] = aux[i];
    }

    for (int i=0; i<stu_len; i++) 
    {
        printf("stu %s %d\n", stu[i].name, stu[i].groud);
    }


    delete[] aux;
    return 0;
}

图示:
在这里插入图片描述
在这里插入图片描述

第一个图我也没看,没眼看,不知道为什么画的这么复杂,增加理解难度啊,真是的、
第二个还是比较容易理解的,就是通过索引,然后找到自己对应的坑位就可以了。

2.1.5 回写

这个就简单了,因为我们在排序的时候,是使用到了辅助数组,所以最后一步需要我们把排序后的结果赋值回去。

2.1.6 完整代码路径

代码已经写出来,不过感觉封装的太水了,目前先这样吧,路径:…/code/strSort.cpp

2.2 低位优先的字符串排序

好像我看着就是在从低位开始,一遍一遍的使用建索引计数法就行的排序。

下面直接上代码:

void LSD::sort(std::string *a, int a_len, int w)
{
    //通过前w个字符将a[]排序
    int N = a_len;
    int R = 256;
    std::string *aux = new std::string[N];

    for (int d= w-1; d >=0; d--)
    {
        //根据第d个字符用键索引计数法排序
        int *count = new int[R+1];
        memset(count, 0, (R+1)*4);
        for (int i = 0; i< N; i++)          // 计算频率
        {
            count[a[i].at(d) + 1]++;
            //printf("%c ", a[i].at(d));
        }

        for (int r =0; r < R; r++)          // 将频率转换为索引
        {
            count[r+1] += count[r];
            //printf("r %d %d\n", r, count[r]);
        } 
    
        for (int i=0; i<N; i++)             // 将元素分类
            aux[count[a[i].at(d)]++] = a[i];

        for (int i=0; i<N; i++)             // 回写
            a[i] = aux[i];

        // printf("\n");
    }

    delete[] aux;
}

std::string temp[] = {
    "4PGC938",
    "2IYE230",
    "3CIO720",
    "1ICK750",
    "10HV845",
    "4JZY524",
    "1ICK750",
    "3CIO720",
    "10HV845",
    "10HV845",
    "2RLA629",
    "2RLA629",
    "3ATW723"
};


#if 1
    int main(int argc, char *argv[])
    {
        printf("aa\n");
        
        LSD::sort(temp, 13, 7);

        for (int i=0; i<13; i++)
        {
            printf("%d %s\n", i, temp[i].c_str());
        }

        return 0;
    }


#endif

是不是有点奇怪,说从低位排起,按照我们的习惯应该是高位排起,不过仔细想想从低位排期会先把低位就绪,然后再排高位,很反过来排序的效果是一样的。

下一次就可以安排高位优先排序了。

完整代码路径:…/code/LSD.cpp

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值