是否可以将嵌套循环替换为计数器变量以降低时间复杂度?
Could a nested loop be replaced with a counter variable to reduce the time complexity?
这可能是一个新手问题,但我有一个使用嵌套循环在二维数组中查找值的方法,假设正文看起来像这样:(假设在二维数组中总是有一个匹配项 1)
// example array ==> int arr = new int[5][5]
int length = arr.length;
int index1 = -1;
int index2 = -1;
loop:
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
if (arr[i][j] == 1) {
index1 = i;
index2 = j;
break loop;
}
}
}
我以这种方式用额外变量 (counter) 替换了第二个循环:
// example array ==> int arr = new int[5][5]
int length = arr.length;
int index1 = -1;
int index2 = -1;
int counter = 0;
for (int i = 0; i < length; i++) {
if (arr[counter][i] == 1) {
index1 = counter;
index2 = i;
break;
}
if (i == length - 1) {
counter++;
i = 0;
}
}
这仍然被认为是具有相同时间复杂度的嵌套循环吗?
并且如果有好的link可以理解如何从总体上分析我的代码复杂度,请附上!
这是一个示例代码,我知道可以做一些改进,但我只是问时间复杂度(忽略边缘情况和错误处理)
它认为O()的内容是执行所需的最大操作数和'n'参数来标识输入的大小。
for 循环通常迭代一定大小,在本例中就是数组的大小。
一个 for 循环因此最多执行 'n' 个操作,这就是为什么它被称为线性时间或 O(n)。
在第一种情况下,您显示了两个嵌套的 for 循环,在这种情况下,必须进行简单的乘法运算,因为复杂度 O(n) 的循环最多完成 'n' 次,所以它称为二次时间或 O(n^2).
O(n)的值越大,复杂度越大
为简单起见:
For循环是O(n)并且没问题
2+ 对于非嵌套循环是 O(n+n),但它仍然说 O(n) 没关系
2 对于嵌套循环是 O(n*n) 并且不好
3 对于嵌套循环是 O(nnn) 并且非常糟糕
这就是为什么你发送的第二个代码,假设没有错误,肯定比第一个更快。
您应该将此条件添加到 for 循环中,否则您将遇到 ArrayOutOfBoundException:
for (int i = 0; i < length && counter < length-1; i++) //if the array is something like new int[5][5]
尝试使用 currentTimeMillis() 测量两者的执行时间,使用不同的数组和迭代:)
您可以使用测试应用来检查每种方法的性能,如下面的代码片段
package test;
public class TestLoops {
static int numOfItrr = 30000;
static int[][] arr = new int[numOfItrr][numOfItrr];
public static void main(String[] args) {
// TODO Auto-generated method stub
initArr();
System.out.println("=====================Start======================");
System.out.println("numOfItrr :" + numOfItrr);
initArr();
testWithForLoops();
testWithCounter();
System.out.println("=====================Finish=====================");
}
public static void initArr() {
for (int i = 0; i < numOfItrr; i++) {
for (int j = 0; j < numOfItrr; j++) {
arr[i][j] = (int) (Math.random() * 1000);
}
}
}
public static void testWithForLoops() {
long start = System.nanoTime();
int max = arr[0][0];
for (int i = 0; i < numOfItrr; i++) {
for (int j = 0; j < numOfItrr; j++) {
if (arr[i][j] > max) {
max = arr[i][j];
}
}
}
long end = System.nanoTime();
System.out.println("time With loops = " + (end - start));
}
public static void testWithCounter() {
long start = System.nanoTime();
int counter = 0;
int max = arr[0][0];
for (int i = 0; i < numOfItrr; i++) {
if (arr[counter][i] > max) {
max = arr[counter][i];
}
if (i == numOfItrr - 1 && counter < numOfItrr - 1) {
counter++;
i = 0;
}
}
long end = System.nanoTime();
System.out.println("time With Counter = " + (end - start));
}
}
这是示例结果
====================开始=====================
数量:10000
循环时间 = 86591300
计数器时间 = 110362600
====================完成=====================
====================开始=====================
数量:30000
循环时间 = 754436400
使用计数器的时间 = 978285600
====================完成=====================
====================开始=====================
数量:30000
循环时间 = 754230300
使用计数器的时间 = 970180300
====================完成=====================
你的两个解的时间复杂度是一样的
有些人犯了只计算循环次数的错误。因此,如果只有一个可识别的循环,他们会说这是一种线性算法。但事实并非如此,它仍然是二次方的。你要检查的是重复的操作重复了多少次。
在这种情况下,让我们专注于比较(将数组值与 1 进行比较)。这是重复次数最多的操作(您可以自己验证),因此它重复的次数与 N(数组一维的大小,意味着数组本身是 NxN)成正比,这决定了大 O。
因此,在您的第一个双循环示例中,每次执行内部循环时,都会进行 N 次比较。由于外循环,内循环本身重复了 N 次,因此你有 NxN 次比较,最后得到 O(N²)。
在您的第二个单循环示例中,比较执行的迭代次数与循环一样多。假设最坏的情况,1 在最后一个单元格中,这里会发生什么?每次到达一行的末尾时,都会重置 i。所以你又开始从 0 数到 N-1。因此,您对 counter = 0
进行了 N 次比较,然后对 counter = 1
进行了 N 次比较,依此类推,直到 counter
达到 N-1。因此,您再次进行了 NxN 次比较,因此 O(N²)。
所以检查循环只是一个经验法则。您真正应该计算的是 operations.
这可能是一个新手问题,但我有一个使用嵌套循环在二维数组中查找值的方法,假设正文看起来像这样:(假设在二维数组中总是有一个匹配项 1)
// example array ==> int arr = new int[5][5]
int length = arr.length;
int index1 = -1;
int index2 = -1;
loop:
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
if (arr[i][j] == 1) {
index1 = i;
index2 = j;
break loop;
}
}
}
我以这种方式用额外变量 (counter) 替换了第二个循环:
// example array ==> int arr = new int[5][5]
int length = arr.length;
int index1 = -1;
int index2 = -1;
int counter = 0;
for (int i = 0; i < length; i++) {
if (arr[counter][i] == 1) {
index1 = counter;
index2 = i;
break;
}
if (i == length - 1) {
counter++;
i = 0;
}
}
这仍然被认为是具有相同时间复杂度的嵌套循环吗? 并且如果有好的link可以理解如何从总体上分析我的代码复杂度,请附上!
这是一个示例代码,我知道可以做一些改进,但我只是问时间复杂度(忽略边缘情况和错误处理)
它认为O()的内容是执行所需的最大操作数和'n'参数来标识输入的大小。
for 循环通常迭代一定大小,在本例中就是数组的大小。
一个 for 循环因此最多执行 'n' 个操作,这就是为什么它被称为线性时间或 O(n)。
在第一种情况下,您显示了两个嵌套的 for 循环,在这种情况下,必须进行简单的乘法运算,因为复杂度 O(n) 的循环最多完成 'n' 次,所以它称为二次时间或 O(n^2).
O(n)的值越大,复杂度越大
为简单起见:
For循环是O(n)并且没问题
2+ 对于非嵌套循环是 O(n+n),但它仍然说 O(n) 没关系
2 对于嵌套循环是 O(n*n) 并且不好
3 对于嵌套循环是 O(nnn) 并且非常糟糕
这就是为什么你发送的第二个代码,假设没有错误,肯定比第一个更快。
您应该将此条件添加到 for 循环中,否则您将遇到 ArrayOutOfBoundException:
for (int i = 0; i < length && counter < length-1; i++) //if the array is something like new int[5][5]
尝试使用 currentTimeMillis() 测量两者的执行时间,使用不同的数组和迭代:)
您可以使用测试应用来检查每种方法的性能,如下面的代码片段
package test;
public class TestLoops {
static int numOfItrr = 30000;
static int[][] arr = new int[numOfItrr][numOfItrr];
public static void main(String[] args) {
// TODO Auto-generated method stub
initArr();
System.out.println("=====================Start======================");
System.out.println("numOfItrr :" + numOfItrr);
initArr();
testWithForLoops();
testWithCounter();
System.out.println("=====================Finish=====================");
}
public static void initArr() {
for (int i = 0; i < numOfItrr; i++) {
for (int j = 0; j < numOfItrr; j++) {
arr[i][j] = (int) (Math.random() * 1000);
}
}
}
public static void testWithForLoops() {
long start = System.nanoTime();
int max = arr[0][0];
for (int i = 0; i < numOfItrr; i++) {
for (int j = 0; j < numOfItrr; j++) {
if (arr[i][j] > max) {
max = arr[i][j];
}
}
}
long end = System.nanoTime();
System.out.println("time With loops = " + (end - start));
}
public static void testWithCounter() {
long start = System.nanoTime();
int counter = 0;
int max = arr[0][0];
for (int i = 0; i < numOfItrr; i++) {
if (arr[counter][i] > max) {
max = arr[counter][i];
}
if (i == numOfItrr - 1 && counter < numOfItrr - 1) {
counter++;
i = 0;
}
}
long end = System.nanoTime();
System.out.println("time With Counter = " + (end - start));
}
}
这是示例结果
====================开始=====================
数量:10000
循环时间 = 86591300
计数器时间 = 110362600
====================完成=====================
====================开始=====================
数量:30000
循环时间 = 754436400
使用计数器的时间 = 978285600
====================完成=====================
====================开始=====================
数量:30000
循环时间 = 754230300
使用计数器的时间 = 970180300
====================完成=====================
你的两个解的时间复杂度是一样的
有些人犯了只计算循环次数的错误。因此,如果只有一个可识别的循环,他们会说这是一种线性算法。但事实并非如此,它仍然是二次方的。你要检查的是重复的操作重复了多少次。
在这种情况下,让我们专注于比较(将数组值与 1 进行比较)。这是重复次数最多的操作(您可以自己验证),因此它重复的次数与 N(数组一维的大小,意味着数组本身是 NxN)成正比,这决定了大 O。
因此,在您的第一个双循环示例中,每次执行内部循环时,都会进行 N 次比较。由于外循环,内循环本身重复了 N 次,因此你有 NxN 次比较,最后得到 O(N²)。
在您的第二个单循环示例中,比较执行的迭代次数与循环一样多。假设最坏的情况,1 在最后一个单元格中,这里会发生什么?每次到达一行的末尾时,都会重置 i。所以你又开始从 0 数到 N-1。因此,您对 counter = 0
进行了 N 次比较,然后对 counter = 1
进行了 N 次比较,依此类推,直到 counter
达到 N-1。因此,您再次进行了 NxN 次比较,因此 O(N²)。
所以检查循环只是一个经验法则。您真正应该计算的是 operations.