简介:据我所知,此问题尚未在SO中提出。
这是一个面试问题
我甚至没有专门寻找代码解决方案,任何算法/伪代码都可以工作。
问题:给定一个整数数组int[] A
及其大小N
,找到2 非后续(不能相邻数组)元素总和最小。答案也不能包含第一个或最后一个元素(索引0
和n-1
)。解决方案也应该在 O(n)
时间和空间复杂度。
E.g。当A = [5, 2, 4, 6, 3, 7]
答案为5
时,自2+3=5
起。{。}
当A = [1, 2, 3, 3, 2, 1]
答案为4
时,由于2+2=4
,您无法选择1
中的任何一个,因为它们位于数组的末尾。
尝试:首先我认为解决方案中的一个必须是数组中最小的一个(除了第一个)最后),但反驳的例子是A = [4, 2, 1, 2, 4]
-> 4 (2+2)
然后我想如果我在数组中找到 2 最小数字(除了第一个和最后一个),解决方案将是那两个。这显然很快就失败了,因为我不能选择2个相邻的数字,如果我必须选择不相邻的数字,那么这就是问题的定义:)。
终于我想,好吧,我会在数组中找到 3 最小数字(除了第一个和最后一个),解决方案必须是两个其中,其中两个 彼此不相邻。
此也由于A = [2, 2, 1, 2, 4, 2, 6]
-> 2+1=3
而失败,这似乎有效,因为我会找到2, 1, 2
,但假设我在索引中找到2, 1, 2
1, 2, 3
这不会必然工作(如果我在索引2
中特别找到了5
,我就不能保证不幸)。
问题: 现在我很难过,有人能想出一个有效的解决方案/想法吗?
答案 0 :(得分:15)
以下是算法的实时javascript实现:
function findMinNonAdjacentPair(a) {
var mins = [];
// quick exits:
if (a.length < 5) return {error: "no solution, too few elements."};
if (a.some(isNaN)) return {error: "non-numeric values given."};
// collect 4 smallest values by their indexes
for (var i = 1; i < a.length - 1; i++) { // O(n)
if (mins.length < 4 || a[i] < a[mins[3]]) {
// need to keep record of this element in sorted list of 4 elements
for (var j = Math.min(mins.length - 1, 2); j >= 0; j--) { // O(1)
if (a[i] >= a[mins[j]]) break;
mins[j+1] = mins[j];
}
mins[j+1] = i;
}
}
// mins now has the indexes to the 4 smallest values
// Find the smallest sum
var result = {
sum: a[mins[mins.length-1]]*2+1 // large enough
}
for (var j = 0; j < mins.length-1; j++) { // O(1)
for (var k = j + 1; k < mins.length; k++) {
if (Math.abs(mins[j] - mins[k]) > 1) { // not adjacent
if (result.sum > a[mins[j]]+a[mins[k]]) {
result.sum = a[mins[j]]+a[mins[k]];
result.index1 = mins[j];
result.index2 = mins[k];
};
if (k < j + 3) return result; // cannot be improved
break; // exit inner loop: it cannot bring improvement
}
}
}
return result;
}
// Get I/O elements
var input = document.getElementById('in');
var output = document.getElementById('out');
var select = document.getElementById('pre');
function process() {
// translate input to array of numbers
var a = input.value.split(',').map(Number);
// call main function and display returned value
output.textContent = JSON.stringify(findMinNonAdjacentPair(a), null, 4);
}
// respond to selection from list
select.onchange = function() {
input.value = select.value;
process();
}
// respond to change in input box
input.oninput = process;
// and produce result upon load:
process();
&#13;
Type comma-separated list of values (or select one):</br>
<input id="in" value="2, 2, 1, 2, 4, 2, 6"> <=
<select id="pre">
<option value="5, 2, 4, 6, 3, 7">5, 2, 4, 6, 3, 7</option>
<option value="1, 2, 3, 3, 2, 1">1, 2, 3, 3, 2, 1</option>
<option value="4, 2, 1, 2, 4">4, 2, 1, 2, 4</option>
<option value="2, 2, 1, 2, 4, 2, 6" selected>2, 2, 1, 2, 4, 2, 6</option>
</select>
</br>
Output:</br>
<pre id="out"></pre>
&#13;
该算法有一些循环具有以下大O复杂性:
因此该算法在 O(n)中运行。
答案 1 :(得分:10)
找到第二个不是第一个的邻居,而不是数组中的第一个或最后一个。然后建立总和。
否则计算第一个数字的两个邻居之和。检查它是否小于第一笔金额
这将始终有效,因为如果第一个总和不是答案,则意味着第一个数字不能成为解决方案的一部分。而另一方面,这意味着,解决方案可以只是第二个总和。
答案 2 :(得分:8)
找到最小的四个并考虑这四个中的所有可能性。最小的与第二,第三或第四小的至少一个不相邻;唯一可能更好的另一种可能性是第二和第三小(假设它们是不相邻的)。
答案 3 :(得分:6)
我认为这不需要任何深层次的推理,并且可以在一次通过中解决,保持到目前为止处理的数组元素的最佳解决方案:
public static int[] minimumSumOfNonAcjacentElements(int[] a) {
// the result for the sequence a[1:i]
int minSum = Integer.MAX_VALUE;
int minSumElement1 = Integer.MAX_VALUE;
int minSumElement2 = Integer.MAX_VALUE;
// the minimum element eligible for joining with a[i], i.e. from a[1 : i-2]
int minElement = a[1];
int prevElement = a[2]; // a[i - 1]
for (int i = 3; i + 1 < a.length; i++) {
int sum = minElement + a[i];
if (sum < minSum) {
minSum = sum;
minSumElement1 = minElement;
minSumElement2 = a[i];
}
if (prevElement < minElement) {
minElement = prevElement;
}
prevElement = a[i];
}
return new int[] {minSumElement1, minSumElement2};
}
以下是一些测试代码,其中包含来自OP问题的极端案例:
private static void test(int minSumIndex1, int minSumIndex2, int... input) {
int[] result = minimumSumOfNonAcjacentElements(input);
if (result[0] == minSumIndex1 && result[1] == minSumIndex2) {
// ok
} else {
throw new AssertionError("Expected: " + minSumIndex1 + ", " + minSumIndex2 + ". Actual=" + Arrays.toString(result));
}
}
public static void main(String[] args) throws Exception {
test(2, 2, 4, 2, 1, 2, 4);
test(1, 2, 2, 2, 1, 2, 4, 2, 6);
test(1, 2, 0, 2, 1, 2, 4, 2, 0);
System.out.println("All tests passed.");
}
答案 4 :(得分:5)
使用动态编程。
array[0] + array[2]
),所以这是一个微不足道的步骤。min(array[0], array[1])
)。整个算法是O(n),因为扩展和更新都是恒定时间操作。通过简单的归纳可以证明该算法是正确的。 O(n)也是一个下界,因为我们必须考虑数组的每个元素,所以这个算法是最优的。
答案 5 :(得分:4)
算法:
通过3和4意味着通过找到两个2来传递案例[4,2,1,2,4] = 4.
public static int minSumNonAdjNonEnd(int[] array)
{
// 1. Find minimum
int minIdx1 = -1;
int minValue1 = Integer.MAX_VALUE;
for (int i = 1; i < array.length - 1; i++)
{
if (array[i] < minValue1)
{
minIdx1 = i;
minValue1 = array[i];
}
}
// 2. Find minimum not among (1) or adjacents.
int minIdx2 = -1;
int minValue2 = Integer.MAX_VALUE;
for (int i = 1; i < array.length - 1; i++)
{
if ((i < minIdx1 - 1 || i > minIdx1 + 1) && (array[i] < minValue2))
{
minIdx2 = i;
minValue2 = array[i];
}
}
boolean sum1Exists = (minIdx1 > -1 && minIdx2 > -1);
int sum1 = minValue1 + minValue2;
// 3. Find minimum not among (1).
int minIdx3 = -1;
int minValue3 = Integer.MAX_VALUE;
for (int i = 1; i < array.length - 1; i++)
{
if ((i != minIdx1) && (array[i] < minValue3))
{
minIdx3 = i;
minValue3 = array[i];
}
}
// 4. Find minimum not among(3) or adjacents.
int minIdx4 = -1;
int minValue4 = Integer.MAX_VALUE;
for (int i = 1; i < array.length - 1; i++)
{
if ((i < minIdx3 - 1 || i > minIdx3 + 1) && (array[i] < minValue4))
{
minIdx4 = i;
minValue4 = array[i];
}
}
boolean sum2Exists = (minIdx3 > -1 && minIdx4 > -1);
int sum2 = minValue3 + minValue4;
if (sum1Exists)
{
if (sum2Exists)
return Math.min(sum1, sum2);
else
return sum1;
}
else
{
if (sum2Exists)
return sum2;
else
throw new IllegalArgumentException("impossible");
}
}
执行4次线性搜索,复杂度为O(n)。
测试用例:
System.out.println(minSumNonAdjNonEnd(new int[] {5, 2, 4, 6, 3, 7}));
System.out.println(minSumNonAdjNonEnd(new int[] {1, 2, 3, 3, 2, 1}));
System.out.println(minSumNonAdjNonEnd(new int[] {4, 2, 1, 2, 4}));
System.out.println(minSumNonAdjNonEnd(new int[] {2, 2, 1, 2, 4, 2, 6}));
System.out.println(minSumNonAdjNonEnd(new int[] {2, 2, 3, 2}));
5
4
4
3
Exception in thread "main" java.lang.IllegalArgumentException: impossible
答案 6 :(得分:4)
这个问题可以用大约 10 行 Java 代码解决。
您可以从一个明显但效率低下的 (O(N^2)
) 解决方案开始:
public class Main {
int solve(int[] array) {
int answer = Integer.MAX_VALUE;
for (int i = 3; i < array.length - 1; i++) {
for (int j = 1; j < i - 1; j++) {
if (array[i] + array[j] < answer) {
answer = array[i] + array[j];
}
}
}
return answer;
}
}
但随后您会注意到您实际上不需要内部 for
循环,因为您可以只保留最小值并在必要时使用每个新元素更新它,这比每次重新查找最小值都要快。因此,最终的 O(N)
解决方案如下所示:
public class Main {
int solve(int[] array) {
int answer = Integer.MAX_VALUE;
int min = array[1];
for (int i = 3; i < array.length - 1; i++) {
min = Math.min(min, array[i - 2]);
if (array[i] + min < answer) {
answer = array[i] + min;
}
}
return answer;
}
}
答案 7 :(得分:3)
编辑:你是对的,我完全忽略了邻接约束。 幸运的是,我想到了一个解决方案。 算法如下:
---
title: "Untitled"
output:
pdf_document:
fig_caption: yes
citation_package: natbib
bibliography: biblio.bib
lang: spanish
---
(O(n))
(O(n))
- 只是一个索引检查)O(1)
)答案 8 :(得分:3)
在above答案的阐述中,您需要一个修改后的插入排序来跟踪最小的四个值和相应的索引(每个元素包含4个元素的数组)。
一旦找到,解决方案将是第一对,其索引的差异将大于1且其总和最小。
解决方案是(0,1)
或(0,2)
或(0,3)
或(1,2)
或(1,3)
或(2,3)
之一,其中值表示指标数组反过来跟踪数组中实际元素的位置。
此外,您还需要处理数组长度5
(arr\[1]+arr[3]
)的特殊情况以及小于5
的数组的错误。
答案 9 :(得分:3)
我不知道我的解决方案是否正确,因为我只是用OP中的数据对其进行了测试,我甚至不知道这是否比其他想法更好或更差,但我想尝试一下。 / p>
static void printMinimalSum(int[] A) {
// Looking for mins so we init this with max value
int[] mins = new int[]{Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE};
// Indices, used just to print the solution
int[] indices = new int[]{-1, -1, -1};
// If the array has length 5 then there's only one solution with the 2nd and 4th elements
if (A.length == 5) {
mins[0] = A[1];
indices[0] = 1;
mins[1] = A[3];
indices[1] = 3;
} else {
// Loop on the array without considering the first and the last element
for (int i = 1; i < A.length - 1; i++) {
// We consider each element which is smaller than its neighbours
if ((i == 1 && A[i] < A[i + 1]) // 1: first element, compare it with the second one
|| (i == A.length - 2 && A[i] < A[i - 1]) // 2: last element, compare it with the previous one
|| (A[i] < A[i + 1] && A[i] < A[i - 1])) { // 3: mid element, compare it with both neighbors
// If the element is "legal" then we see if it's smaller than the 3 already saved
if (A[i] < mins[0]) {
mins[0] = A[i];
indices[0] = i;
} else if (A[i] < mins[1]) {
mins[1] = A[i];
indices[1] = i;
} else if (A[i] < mins[2]) {
mins[2] = A[i];
indices[2] = i;
}
}
}
}
// Compute the 3 sums between those 3 elements
int[] sums = new int[]{Math.abs(mins[0]+mins[1]), Math.abs(mins[0]+mins[2]), Math.abs(mins[1]+mins[2])};
// Find the smaller sum and print it
if (sums[0] < sums[1] || sums[0] < sums[2]){
System.out.println("Sum = " + sums[0] + " (elements = {" + mins[0] + "," + mins[1] + "}, indices = {" + indices[0] + "," + indices[1] + "}");
} else if (sums[1] < sums[0] || sums[1] < sums[2]){
System.out.println("Sum = " + sums[1] + " (elements = {" + mins[0] + "," + mins[2] + "}, indices = {" + indices[0] + "," + indices[2] + "}");
} else {
System.out.println("Sum = " + sums[2] + " (elements = {" + mins[1] + "," + mins[2] + "}, indices = {" + indices[1] + "," + indices[2] + "}");
}
}
public static void main(String[] args) {
printMinimalSum(new int[]{5, 2, 4, 6, 3, 7});
printMinimalSum(new int[]{1, 2, 3, 3, 2, 1});
printMinimalSum(new int[]{4, 2, 1, 2, 4});
printMinimalSum(new int[]{2, 2, 1, 2, 4, 2, 6});
}
输出是:
Sum = 5 (elements = {2,3}, indices = {1,4}
Sum = 4 (elements = {2,2}, indices = {1,4}
Sum = 4 (elements = {2,2}, indices = {1,3}
Sum = 3 (elements = {1,2}, indices = {2,5}
看起来很好。
答案 10 :(得分:1)
怎么样:你找到k
个最小的数字(或者更确切地说是他们的指数)(k
足够大,比如10
)。可以肯定的是,想要的对在它们之间。现在,您只需检查可能的50
对,并选择满足约束条件的最佳对。
您不需要10
,少可以做 - 但超过3
:)
编辑:查找k
个最小数字是O(n)
,因为您只是在堆中保留最佳10
(添加新元素,删除最大O(k*logk)=O(1)
个操作) 。
然后会有一对满足约束(彼此不相邻)。同样清楚的是,如果使用不是来自那些k
的元素构建总和,那么它将大于从那些k
元素中选择的最佳对。
最多检查k*k
对也是O(1)
,因此整个运行时间为O(n)
。
答案 11 :(得分:1)
我认为这应该有效:
找到最少3个元素及其索引。由于它们都不能相邻,所以选择其中2个。
如果它们都是相邻的并且最小数字位于它们的中间,则遍历所有元素,找到第四个最小元素,选择min1+min4
,min2+min3
的最小值,以较小者为准。
您也可以在一次迭代中完成此操作。
答案 12 :(得分:1)
我使用动态编程来解决它。
想法是首先创建跟踪到目前为止的最小值的数组,如下所示:
输入数组= [1, 3, 0, 5, 6]
最小数组= [1, 1, 0, 0, 0]
现在使用最小数组和输入数组,我们可以在下面使用:
DP[i] = min(DP[i-1], min(first_data, second_data))
其中DP[i]
表示到目前为止发现的最小值,它是前两个备用元素的总和。
first_data
=输入数组中current
元素的总和+最小数组中current-2
元素的总和
second_data
=输入数组中current-1
元素的总和+最小数组中current-3
元素的总和
import random
def get_min(numbers):
#disregard the first and last element
numbers = numbers[1:len(numbers)-1]
#for remembering the past results
DP = [0]*len(numbers)
#for keeping track of minimum till now found
table = [0]*len(numbers)
high_number = 1 << 30
min_number = numbers[0]
table[0] = min_number
for i in range(0, len(numbers)):
DP[i] = high_number
for i in range(1, len(numbers)):
if numbers[i] < min_number:
min_number = numbers[i]
table[i] = numbers[i]
else:
table[i] = min_number
for i in range(0, len(numbers)):
min_first, min_second = high_number, high_number
if i >= 2:
min_first = numbers[i] + table[i-2]
if i >= 3:
min_second = numbers[i-1] + table[i-3]
if i >= 1:
DP[i] = min(min(DP[i-1], min_first), min_second)
return DP[len(numbers)-1]
input = random.sample(range(100), 10)
print(input)
print(get_min(input))
答案 13 :(得分:0)
因为我们只需要跟踪两个不相邻值的最小和,我们可以通过迭代数组来完成,不包括第一个和最后一个元素,并保持跟踪最小值和最小和。当前最小值将是当前值之前的两个索引。例如,如果我们正在检查当前索引 i
,那么 minValue 是从索引 1
到 i-2
的最小值。
代码:
int minSum(int[] A){
int minSum=Integer.MAX_VALUE;
int min= Integer.MAX_VALUE;
for(int i=3; i<A.length-1; i++){
min= Math.min(A[i-2], min);
minSum = Math.min(min+A[i], minSum);
}
return minSum;
}