51-【巧解】统计无序数组各元素出现的次数--时间复杂度O(n),空间复杂度O(1)

一、问题描述

【题型一】

一个长度大小为n的数组,数组中的每个元素的取值范围在[1,n],且为正整数。
问:如何在时间复杂度为O(n),空间复杂度为O(1)的条件下,统计数组中不同元素出现的次数。

【题型二】

在一个长度为n的数组里的所有数字都在0-n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
【例如】
如果输入长度为7的数组(2,3,1,0,2,5,3),那么对应的输出是重复的数字2或者3

二、解题思路

【题型一】

    数组按序扫描,通过当前元素的值作为下标,找到下一个元素。最后得到的数组中,下标(因为下标从0开始的,故输出时需要+1)为数组中出现的元素,每个下标对应的值取反输出即是该元素出现的频率。
    若当前元素小于0,
        则跳过;
    若当前元素大于0,
        则判断其作为下标索引到的元素是否大于0,
            若大于0,则索引到的元素赋值给当前元素,索引到的元素置为-1;
            若小于0,则索引到的元素自减1,当前元素置为0;

    用正数/负数来区分a[i]上是原来的值,还是用于计数的值
    随便举个例子,比如 { 2, 5, 5, 2, 3 }
    看到第一个是 2,即2有1个,所以先将这个“1”保存到a[2-1]的位置上。但a[2-1]有有效数呀,咋办?移动一下就行。假设我不用负数,而是用中括号来表示计数,步骤依次是
    { 2, 5, 5, 2, 3 }
    5, [1], 5, 2, 3
    3, [1], 5, 2, [1]
    5, [1], [1], 2, [1]
    [0], [1], [1], 2, [2]
    [0], [2], [1], [0], [2]

    结果是 1有0个, 2有2个, 3有1个, 4有0个, 5有2个

    此类以值做下标的方法适用条件:

  •     正整数
  •     取值范围[1,n]
  •     n个数

【题型二】

思路一:排序 -- 时间复杂度O(nlogn)
思路二:哈希表 -- 时间复杂度O(1) 空间复杂度O(n)
思路三:值为下标 -- 时间复杂度O(n) 空间复杂度O(1)

思路三:以2,3,1,0,2,5,3为例
1)数组第0号位置为2,2!=0,将0号位置的2与2号位置的1交换,变成 1,3,2,0,2,5,3
2)数组第0号位置为1,1!=0,将0号位置的1与1号位置的3交换,变成 3,1,2,0,2,5,3
3)数组第0号位置为3,3!=0,将0号位置的3与3号位置的0交换,变成 0,1,2,3,2,5,3
4)数组第0号位置为0,0==0,游标右移至第1号位置,1==1,继续右移,直到第4号位置
5)数组第4号位置为2, 2!=4, 2==array[2],说明2重复了,输出2,算法结束

思路三前提条件:
1)数组中数字范围为[0,n-1]
2)数组长度为n

三、算法代码

【题型一】n个正整数 -- [1,n] 找重复

#include <stdio.h>
#include <stdlib.h>
/*******************************
Author:tmw
date:2018-3-17
********************************/
int* mark_times_appear(int* array, int len)
{
    if(array==NULL || len<0) return NULL;
    int index = 0;
    int i = 0;
    while(i<len)
    {
        /**当前位为正数有效位,则执行交换**/
        while( array[i] > 0 )
        {
            index = array[i];

            /**待交换的位置上的元素是有效值的情况:
            交换,并将当前待交换的元素下的值改成负数计数值
            **/
            if(array[index-1]>0) /**数组下标从0开始**/
            {
                array[i] = array[index-1];
                array[index-1] = -1;
            }
            /**待交换的位置上的元素是负数的情况:
            说明该元素出现过一次了,负数计数需要减减来更新记录
            **/
            else/**若需要返回出现两次的元素,可以直接在这里做返回**/
            {
                //return array[i];
                array[i] = 0;
                array[index-1]--;
            }
        }
        /**当前位为负数,紧接着判断下一位**/
        i++;
    }
    return array;
}

【题型二】n个数[0,n] 找重复

/**************************************************
author:tmw
date:2018-8-22
**************************************************/
#include <stdio.h>
#include <stdlib.h>

#define swap(a,b,t) (t=a,a=b,b=t)
/** findDuplicatedNumber 找到任意一个重复的元素并输出
* @param int* array -- 存数字的数组
* @param int array_len -- 数组长度
**/
int findDuplicatedNumber( int* array, int array_len )
{
    /**入参判断**/
    if( array == NULL || array_len <= 0 )
        return -1;
    int i;
    /**这一步可省略:验证数组内的元素满足给定条件:元素取值[0,n-1],长度为n**/
    for( i=0; i<array_len; i++ )
    {
        if( array[i] < 0 || array[i] > array_len-1 )
            return -1;
    }

    /**主算法**/
    int temp=0;
    for( i=0; i<array_len; i++ )
    {
        while( array[i] != i )
        {
            if( array[i] != array[array[i]] )
                swap(array[i], array[array[i]], temp);
            else
                return array[i];
        }
    }
    return -1; //没找着,返回-1
}

四、测试代码及结果

【题型一】的测试结果

int main()
{
    printf("测试代码!\n");

    int* array;
    int i;
    array = (int*)malloc(5*sizeof(int));
    printf("请输入5个数组元素:\n");
    for( i=0;i<5;i++ )
        scanf("%d",&array[i]);
    array = mark_times_apear(array,5);

    for( i=0;i<5;i++ )
        printf("%d ",array[i]);

    return 0;
}

 

梦想还是要有的,万一实现了呢~~~ヾ(◍°∇°◍)ノ゙~~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值