C语言——第一个只出现一次的字符

一、问题描述(来源:计蒜客

二、思路一

从字符串的第二个字母开始循环,如果后续有相同的字母,则将后面相同的字母删除,该删除字母后面的字母全部往前移一位。一轮循环完后,再将开头的字母删除(如果该字母有重复)。如果后面没有找到与这个字母相同的字母,就结束循环,打印字符串的第一个字母;如果检测到这个字符串是空的,则打印no。

注:这种方法比较复杂,代码运行耗时较长,而且有几个地方不注意容易出错。注释里有详细说明。

代码实现:

#include<stdio.h>
#include<stdlib.h>
int main()
{
    char str[100000];
    int i, j, flag=0;
    gets(str);
    //i从1开始递增,让str[i]与str[0]比较
    for (i = 1; i < strlen(str); i++)
    {
        //如果字母str[i]与字母str[0]相同,
        //则将str[i]后面的字母全部往左移一位
        if (str[i] == str[0])
        {
            for (j = i; j < strlen(str)-1; j++)
            {
                str[j] = str[j + 1];
            }
            //将str最后一个元素赋值为\0,\0为字符串结束标识符
            str[strlen(str) - 1] = '\0';
            printf("%s\n", str);
            //flag用于标记是否有字母和第一个字母相同
            //如果有赋值为1,没有就为默认值0
            flag = 1;
            //因为在当前循环结束后,i的值会加1,所以,这里将i减1后,下一次循环i的值不变
            //为什么要这一步?
            //这里是考虑到如果有两个相同的字母相邻且和第一个字母相同,例如str="abcaabc"
            //当i=3时,检测到str[3]==str[0]==a,这时将后三个字母abc左移一位,最后一个赋值\0,
            //此时str="abcabc",如果不对i进行处理的话,下一次循环i的值就为4,进而比较的是str[4]和str[0],
            //即比较a和b了,原来字符串中两个相邻的a中的第二个a就被跳过比较了。
            i = i - 1;
        }
        //如果第一个字母之后有与其相同的字母(flag==1)
        //而且已经比较到字符串的最后一个字母了,这时与第一个字母相同的字母都已经删除了
        //然后需要将第一个字母也删除掉。
        if (flag == 1 && str[i + 1] == '\0')
        {
            //从第二个字母开始到最后一个字母全部向左移一位,最后一个字母赋值为\0
            for (j = 0; j < strlen(str) - 1; j++)
            {
                str[j] = str[j + 1];
            }
            str[strlen(str) - 1] = '\0';
            //初始化标记
            flag = 0;
            //因为第一个字母已经不是原来的字母了,为了继续删除其他相同的字母,
            //还需要将第一个之后的字母逐一与之比较,这里将i赋值为0,
            //执行完当前循环后i加1变成1了,而str[1]正好是字符串的第二个元素,
            //这样又可以从第二个字母开始,将后续的字母逐一与第一个字母进行比较。
            i = 0;
            printf("%s\n", str);
        }
        //如果比较到最后一个字母还是没有与第一个字母相同的字母
        //这时就退出循环,字符串str的第一个字母就是第一个只出现一次的字母
        else if (flag == 0 && str[i + 1] == '\0')
        {
            break;
        }
    }
    //如果字符串str的第一个字母不为空,那么打印第一个字母
    //如果字符串str的第一个字母为空,说明整个字符串中没有不重复的字母,
    //经过上面的一系列操作后使str变成了空字符串,此时打印no。
    if (str[0] != '\0')
        printf("%c", str[0]);
    else
        printf("no");
    return 0;
}

测试结果:

、思路二

从第一个字母开始循环,如果后面有相同的字母,将其替换成一个特殊字符,一轮循环后,如果有与循环开始的字母相同的字母,还需要将循环开始的那个字母替换成这个特殊字符,如果没有找到相同的字母,则结束循环,打印这个字母。

注:因为题目已经说明字符串中全部是小写字母,所以可以将重复的全部替换成一个特殊字符。

代码实现:

#include<stdio.h>
#include<stdlib.h>
int main()
{
    char str[100000];
    int n, i, j, flag;
    gets(str);
    n = strlen(str);	//字符串str的长度
    for (i = 0; i < n-1; i++)	//外循环n-1轮
    {
        //如果str[i]不是字符*,那么需要将其和后面的字母进行比较
        //如果str[i]是字符*,那么就不需要将其和后续字母进行比较了
        if (str[i] != '*')
        {
            //初始化标记,默认值为0
            //flag为0表示字母str[i]后面没有与其相同的字母,
            //这时内循环结束一次后不需要将str[i]赋值为字符*;
            //flag为1表示字母str[i]后面的又与其相同的字母,
            //这时内循环结束一次后需要将str[i]赋值为字符*;
            flag = 0;
            for (j = i + 1; j < n; j++)	//内循环从str[i]后面一个字符开始
            {
                //如果str[j]不是字符*,且字母str[i]和字母str[j]相同
                //将str[j]赋值为*,并改变标记flag的值为1
                if (str[j] != '*' && str[i] == str[j])
                {
                    str[j] = '*';
                    flag = 1;
                    printf("%s\n",str);
                }
            }
            //如果一轮内循环结束后,判断flag的值是否为1,
            //如果flag为1,表示字符串中字母str[i]后有与其相同的字母,
            //这时将str[i]赋值为*
            if (flag == 1)
            {
                str[i] = '*';
                printf("%s\n", str);
            }
        }
    }
    //printf("%s\n",str);
    //遍历字符串str的每个元素
    for (i = 0; i < n; i++)
    {
        //如果第i个元素不是字符*,说明这个字母在该字符串中只出现了一次
        if (str[i] != '*')
        {
            printf("%c", str[i]);
            break;	//只需要打印第一个只出现一次的字母,因此要退出循环
        }
        //如果遍历到最后一个元素,说明前面的元素都是字符*,
        //这时,如果最后一个元素还是字符*的话,说明没有不重复的字母。
        if (i == n - 1 && str[i- 1] == '*')
        {
            printf("no");
        }
    }
    return 0;
}

测试结果:

四、思路三

题目中已说明字符串中只包含小写字母。小写字母一共只有26个,如果定义一个26个元素的数组,分别存储a~z在字符串中出现的次数,那么只需要找出这个数组中值为1的第一个元素的下标,然后将其对应的字母打印出来就可以了,如果没有值为1的元素,那么打印no。

注:这种方法最好理解,也更容易实现。

代码实现:

#include<stdio.h>
#include<stdlib.h>
int main()
{
    char str[100000];
    int i, array[26] = { 0 };
    gets(str);
    for (i = 0; i < strlen(str); i++)
    {
        array[str[i] - 'a']++;
    }
    for (i = 0; i < 26; i++)
    {
        if (array[i] == 1)
        {
            printf("%c",'a'+i);
            break;
        }
        else if (i == 25 && array[i] != 1)
        {
            printf("no");
        }
    }
    return 0;
}

 测试结果:

五、思路三问题更正

非常感谢网友“ForTres”提出的问题。思路三中的代码无法做到“找到第一个只出现一次”,比如字符串序列 “ abzcabd”,'z','c','d'都是下标为1,返回的结果是'c'。在原基础上做了修改,代码如下,具体的思路请看代码中的注释。

#include<stdio.h>
#include<stdlib.h>
int main()
{
	char str[100000];
	//定义二维数组,第一行记录a~z出现的次数
	//第二行记录a~z在字符串str中出现的位置下标
	int i, array[2][26] = { 0 };
	gets(str);
	//minIndex记录最小的位置下标,初值为字符串str长度
	int minIndex = strlen(str);
	//index记录第一个只出现一次的字符在array数组中的位置下标
	int index = -1;
	for (i = 0; i < strlen(str); i++)
	{
		//数组array第一行计数
		array[0][str[i] - 'a']++;
		//数组array第二行记录最新出现的位置下标
		array[1][str[i] - 'a'] = i;
	}
	for (i = 0; i < 26; i++)
	{	
		//如果数组array中第一行中的第i列为1,则对应的字符自出现了一次
		if (array[0][i] == 1)
		{	
			//如果当前只出现一次的字符在字符串str中的位置下标小于minIndex
			if (array[1][i] < minIndex){
				//将该字符出现的位置下标赋值给minIndex
				minIndex = array[1][i];
				//记录这个字符在数组array中的位置下标
				index = i;
			}
		}
	}
	//如果index没有变化,仍然等于-1,说明字符串str中没有只出现一次的字符
	if (index != -1){
		printf("%c", 'a' + index);
	}
	else{
		printf("no");
	}
	return 0;
}

 

  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值