《编程之法》-第二章

来源:互联网 发布:小程序 json 双引号 编辑:程序博客网 时间:2024/05/16 09:42

1、寻找最小的 k 个数
输入n个整数,输出其中最小的k个。

2、 寻找和为定值的两个数(3-sum、4-sum)
输入一个数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字。 要求时间复杂度是O(N)。如果有多对数字的和等于输入的数字,输出任意一对即可。 例如输入数组1、2、4、7、11、15和数字15。由于4+11=15,因此输出4和11。

法一:如果数组是无序的,先排序(N log N),然后用两个指针i,j,各自指向数组的首尾两端,令i=0,j=n-1, 然后i++,j–,逐次判断a[i]+a[j]?=sum,如果某一刻a[i]+a[j] > sum,则要想办法让sum的值减小,所以此刻i不动,j–; 如果某一刻a[i]+a[j] < sum,则要想办法让sum的值增大,所以此刻i++,j不动。所以,数组无序的时候,时间复杂度最终为O(N log N + N)=O(N log N)。如果原数组是有序的,则不需要事先的排序,直接用两指针分别从头和尾向中间扫描,O(N)搞定,且空间 复杂度还是O(1)。下面,咱们先来实现此思路(这里假定数组已经是有序的),代码可以如下编写:

void TwoSum(int data[], unsigned int length, int sum) {    //sort(s, s+n); 如果数组非有序的,那就事先排好序O(N log N)    int begin = 0;    int end = length - 1;        //俩头夹逼,或称两个指针两端扫描法,很经典的方法,O(N)    while(begin < end){        long currSum = data[begin] + data[end];        if (currSum == sum) {        //题目只要求输出满足条件的任意一对即可 printf("%d %d\n", data[begin], data[end]);        //如果需要所有满足条件的数组对,则需要加上下面两条语句:         //begin++        //end--        break;        } else{        if (currSum < sum) begin++;        else end--;        } }    }

法二:当题目对时间复杂度要求比较严格时,我们可以考虑下用空间换时间,上述解法一即是此思想,此外,构 造hash表也是典型的用空间换时间的处理办法。即给定一个数字,根据hash映射查找另一个数字是否也在数组中,只需用O(1)的时间,前提是经过O(N)时 间的预处理,和用O(N)的空间构造hash表。

3、 寻找和为定值的多个数
输入两个整数n和sum,从数列1,2,3…….n 中随意取几个数,使其和等于sum,要求将其中所有的可能 组合列出来。
解法一:递归方法,注意到取n和不取n个区别即可,考虑是否取第n个数的策略,可以转化为一个只和前n-1个数相关的问 题。
定义递归函数void SumOfkNumber(int sum, int n) 。
如果取第n个数,那么问题就转化为“取前n-1个数使得它们的和为sum-n”,对应的代码语句就是 sumOfkNumber(sum - n, n - 1); 如果不取第n个数,那么问题就转化为“取前n-1个数使得他们的和为sum”,对应的代码语句为 sumOfkNumber(sum, n - 1)。

4、 最大连续子数组和
输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组 都有一个和。 求所有子数组的和的最大值,要求时间复杂度为O(n)。

result = a[1]sum = a[1]for i: 2 to LENGTH[a]  if sum > 0    sum += a[i]  else    sum = a[i]  if sum > result    result = sumreturn result

4.1、给定整型数组,其中每个元素表示木板的高度,木板的宽度都相同,求这些木板拼出的最大矩形的面 积。并分析时间复杂度。
解析:请看我的这篇博文

4.3、最大子矩阵和
一个M_N的矩阵,找到此矩阵的一个子矩阵,并且这个子矩阵的元素的和是最大的,输出这个最大的值。 如果所有数都是负数,就输出0。 例如:3_5的矩阵:
1 2 0 3 4
2 3 4 5 1
1 1 5 3 0
和最大的子矩阵是:
4 5
5 3
最后输出和的最大值17。
解析:这篇文章总结的很好,我就不再赘述了

5、跳台阶
一个台阶总共有n 级,如果一次可以跳1 级,也可以跳2 级。 求总共有多少总跳法,并分析算法的时间复杂度。
解法一:首先考虑最简单的情况。如果只有1级台阶,那显然只有一种跳法。如果有2级台阶,那就有两种跳的方法 了:一种是分两次跳,每次跳1级;另外一种就是一次跳2级。
现在我们再来讨论一般情况。我们把n级台阶时的跳法看成是n的函数,记为f(n)。
当n>2时,第一次跳的时候就有两种不同的选择: (1)一是第一次只跳1级,此时跳法数目等于后面剩下的n-1级台阶的跳法数目,即为f(n-1);(2) 另外一种选择是第一次跳2级,此时跳法数目等于后面剩下的n-2级台阶的跳法数目,即为f(n-2)。
因此n级台阶时的不同跳法的总数f(n)=f(n-1)+f(n-2)。

解法二:上述方法中用的递归的方法有许多重复计算的工作,事实上,我们可以从后往前推,一步步利用之前计算的结 果递推。
初始化时,dp[0]=dp[1]=1,然后递推计算即可:dp[n] = dp[n-1] + dp[n-2]。

int ClimbStairs(int n){    int dp[3] = { 1, 1 };    if (n < 2)    {        return 1;    }    for (int i = 2; i<n; i++){        dp[2]=dp[0]+dp[1];        dp[1]=dp[0];        dp[0]=dp[2];    }    return dp[2];}

6、奇偶排序
输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后 半部分。要求时间复杂度为O(n)。
方法一:维护两个指针,一个指针指向数组的第一个数字,我们称之为头指 针,向右移动;一个指针指向最后一个数字,称之为尾指针,向左移动。这样,两个指针分别从数组的头部和尾部向数组的中间移动,如果第一个指针指向的数字是偶数而第二个 指针指向的数字是奇数,我们就交换这两个数字。
因为按照题目要求,最终是为了让奇数排在数组的前面,偶数排在数组的后面,所以头指针理应指向的就 是奇数,尾指针理应指向的就是偶数,故当头指针指向的是偶数且尾指针指向的是奇数时,我们就当立即 交换它们所指向的数字。

bool IsOddNumber(int data) {return data & 1 == 1; }//交换两个元素void swap(int* x, int* y) {int temp = *x; *x = *y;*y = temp;}//奇偶互换void OddEvenSort(int *pData, unsigned int length) {if (pData == NULL || length == 0) return;int *pBegin = pData;int *pEnd = pData + length - 1;while (pBegin < pEnd) {//如果pBegin指针指向的是奇数,正常,向右移 if (IsOddNumber(*pBegin)){pBegin++; }//如果pEnd指针指向的是偶数,正常,向左移 else if (!IsOddNumber(*pEnd)){pEnd--; }else {//否则都不正常,交换swap(*pBegin, *pEnd); }} }

本方法通过头尾两个指针往中间扫描,一次遍历完成所有奇数偶数的重新排列,时间复杂度为O(n)。
此题可联想到保持相对位置不变的解法。

7、荷兰国旗
现有n个红白蓝三种不同颜色的小球,乱序排列在一起,请通过两两交换任意两 个球,使得从左至右,依次是一些红球、一些白球、一些蓝球。
解析:通过前面的分析得知,这个问题类似快排中partition过程,只是需要用到三个指针:一个前指针begin, 一个中指针current,一个后指针end,current指针遍历整个数组序列,当
(1). current指针所指元素为0时,与begin指针所指的元素交换,而后current++,begin++ ; 2. current指针所指元素为1时,不做任何交换(即球不动),而后current++ ;
(2). current指针所指元素为2时,与end指针所指的元素交换,而后,current指针不动,end– 。
为什么上述第3点中,current指针所指元素为2时,与end指针所指元素交换之后,current指针不能动 呢?因为第三步中current指针所指元素与end指针所指元素交换之前,如果end指针之前指的元素是0,那 么与current指针所指元素交换之后,current指针此刻所指的元素是0,此时,current指针能动么?不能 动,因为如上述第1点所述,如果current指针所指的元素是0,还得与begin指针所指的元素交换。

8、矩阵相乘
根据wikipedia上的介绍:两个矩阵的乘法仅当第一个矩阵A的行数和另一个矩阵B的列数相等时才能定 义。如A是m×n矩阵,B是n×p矩阵,它们的乘积AB是一个m×p矩阵,它的一个元素其中 1 ≤ i ≤ m, 1 ≤ j ≤ p。
解法一:暴力,时间复杂度O(N^3)

void MulMatrix(int** matrixA, int** matrixB, int** matrixC) {for(int i = 0; i < 2; ++i) {for(int j = 0; j < 2; ++j) {matrixC[i][j] = 0;for(int k = 0; k < 2; ++k) {matrixC[i][j] += matrixA[i][k] * matrixB[k][j]; }} }}

解法二:本题有个时间复杂度为O(N^2.80)的算法,自己度娘吧

0 0
原创粉丝点击