1.4.12 编写一个程序,有序打印给定的两个有序数组(含有N个int值)中的所有公共元素,程序在最坏情况下所需的运行时间应该和N成比。

TO GPT: 我的答案, 是否符合要求?

基于 “时间复杂度” 的考虑, 是我想到这个基础解答方法的关键因素:

  • N个元素的数组遍历, 时间复杂度必然是O(N).

  • 如果要想保证算法复杂度依然是O(N), 那就必须保证进行 常数次 的 数组遍历.

我的初版代码如下:

public class Answer1412 {
    public static void printCommon(int[] a, int[] b) {
        int N = a.length;
        if (N < 1 || N != b.length) {
            return;
        }

        int cur_a = a[0];
        int cur_b = a[0];
        if (cur_a == cur_b) {
            StdOut.println(cur_a);
        }

        int i_a = 1;
        int i_b = 1;

        // 基本策略, 数组a和b,交叉遍历. 这样,最大执行次数是 2N. 时间复杂度依然是O(N).
        for (; i_a < N && i_b < N;) {
            for (; i_a < a.length; i_a++) {
                if (cur_a == a[i_a]) {
                    continue;
                }

                cur_a = a[i_a];

                if (cur_a == cur_b) {
                    StdOut.println(cur_a);
                } else if (cur_a > cur_b) {
                    break;
                }
            }

            for (; i_b < b.length; i_b++) {
                if (cur_b == b[i_b]) {
                    continue;
                }

                cur_b = b[i_b];

                if (cur_a == cur_b) {
                    StdOut.println(cur_a);
                } else if (cur_b > cur_a) {
                    break;
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] a = { 1, 2, 2, 2, 3, 4, 5, 6 };
        int[] b = { 1, 2, 3, 6, 6, 6, 7, 8 };

        printCommon(a, b);
    }
}

GPT 的回答总结:

  • 时间复杂度确实是 O(N), 符合要求.

  • GPT 找到了我未曾察觉的初始化错误:

int cur_b = a[0];
  • GPT 给出了一个更简洁的版本, 基于 “双指针法”. 我修当修改后, 如下:
package algs4;

public class Answer1412Gpt {
    public static void printCommon(int[] a, int[] b) {
        int N = a.length;
        if (N < 1 || N != b.length) {
            return;
        }

        int i = 0, j = 0;
        while (i < N && j < N){
            if (a[i] < b[j]){
                i++;
            } else if (a[i] > b[j]){
                j++;
            } else {
                System.out.println(a[i]);
                // 如果下一个元素与当前元素相同,跳过
                while(i+1 < N && a[i] == a[i+1]){
                    i++;
                }
                while(j+1 < N && b[j] == b[j+1]){
                    j++;
                }
                i++;
                j++;
            }
        }
    }

    public static void main(String[] args) {
        int[] a = { 1, 2, 2, 2, 3, 4, 5, 6 };
        int[] b = { 1, 2, 3, 6, 6, 6, 7, 8 };
        printCommon(a, b);
    }
}

我对两个版本的算法评价: 我的更直观, GPT的方法, 扩展性更强.

  • 对于没有接触过 “双指针” 方法的人, 我的解题方法, 可能会更直观, 更便于理解. 虽然代码略有些冗余, 但是交叉遍历的思路, 应该是极容易想到的.

  • GPT 的算法, 不仅是更直观. 和我的方法, 本质上的 “抽象” 内容也是不一样的.

  • 我的方法, 其实是把两个数组, 当作两个独立的个体, 独立去遍历; 但是 GPT 的双指针法, 本质上, 把两个数组看作一个整体, 在单次循环中, 基于情况, 去调整某个数组对应的指针.

  • GPT 的算法抽象, 更便于简化类似问题的处理方法.如: 找出三个数组的共同元素.如果对 “双指针” 法有了解, 知道将将所有数组看作一个整体, 把不同数组的遍历, 转换为对数组指针的同时操作, 会很自然地想到 “三指针法”.

更进一步: 我做了一个找到三个数组共同元素的 “三指针” 解法.

编写一个程序,有序打印给定的三个有序数组(含有N个int值)中的所有公共元素,程序在最坏情况下所需的运行时间应该和N成比。

package algs4;

public class Answer1412GptExtend {
    public static void printCommon(int[] a, int[] b, int[] c) {
        int N = a.length;
        if (N < 1 || N != b.length || N != c.length) {
            return;
        }

        int i = 0, j = 0, k = 0;
        while (i < N && j < N) {
            if (a[i] < b[j] || a[i] < c[k]) {
                i++;
            } else if (b[j] < a[i] || b[j] < c[k]) {
                j++;
            } else if (c[k] < b[j] || c[k] < a[i]) {
                k++;
            } else {
                System.out.println(a[i]);
                // 如果下一个元素与当前元素相同,跳过
                while (i + 1 < N && a[i] == a[i + 1]) {
                    i++;
                }
                while (j + 1 < N && b[j] == b[j + 1]) {
                    j++;
                }
                while (k + 1 < N && c[k] == c[k + 1]) {
                    k++;
                }
                i++;
                j++;
                k++;
            }
        }
    }

    public static void main(String[] args) {
        int[] a = { 1, 2, 2, 2, 3, 4, 5, 6 };
        int[] b = { 1, 2, 3, 6, 6, 6, 7, 8 };
        int[] c = { 0, 2, 3, 6, 6, 6, 7, 9 };

        printCommon(a, b, c);
    }
}

GPT 对话附图:

注意: gpt token 太贵.我 reset 了部分对话. 所以看到的GPT的代码输出, 可能和上文描述的不完全一致.无论如何, 重要问题, 适当多角度问问, 可能会有不同的意外收获.

1412

1412_more