数组查重排序问题

小白第一次发博客,初来乍到,请多关照//

最近在学C语言,发现数组排序去重这类问题有一些细节,产生的问题较多,正好最近想试一下写第一篇博客,于是就有了这个文章_

下面两题的基本思路一样,区别是重复的元素保留一个还是全部删去,以及最多重复的个数。

问题1:字符串中字符排序

【问题描述】

编写一个程序,从键盘接收一个字符串,然后按照字符顺序从小到大进行排序,并删除重复的字符。

【输入形式】

用户在第一行输入一个字符串。

【输出形式】

程序按照字符(ASCII)顺序从小到大排序字符串,并删除重复的字符进行输出。

【样例输入】

badacgegfacb
【样例输出】

abcdefg

问题2:数组异或集

【问题描述】

从标准输入中输入两组整数(每行不超过20个整数,每组整数中元素不重复),合并两组整数,去掉在两组整数中都出现的整数,并按从大到小顺序排序输出(即两组整数集"异或")。

【输入形式】

首先输入第一组整数,以一个空格分隔各个整数;然后在新的一行上输入第二组整数,以一个空格分隔,行末有回车换行。

【输出形式】

按从大到小顺序排序输出合并后的整数集(去掉在两组整数中都出现的整数,以一个空格分隔各个整数)。

【样例输入】

5 1 4 32 8 7 9 -6
5 2 87 10 1

【样例输出】

87 32 10 9 8 7 4 2 -6

思路:

两道题都需要排序去重,故先排序;

问题1重复的保留一个,所以可以逐个遍历排序后的数组,如果该元素和它的下一个元素相同,则覆盖掉该元素,保留下一个;然后从下一个开始继续遍历。
但是实现的时候,会出现这种情况:(粗体位置为判定点a[i]和a[i+1])
aaabcdeg ⇒ aabcdeg ,大循环i++之后变成 aabcdeg ,这里a不会再删去,最终两个a都会留下。
因此这里去重之后要 i-- 让判定点归位, 变回 aabcdeg ,重新判定。

问题2两组数组不会重复,因此合并后的数组不会出现3个或以上元素相同的情况。
因此如果保留一个,可以不必i–。
但是由于需要两个均删除,由演示:(这里用字符替代数字了)
aabbccdegt ⇒ bbccdegt(覆盖2个) ⇒ bbccdegt ,最终留下两个b。
所以同样需要i–。

另有一套思路:重复的不输出,从输出端进行去重。

对于问题1,如果a[i]和a[i+1]相同,则不输出。这样对于长串的相同字符,只有最后一个会输出。
对于问题2,如果a[i]和a[i+1]相同,则跳过这两个直接判定下下个。由于每次枚举i自动加一,因此需要补充一个i++。

代码:(数组覆盖思路)

(问题1:字符串中字符排序)

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

char c[19999];

void swap(char *a,char *b){    //交换函数,即 {char x = a;a=b;b=x;} 
    char x = *a;
    *a = *b;
    *b = x;
}

int main(){
    fgets(c, 19997, stdin); //输入,因为vs里面gets()已经无了所以作为替代
    int lc = strlen(c);     //字符串长度(统计多少个字符)
    for (int i = 0;i<lc;i++){   //冒泡排序
        for (int j = 0;j<lc-i-1;j++){   //lc-i再减一防止i==0时,j+1==lc越界
            if (c[j]>c[j+1]) swap(&c[j],&c[j+1]);
        }
    }
    for (int i = 0;i<lc-1;i++){ //判断去重,lc-1同理防越界
        if (c[i]==c[i+1]) { //相等
            for (int j = i;j<lc;j++) c[j]=c[j+1];//覆盖掉c[j],保留了c[j+1]

            //这里为什么可以j+1==lc?因为要把字符串末尾的\0也挪下来

            i--;    //因为存在三个连续相等的情况,这里需要再次判断第二个和第三个是否相等

            lc--;   //串长-1(用来约束循环,不然c[i]会走到字符串的\0处,c[i+1]越界)
        }
    }
    printf("%s",c);
    return 0;
}

(问题2:数组异或集)

#include <stdio.h>

int a[999];

int main(){
    int temp;
    int s = 0;  //数的个数
    do {        //获取第一个数组,直到回车换行
        scanf("%d",&temp);  
        a[s++] = temp;
    } while (getchar() != '\n');    //遇到数间空格继续,遇回车结束
    do {        //获取第二个数组,直到回车换行
        scanf("%d",&temp);
        a[s++] = temp;
    } while (getchar() != '\n');    //遇到数间空格继续,遇回车结束

    for (int i = 0;i<s;i++){    //冒泡排序
        for (int j = 0;j<s-i-1;j++){    //s-i再减一,防i==0时j+1==s越界
            if (a[j]<a[j+1]) {
                int t = a[j];
                a[j]=a[j+1];
                a[j+1]=t;
            }
        }
    }

    for (int i = 0;i<s;i++){    //关键点: 逐个查重
        if (a[i]==a[i+1]) { //与下一个重复
            for (int j = i;j<s-2;j++) a[j]=a[j+2];  //将两个重复覆盖

            s-=2;   //总数少两个
            i--;    //重点! 删重复之后,下一次判定a[i+1]与a[i+2],会使得新的a[i]与a[i+1]相等情况被忽略
        }
    }
    for (int i = 0;i<s;i++){    //输出
        printf("%d ",a[i]);
    }
    return 0;
}

代码:(输出去重思路)

(问题1:字符串中字符排序)

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

char c[19999];

void swap(char *a,char *b){    //交换函数,即 {char x = a;a=b;b=x;} 
    char x = *a;
    *a = *b;
    *b = x;
}

int main(){
    fgets(c, 19997, stdin); //输入,因为vs里面gets()已经无了所以作为替代
    int lc = strlen(c);     //字符串长度(统计多少个字符)
    for (int i = 0;i<lc;i++){   //冒泡排序
        for (int j = 0;j<lc-i-1;j++){   //lc-i再减一防止i==0时,j+1==lc越界
            if (c[j]>c[j+1]) swap(&c[j],&c[j+1]);
        }
    }

    for (int i = 0;i<lc;i++){ //输出去重,lc不减一为了让最后一个元素通过和结尾\0判定输出出来
        if (c[i]!=c[i+1]) { //c[i]和下一个不相等
            printf("%c",c[i]);   //才输出
        }   //否则不输出
    }
    return 0;
}

(问题2:数组异或集)

#include <stdio.h>

int a[999];

int main() {
    int temp;
    int s = 0; //数的个数
    do {       //获取第一个数组,直到回车换行
        scanf("%d", &temp);
        a[s++] = temp;
    } while (getchar() != '\n'); //遇到数间空格继续,遇回车结束
    do {                         //获取第二个数组,直到回车换行
        scanf("%d", &temp);
        a[s++] = temp;
    } while (getchar() != '\n'); //遇到数间空格继续,遇回车结束

    for (int i = 0; i < s; i++) {             //冒泡排序
        for (int j = 0; j < s - i - 1; j++) { // s-i再减一,防i==0时j+1==s越界
            if (a[j] < a[j + 1]) {
                int t = a[j];
                a[j] = a[j + 1];
                a[j + 1] = t;
            }
        }
    }

    for (int i = 0; i < s; i++) { //输出查重
        if (a[i] == a[i + 1]) {   //与下一个重复
            i++; //直接跳过a[i+1],下一轮判a[i+2]
        } else
            printf("%d ", a[i]); //不重复 才输出
    }
    return 0;
}

最后附上变式问题:读入字符串,可以3个或以上字符重复,要求重复的字符均不输出,如何操作?
变式问题代码:
(覆盖数组思路)

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

char c[19999];

void swap(char *a,char *b){    //交换函数,即 {char x = a;a=b;b=x;} 
    char x = *a;
    *a = *b;
    *b = x;
}

int main(){
    fgets(c, 19997, stdin); //输入,因为vs里面gets()已经无了所以作为替代
    int lc = strlen(c);     //字符串长度(统计多少个字符)
    for (int i = 0;i<lc;i++){   //冒泡排序
        for (int j = 0;j<lc-i-1;j++){   //lc-i再减一防止i==0时,j+1==lc越界
            if (c[j]>c[j+1]) swap(&c[j],&c[j+1]);
        }
    }
    int cnt = 1; //重复个数,默认为1,即不重复
    for (int i = 0;i<lc;i++){ //覆盖去重plus,这里i+1可以取到lc为了把最后一个元素判定掉
        if (c[i]==c[i+1]) { //相等
            cnt++; //攒一个重复数量
        } else if (cnt-1>0) { //不相等且需要覆盖
            for (int j = i-(cnt-1);j<=lc-cnt;j++) c[j]=c[j+cnt];//覆盖cnt个下来
            //j的起点是开始重复的位置
            //这里需要j+cnt==lc,因为要把字符串末尾的\0也挪下来
            i-= cnt; //回去重判被覆盖的部分
            lc-= cnt;//串长暴减
            cnt=1; //cnt重新计数
        } //不相等又不需要覆盖,无事发生
    }
    printf("%s",c);
    return 0;
}

(输出去重思路)

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

char c[19999];

void swap(char *a,char *b){    //交换函数,即 {char x = a;a=b;b=x;} 
    char x = *a;
    *a = *b;
    *b = x;
}

int main(){
    fgets(c, 19997, stdin); //输入,因为vs里面gets()已经无了所以作为替代
    int lc = strlen(c);     //字符串长度(统计多少个字符)
    for (int i = 0;i<lc;i++){   //冒泡排序
        for (int j = 0;j<lc-i-1;j++){   //lc-i再减一防止i==0时,j+1==lc越界
            if (c[j]>c[j+1]) swap(&c[j],&c[j+1]);
        }
    }
    int cnt = 1; //重复个数,默认为1,即不重复
    if (c[0]!=c[1]) printf("%c",c[0]);  //特判c[0],因为i-1得-1会越界
    for (int i = 1;i<lc;i++){ //输出去重,lc不减一为了让最后一个元素通过和结尾\0判定输出出来
        if (c[i]!=c[i+1] && c[i]!=c[i-1]) { //c[i]和下一个不相等,和前一个也不相等
            printf("%c",c[i]);   //才输出
        }   //否则不输出
    }
    return 0;
}

总结

要求重复保留一个的情况下:
无论多少个重复,都可以一个一个判定,但要注意覆盖数组法判定后i要减一(i–),输出去重不必i–;
要求重复元素均不保留的情况下:
存在2个以上元素互相重复时,需要注意判定该元素和前一个的关系,防止重复的最后一个没有判定到;最多重复2个的时候不需要(因为上一个重复完就不会和下一个重复啦,直接跳过就好)。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值