编程技巧:C语言实现变位词排序

开篇

本篇文章的主题是使用C语言来解决变位词排序的问题,题目来源是《编程珠玑》第2章的第三个问题。文中代码实现是根据原文中的思路分析的一个个人尝试。后续,原文在拓展阅读中提到的Tom Cargill版本的代码,也许会在后面以单独的文章来介绍。

不过本文代码的实现思路,和Tom Cargill版非常相似,代码虽有不同,但思想是一致的,也可以作为参考。

问题概要

给定一个英语词典,找出其中的所有变位词集合。例如:“pots”“stop”“tops”互为变位词,因为每一个单词都可以通过改变其他单词中字母的顺序来得到。

思路分析

标识词典中的每一个词,使得在相同变位词类中的单词具有同样的标识。然后,将所有具有相同标识的单词集中在一起。这将原始的变位词问题简化为两个子问题:选择标识和集中具有相同标识的单词。

对第一个问题,即选择标识的问题,可以使用基于排序的标识:将单词中的字母按照字母表顺序排列。"deposit"的标识就是"deiopst",这也是"dopiest"和其他任何在该类中的单词的标识。

对第二个问题,即集中具有相同标识的单词的问题,我们将所有的单词按照其标识的顺序排序。关于该算法的最好的描述就是Tom Cargill的翻手表示:先用一种方式排序(水平翻手),再用另一种方式排序(垂直翻手)。

上面的思路分析,采摘自《编程珠玑》原文,通俗解释就是:对于变位词,虽然字母的顺序不一样,但使用的字母以及字母的个数都是一样的,比如说"stop"和"pots",那么,我们对这写词进行一个排序就好了,排序后均为:opst。也就是说,如果给这两个词添加标识的话,这两个词的标识均为"opst",如此,便可轻松识别出变位词了。

代码实现

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

// 字符串比较函数,用于qsort
int cmpChar(const void* a, const void* b) {
	return *(const char*)a - *(const char*)b;
}

// 将字符串按字典顺序排序
char* sortString(char* s) {
	char* sorted = strdup(s); // 复制字符串,以便排序
	qsort(sorted, strlen(sorted), sizeof(char), cmpChar);
	return sorted; 
}

// 结构体,用于存储单词和其排序后的标识
typedef struct {
	char* word;
	char* sortedWord;
} WordEntry;

// 比较排序后的标识 
int cmpWordEntry(const void* a, const void* b) {
	return strcmp(((WordEntry*)a)->sortedWord, ((WordEntry*)b)->sortedWord);
}

int main() {
	char* dictionary[] = {"pots", "stop", "dopiest", "tops", "deposit", "pans", "snap", "opt"};
	int n = sizeof(dictionary) / sizeof(dictionary[0]);
	WordEntry entries[n];
	int i;
	// 对每个单词进行排序,并填充entries数组
	for (i = 0; i < n; ++i) {
		entries[i].word = dictionary[i];
		entries[i].sortedWord = sortString(dictionary[i]);
	}
	// 根据排序后的单词进行排序
	qsort(entries, n, sizeof(WordEntry), cmpWordEntry);
	// 打印变位词集合
	printf("变位词集合: \n");
	for (i = 0; i < n;) {
		int j = i;
		// 查找具有相同的标识的词 
		while (j < n && strcmp(entries[i].sortedWord, entries[j].sortedWord) == 0) {
			printf("%s ", entries[j].word);
			++j;
		}
		printf("\n");
		i = j;
	}
	// 释放分配的内存
	for (i = 0; i < n; i++) {
		free(entries[i].sortedWord);
	} 
	return 0;
}

变位词的问题,至此还没有结束!后面Tom Cargill版本的代码,以及对于该问题的扩展,都会以单独的文章来解读。因为我个人对于技术博客的期望是:每天沉淀一个知识点,每天进步一点点。所以,每篇文章并不会特别长。

但愿今天的代码,能对正在阅读的你有些许参考意义。

感谢阅读。

  • 26
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值