数组中字符出现的次数问题

来源:互联网 发布:个人信用数据库 编辑:程序博客网 时间:2024/04/26 17:41

在编程题中经常会遇到数字在数组中出现的次数这种问题,常见的问题有:第一次出现的数字,只出现一次的数字,一个数字出现几次,要注意数组的形式,是排序数组还是没有规律数组,考虑这几个方面,解题思路是不同的。

下面就剑指offer中遇到的问题,列举如下:

1、第一个只出现一次的字符

题目描述:在一个字符串(1<=字符串长度<=10000,全部由大写字母组成)中找到第一个只出现一次的字符,并返回它的位置

解题思路:如果能够按字符出现的顺序统计数字出现的次数,那么第一个次数等于1的字符位置就是要求结果,可以新开辟一个大小为256的数组当做hashmap(为什么是256,因为char是8bit 能有256种可能),来统计字符出现的次数。

函数方程如下 :输入一个字符串

int FirstNotRepeatingChar(string str) {     if(str.length() <= 0) return -1;                 int hash[256] = {0};        int i = 0;        for(i; i < str.length(); i++)//统计每个字符出现的次数        {            hash[str[i]]++;                  }        i = 0;        while(str[i] != '\0')        {            if(hash[str[i]] == 1){break;}//找出第一个出现一次的字母,并返回它的位置            i++;        }               return i;    }


2、数组中只出现一次的数字

题目描述:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

解题思路:这个题也可以题一的方法解决,但是如果要求不能开辟新的空间,那应该怎么计算呢。数字出现2次的,如果给第一次出现的数字赋值true,那么第二次出现赋值false,那最后只有出现一次的数字还是true,其他都是false。但是用什么方法实现这个呢。

相同的数字进行二进制异或 结果为0,所以如果数组中只存在一个出现一次的数字,那么把数组中所有的数字异或结果之和就是出现一次的数字。但是数组中有2个不同的数字时,如果把数组分成两组,每一组只存在一个出现一次的数字,就可以直接求出这个数组是什么了;

函数方程如下:

 void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {               int len = data.size();        if(len <= 1) return;        int resultXOR = 0;//采用异或位运算,二进制时,相同为0,不同为1        for(int i = 0; i < len; i++)        {            resultXOR ^= data[i];//最终resultXOR的结果为那两个不同的数异或结果                   }        //找出resultXOR的二进制中从右开始计算,第一个出现1的位置,位置记为n,判断数组中数字位置n是否为1,把数组分成两部分,这样就把两个不同的数字分到了两组        //然后每一组分别进行异或运算,异或结果就是出现一次的数字;        int flag = 1; //判断第一次出现1的位置       while(resultXOR)       {          if((resultXOR & flag) != 0){break;}            flag = flag << 1;//error:容易错误的地方,我之前左移一直写的是 flag << 1;这样你一直没有把移后的值赋值给flag,所以一直循环 正确写法:flag = flag << 1;       }              *num1 = *num2 = 0; //存储2个不同的数字的变量赋初值为0                for(int i = 0; i < len; i++)        {           if((data[i] & flag) != 0)           {               *num1 ^= data[i];           }           else{               *num2 ^= data[i];           }                   }                  }
找出只出现一次的值,把结果赋值给num1和num2;


3、数字在排序数组中的次数

题目描述:统计一个数字在排序数组中出现的次数。

题目解析:如果直接遍历数组,直接统计所求数字的字数这个方法也是可以的,但是这个方法对于数组是否为排序数组所用的时间复杂度都是一样的,但是竟然都是排序数组了,这种方法不是最好的,排序数组查找一个数,首先就会想到二分查找。

二分查找先找到这个需要统计的数。然后以这个数为界限分为左右两边,继续用二分查找左边求出数字的第一次出现的位置,右边找出数字最后一次出现的位置。(因为排序数组相同的数字总是连在一起的),用最后一次出现的位置减去第一次出现的位置+ 1 就是数字出现的次数

函数代码如下:

  int GetNumberOfK(vector<int> data ,int k) {        if(data.size() <= 0) return 0;//数组为空返回0        int length = data.size();        int left = 0, right = length-1;        int num = 0;//统计数字重复的个数        //利用二分查找进行计算        while(left <= right)        {           int mid = (left + right)/2;            if(data[mid] > k)            {                right = mid - 1;                            }            if(data[mid] < k)            {                left = mid + 1;                           }            //当中间数字为要查找的数时,分为左右两组进行二分查找,找出要查找数字的最左边界和最右边界            if(data[mid] == k)            {                         int ll = left, lr = mid;//定义中间以左的左边界和右边界               int rl = mid, rr = right;//定义中间以右的左边界和右边界               while(data[ll] != k && lr >= 0 && ll <= lr)//找出左边第一个值为k的结点               {   int lmid = (ll + lr)/2;                   if(data[lmid] < k )                   {                       ll = lmid + 1;                   }                   if(data[lmid] == k )                   {                       lr = lmid - 1;                   }                                  }               while(data[rr] != k && rl <= length && rl <= rr)//找出右边第一个值为k的结点               {                   int  rmid = (rl + rr)/2;                    if(data[rmid] > k)                   {                       rr = rmid - 1;                   }                   if(data[rmid] == k)                   {                       rl = rmid + 1;                                        }               }               num = rr - ll + 1;                break;            }        }        return num;    }