我正在解决一个问题。在17个测试案例中,10个工作正常并且在不到一秒的时间内得到结果,但在7个案例中,它花费了2秒,超出了时间限制。以下是代码
import java.util.*;
import java.io.*;
class TestClass
{
static PrintWriter wr = new PrintWriter(System.out);
public static void func1(int arr[], int n)
{
int temp = arr[0];
for (int jj = 0; jj < n; jj++)
{
if (jj == (n - 1))
arr[jj] = temp;
else
arr[jj] = arr[jj + 1];
}
}
public static void func2(int arr[], int n, int rt)
{
int count = 0;
for (int a = 0; a < n; a++)
{
for (int b = a; b < n; b++)
{
if (arr[a] > arr[b])
count++;
}
}
if (rt == (n - 1))
wr.print(count);
else
wr.print(count + " ");
}
public static void main(String args[]) throws Exception
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = br.readLine().trim();
StringTokenizer st = new StringTokenizer(str);
int t = Integer.parseInt(st.nextToken());
for (int i = 0; i < t; i++) //for test cases
{
str = br.readLine().trim();
st = new StringTokenizer(str);
int n = Integer.parseInt(st.nextToken());
int arr[] = new int[n];
str = br.readLine().trim();
st = new StringTokenizer(str);
for (int j = 0; j < n; j++) //to take input of array for each test case
{
arr[j] = Integer.parseInt(st.nextToken());
}
for (int rt = 0; rt < n; rt++) //for number of times circular shifting of array is done
{
func1(arr, n); //circularly shifts the array by one position
func2(arr, n, rt); //prints the number of inversion counts
}
if (i != (t - 1))
wr.println();
}
wr.close();
br.close();
}
}
有人可以建议如何优化代码,以便花费更少的时间执行。 我知道与Scanner和System.out.print相比,BufferReader和PrintWriter花费的时间更少。我之前使用过扫描仪和System.out.print,但后来改了它,希望花更少的时间,但没有用。我也是在没有使用func1和func2的情况下提前完成的,只在main中执行了所有操作。两种情况下的时间保持不变。 我在所有情况下获得当前输出,因此代码是正确的,我只需要帮助优化它。
答案 0 :(得分:1)
您使用的网站从过去的节目比赛中获取问题。我认为这是一个熟悉的问题
与大多数优化问题一样,首选步骤如下:
在您的情况下,您有一个数组,并且您希望旋转它多次,然后从旋转位置处理它。
旋转数组是一项非常昂贵的操作,因为您通常需要将数组中的每个元素复制到一个新位置。更糟糕的是,你正在以最简单的方式进行操作,你需要为需要旋转的每一步旋转数组一步。
因此,如果您有一个需要旋转45步的100个元素数组,那么您将拥有(每个元素交换3个副本)100 * 45 * 3个副本来执行旋转。
在上面的示例中,更好的方法是找出一次旋转数组45个元素的例程。有很多方法可以做到这一点。最简单的是将RAM要求加倍并且只有两个数组
b [x] = a [(mod(x + 45),a.length)]
更快&#34;做得更少&#34;将永远不会旋转数组,而是反向执行计算。这在概念上是旋转阵列中所需索引与预旋转阵列中的实际索引的函数。这样可以避免所有复制,并且索引号(由于在数学处理单元中被大量操作)已经存储在CPU寄存器中,这是计算机中最快的RAM。
请注意,一旦在原始数组中有起始索引,就可以计算下一个索引,而无需再次进行计算。
我可能读过这个问题有点不对劲;因为,没有写出来强调要解决的问题。但是,上述核心原则适用,您可以将它们应用于编程挑战的具体细节。
An example of a faster rotate that does less
public static void func1(int arr[], int shift) {
int offset = shift % arr.length;
int [] rotated = new int[arr.length];
// (arr.length - 1) is the last index, walk up till we need to copy from the head of arr
for (int index = 0; index < (arr.length - 1) - offset; index++) {
rotated[index] = arr[index+offset];
}
// copy the end of the array back into the beginning
for ( int index = (arr.length - 1) - offset; index < arr.length; index++ ) {
rotated[index] = (offset - ((arr.length - 1) - index) - 1);
}
System.arraycopy(rotated, 0, arr, 0, arr.length);
}
这会在一次传递中将数组复制到其旋转位置,而不是为每个要旋转的索引执行传递。
答案 1 :(得分:0)
优化的第一条规则(已经确定有必要)是使用分析器。这会计算调用方法的次数,并测量每种方法中的累计时间,并为您提供报告。
如果您只运行几次方法很慢,则无关紧要。如果你运行它数十万次,你需要让它更快,或者运行它的次数更少。
如果您使用的是主流IDE,那么您已经拥有了一个分析器。阅读其文档并使用它。
另一个优化的第一条规则是,如果已经有关于你试图解决的问题的文献,请阅读它。我们大多数人可能独立发明了泡泡排序。我们很少有人想出QuickSort,但这是一个更好的解决方案。
看起来好像是在计算数组中的反转。鉴于这种天真的方法,你的实现效果和你能得到的效率一样高。
for(int i=0; i< array.length; i++) {
int n1 = array[i];
for(int j=i+1; j< array.length; j++) {
n2 = array[j];
if(n1 > n2) {
count++;
}
}
}
对于长度为l
的数组,这将需要( l - 1) + ( l - 2 ) ... 1
- 这是三角数,并且与l
的平方成比例增长。
所以对于l=1000
你正在进行~500,000次比较。然后,因为你重复数组的所有1000次旋转的计数,那将是500,000,000次比较,这绝对是事情开始花费大量时间的那种数字。
Google inversion count
的Google搜索显示了一种更复杂的方法,即执行合并排序,在遇到它们时计算反转。
否则,我们需要寻找大量循环迭代的机会。循环内的循环会产生大数字。另一个循环内部循环内的循环会产生更大的数字。
你有:
for (int i = 0; i < t; i++) {
// stuff removed
for (int rt = 0; rt < n; rt++) {
// snip
func2(arr, n, rt); //prints the number of inversion counts
}
// snip
}
public static void func2(int arr[], int n, int rt) {
// snip
for (int a = 0; a < n; a++) {
for (int b = a; b < n; b++) {
// stuff
}
}
// snip
}
这是循环的四个级别。查看慢速测试的输入值,并计算出n * n * n * t
是什么 - 表示它将在内部块中完成工作的次数。
我们不知道您的算法应该实现什么。但想想你是否在这些循环中做了两次相同的事情。
看起来func1()
应该旋转一个数组。看看System.arrayCopy()
一次移动整个数组块。大多数CPU都会在一次操作中执行此操作。