《Thinking in Java》读书笔记——第16章 数 组

Arrays的实用功能

1. equals()

用于比较两个数组是否相等,比较方法较简单。

public static boolean equals(int[] a, int[] a2) {
    
    
        if (a==a2)
            return true;
        if (a==null || a2==null)
            return false;

        int length = a.length;
        if (a2.length != length)
            return false;

        for (int i=0; i<length; i++)
            if (a[i] != a2[i])
                return false;

        return true;
    }

2. sort()

对数组进行排序,采用快速排序(以int为例),可以传入的参数有:

  1. 一个数组
  2. 一个数组,排序的起始位置和末尾位置
  3. 一个数组和自己定义的比较器Comparator来实现比较

2.1 传入参数是一个数组时:

public static void sort(int[] a) {
    
    
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }

当数组元素小于286个时使用快速排序

	private static final int QUICKSORT_THRESHOLD = 286;

    static void sort(int[] a, int left, int right,
                     int[] work, int workBase, int workLen) {
    
    
        // Use Quicksort on small arrays
        if (right - left < QUICKSORT_THRESHOLD) {
    
    
            sort(a, left, right, true);
            return;
        }

在数组长度小于阈值47时采用插入排序

private static final int INSERTION_SORT_THRESHOLD = 47;

// Use insertion sort on tiny arrays
        if (length < INSERTION_SORT_THRESHOLD)

大于则采用双轴快排,代码较长可自己查阅

主要思路

  • 是选两个轴 pivot1 和 pivot2 可以选择第一个和最后一个元素
    注意:pivot1 要小于pivot2,否则交换位置或者选取其他元素
    两个轴心可以将整个数组分成四个部分
  1. 小于pivot1的部分 (left part)
  2. 大于pivot1小于pivot2的部分 (center part)
  3. 大于pivot2的部分 (right part)
  4. 未经过排序操作的部分 (?)
 * Partitioning:
 *
 *   left part           center part                   right part
 * +--------------------------------------------------------------+
 * |  < pivot1  |  pivot1 <= && <= pivot2  |    ?    |  > pivot2  |
 * +--------------------------------------------------------------+
 *               ^                          ^       ^
 *               |                          |       |
 *              less                        k     great
*
  • 然后将a[k]的值与 pivot1 和 pivot2 进行比较 并插入合适的位置,然后改变三个指针 less, k, great的位置。
  • 循环执行上一步直到未排序的区域没有元素
  • pivot1left part 的最后一个元素交换。将pivot2right part 的第一个元素交换。
  • 继续递归调用将三个部分排序

2.2 传入参数是一个数组和比较器时:

需要实现 Comparator 接口的 compare方法 :

Arrays.sort(arr, new Comparator<Integer>() {
    
    
            @Override
            public int compare(Integer o1, Integer o2) {
    
    
                return o1 - o2;
            }
        });

当返回 o1 - o2 时,是升序排列,而 o2 - o1 则是降序排列,为什么会这样呢?

源码中是这样写的:

    public static <T> void sort(T[] a, Comparator<? super T> c) {
    
    
        if (c == null) {
    
    
            sort(a);
        } else {
    
    
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, c);//进入此方法
            else
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }

此处显然跳进了else legacyMergeSort() 的部分

    private static <T> void legacyMergeSort(T[] a, Comparator<? super T> c) {
    
    
        T[] aux = a.clone();
        if (c==null)
            mergeSort(aux, a, 0, a.length, 0);
        else
            mergeSort(aux, a, 0, a.length, 0, c);//进入此方法
    }

然后又进了有c参数的第二个方法的部分

    private static void mergeSort(Object[] src,
                                  Object[] dest,
                                  int low, int high, int off,
                                  Comparator c) {
    
    
        int length = high - low;

        // Insertion sort on smallest arrays
        if (length < INSERTIONSORT_THRESHOLD) {
    
    
            for (int i=low; i<high; i++)
                for (int j=i; j>low && c.compare(dest[j-1], dest[j])>0; j--)//要点
                    swap(dest, j, j-1);
            return;
        }

        // Recursively sort halves of dest into src
        int destLow  = low;
        int destHigh = high;
        low  += off;
        high += off;
        int mid = (low + high) >>> 1;
        mergeSort(dest, src, low, mid, -off, c);
        mergeSort(dest, src, mid, high, -off, c);

        // If list is already sorted, just copy from src to dest.  This is an
        // optimization that results in faster sorts for nearly ordered lists.
        if (c.compare(src[mid-1], src[mid]) <= 0) {
    
    
           System.arraycopy(src, low, dest, destLow, length);
           return;
        }

        // Merge sorted halves (now in src) into dest
        for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
    
    
            if (q >= high || p < mid && c.compare(src[p], src[q]) <= 0)//要点
                dest[i] = src[p++];
            else
                dest[i] = src[q++];
        }
    }

当元素小于 7 个时,使用插入排序,和比较器相关的主要内容是

 for (int j=i; j>low && c.compare(dest[j-1], dest[j])>0; j--)
	swap(dest, j, j-1);

可以看到当c.compare(dest[j-1], dest[j]) > 0 的时候交换两者位置,如最初所说 o1 - o2 如果大于0则交换位置,则把更大的 o1 交换到了后面,所以实现了升序,而降序也同理。

当元素数量较多时,则使用归并排序

// Merge sorted halves (now in src) into dest
for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
    
    
            if (q >= high || p < mid && c.compare(src[p], src[q]) <= 0)
                dest[i] = src[p++];
            else
                dest[i] = src[q++];
        }

源码所写 c.compare(src[p], src[q]) <= 0 表示当返回值小于零时,将第一个参数传入目标数组,大于零则是第二个参数传入目标数组,目标数组下标递增,如前文,o1 - o2 当小于0则表示o1更小,则率先传入了o1 实现了升序排列,若 o2 - o1 小于零,则表示o2更小,而该语句同样是把第一个参数就是o1传入目标数组,从而实现降序。

3. binarySearch()

在已经排序的数组中用二分查找,查找元素

    public static int binarySearch(int[] a, int key) {
    
    
        return binarySearch0(a, 0, a.length, key);
    }


    private static int binarySearch0(int[] a, int fromIndex, int toIndex,
                                     int key) {
    
    
        int low = fromIndex;
        int high = toIndex - 1;

        while (low <= high) {
    
    
            int mid = (low + high) >>> 1;
            int midVal = a[mid];

            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }

猜你喜欢

转载自blog.csdn.net/weixin_48922154/article/details/113405973