一些数组的算法题

1、快速找出一个数组中的最大数、第二大数。

  思路:如果当前元素大于最大数 max,则让第二大数等于原来的最大数 max,再把当前元素的值赋给 max。如果当前的元素大于等于第二大数secondMax的值而小于最大数max的值,则要把当前元素的值赋给 secondMax。
void GetSecondMaxNumber(int *arr , int n)
{
	int i , max , second_max;
	max = arr[0];
	second_max = 0x80000000;
	for(i = 1 ; i < n ; ++i)
	{
		if(arr[i] > max)
		{
			second_max = max;
			max = arr[i];
		}
		else
		{
			if(arr[i] > second_max)
				second_max = arr[i];
		}
	}
	cout<<max<<"  "<<second_max<<endl;
}




2、试着用最小的比较次数去寻找数组中的最大值和最小值。

每次比较相邻两个数,较大者与MAX比较,较小者与MIN比较,找出最大值和最小值。
方法如下:先将一对元素互相进行比较,然后把最小值跟当前最小值进行比较,把最大值跟当前最大值进行比较。因此每两个元素需要3次比较。如果n为奇数,那么比较的次数是3*(n/2)次比较。如果n为偶数,那么比较的次数是3n/2-2次比较。因此,不管是n是奇数还是偶数,比较的次数至多是3*(n/2),具体的代码如下:

void GetMaxAndMin(int *arr , int n , int &max , int &min)
{
	int i = 0 ;
	if(n & 1)     // 奇数
	{
		max = min = arr[i++];
	}
	else
	{
		if(arr[0] > arr[1])
		{
			max = arr[0];
			min = arr[1];
		}
		else
		{
			max = arr[1];
			min = arr[0];
		}
		i += 2;
	}
	
	for( ; i < n ; i += 2)
	{
		if(arr[i] > arr[i+1])
		{
			if(arr[i] > max)
				max = arr[i];
			if(arr[i+1] < min)
				min = arr[i+1];
		}
		else
		{
			if(arr[i+1] > max)
				max = arr[i+1];
			if(arr[i] < min)
				min = arr[i];
		}
	}
}

3、重排问题

给定含有n个元素的整型数组a,其中包括0元素和非0元素,对数组进行排序,要求:
1、排序后所有0元素在前,所有非零元素在后,且非零元素排序前后相对位置不变
2、不能使用额外存储空间
例子如下
输入 0、3、0、2、1、0、0
输出 0、0、0、0、3、2、1
分析
此排序非传统意义上的排序,因为它要求排序前后非0元素的相对位置不变,或许叫做整理会更恰当一些。我们可以从后向前遍历整个数组,遇到某个位置i上的元素是非0元素时,如果arr[k]为0,则将arr[i]赋值给arr[k],arr[i]赋值为0。实际上i是非0元素的下标,而k是0元素的下标。
void Arrange(int *arr , int n)
{
	int i , k = n-1;
	for(i = n-1 ; i >=0 ; --i)
	{
		if(arr[i] != 0)
		{
			if(arr[k] == 0)
			{
				arr[k] = arr[i];
				arr[i] = 0;
			}
			--k;
		}
	}
}

4、找出绝对值最小的元素

给定一个有序整数序列(非递减序),可能包含负数,找出其中绝对值最小的元素,比如给定序列 -5、-3、-1、2、8 则返回1。
分析:由于给定序列是有序的,而这又是搜索问题,所以首先想到二分搜索法,只不过这个二分法比普通的二分法稍微麻烦点,可以分为下面几种情况
    如果给定的序列中所有的数都是正数,那么数组的第一个元素即是结果。
    如果给定的序列中所有的数都是负数,那么数组的最后一个元素即是结果。
    如果给定的序列中既有正数又有负数,那么绝对值的最小值一定出现在正数和负数的分界处。
为什么?因为对于负数序列来说,右侧的数字比左侧的数字绝对值小,如上面的-5、-3、-1,而对于整整数来说,左边的数字绝对值小,比如上面的2、8,将这个思想用于二分搜索,可先判断中间元素和两侧元素的符号,然后根据符号决定搜索区间,逐步缩小搜索区间,直到只剩下两个元素。
单独设置一个函数用来判断两个整数的符号是否相同


bool SameSign(int m , int n)
{
	if((m>>31) == (n>>31))
		return true;
	else
		return false;
}

// 找出一个非递减序整数序列中绝对值最小的数
int MiniNumAbsoluteValue(int *arr , int n)
{
	if(n == 1)
		return arr[0];
	if( SameSign(arr[0] , arr[n-1]) )
		return arr[0] >= 0 ? arr[0] : abs(arr[n-1]);

	// 二分搜索
	int mid , low = 0 , high = n - 1;
	while(low < high)
	{
		if(low + 1 == high)
			return abs(arr[low]) < abs(arr[high]) ? abs(arr[low]) : abs(arr[high]);
		mid = (low + high)>>1;
		if( SameSign(arr[low] , arr[mid]) )
		{
			low = mid ;     // 有可能分界点就在mid处
			continue;
		}
		if( SameSign(arr[high] , arr[mid]) )
		{
			high = mid;
			continue;
		}
	}
	return abs(arr[low]);
}

5、一道经典的额递归题目
函数 int func(int i ,int N);
其中i <= N,功能输出i递增到N再递减到i的整数,每行输出一个数。比如func(1,5)就是
1
2
3
4
5
4
3
2
1
要求:
1、只能有1个语句,即一个分号
2、不能使用do while until goto for if关键字,不能使用?:和逗号运算符
3、唯一能使用的库函数为printf 

int func(int i , int n)
{
	return (i==n && printf("%d\n",i)) || (printf("%d\n",i) && func(i+1,n) && printf("%d\n",i));
}

6、三色旗排序问题
一个字符串Color,其中每个元素值为‘R‘’G’‘B’三者之一,实现把数组中元素重新排列为红、绿、蓝的顺序,所有红色在前,绿色其后,蓝色最后,要如何移动次数才会最少,编写这么一个程序。
问题的解法很简单,您可以自己想像一下在移动旗子,从绳子开头进行,遇到红色往前移,遇到绿色留在中间,遇到蓝色往后移。
设有三个指针rindex、gindex、bindex,其中gindex来遍历这个数组序列
1、gindex指向G的时候,gindex++,
2、gindex指向R的时候,与rindex交换,而后gindex++,rindex++,
3、gindex指向B的时候,与bindex交换,而后,gindex不动,bindex--。
    为什么,第三步,gindex指向B的时候,与bindex交换之后,gindex不动。
    因为你最终的目的是为了让R、G、B成为有序排列,试想,如果第三步,gindex与bindex交换之前,万一bindex指向的是R,而gindex交换之后,gindex此刻指向的是R了,此时,gindex能动么?不能动啊,指向的是R,还得与rindex交换。
// 三色旗排序问题
// char str[] = "RGRBRB";
void mysort(char *str , int n)
{
    int rindex = 0 , gindex = 0 , bindex = n - 1 ;
    while(gindex <= bindex)
    {
        if(str[gindex] == 'G')
            ++gindex;
        else if(str[gindex] == 'R')
        {
            swap(str[gindex] , str[rindex]);
            ++rindex , ++gindex;
        }
        else           // str[gindex] == 'B'
        {
            swap(str[gindex] , str[bindex]);
            --bindex;
            //++gindex;
        }
    }
}
7、字符串移动
字符串为*号和26个字母、阿拉伯数字的任意组合,把*号都移动到最左侧,把其他字母和数字都移到最右侧并保持相对顺序不变,返回字符*的个数,要求时间和空间复杂度最小。
第一种方法:跟上面的重排问题是一样的
int MoveStar(char *str , int n)
{
	int i , j = n-1;
	for(i = n - 1 ; i >= 0 ; --i)
	{
		if(str[i] != '*')
		{
			if(str[j] == '*')
			{
				str[j] = str[i];
				str[i] = '*';
			}
			--j;
		}
	}
	return j+1;
}

8、求数组中两个元素差的最大值
后面的元素减去前面的元素(你可以认为你在炒股票,买入价格和卖出价格就是你的盈利),要求:O(N)时间复杂度,O(1)空间复杂度 
思路:首先从包含两个元素的数组开始考虑问题,当这个包含两个元素的问题解决了,那么加一个新的元素会造成什么影响?要改变哪些值?每次都添加一个元素,每次都将这些可能的改变考虑进来,这样就能做到遍历整个数组的时候,问题就解决了。
// 后面的元素减去前面的元素 差的最大值
int max_difference(int *arr , int n)
{
	if(arr == NULL || n < 2)    // 非法输入
		return 0;
	int min = arr[0];
    int maxDiff = arr[1] - arr[0];
	for(int i = 2 ; i < n ; ++i)
	{
		if(arr[i-1] < min)
			min = arr[i-1];
		if(arr[i] - min > maxDiff)
			maxDiff = arr[i] - min;
	}
	return maxDiff;
}

9、输入一个正数 n,输出所有和为 n 连续正数序列。
例如输入 15,由于 1+2+3+4+5=4+5+6=7+8=15,所以输出 3 个连续序列 1-5、4-6 和 7-8。 
方法一:
可以发现任意自然数序列其实是公差为1的等差数列,假设从i开始的连续k个数的和为n,即[i , i+k-1],则n=k*(2*i+k-1)/2,所以转化为一元二次方程为:k*k+(2*i-1)*k-2*n=0,解得k=(1-2*i+sqrt((2*i-1)*(2*i-1)+8*n))/2
要满足k为整数,根号里面的肯定必须是平方数,且分子为偶数,否则k就是小数。

//输入一个正数 n,输出所有和为n 连续正数序列。
void plusSequence(int n)
{
	int i , j , k , m;
	double num;
	for(i = 1 ; i <= n/2 ; ++i)
	{
		m = (2*i-1)*(2*i-1) + 8*n;
		num = sqrt(m * 1.0);
		if(num != (int)num)
			continue;
		k = 1 - 2*i + (int)num;
		if(0 == (k&1) && k > 0)
		{
			for(j = 0 ; j < k/2 ; ++j)
				printf("%d",i + j);
			printf("\n");
		}
	}
}





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值