1. 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
如果直接排序,后进行选择,则时间复杂度最优为O(nlogn),这里有两种思路。
第一种,利用快速排序的思想,找到第n/2位置的数。然后扫描数组,确保这个数的确超过数组长度的一半。
第二种,设置两个标记,一个记录值,一个记录次数,当后一个与前一个不同时,记录数减一,当记录数减少到0时候,需要更改值的标记。
时间复杂度都为O(n),两种方法代码分别如下:
int MoreThanHalfNum_Solution1(int[] array) {
int length = array.length;
//基于快排思想
int loc = partition(array, 0, length - 1);
while (loc != length / 2) {
if (loc > length / 2) {
loc = partition(array, 0, loc - 1);
} else {
loc = partition(array, loc + 1, length - 1);
}
}
int value = array[loc];
int count = 0;
for (int i = 0; i < length; i++) {
if (array[i] == value)
count++;
}
if (count > length / 2)
return value;
else
return 0;
}
int partition(int[] array, int low, int high) {
if (low >= high) return low;
int temp = array[low];
while (low < high) {
while (low < high && array[high] >= temp) high--;
array[low] = array[high];
while (low < high && array[low] <= temp) low++;
array[high] = array[low];
}
array[low] = temp;
return low;
}
设置两个标记来处理,最后还得验证是否超过一半:
int MoreThanHalfNum_Solution2(int[] array) {
int length = array.length;
int key = array[0], times = 1;
for (int i = 1; i < length; i++) {
if (times == 0) {
key = array[i];
times++;
} else {
if (array[i] != key)
times--;
else
times++;
}
}
int count = 0;
for (int i = 0; i < length; i++) {
if (array[i] == key)
count++;
}
if (count > length / 2)
return key;
else
return 0;
}
2.把只包含素因子2、3和5的数称作丑数(Ugly Number), 求按从小到大的顺序的第N个丑数。
不考虑的效率的话,直接迭代去判断每个数是否是丑数,直到得到第N个丑数。效率比较低下。
由于后面的丑数可以通过前面的丑数乘上因子获得,现在设置三个标记来标记依次产生的最小丑数,这个丑数则为下一个丑数,则这个丑数的标记前进一步,若多个标记产生的丑数都是最小的,则多个标记共同前进一步,直到获得第N个丑数。
代码如下:
int GetUglyNumber_Solution(int index) {
if (index < 2) return index;
int[] uglyArray = new int[index];
uglyArray[0] = 1;
int index_2 = 0, index_3 = 0, index_5 = 0;
int i = 0, max = 0;
while (i < index - 1) {
int max_2 = uglyArray[index_2] * 2;
int max_3 = uglyArray[index_3] * 3;
int max_5 = uglyArray[index_5] * 5;
max = max_2 <= max_3 ? max_2 : max_3;
max = max <= max_5 ? max : max_5;
uglyArray[++i] = max;
if (max == max_2)
index_2++;
if (max == max_3)
index_3++;
if (max == max_5)
index_5++;
}
return uglyArray[i];
}
3.输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序。
若限制为两个数的和,可以首尾各设一个指针,计算两个指针指向值的和,大于S,则尾指针向前移动,再迭代计算。若小于S,则首指针向后移动,计算和。一直到满足要求为止。
这个题目要求求出连续数字的和,不限元素个数。这里继续设置两个指针,一个放在第一位,一个放在第二位,求累积和,小于S则后面指针向后移动,再累计比较。若大于S则前一个指针向后移动,再累计求和比较,直到满足要求为止,可以求出多个结果。
ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> array = new ArrayList<ArrayList<Integer>>();
ArrayList<Integer> one = new ArrayList<Integer>();
int len = sum / 2 + 1;
int start = 1, end = 2;
int total = start + end;
while (start != len) {
if (total == sum) {
for (int i = start; i <= end; i++) {
one.add(i);
}
array.add((ArrayList<Integer>)one.clone());
one.clear();
if (end == len)
break;
else {
total += ++end;
}
}
if (total < sum) {
total += ++end;
}
if (total > sum) {
total -= start++;
}
}
return array;
}
4.在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数
如果直接暴力来计算,可以想象成直接插入排序算法,当数组通过插入排序算法处理完毕时候,数字移动的个数就是逆序对的总数。时间复杂度没有下面的算法优。
利用归并算法。将数组分为两部分,将这两部分再递归处理。当两个数组合并成有序的序列时候,进行逆序统计,生成新排序数组是从数组尾部开始生成,也就是从两个有序的数组中,先找最大的数。设置两个指针,一个指向前一个有序数组的尾部,一个指向后一个有序数组的尾部。进行比较,当前一个大比后一个大的时候,逆序数增加后一个有序数组的头部到指针处的元素个数(若后一指针索引为j,头部的索引为low,则增加j-low+1个),前一个指针前移。当后一个指针大,则逆序数不变。
最后的总逆序数为前一半数组的逆序数加上后一半数组的逆序数,再加上两个数组合并过程中统计的逆序数,即为总逆序数。
int InversePairs(int [] array) {
if(array==null||array.length==0)
return 0;
int length = array.length;
return merge(array,0,length-1);
}
int merge(int[] array, int low, int high) {
int count1 = 0, count2 = 0;
if (low < high) {
int mid = (low + high) / 2;
count1 = merge(array, low, mid);
count2 = merge(array, mid + 1, high);
count1 = count1 + count2 + mergeAndCount(array, low, mid, high);
}
return count1;
}
int mergeAndCount(int[] array, int low, int mid, int high) {
int[] b = new int[array.length];
for (int i = 0; i < b.length; i++) {
b[i] = array[i];
}
int count = 0;
int i, j, k;
for (i = mid, j = high, k = high; i >= low && j > mid; ) {
if (b[i] > b[j]) {
count += j - mid;
array[k--] = b[i--];
} else {
array[k--] = b[j--];
}
}
while (i >= low) array[k--] = b[i--];
while (j > mid) array[k--] = b[j--];
return count;
}
>5. 设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。
回溯法,设置一个标记,标记是否可以访问。匹配成功一个字符后,则向四个方向继续匹配,失败则清除不可访问标记。
代码如下:
boolean hasPath(char[] matrix, int rows, int cols, char[] str) {
int[] flag = new int[matrix.length];
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++) {
if (isHas(matrix, rows, cols, i, j, str, 0, flag))
return true;
}
return false;
}
boolean isHas(char[] a, int rows, int cols, int i, int j, char[] str, int k, int[] flag) {
int index = i * cols + j;
if (i < 0 || j < 0 || i >= rows || j >= cols || flag[index] == 1 || a[index] != str[k])
return false;
if (k == str.length - 1)
return true;
flag[index] = 1;
if (isHas(a, rows, cols, i - 1, j, str, k + 1, flag) || isHas(a, rows, cols, i + 1, j, str, k + 1, flag) ||
isHas(a, rows, cols, i, j - 1, str, k + 1, flag) || isHas(a, rows, cols, i, j + 1, str, k + 1, flag))
return true;
flag[index] = 0;//失败则去除标记
return false;
}
算法解析:数组问题高效解决方案
这篇博客探讨了数组相关的算法题,包括寻找超过一半次数的数字、计算丑数、求连续正数序列和逆序对总数的方法。还讨论了解决矩阵中字符串路径存在的回溯法策略。文章提供了多种时间复杂度为O(n)的解决方案,并附有代码示例。
1714

被折叠的 条评论
为什么被折叠?



