面试题39:数组中出现次数超过一半的数字
解题方案1:排序后统计出现次数。
解题方案2:利用快速排序中的分片,随机选择一个数分片后,若这个这数的位置恰好在n/2位置,那么就找到了中位数。若下标大于n/2,那么中位数在这个数的左边,然后在左边的数组中继续查找。
解题方案3:利用题目的特殊性,这个数字出现的次数比其他数字出现的总和还要多,使用两个变量,一个存数组中的一个数字,一个存次数,遍历一遍数组,若数字和存的数字相同则计数加一,若不等计数减一,计数为0的时候需要存下个数字。
面试题40:最小的K个数
解题方案1:分片函数,时间复杂度O(n),但是会修改原数组;
解题方案2:最小堆,时间复杂度O(nlogk)
面试题41:数据流中的中位数
解题方案:使用一个最小堆,和一个最大堆,若插入最小堆的数据大于最大堆的部分数据,则将这个数据插入到最大堆,并从最大堆中取出最大值插入到最小堆中,保证两个堆的数量一致。
面试题42:连续子数组的最大和
解决方案:动态规划,函数 f(i) 表示第i个数字结尾的子数组的最大和,那么我们需要求出 max[f(i)]
f
(
i
)
=
{
d
a
t
a
[
i
]
i=0 或者 f(i-1)<=0&&data[i]>f(i-1)
f
(
i
−
1
)
+
d
a
t
a
[
i
]
i>0并且f(i-1)>0
f(i) = \begin{cases} data[i] & \text{i=0 或者 f(i-1)<=0\&\&data[i]>f(i-1)} \\[2ex] f(i-1)+data[i] & \text{i>0并且f(i-1)>0} \end{cases}
f(i)=⎩⎨⎧data[i]f(i−1)+data[i]i=0 或者 f(i-1)<=0&&data[i]>f(i-1)i>0并且f(i-1)>0
public void fortySecond() {
int[] array = {-2, -3, -4, -1};
// int[] array = {1, -2, 3, 10, -4, 7, 2, -5};
int sumOfSubArray = findGreatestSumOfSubArray(array);
System.out.println(sumOfSubArray);
}
private int findGreatestSumOfSubArray(int[] array) {
if (array == null || array.length == 0) {
throw new RuntimeException("Invalid Input");
}
int curSum = array[0];
int nGreatestSum = curSum;
for (int i = 1; i < array.length; i++) {
if (curSum <= 0 && array[i] > curSum) {
curSum = array[i];
}else if (curSum > 0) {
curSum += array[i];
}
if (curSum > nGreatestSum) {
nGreatestSum = curSum;
}
}
return nGreatestSum;
}
面试题43:1-n整数中1出现的次数
题目:输入一个整数n,求1-n这n个整数十进制表示中1出现的次数。
解题方案1:遍历1-n的n个整数,汇总每个数中1出现的次数,时间复杂度O(nlogn)
解题方案2:找规律,8171 可分为 1-171 和 172-8171,172-8171中出现在万位的次数为103,172-8171 可再分为 172-1171、1172-2171等,172-8171 中出现在除万位其他位的次数为 8*3*102,再递归找 1-171 中出现的次数。
public void fortyThird() {
System.out.println(getNumberOf1(1));
System.out.println(getNumberOf1(10));
System.out.println(getNumberOf1(8171));
}
private int getNumberOf1(int num) {
if (num <= 0) {
return 0;
}
// 8171 拆分为 1-171 和 172 - 8171
String number = Integer.valueOf(num).toString();
if (number.length() == 1) {
return 1;
}
int numFirstDist = 0;
int numOtherDigits = 0;
int numRecursive = 0;
int firstNum = number.charAt(0) - '0';
int low = num % (powerBase10(number.length() - 1));
// 1出现在最高位
if (firstNum > 1) {
numFirstDist = powerBase10(number.length() - 1);
} else {
numFirstDist = low + 1;
}
// 1出现在其他的低位 172-1171 1172-2171 2172-3173
numOtherDigits = firstNum * (number.length() - 1) * powerBase10((number.length() - 2));
numRecursive = getNumberOf1(low);
return numFirstDist + numOtherDigits + numRecursive;
}
private int powerBase10(int n) {
int result = 1;
while (n > 0) {
result *= 10;
n--;
}
return result;
}
面试题44:数字序列中某一位的数字
题目:数字以 012346789101112131415… 的格式序列化到一个字符串序列中。求任意第N位(从0开始计数)的数字。
public void fortyForth() {
System.out.print(digitAtIndex(1));
System.out.print(digitAtIndex(2));
System.out.print(digitAtIndex(3));
System.out.print(digitAtIndex(10));
System.out.print(digitAtIndex(11));
System.out.print(digitAtIndex(12));
System.out.println();
System.out.print(digitAtIndex(1000));
System.out.print(digitAtIndex(1001));
System.out.print(digitAtIndex(1002));
System.out.print(digitAtIndex(1003));
System.out.print(digitAtIndex(1004));
System.out.print(digitAtIndex(1005));
System.out.print(digitAtIndex(1006));
}
private int digitAtIndex(int index) {
if (index <= 0) {
return 0;
}
int digit = 1;
while (true) {
int numbers = countOfInteger(digit);
if (index <= numbers) {
return digitAtIndex(index, digit);
}
index -= numbers;
digit++;
}
}
/**
* digit 位有多少个数字
*
* @param digit 个位1、十位2、百位3...
*/
private int countOfInteger(int digit) {
if (digit <= 1) {
return 9;
}
return 9 * (int) Math.pow(10, digit - 1) * digit;
}
/**
* @param index 某个位对应的下标
* @param digit 位
* @return 结果
*/
private static int digitAtIndex(int index, int digit) {
if (digit == 1) {
return index;
}
int begin = (int) Math.pow(10, digit - 1);
int num = index / digit;
int mod = index % digit;
int number;
if (mod == 0) {
number = begin + num - 1;
return number % 10;
} else {
number = begin + num;
return (number / (int) Math.pow(10, digit - mod)) % 10;
}
}
面试题45:把数组排成最小的数
题目:给一个数组 {3, 32, 321, 324, 4, 42, 5},把数组中的数拼接起来使它最小。
解题方案:利用快速排序,小的值排在前面,怎么判断两个数值哪个小,比较合成的两个字符串字符串
public void fortyFifth() {
int[] arrays = {3, 32, 321, 324, 4, 42, 5};
String[] stringArrays = new String[arrays.length];
for (int i = 0; i < arrays.length; i++) {
stringArrays[i] = String.valueOf(arrays[i]);
}
System.out.println(Arrays.deepToString(stringArrays));
quickSort(stringArrays, 0, stringArrays.length - 1);
System.out.println(Arrays.deepToString(stringArrays));
}
private void quickSort(String[] stringArrays, int beginIndex, int endIndex) {
if (beginIndex < endIndex) {
int partition = partition(stringArrays, beginIndex, endIndex);
quickSort(stringArrays, beginIndex, partition - 1);
quickSort(stringArrays, partition + 1, endIndex);
Merge(stringArrays, beginIndex, partition, endIndex);
}
}
private void Merge(String[] stringArrays, int beginIndex, int partition, int endIndex) {
String[] temp = new String[endIndex - beginIndex + 1];
int t = 0; //临时数组指针
int i = beginIndex;
int j = partition + 1;
while (i <= partition && j <= endIndex) {
if (compareString(stringArrays[i], stringArrays[j]) <= 0) {
temp[t++] = stringArrays[i++];
} else {
temp[t++] = stringArrays[j++];
}
}
while (i <= partition) {
temp[t++] = stringArrays[i++];
}
while (j <= endIndex) {
temp[t++] = stringArrays[j++];
}
for (int k = 0; k < t; k++) {
stringArrays[beginIndex + k] = temp[k];
}
}
private int partition(String[] stringArrays, int beginIndex, int endIndex) {
String temp = stringArrays[beginIndex];
while (beginIndex < endIndex) {
while (beginIndex < endIndex && compareString(temp, stringArrays[endIndex]) <= 0) {
endIndex--;
}
stringArrays[beginIndex] = stringArrays[endIndex];
while (beginIndex < endIndex && compareString(stringArrays[beginIndex], temp) <= 0) {
beginIndex++;
}
stringArrays[endIndex] = stringArrays[beginIndex];
}
stringArrays[beginIndex] = temp;
return beginIndex;
}
private int compareString(String stringNumber1, String stringNumber2) {
String stringCombine1 = stringNumber1 + stringNumber2;
String stringCombine2 = stringNumber2 + stringNumber1;
// stringCombine1<stringCombine2 , stringNumber1应该排在stringNumber2的前面
return stringCombine1.compareTo(stringCombine2);
}
面试题46:把数字翻译成字符串
题目:1 可翻译为 a,2 可翻译为b,11 可翻译为l……25 可翻译为z。
解题方案:自下而上消除重复子问题,从字符串后面往前遍历,存储每个位置可翻译的选择。
public void fortySixth() {
System.out.println(getTransactionCount(12258));
}
private int getTransactionCount(int number) {
if (number < -0) {
return 0;
}
String string = String.valueOf(number);
char[] chars = string.toCharArray();
int length = chars.length;
int[] counts = new int[length];
int count = 0;
for (int i = length - 1; i >= 0; i--) {
if (i < length - 1) {
count = counts[i + 1];
int digit1 = chars[i] - '0';
int digit2 = chars[i + 1] - '0';
int converted = digit1 * 10 + digit2;
if (converted >= 10 && converted <= 25) {
if (i < length - 2) {
count += counts[i + 2];
} else {
count++;
}
}
} else {
count = 1;
}
counts[i] = count;
}
return counts[0];
}
面试题47:礼物的最大值
解题方案:动态规划,f(i,j) = max(f((i-1) , j ) , f(i , (j-1)) ) + value[i][j]。
面试题48:最长不含重复字符的子字符串
解题方案:动态规划,f(i)表示以第i个字符结尾的不含重复字符的子字符串的最大长度,另外使用一个数组存储每个字符在字符串中的下标。
public void fortyEighth() {
System.out.println(getLongestSubStringWithoutDuplication("arabcacfr"));
System.out.println(getLongestSubStringWithoutDuplication("nuttertools"));
}
private int getLongestSubStringWithoutDuplication(String str) {
int[] position = new int[26];
for (int i = 0; i < 26; i++) {
position[i] = -1;
}
int curLength = 0;
int maxLength = 0;
for (int i = 0; i < str.length(); i++) {
int pervIndex = position[str.charAt(i) - 'a'];
if (pervIndex == -1 || (i - pervIndex) > curLength) {
curLength++;
} else {
if (curLength > maxLength) {
maxLength = curLength;
}
curLength = i - pervIndex;
}
position[str.charAt(i) - 'a'] = i;
}
if (curLength > maxLength) {
maxLength = curLength;
}
return maxLength;
}
面试题49:丑数
题目:只包含2、3、5因子的数称为丑树,求第N个丑树。
解题方案1:从1开始每次增加一往后找,每次都计算这个数是否是丑数。
public void fortyNinth1() {
System.out.println(getUglyDigit(10));
System.out.println(getUglyDigit(1000));
}
private int getUglyDigit(int index) {
if (index <= 0) {
return 0;
}
int uglyNumIndex = 0;
int num = 0;
while (uglyNumIndex < index) {
num++;
if (isUgly(num)) {
uglyNumIndex++;
}
}
return num;
}
private boolean isUgly(int num) {
while (num % 2 == 0) {
num = num / 2;
}
while (num % 3 == 0) {
num = num / 3;
}
while (num % 5 == 0) {
num = num / 5;
}
return num == 1;
}
解题方案2:从第一个丑数开始,计算并存储每个丑数。
public void fortyNinth2() {
System.out.println(getUglyDigit2(10));
System.out.println(getUglyDigit2(1000));
}
private int getUglyDigit2(int index) {
if (index <= 0) {
return 0;
}
int[] arrays = new int[index];
arrays[0] = 1;
int index2 = 0;
int index3 = 0;
int index5 = 0;
int nextUglyIndex = 1;
while (nextUglyIndex < index) {
int min = min(arrays[index2] * 2, arrays[index3] * 3, arrays[index5] * 5);
arrays[nextUglyIndex] = min;
while ((arrays[index2] * 2) <= min) {
index2++;
}
while ((arrays[index3] * 3) <= min) {
index3++;
}
while ((arrays[index5] * 5) <= min) {
index5++;
}
nextUglyIndex++;
}
return arrays[index - 1];
}
public int min(int a, int b, int c) {
if (b < a) {
a = b;
}
if (c < a) {
a = c;
}
return a;
}
面试题50:第一个只出现一次的字符
题目:只包含2、3、5因子的数称为丑树,求第N个丑树。
解题方案:遍历字符,判断当前字符是否在后面出现,时间复杂度O(n2);利用一个哈希表存储字符和字符出现的次数。
面试题51:数组中的逆数对
解题方案:利用归并排序,先将数组拆分为一个个小元素,然后进行两两合并,合并的时候,将较小的数排在前面。对两个数组进行合并时,若左边数组中的最大数大于右边数组中的最大数,说明左边的这个数能和右边的任何的数组成一个逆数对,左边的数组指针减一继续判断;若小于,将右边数组的指针左移一位继续判断。
public void fifthFirst() {
int[] arrays = {7, 5, 6, 4};
int count = inversePairsCore(arrays, new int[arrays.length], 0, arrays.length - 1);
System.out.println(count);
arrays = new int[]{5, 7, 5, 4};
System.out.println(inversePairsCore(arrays, new int[arrays.length], 0, arrays.length - 1));
}
private int inversePairsCore(int[] arrays, int[] tempArrays, int beginIndex, int endIndex) {
if (beginIndex >= endIndex) {
return 0;
}
int mid = (endIndex + beginIndex) / 2;
int left = inversePairsCore(arrays, tempArrays, beginIndex, mid);
int right = inversePairsCore(arrays, tempArrays, mid + 1, endIndex);
int merge = merge(arrays, tempArrays, beginIndex, mid, endIndex);
return left + right + merge;
}
private int merge(int[] array, int[] temp, int low, int mid, int high) {
int t = high; //临时数组指针
int i = mid;
int j = high;
int count = 0;
while (i >= low && j > mid) {
if (array[i] > array[j]) {
temp[t--] = array[i--];
count += j - mid;
} else {
temp[t--] = array[j--];
}
}
while (i >= low) {
temp[t--] = array[i--];
}
while (j > mid) {
temp[t--] = array[j--];
}
for (int k = high; k >= low; k--) {
array[k] = temp[k];
}
return count;
}
面试题52:两个链表的第一个公共节点
解决方案1:蛮力破解,把第一个链表中的每个元素放到第二个中去对比。
解决方案2:遍历两个链表并分别将元素存储到两个不同的栈中,再同时遍历两个栈。
解决方案3:遍历两个链表获取两个链表的长度,长度长的链表先走N和长度短的长度一致时,再同时遍历找相同节点。