<剑指offer>_有趣的数组

本文精选数组相关的经典算法题目,包括寻找旋转数组中的最小值、求连续子数组的最大和、计算1到n中1出现的次数等,并提供详细的实现代码与解析。

【数组相关的题目】

最近投简历,面试耽误不少进度,有些迷茫,不知道自己究竟适不适合程序员这行业,半道出家的娃只能且走且看,算法也只是突击了两个月,心里实在没地,觉得要是拿到那些CS大牛人不屑于顾的offer肯定欢天喜地 =_=   I am a slow walker, but I never walk backward.
【8旋转数组中的最小数】
//题目:把一个数组最开始的若干数字搬到数组的末尾,我们称之为数组的旋转。输入一个递增的数组
//输出旋转数组的最小元素。例如:3,4,5,1,2是1,2,3,4,5,的一个旋转数组,该数组的最小值是1
//思路类似二分查找,旋转数组的性质就是前半序列大于后半序列。
//首先设置三个指针,pl,pm,ph头指针,中间指针和尾指针
//如果中间的数字大于头指针,那么证明最小的数在后半序列,在对后半序列进行递归
递归写法
[cpp]  view plain copy
  1. int get_min_reverse_array(int a[], int low, int high)  
  2. {  
  3.     if (high - low == 1)  
  4.         return a[high];  
  5.     int mid = (low + high)/2;  
  6.     if (a[mid] > a[low])  
  7.         return get_min_reverse_array(a, mid, high);  
  8.     if (a[mid] < a[high])  
  9.         return get_min_reverse_array(a, low, mid);  
  10.   
  11. }  

//非递归写法
[cpp]  view plain copy
  1. int solu2_get_min_reverse_array(int a[], int len)  
  2. {  
  3.     int low = 0;  
  4.     int high = len - 1;  
  5.     int mid;  
  6.     while(a[low] >= a[high])  
  7.     {  
  8.         if (high - low == 1)  
  9.         {  
  10.             mid = high;  
  11.             break;  
  12.         }  
  13.         mid = (low+high)/2;  
  14.         if (a[mid] > a[low])  
  15.             low = mid;  
  16.         if (a[mid] < a[high])  
  17.             high = mid;  
  18.     }  
  19.     return a[mid];  
  20. }  

//另外,上面写法不完整,当碰见1 0 1 1 1的时候也就是三个指针的数相同时候,只能遍历找到最小的数
【31连续子数列的最大和】
//题目:输入一个整数,里面有正有负,数组中一个或者多个连续数形成一个子序列,
//求所有子数组中和的最大值,要求时间复杂度为O(n),这个需要慢慢分析,
//主要考虑前面累加的和为负值跟当前元素的关系,碰见负值如何处理
[cpp]  view plain copy
  1. int get_max_subarray(int a[], int len)  
  2. {  
  3.     int sum = 0;  
  4.     int max_sub_sum = 0x80000000;//最小的负值  
  5.     for (int i = 0; i < len; i++)  
  6.     {  
  7.         if(sum <= 0)  
  8.             sum = a[i];  
  9.         else  
  10.             sum += a[i];  
  11.         if (sum > max_sub_sum)  
  12.             max_sub_sum = sum;    
  13.     }  
  14.     return max_sub_sum;  
  15.   
  16. }  

【32 从1到n中1出现的次数】
//题目输入一个整数n,求从1到n这n个整数十进制表示中1出现的次数
//思路一,直接判断每个数的每一位,时间复杂度是nlgn
[cpp]  view plain copy
  1. long long  count_1s(long n)  
  2. {  
  3.     long long cc = 0;  
  4.     while(n)  
  5.     {  
  6.         if (n%10 == 1)  
  7.             cc++;  
  8.         n = n/10;  
  9.     }  
  10.     return cc;  
  11. }  
  12. long long stupid_sum1s(long n)  
  13. {  
  14.     long long count = 0;  
  15.     while(n)  
  16.     {  
  17.         count += count_1s(n);  
  18.         n--;  
  19.     }  
  20.           
  21.     return count;  
  22. }  

//思路二
//@32 从1到n整数中出现1的次数,结论:假设abcde代表整数的每一位,c是百位,百位数为1的整数的总的个数分以下几种情况
//百位为0的话,例如13045,百位上是1的数是 xx100~xx199 (x范围00~12) 所以又13*100 = 1300
//百位为1的话,例如13145,百位上是1的数是 xx100~xx199 (x范围00~12) 再加上13100~13145 一共有1300+145+1个
//百位大于1的话,如13245,百位上是1的数是 xx100~xx199 (x范围00~13) 一共有(13+1)*100个
//理清楚了上面的思路在写就很容易了 
[cpp]  view plain copy
  1. long long sum1s(long n)  
  2. {  
  3.     long long  count = 0;     
  4.     int bit = 1;  
  5.     long before, current, remain;  
  6.     while (n/bit)  
  7.     {     
  8.         before = n/(bit*10);  
  9.         current =(n/bit)%10;  
  10.         remain = n - (n/bit)*bit;  
  11.         if (current == 0)  
  12.             count += before*bit;  
  13.         if (current == 1)  
  14.             count +=(before*bit + remain + 1);  
  15.         if (current > 1)  
  16.             count += ((before + 1)*bit);  
  17.   
  18.         bit *= 10;  
  19.     }  
  20.     return count;  
  21. }  

[cpp]  view plain copy
  1. #include <iostream>  
  2. using namespace std;  
  3. void test_sum1s()  
  4. {  
  5.     std::cout<<"13的1的数目:"<<sum1s(13)<<std::endl;  
  6.     std::cout<<"23的1的数目:"<<sum1s(23)<<std::endl;  
  7.     std::cout<<"33的1的数目:"<<sum1s(33)<<std::endl;  
  8.     std::cout<<"93的1的数目:"<<sum1s(93)<<std::endl;  
  9.     std::cout<<"93的1的数目:"<<sum1s(10000000)<<std::endl;  
  10.     std::cout<<"13的1的数目:"<<stupid_sum1s(13)<<std::endl;  
  11.     std::cout<<"23的1的数目:"<<stupid_sum1s(23)<<std::endl;  
  12.     std::cout<<"33的1的数目:"<<stupid_sum1s(33)<<std::endl;  
  13.     std::cout<<"93的1的数目:"<<stupid_sum1s(93)<<std::endl;  
  14.     std::cout<<"93的1的数目:"<<stupid_sum1s(10000000)<<std::endl;  
  15.   
  16. }  

【数字在排序中出现的次数】
//@38数字在排序数组中出现的次数,注意是排序,比如1,2,3,4,4,4,4,4,6,9,10和4,返回4的个数5
//使用二分查找,a[mid]不是4的话,跟一般的二分查找没区别,当a[mid]=4时候我们需要找到的是4的左右端点,看下
//规律,最左边的端点前面的数字比这个数小,最右边的端点右边的数字比这个数大
[cpp]  view plain copy
  1. int get_last_k(int a[], int low, int high, int K)  
  2. {  
  3.     if(a == NULL || low > high)  
  4.         return -1;  
  5.     int mid = (low + high)>>1;  
  6.     if (a[mid] == K)  
  7.     {  
  8.         if (mid < high && a[mid+1] != K || mid == high)  
  9.             return mid;  
  10.         else  
  11.             low = mid + 1;  
  12.     }  
  13.     if (a[mid] > K)  
  14.         high = mid - 1;  
  15.     if(a[mid] < K)  
  16.         low = mid + 1;  
  17.     return get_last_k(a, low ,high , K);  
  18.   
  19. }  
  20. int get_first_k(int a[], int low, int high, int K)  
  21. {  
  22.     if(a == NULL || low > high)  
  23.         return -1;  
  24.     int mid = (low + high)>>1;  
  25.     if (a[mid] == K)  
  26.     {  
  27.         if (mid > 0 && a[mid-1] != K || mid == 0)  
  28.             return mid;  
  29.         else  
  30.             high = mid - 1;  
  31.     }  
  32.     if (a[mid] < K)  
  33.         low = mid + 1;  
  34.     if (a[mid] > K)  
  35.         high = mid - 1;  
  36.     return get_first_k(a, low , high , K);  
  37. }  
  38. int get_num_of_k(int a[], int len, int K)  
  39. {  
  40.     int i = get_first_k(a,0,len-1,K);  
  41.     int j  = get_last_k(a, 0, len-1, K);  
  42.     if (i == -1 || j == -1)  
  43.         return -1;  
  44.     else  
  45.         return j - i + 1;  
  46. }  
  47.   
  48. void test_get_num_of_K()  
  49. {  
  50.     int a[] = {1,2,3,4,4,4,5,6,7,8};  
  51.     int a1[] = {4,4,4,4,4,4,4,4,4,4};  
  52.     int a2[] = {0,1,3,5,7,8,9,89,98,32};  
  53.     cout<<get_num_of_k(a, sizeof(a)/sizeof(a[0]), 4)<<endl;  
  54.     cout<<get_num_of_k(a1, sizeof(a1)/sizeof(a1[0]), 4)<<endl;  
  55.     cout<<get_num_of_k(a2, sizeof(a2)/sizeof(a2[0]), 4)<<endl;  
  56.   
  57. }  
【数组中出现一次的两个数】
//@40数组中只出现一次的数,一个数组中,找出只出现一次的两个数,除这两个数以外,其余的数都出现两次。
//要求时间复杂度是O(n),空间复杂度是O(1)
//假设数组中只有一个这种元素,使用异或就可以搞定,因为数组中相同的元素异或为0,最终得到的结果肯定是那个出现一次的元素
//两个呢?两个最后异或的结果肯定不是0,是两个元素的结果,有什么规律呢?
//比如5,和7,0101^0111 = 0010,按照这个思路来说,0101和0111的二进制表示的倒数第二位肯定不一样
//这样,原数组中可以按照倒数第二位是1或者0来分成两组,两个组内再分别异或,得到的两个结果肯定是5和7
[cpp]  view plain copy
  1. void find_num_appear_once(int a[], int len, int& diff_1, int& diff_2)  
  2. {  
  3.     if (a == NULL || len < 2 )  
  4.         return;   
  5.     diff_1 = diff_2 = 0;  
  6.     int bit_to_shift = 0;  
  7.     int tmp_or_res = 0;  
  8.     for (int i = 0; i < len; i++)  
  9.         tmp_or_res ^= a[i];  
  10.     while((tmp_or_res & 1) == 0)  
  11.     {  
  12.         tmp_or_res = tmp_or_res>>1;  
  13.         bit_to_shift++;  
  14.     }  
  15.     tmp_or_res = 1<<bit_to_shift;  
  16.     for (int i = 0; i < len; i++)  
  17.     {  
  18.         if((a[i] & tmp_or_res) == 0)  
  19.             diff_1 ^= a[i];  
  20.         else  
  21.             diff_2 ^= a[i];  
  22.     }  
  23. }  
  24.   
  25.   
  26.   
  27.   
  28. void test_find_num_appear_once()  
  29. {  
  30.     int a, b;  
  31.     int array[] = {2,4,3,6,3,2,5,5};  
  32.     int array1[] = {4, 6};  
  33.     int array2[] = {4, 6, 1, 1, 1, 1};  
  34.     find_num_appear_once(array, 8, a, b);  
  35.     find_num_appear_once(array1, 2, a, b);  
  36.     find_num_appear_once(array2, 6, a, b);  
  37. }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值