统计字符串中各字母出现的次数,并按照规定格式和 自然顺序 输出给定字符串。如:输入”asdfdfdassssddf” ,输出:a:2 d:5 f:3 s:5 。
题目的层次分析
分析题目可得,目标程序需要实现字符串的输入、遍历、排序、输出四项基本操作。其中字符串的输入与输出都很基础,则重心放在遍历与排序上。
字符串的遍历
在给定字符串进行遍历的情况下,我们有两个选择进行遍历:数组与链表。但链表更容易实现动态插入,所以在这个题目中我们选择用链表来实现其操作。
定义结构体,分别存放字母和频次用以输出。在遍历时,第一次检测到某个字母时将其插入链表,同时令其频次+1,之后的遍历也在此基础上进行。
其中字母的抓取就是单纯的对字符串进行遍历,根据字母出现的先后顺序构建链表。
代码如下:
struct Node *getNodeByKey(char key)
{
struct Node *node;
node = list_head;
while (node != NULL) {
if (node->data.key == key)
return node;
node = node->next;
}
return NULL;
}
void insert(char key)
{
struct Node *node = (Node*)malloc(sizeof(struct Node));
node->data.key = key;
node->data.num = 1;
node->next = NULL;
if (list_head == NULL) {
list_head = node;
}
if (list_tail == NULL) {
list_tail = node;
}
else {
list_tail->next = node;
list_tail = node;
}
}
链表的排序
排序是此题中的一个难点。最终选择的方法是冒泡排序,来实现顺序输出。
基本思想:改变链表指针的指向,字符两两进行比较(ASCLL码),将较大的一方“冒”到最后,和数组的冒泡排序基本方法类似,只是过程稍稍麻烦一些。指针的指向很容易搞错,是其中很需要注意的一个问题。
建立四个指针,分别是current,prevent,temporary,last,我们通过这四个指针来改变链表的顺序。
current和prevent开始时都指向头结点(prevent作为current的父结点,指向其前一个位置),temporary用来作为缓存位置记录指针指向,last则是指向尾结点。
需要注意的是在交换结束时我们还需要注意让prevent和current的重新指回头结点。
尾结点和头结点都是循环中非常需要重要的部分