关于数组的面试笔试题
来源:互联网 发布:vb应用程序 编辑:程序博客网 时间:2024/05/22 01:01
1.求数组中最长递增子序列LIS的长度
如1,-1,2,-3,4,-5,6,7得到1,2,4,6长度为4
动态规划:LIS[i+1] = max{1, LIS[k] + 1},array[i + 1] > array[k], for any k <= i
public static int LIS(int[] a){int[] lis = new int[a.length];//lis[i]表示前i元素的最长递增子序列的长度int maxLength = 1;for(int i = 0; i < a.length; i++){lis[i] = 1;for(int j = 0; j < i; j ++){if (a[i] > a[j] && lis[j] + 1 > lis[i]){lis[i] = lis[j] + 1;}}}for (int i : lis){if (i > maxLength){maxLength = i;}}return maxLength;}时间复杂度O(N2),空间复杂度O(N)
2.数组循环移位:将一个含有N个元素的数组循环右移K位,时间复杂度O(N),只允许使用两个附加变量
tips:
1)每个元素右移N位后都会回到自己的位置上
2)abcd1234->1234abcd
逆序abcd:abcd1234->dcba1234
逆序1234:dcba1234->dcba4321
全部逆序dcba4321->1234abcd
public void rightShift(int[] a, int k){int n = a.length;k %= n;reverse(a, 0, k - 1);reverse(a, k, n - 1);reverse(a, 0, n - 1);}public void reverse(int[] a, int left, int right){int temp = 0;while (left <= right){temp = a[left];a[left++] = a[right];a[right--] = temp;}}
3.求数组的连续子数组之和的最大值
动态规划:
数组的第一个元素A[0]以及最大的一段数组(A[i],...,A[j])的之间的关系:
1)当0 = i = j , A[0]本身构成和最大的一段;
2)当0 = i < j , 和最大的一段以A[0]开始;
3)当0 < i , 元素A[0]跟和最大的一段没有关系
A[0],...A[n - 1] 中问题的解All[0] = max{A[0], A[0] + Start[1], All[1]}
public int maxSum(int[] a){int nStart = a[a.length - 1];int nAll = nStart;for(int i = a.length - 2; i >=0; i--){nStart = Math.max(a[i], nStart + a[i]);nAll = Math.max(nStart, nAll);}return nAll;}
时间复杂度O(N),空间复杂度O(1)
4.找出数组中两个数之和等于一个给定值,时间复杂度O(n)
1)对数组进行排序
2)令i = 1, j = n - 1, 看a[i] + a[j] 是否等于sum,若等于,则结束;若小于sum,则i = i + 1; 若大于sum,则j = j - 1
伪代码:
searchNumbers(int[] a, int sum){quickSort();for(int i = 0, j = a.length - 1; i < j;){if (a[i] + a[j] == sum){return (i,j);}else if (a[i] + a[j] < sum) {i ++;}else {j --;}}return (-1, -1);}
延伸题:输入一个正数s,打印出所有和为s的连续整数序列(至少有2个数),例如输入15,1+2+3+4+5 = 4+5+6 = 7 + 8
分析:使用small和big初始化1和2,如果从small到big的序列和小于s,增大big,让这个序列包含更多的数字;如果和大于s,则去掉small的值,即增大small,一直到small到(1+s)/2为止。
public void findContinuousSequeue(int sum){if (sum < 3){return;}int small = 1, big = 2;int middle = (1 + sum) / 2;int curSum = small + big;//在前一个序列的和的基础上求操作之后的序列的和while (small < middle){if (curSum == sum){printContinuesSequeue(small, big);}while(curSum > sum && small < middle)//直到cursum小于等于sum,big再增加{curSum -= small;small++;if (curSum== sum){printContinuesSequeue(small, big);}}big++;curSum += big;}}
5.寻找数组中的最大值和最小值
method1:利用max和min存储当前最大值和最小值,奇数位和偶数位相比完再分别和max和min比,只用遍历一次
method2:分治算法:只需分别求出前后N/2个数的Min和Max,然后取较小的Min和较大的Max
伪代码:
(max, min) SearchMaxAndMin(int[] a, int left, int right){if (left - right <= 1){if (a[left] < a[right]){return (a[right], a[left]);}else return (a[left], a[right]);}(maxLeft, minLeft) = SearchMaxAndMin(a, left, (left + right) / 2);(maxRight, minRight) = SearchMaxAndMin(a, (left + right) / 2 + 1, right);if (maxLeft > maxRight){maxV = maxLeft;}else {maxV = maxRight;}if (minLeft < minRight){minV = minLeft;}else {minV = minRight;}return (maxV, maxRight);}复杂度1.5N
6.求数组中出现次数超过一半的数,时间复杂度O(N),空间复杂度O(1)
使用一个计数器,如果下一个数和本数相同则counter++,若不相同则counter--;如果counter等于0,则number设置为该下标对应的数,因为其出现次数大于一半,所有counter肯定>0
public int find(int[] a){int number = -1, count = 0;for(int i = 0; i < a.length; i++){if (count == 0){number = a[i];count = 1;}else {if (number == a[i]){count++;}elsecount--;}}return number;}
7.子数组的最大乘积:计算任意N-1个数的组合中乘积最大的一组
解法一:
空间换时间
S[i]表示数组前i个元素的乘积,S[i] = a[0]*a[1]*...*a[i-1];s[0] = 1;
t[i]表示数组后N-i个元素的乘积,t[i] = a[i]*...*a[n-1];t[n+1] = 1;
P[i]表示数组除i个元素外,其他N-1个元素的乘积
P[i] = S[i] * t[i + 1]
public long[] multiply(long[] arrayA){if (arrayA == null || arrayA.length == 0){return null;}int length = arrayA.length;long[] arrayB = new long[length];for(int i = 0; i < length; i++)arrayB[i] = 1;for(int i = 1; i < length; i++)arrayB[i] = arrayB[i - 1] * arrayA[i - 1];//slong temp = 1;for(int i = length - 2; i > length; i--){temp *= arrayA[i + 1];//tarrayB[i] *= temp;//p}return arrayB;}
时间复杂度O(N)
解法二:
假设N个整数的乘积为P,N-1个整数的乘积为Q,对P的正负性进行分析
1)P为0
数组中至少有一个0:
若Q为0,数组中至少有两个0,那么N-1个数的乘积只能为0,返回0;
若Q为正,返回Q,因为以0替换此时其中任何一个数,所得Q’必然为0;
若Q为负,返回0,因为以0替换此时其中任何一个数,所得Q’必然为0;
2)P为负数
把绝对值最小的负数去掉;
3)P为正数
如果数组中存在正数,应该去掉最小的正整数,否则去掉绝对值最大的负整数;
tips:求n个数的乘积会有溢出风险->求出数组中正数、负数、0的个数
8.数组分割:将元素个数为2n的正整数数组分割为元素个数为n的两个数组,并使两个子数组的和最接近
动态规划:
解法一:把任务分成2N步,第k步定义为前k个元素中任意i个元素的和,所有可能的取值之集合为Sk,0<i<=n,
定义Heap[i]表示存储从a中取i个数所能产生的和之集合的堆
伪代码:
for(int k = 1; k <= 2 * n; k++){int i_max = min(k -1 , n - 1);for(int i = i_max; i >= 0; i--){for each v in heap[i]insert(v + a[k], heap[i + 1]);}}时间复杂度O(2^N)
解法二:
给定Sk的可能值v和a[k],查找v-a[k]是否在Sk-1中
isOk[i][v]表示是否可以找到i个数,使得它们之和等于v
伪代码:
for(int k = 1; k <= 2 * n; k ++){for(int i = min(k,n); i >= 1; i--){for(int v = 1; v < sum / 2; v++){if (v > a[k] && isOk[i - 1][v - a[k]]){isOk[i][v] = true;}}}}时间复杂度O(N2*sum)
9.给出平面上N个点的坐标,找出距离最近的两个点
分治算法:根据水平方向的坐标x=M把平面上的N个点分成Left和Right两部分,求出MinDist(Left)和MinDist(Right),
再求出M-dist < x<M-dist,dist = min(MinDist(Left),MinDist(Right))之间的最小点对;然后和dist比较
tips:如果一个点对的距离小于dist,则它们一定在dist*(2dist)的区域内,一个dist*(2dist)的区域内最多有8个点,对于任意一个带状区域内的顶点,只要考察它与按Y坐标排序且紧接着的7个店之间的距离就可以了。
f(N) = 2*f(N/2) + O(N),(N>2)
时间复杂度:O(NlogN)
10.在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下的顺序排序。请完成这样一个函数,public boolean(int[][] a, number)
解法:首先选取数组中右上角(或左下角)的数字,如果该数字等于要查找的数字,查找过程结束;如果该数字大于要查找的数字,剔除这个数字所在列;如果该数字小于要查找的数字,剔除这个数字所在的行。
public static boolean find(int[][] a, int number){boolean found = false;if (a != null && a.length > 0){int row = 0;int column = a[row].length - 1;while (row < a.length && column > 0){column = Math.min(a[row].length - 1, column);//不规则数组if (a[row][column] == number){found = true;break;}else if (a[row][column] > number){column--;}elserow++;}}return found;}
11.递增旋转数组的最小数字,旋转:将一个数组最开始的若干个元素搬到数组的末尾。如{1,2,3,4,5},{3,4,5,1,2}
二分查找:二个排过序的子数组
public int MinMumberInRotateArray(int[] a){if (a == null){try{throw new Exception("Invalid parameters");}catch (Exception e){e.printStackTrace();}}int i = 0, j = a.length - 1;int mid = i;while (a[i] > a[j]){if (j - i == 1){mid = j;break;}mid = (i + j) / 2;//如果下标为i,mid,j指向的三个数字相等,只能顺序查找if (a[i] == a[j] && a[j] == a[mid]){int result = a[i];for (int k = i + 1; k <= j; k++){if (result < a[k]){result = a[i];}}return result;}if (a[i] <= a[mid]){i = mid;}else if (a[mid] <= a[j]){j = mid;}}return a[mid];}
12.输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,偶数位于数组的后半部分;
分析:维护两个指针,第一个指针指向数组的第一个元素,指向的元素是奇数时移动;第二个指针指向数组的第二个元素,指向的元素是偶数时移动;两个指针都停止时且i<j时交换元素
public void Reorder(int[] a){int left = 0, right = a.length - 1;for(;;){while((a[left] & 0x1) != 0){left++;}while((a[right] & 0x1) == 0){right--;}if(left < right){int tmp = a[left];a[left] = a[right];a[right] = tmp;}elsebreak;}}扩展性:按正负数排序,被3整除排序
C++中可以利用函数指针来操作,
java利用函数对象来操作,即策略模式;或者java8的λ表达式
interface Comparator{boolean isSatisfied(int n);}
public void Reorder(int[] a, Comparator comparator){int left = 0, right = a.length - 1;for(;;){while(!comparator.isSatisfied(a[left])){left++;}while(comparator.isSatisfied(a[right])){right--;}if(left < right){int tmp = a[left];a[left] = a[right];a[right] = tmp;}elsebreak;}}
Test test = new Test();int[] a = {1, 2, 3, 4, 5};test.Reorder(a, new Comparator(){@Overridepublic boolean isSatisfied(int n){return (n & 0x1) == 0;}});
13.顺时针打印矩阵:从外向里以顺时针的顺序依次打印出每一个数字
如输入
1 2 3 4
5 6 7 8
9 10 1112
13 14 15 16
输出:1,2,3,4,8,12,16,15,14,13,9,5,5,7,11,15,14,10
分析:把矩阵看成若干个顺时针的圈组成
打印一圈分成四步:从左到右,从上到下,从右到左,从下往上;第一步必定会执行
public void printMatrixClockwisely(int[][] a){int rows = a.length, columns = a[0].length;//规则矩形int start = 0;while(columns > start * 2 && rows > start * 2){printMatrixInCircle(a, columns, rows, start);start++;}}public void printMatrixInCircle(int[][] a, int columns, int rows, int start){int endCol = columns - 1 - start;int endRow = rows - 1 - start;//从左到右打印一行for(int i = start; i <= endCol; i++){System.out.print(a[start][i]);}//从上到下打印一列if (start < endRow){for(int i = start + 1; i <= endRow; i++){System.out.print(a[i][endCol]);}}//从右到左打印一行if (start < endRow && start < endCol){for(int i = endCol - 1; i >= start; i--){System.out.print(a[endRow][i]);}}//从下到上打印一列if (start < endRow && start < endCol){for(int i = endRow - 1; i >= start + 1; i--){System.out.print(a[i][start]);}}}
14.最小的k个数,输入n个整数,找出其中最小的k个数,例如输入4,5,1,6,2,7,3,8,则最小的4个数为1,2,3,4
分析:
解法一:可以使用分割的算法,复杂度O(N),但是需要修改输入数据,不适合处理海量数据;
解法二:使用容器存储k个数字,使用最大堆(优先队列)或者红黑树;
如果容器中已有的数字小于k个,则直接把这次读入的整数放入容器;
如果容器中已有k个数字,此时我们找出k个数中的最大值,然后拿这次待插入的整数和最大值比较。
TreeSet不能处理重复元素,所有选用PriorityQueue
Comparator<Integer> comparator = Collections.reverseOrder();//降序PriorityQueue<Integer> queue = new PriorityQueue<Integer>(4, comparator);
public void getLeastNumbers(int[] a, PriorityQueue<Integer> queue, int k){if (k < 1 || a.length < k){return ;}for (int i = 0; i < a.length; i++){if (queue.size() < k){queue.offer(a[i]);//入队,自动装箱}else{if (a[i] < queue.peek())//最大的元素{queue.poll();//出队queue.offer(a[i]);}}}}
15.把数组排成最小的数:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接处的所有数字中的最小的一个。如{3,32,321},输出321323
分析:即定义一个比较规则,数字m和n,如果mn<nm,则m排在n前,另外防止数溢出,使用字符串存储数字
public void printMinNumber(int[] a){int length = a.length;//若a中元素有负数,不打印for (int i : a){if (i <= 0){return;}}//初始化字符串数组String[] strArr = new String[length];for(int i = 0; i < length; i++){strArr[i] = new String(String.valueOf(a[i]));}Arrays.sort(strArr, new Comparator<String>()//策略模式,对数组进行排序{public int compare(String s1, String s2){return (s1 + s2).compareTo(s2 + s1);//升序}});//打印for(int i = 0; i < length; i++){System.out.print(strArr[i]);}}
16.求数组中的逆序对:在数组中两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对,输入一个数组,求出这个数组中的逆序对的总数。如{7,5,6,4},存在5个逆序对,分别是(7,6),(7,5),(7,4),(6,4),(5,4)
解析:先把数组分成两部分,分别统计出子数组内部的逆序对的数目,再统计出两个相邻子数组之间的逆序对数组。在统计逆序对的过程中,还需要对数组进行排序。其实就是由后向前的归并排序。
5 7 | 4 6 5 7 | 4 6 5 7 | 4 6
i j i j i j
public int inversePairs(int[] a){int[] tmpArray = new int[a.length];for (int i = 0; i < a.length; i++){tmpArray[i] = a[i];}return inversePairs(a, tmpArray, 0, a.length - 1);}private int inversePairs(int[] a, int[] tmpArray, int left, int right){if (left == right) // base case{tmpArray[left] = tmpArray[right];return 0;}int center = (left + right) / 2;int number = right - left + 1;int leftCount = inversePairs(a, tmpArray, left, center);int rightCount = inversePairs(a, tmpArray, center + 1, right);int leftPos = center;// 前半段最后一个数字的下标int rightPos = right;// 后半段最后一个数字的下标int count = 0, tmpPos = right;while (leftPos >= left && rightPos >= center + 1){if (a[leftPos] > a[rightPos]){tmpArray[tmpPos--] = a[leftPos--];count += rightPos - center;}else{tmpArray[tmpPos--] = a[rightPos--];}}while (leftPos >= left)tmpArray[tmpPos--] = a[leftPos--];while (rightPos >= center + 1)tmpArray[tmpPos--] = a[rightPos--];//copy tmpArray backfor(int i = 0; i < number; i++, right--)a[right] = tmpArray[right];return leftCount + rightCount + count;}
17.求一个数字在排序数组中出现的次数。例如输入排序数组{1,2,3,3,3,3,4,5},则3出现的次数为4
分析:本题是二分查找的增强版,即找出k第一次出现和最后一次出现的位置。
求k第一次出现的位置:根据二分查找的思想,如果中间的数字比k大,k出现在数组的前半段;如果中间的数字比k小,则k出现在数组的后半段;如果中间的数字等于k,并且前一个数字不等于k则中间位置是第一次出现k,否则第一次k在数组前半段。k最后一次出现的位置分析类似。
public int getNumberOfK(int[] a, int k){int first = getFirstK(a, k);int last = getLastK(a, k);if (first > -1 && last > -1){return last - first + 1;}return 0;}public int getFirstK(int[] a, int k){int left = 0, right = a.length - 1, middle = 0;while (left <= right){middle = (left + right) / 2;if (a[middle] == k){if ((middle > 0 && a[middle - 1] != k) || middle == 0)//中间的数等于k,并且前面一个数不等于k则此时中间的数是第一个k{return middle;}elseright = middle - 1;}else if (a[middle] > k){right = middle - 1;}else{left = middle + 1;}}return - 1;}public int getLastK(int[] a, int k){int left = 0, right = a.length - 1, middle = 0;while (left <= right){middle = (left + right) / 2;if (a[middle] == k){if ((middle < a.length - 1 && a[middle + 1] != k) || middle == a.length - 1)//中间的数等于k,并且后面一个数不等于k则此时中间的数是最后一个k{return middle;}elseleft = middle + 1;}else if (a[middle] > k){right = middle - 1;}else{left = middle + 1;}}return - 1;
18.数组中只出现一次的数字:一个数组中除了两个数字之外,其他的数字都出现了两次,找出这两个只出现一次的数字。时间复杂度O(n),空间复杂度O(1)
分析:{2,4,3,6,3,2,5,5}得到4和6,一个数组中只出现一次的数字可以通过异或求得,x^x = 0, x^0 = x;所以可以通过把原数组分割成两个只含有只出现一次的子数组。所有数字异或后肯定不会0,找出一个出现为1的位置,记为n位,通过n位是不是1把原数组分成两个子数组。
public int[] findNumberAppearOnce(int[] a){int resultExclusiveOr = 0;for(int i = 0; i < a.length; i++)resultExclusiveOr ^= a[i];//从右到左找出第一个出现1的位int indexOf1 = 0;while(((resultExclusiveOr & 1) == 0) && indexOf1 < 32){resultExclusiveOr >>= 1;indexOf1++;}int[] number = new int[2];number[0] = number[1] = 0;for(int i = 0; i < a.length; i++){if (((a[i] >> indexOf1) & 1) != 0)//把数组分成两个子数组{number[0] ^= a[i];}else {number[1] ^= a[i];}}return number;}
19.数组中重复数字:在一个长度为n的数组里的所有数字都在0~n-1的范围内,找出数组中任意一个重复的数字。
解法一:采用hash表,时间复杂度O(n),空间复杂度O(n);
解法二:利用数字在0~n-1的条件,扫描到第i个数字时,首先比较这个数字是不是等于i,如果是扫描下一个数字;如果不是,再比较该数字(即a[i])和第a[i]个数字(即a[a[i]),如果相等就找到了第一个重复的,如果不等,则交换位置。每个数字最多交换两次,所有时间复杂度为O(n),空间O(1)
public int duplicate(int[] number){if (number == null || number.length == 0){return -1;}for(int i = 0; i < number.length; i++){if (number[i] < 0 || number[i] > number.length - 1){return -1;}}for(int i = 0; i < number.length; i++){while(number[i] != i){if (number[i] == number[number[i]]){return number[i];}int temp = number[i];number[i] = number[temp];number[temp] = temp;}}return - 1;}
- 关于数组的面试笔试题
- 关于数字的面试笔试题
- 关于数组的面试题
- 关于数组的面试题
- 关于数组的面试题
- Java常用的面试笔试题
- 一般的c#面试笔试题
- Spring,hibernate,struts的面试笔试题
- hibernate,struts,Spring的面试笔试题
- 常见的面试笔试题 及其解答
- Spring,hibernate,struts的面试笔试题
- Spring,hibernate,struts的面试笔试题
- Spring,hibernate,struts的面试笔试题
- Spring,hibernate,struts的面试笔试题
- Spring,hibernate,struts的面试笔试题
- 最近的面试笔试题总结
- Spring,hibernate,struts的面试笔试题
- Spring,hibernate,struts的面试笔试题
- android input子系统之-常用命令及技巧
- 使用一个shape.xml文件,使用代码设置不同圆角背景颜色
- vim显示行号、语法高亮、自动缩进的设置
- iOS 响应者链完全剖析
- animate.css效果预览页
- 关于数组的面试笔试题
- 品味人生20140930-优雅
- 在vim中配置python补全,fedora 19
- 对象池技术模拟
- IOS交叉编译configure
- 11gR2RAC环境DBCA创建数据库报错ORA-15055 ORA-15001
- 并发模型(一)——Future模式
- c++中vector的用法详解-函数实现
- 戴比尔斯视中国为“最核心”市场