使用C语言去掉字符串集合重复元素

简介:
有一种最直接的方法可以去掉一个集合中重复的元素,这种方法据说就是“交给下面去做”,然而有时候,你自己动手去做一下也是不错的。如果交给下面去做,最直接的选择就是使用map,在java中,我们有HashMap,TreeMap等等实现了map接口的类可用,c++中,同样有STL的同类集合可以使用,在各类高级语言中,就更不必说了,然而在c中,就没有那么幸运了,很多东西需要你来自己实现。
       根据《C语言内力修炼与软件工程》,用c语言自行实现这个东西,其实对于软件工程而言没有必要,然而可以训练一下自己,增加一些内力。我不认为自己是个高手,更非大侠,然而因为我懂得少,只能自己重新来做,真恨自己没有在5年前多学习一些编程语言。
       先来简单分析一下需求,就是一个字符串集合中,去掉重复的字符串,换句话说就是每一个字符串只保留一个。题目没有说是否保持原有的字符串输入顺序,作为完美主义的我,我还是将其当成了一个隐含的需求。那么下一步就是将问题进行简化和转化,如果我们能将这一堆字符串进行排序,那么最终遍历这个排过序的字符串集合,发现和前一个相同的字符串就跳过不输出,对于排序,再简单不过了,至少N中排序算法,本文不讨论各种排序算法,只使用最简单的冒泡排序来分析。那么怎么保留原有的输入序呢?这也很简单,就是在排序元素中增加一个指向原有序的指针即可,另外还有一种方法,那就是排序过程仅仅是一个删除重复元素的过程,而不影响原有的输入序列,这个动态行为可以用二叉树的插入来实现,或者其它的AVL树以及红黑树都可以,本文不会去谈这几棵树的特性,只是用最简单的排序二叉树来分析。
       我们知道,在二叉树插入中,首先要进行一次查找,现在要做的是,如果没有找到相同的,则插入,如果找到了相同的,则不插入,同时为该元素置入删除标识。代码如下:
// //  main.c //  dup-del // //  Created by ya zhao on 11-12-17. //  Copyright 2011年 __MyCompanyName__. All rights reserved. //  #include <stdio.h> #include <stdlib.h>  struct sorted_order_str_map_with_thread {     char *sorted_order_str;  //保存排序后的字符串     char *normal_order_str;  //保存原始字符串     int tag;                //指示是否要删除     struct sorted_order_str_map_with_thread *self; //指向原始的位置 };  void sort(struct sorted_order_str_map_with_thread smwt[], const int size,                     int (*cmp)(void *, void *),                     void (*swap)(void *q1, void *q2)); int cmp_node(void *, void *); //比较函数,如果相同则将其tag位设置为0,标示要删除 int cmp_node(void *q1, void *q2) {     int res;     struct sorted_order_str_map_with_thread *cmp1, *cmp2;     cmp1 = (struct sorted_order_str_map_with_thread*)q1;     cmp2 = (struct sorted_order_str_map_with_thread*)q2;     res = strcmp(cmp1->sorted_order_str, cmp2->sorted_order_str);     if (res == 0) {         struct sorted_order_str_map_with_thread *p = cmp2->self;         p->tag  = 0;     }     return res; } //交换函数,不光要交换元素,还要交换其self指针 void swap_node(void *q1, void *q2) {     struct sorted_order_str_map_with_thread *swp1, *swp2,*temp;     char *strTemp;     swp1 = (struct sorted_order_str_map_with_thread*)q1;     swp2 = (struct sorted_order_str_map_with_thread*)q2;     strTemp = swp1->sorted_order_str;     temp = (swp1->self);     swp1->sorted_order_str = swp2->sorted_order_str;     swp1->self = swp2->self;     swp2->sorted_order_str = strTemp;     swp2->self = temp; }  //标准冒泡排序 void sort(struct sorted_order_str_map_with_thread smwt[], const int size,                 int (*cmp)(void *q1, void *q2),                 void (*swap)(void *q1, void *q2)) {     int flag = 1;     for (int i = 0; i < size - 1; i ++) {         flag = 1;         for (int j = 0; j < size - i - 1; j ++) {             int res = 0;             if ((res = cmp(&smwt[j], &smwt[j+1])) > 0) {                 swap(&smwt[j], &smwt[j+1]);                 flag = 0;             }         }                  if (flag == 1)             break;     } }  int main (int argc, const char * argv[]) {     int i = 0;     //为了简化,下面使用了最恶心的初始化方法。方便复制粘贴     struct sorted_order_str_map_with_thread smwt[20] = {{NULL, NULL, 0 NULL}};     smwt[0].sorted_order_str =smwt[0].normal_order_str =  "323";     smwt[0].self = &smwt[0];     smwt[0].tag = 1;     smwt[1].sorted_order_str = smwt[1].normal_order_str="223";     smwt[1].self = &smwt[1];     smwt[1].tag = 2;     smwt[2].sorted_order_str =smwt[2].normal_order_str= "723";     smwt[2].self = &smwt[2];     smwt[2].tag = 3;     smwt[3].sorted_order_str =smwt[3].normal_order_str= "823";     smwt[3].self = &smwt[3];     smwt[3].tag = 4;     smwt[4].sorted_order_str =smwt[4].normal_order_str= "123";     smwt[4].self = &smwt[4];     smwt[4].tag = 5;     smwt[5].sorted_order_str =smwt[5].normal_order_str= "423";     smwt[5].self = &smwt[5];     smwt[5].tag = 6;     smwt[6].sorted_order_str =smwt[6].normal_order_str= "123";     smwt[6].self = &smwt[6];     smwt[6].tag = 7;     smwt[7].sorted_order_str =smwt[7].normal_order_str= "723";     smwt[7].self = &smwt[7];     smwt[7].tag = 8;     smwt[8].sorted_order_str = smwt[8].normal_order_str="523";     smwt[8].self = &smwt[8];     smwt[8].tag = 9;     smwt[9].sorted_order_str =smwt[9].normal_order_str= "823";     smwt[9].self = &smwt[9];     smwt[9].tag = 10;      sort(smwt, 10, cmp_node, swap_node);     //下面使用了最恶心的输出,经典###     for (i = 0; i< 10; i++) {         printf("###:%s    tag:%d\n", smwt[i].normal_order_str, smwt[i].tag);     }     for (i = 0; i< 10; i++) {             printf("@@@:%s  tag:%d\n", smwt[i].sorted_order_str, smwt[i].tag);     }     for (i = 0; i< 10; i++) {         if (smwt[i].tag != 0){         printf("@@@&&:%s\n", smwt[i].normal_order_str);         }     }     return 0; }


下面的一种方法使用了标准的二叉树插入,注意,插入仅仅是为了删除重复元素,实际上,各种语言各种库的标准Map实现很多也是使用了树,比如java.util中的TreeMap就是使用了红黑树。下面直接给出代码,基于排序二叉树的代码:
// //  main.c //  test-xcode // //  Created by ya zhao on 11-12-17. //  Copyright 2011年 __MyCompanyName__. All rights reserved. //  #include <stdio.h> #include <stdlib.h> #include <string.h>  struct string_node {     char  *string;     int tag;  //标示是否被删除 }; //标准排序二叉树 struct string_tree {     struct string_node  *strn;     struct string_tree* left,*right; };  struct string_tree *add_node(struct string_tree *, struct string_node *str, int (*cmp)(struct string_node *, struct string_node *)); int normalcmp(struct string_node *, struct string_node *); //简单的字符串比较 int normalcmp(struct string_node *n1, struct string_node *n2) {     return  strcmp (n1->string,n2->string); }  int main(int argc, char **argv) {          int j = 0;     for (j = 0; j < 1; j++) {         struct string_tree *root;         struct string_node str[9] = {{"123",1},{"456",1},{"234",1},{"123",1},{"347",1},{"129",1},{"888",1}, {"568",1}, {"456",1}};         root = NULL;         int i = 0;         while (i<9) {             root = add_node(root, &str[i], normalcmp);             i ++;         }         i=0;         while (i<9){             if (str[i].tag) {                 printf("&&&:%s\n", str[i].string);             }             i++;         }     }     return 0; }  struct string_tree *add_node(struct string_tree *p, struct string_node *new, int (*cmp)(struct string_node *n1, struct string_node *n2)) {     int cmp_ret;     if (p == NULL) {         p = (struct string_tree *)calloc(1, sizeof(struct string_tree));         p->strn = (struct string_node*)calloc(1, sizeof(struct string_node));         memcpy(p->strn, new, sizeof(struct string_node));         p->left = p->right = NULL;     } else if ((cmp_ret = cmp(new, p->strn)) == 0) {         new->tag  =0;     } else if (cmp_ret < 0) {         p->left = add_node(p->left, new, cmp);     } else {         p->right = add_node(p->right, new, cmp);     }     return p; }

经过测试,自己实现的上述算法效率还可以,当然这里不该去比较效率,留下个思路即可,在没有库可用的情况下,也可以自己实现它。在现实中,特别是在软件工程中,还是使用现成的map比较好。



 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1270886

相关文章
|
1月前
|
C语言 C++
【C语言】解决不同场景字符串问题:巧妙运用字符串函数
【C语言】解决不同场景字符串问题:巧妙运用字符串函数
|
2月前
|
存储 C语言
【C语言基础考研向】10 字符数组初始化及传递和scanf 读取字符串
本文介绍了C语言中字符数组的初始化方法及其在函数间传递的注意事项。字符数组初始化有两种方式:逐个字符赋值或整体初始化字符串。实际工作中常用后者,如`char c[10]=&quot;hello&quot;`。示例代码展示了如何初始化及传递字符数组,并解释了为何未正确添加结束符`\0`会导致乱码。此外,还讨论了`scanf`函数读取字符串时忽略空格和回车的特点。
|
2月前
|
存储 Serverless C语言
【C语言基础考研向】11 gets函数与puts函数及str系列字符串操作函数
本文介绍了C语言中的`gets`和`puts`函数,`gets`用于从标准输入读取字符串直至换行符,并自动添加字符串结束标志`\0`。`puts`则用于向标准输出打印字符串并自动换行。此外,文章还详细讲解了`str`系列字符串操作函数,包括统计字符串长度的`strlen`、复制字符串的`strcpy`、比较字符串的`strcmp`以及拼接字符串的`strcat`。通过示例代码展示了这些函数的具体应用及注意事项。
142 7
|
2月前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
2月前
|
C语言
C语言 字符串操作函数
本文档详细介绍了多个常用的字符串操作函数,包括 `strlen`、`strcpy`、`strncpy`、`strcat`、`strncat`、`strcmp`、`strncpy`、`sprintf`、`itoa`、`strchr`、`strspn`、`strcspn`、`strstr` 和 `strtok`。每个函数均提供了语法说明、参数解释、返回值描述及示例代码。此外,还给出了部分函数的自实现版本,帮助读者深入理解其工作原理。通过这些函数,可以轻松地进行字符串长度计算、复制、连接、比较等操作。
|
3月前
|
C语言
【C语言】字符串及其函数速览
【C语言】字符串及其函数速览
31 4
|
3月前
|
存储 C语言
【数据结构】c语言链表的创建插入、删除、查询、元素翻倍
【数据结构】c语言链表的创建插入、删除、查询、元素翻倍
【数据结构】c语言链表的创建插入、删除、查询、元素翻倍
|
3月前
|
C语言
【C语言篇】字符和字符串以及内存函数详细介绍与模拟实现(下篇)
perror函数打印完参数部分的字符串后,再打印⼀个冒号和⼀个空格,再打印错误信息。
63 0
|
3月前
|
存储 安全 编译器
【C语言篇】字符和字符串以及内存函数的详细介绍与模拟实现(上篇)
当然可以用scanf和printf输入输出,这里在之前【C语言篇】scanf和printf万字超详细介绍(基本加拓展用法)已经讲过了,这里就不再赘述,主要介绍只针对字符的函数.
56 0
|
4月前
|
安全 C语言
C语言8 数组与字符串
C语言8 数组与字符串
34 0
下一篇
无影云桌面