【数组相关的题目】
最近投简历,面试耽误不少进度,有些迷茫,不知道自己究竟适不适合程序员这行业,半道出家的娃只能且走且看,算法也只是突击了两个月,心里实在没地,觉得要是拿到那些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头指针,中间指针和尾指针
//如果中间的数字大于头指针,那么证明最小的数在后半序列,在对后半序列进行递归
递归写法
int get_min_reverse_array(int a[], int low, int high)
{
if (high - low == 1)
return a[high];
int mid = (low + high)/2;
if (a[mid] > a[low])
return get_min_reverse_array(a, mid, high);
if (a[mid] < a[high])
return get_min_reverse_array(a, low, mid);
}
//非递归写法
int solu2_get_min_reverse_array(int a[], int len)
{
int low = 0;
int high = len - 1;
int mid;
while(a[low] >= a[high])
{
if (high - low == 1)
{
mid = high;
break;
}
mid = (low+high)/2;
if (a[mid] > a[low])
low = mid;
if (a[mid] < a[high])
high = mid;
}
return a[mid];
}
//另外,上面写法不完整,当碰见1 0 1 1 1的时候也就是三个指针的数相同时候,只能遍历找到最小的数
【31连续子数列的最大和】
//题目:输入一个整数,里面有正有负,数组中一个或者多个连续数形成一个子序列,
//求所有子数组中和的最大值,要求时间复杂度为O(n),这个需要慢慢分析,
//主要考虑前面累加的和为负值跟当前元素的关系,碰见负值如何处理
int get_max_subarray(int a[], int len)
{
int sum = 0;
int max_sub_sum = 0x80000000;//最小的负值
for (int i = 0; i < len; i++)
{
if(sum <= 0)
sum = a[i];
else
sum += a[i];
if (sum > max_sub_sum)
max_sub_sum = sum;
}
return max_sub_sum;
}
【32 从1到n中1出现的次数】
//题目输入一个整数n,求从1到n这n个整数十进制表示中1出现的次数 //思路一,直接判断每个数的每一位,时间复杂度是nlgn
long long count_1s(long n)
{
long long cc = 0;
while(n)
{
if (n%10 == 1)
cc++;
n = n/10;
}
return cc;
}
long long stupid_sum1s(long n)
{
long long count = 0;
while(n)
{
count += count_1s(n);
n--;
}
return count;
}
//思路二 //@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个
//理清楚了上面的思路在写就很容易了
long long sum1s(long n)
{
long long count = 0;
int bit = 1;
long before, current, remain;
while (n/bit)
{
before = n/(bit*10);
current =(n/bit)%10;
remain = n - (n/bit)*bit;
if (current == 0)
count += before*bit;
if (current == 1)
count +=(before*bit + remain + 1);
if (current > 1)
count += ((before + 1)*bit);
bit *= 10;
}
return count;
}
#include <iostream>
using namespace std;
void test_sum1s()
{
std::cout<<"13的1的数目:"<<sum1s(13)<<std::endl;
std::cout<<"23的1的数目:"<<sum1s(23)<<std::endl;
std::cout<<"33的1的数目:"<<sum1s(33)<<std::endl;
std::cout<<"93的1的数目:"<<sum1s(93)<<std::endl;
std::cout<<"93的1的数目:"<<sum1s(10000000)<<std::endl;
std::cout<<"13的1的数目:"<<stupid_sum1s(13)<<std::endl;
std::cout<<"23的1的数目:"<<stupid_sum1s(23)<<std::endl;
std::cout<<"33的1的数目:"<<stupid_sum1s(33)<<std::endl;
std::cout<<"93的1的数目:"<<stupid_sum1s(93)<<std::endl;
std::cout<<"93的1的数目:"<<stupid_sum1s(10000000)<<std::endl;
}
【数字在排序中出现的次数】
//@38数字在排序数组中出现的次数,注意是排序,比如1,2,3,4,4,4,4,4,6,9,10和4,返回4的个数5
//使用二分查找,a[mid]不是4的话,跟一般的二分查找没区别,当a[mid]=4时候我们需要找到的是4的左右端点,看下
//规律,最左边的端点前面的数字比这个数小,最右边的端点右边的数字比这个数大
int get_last_k(int a[], int low, int high, int K)
{
if(a == NULL || low > high)
return -1;
int mid = (low + high)>>1;
if (a[mid] == K)
{
if (mid < high && a[mid+1] != K || mid == high)
return mid;
else
low = mid + 1;
}
if (a[mid] > K)
high = mid - 1;
if(a[mid] < K)
low = mid + 1;
return get_last_k(a, low ,high , K);
}
int get_first_k(int a[], int low, int high, int K)
{
if(a == NULL || low > high)
return -1;
int mid = (low + high)>>1;
if (a[mid] == K)
{
if (mid > 0 && a[mid-1] != K || mid == 0)
return mid;
else
high = mid - 1;
}
if (a[mid] < K)
low = mid + 1;
if (a[mid] > K)
high = mid - 1;
return get_first_k(a, low , high , K);
}
int get_num_of_k(int a[], int len, int K)
{
int i = get_first_k(a,0,len-1,K);
int j = get_last_k(a, 0, len-1, K);
if (i == -1 || j == -1)
return -1;
else
return j - i + 1;
}
void test_get_num_of_K()
{
int a[] = {1,2,3,4,4,4,5,6,7,8};
int a1[] = {4,4,4,4,4,4,4,4,4,4};
int a2[] = {0,1,3,5,7,8,9,89,98,32};
cout<<get_num_of_k(a, sizeof(a)/sizeof(a[0]), 4)<<endl;
cout<<get_num_of_k(a1, sizeof(a1)/sizeof(a1[0]), 4)<<endl;
cout<<get_num_of_k(a2, sizeof(a2)/sizeof(a2[0]), 4)<<endl;
}
【数组中出现一次的两个数】 //@40数组中只出现一次的数,一个数组中,找出只出现一次的两个数,除这两个数以外,其余的数都出现两次。
//要求时间复杂度是O(n),空间复杂度是O(1)
//假设数组中只有一个这种元素,使用异或就可以搞定,因为数组中相同的元素异或为0,最终得到的结果肯定是那个出现一次的元素
//两个呢?两个最后异或的结果肯定不是0,是两个元素的结果,有什么规律呢?
//比如5,和7,0101^0111 = 0010,按照这个思路来说,0101和0111的二进制表示的倒数第二位肯定不一样
//这样,原数组中可以按照倒数第二位是1或者0来分成两组,两个组内再分别异或,得到的两个结果肯定是5和7
void find_num_appear_once(int a[], int len, int& diff_1, int& diff_2)
{
if (a == NULL || len < 2 )
return;
diff_1 = diff_2 = 0;
int bit_to_shift = 0;
int tmp_or_res = 0;
for (int i = 0; i < len; i++)
tmp_or_res ^= a[i];
while((tmp_or_res & 1) == 0)
{
tmp_or_res = tmp_or_res>>1;
bit_to_shift++;
}
tmp_or_res = 1<<bit_to_shift;
for (int i = 0; i < len; i++)
{
if((a[i] & tmp_or_res) == 0)
diff_1 ^= a[i];
else
diff_2 ^= a[i];
}
}
void test_find_num_appear_once()
{
int a, b;
int array[] = {2,4,3,6,3,2,5,5};
int array1[] = {4, 6};
int array2[] = {4, 6, 1, 1, 1, 1};
find_num_appear_once(array, 8, a, b);
find_num_appear_once(array1, 2, a, b);
find_num_appear_once(array2, 6, a, b);
}