023 排序

本贴最后更新于 2017 天前,其中的信息可能已经沧海桑田

本文为《Java 语言程序设计》第十版 章节笔记

23.1 选择排序

选择排序:假设要按升序排列一个数列。先找到数列中最小的数,然后将它第一个元素交换。接下来,在剩下的数中找到最小数,将它和第二个元素交换,以此类推,直到数列中仅剩一个数为止。

public class SelectionSort {
    /** The method for sorting the numbers */
    public static double[] selectionSort(double[] list) {
        for (int i = 0; i < list.length - 1; i++) {
            // Find the minimum in the list[i...list.length-1]
            double currentMin = list[i];
            int currentMinIndex = i;

            for (int j = i + 1; j < list.length; j++) {
                if (currentMin > list[j]) {
                    currentMin = list[j];
                    currentMinIndex = j;
                }
            }

            // Swap list[i] with list[currentMinIndex] if necessary
            if (currentMinIndex != i) {
                list[currentMinIndex] = list[i];
                list[i] = currentMin;
            }
        }
        return list;
    }
}

23.2 插入排序

为了将 元素 list[i] 插入 数组 list[0..i-1] 中,需要将 list[i] 存储在一个名为 currentElement 的临时变量中。

如果 list[i-1]>currentElement,就将 list[i-1] 移到 list[i] ;如果 list[i-2]>currentElement,就将 list[i-2] 移到 list[i-1],依次类推,直到 list[i-k]<=currentElement 或者 k>i (传递的是排好序的数列的第一个元素)。将 currentElement 赋值给 list[i-k+1]。

public class InsertionSort {
    public static int[] insertionSort(int[] list){
        for (int i = 1; i < list.length; i++){
            int currentElement = list[i];
            int k;

            for (k = i - 1; k > 0 && list[k] > currentElement; k--){
            list[k + 1] = list[k];
        }

        list[k + 1] = currentElement;
        }
        return list;
    }
}

插入排序算法的复杂度为 O(n^2)。

23.3 冒泡排序

冒泡排序算法需要历遍几次数组。

在每次历遍中,连续比较相邻的元素。如果某一对元素时降序,则互换它们的值;否则,保持不变。

由于较小的值像“气泡”一行逐渐浮向顶部,而较大的值沉向底部,所以称为冒泡排序(bubble sort)或下沉排序(sinking sort)。

第一次历遍之后,最后一个元素称为数组中最大数。第二遍历遍之后,倒数第二个元素称为数组中的第二大数。整个过程持续到所有元素都已排好序。

如果某次历遍中没有发生交换,那么就不必进行下一次历遍,因为所有元素都已经排好序了。

public class BubbleSort {
    public static void bubbleSort(int[] list){
        boolean needNextPass = true;

        for (int k = 1; k < list.length && needNextPass; k++){
            needNextPass = false;
            for (int i = 0; i < list.length - k; i++) {
                if (list[i] > list[i + 1]){
                    int temp = list[i];
                    list[i] = list[i + 1];
                    list[i + 1] = temp;

                    needNextPass = true;
                }
            }
        }
    }

    public static void main(String[] args){
        int[] list = {2,4,5,3,5,62,4,5,2,99,48,47,458,1290};
        bubbleSort(list);
        for (int c: list){
            System.out.print(c);
        }
    }
}

最佳的情况下,算法只需历遍一次,冒泡排序的时间复杂度为 O(n);最差的需要进行 n-1 次,时间复杂度为 O(n^2)。

23.4 归并排序

归并排序算法将数组分为两半,对每部分递归地应用归并排序。在两部分都排好序后,对它们进行归并。

public class MergeSort {
    /** The method for sorting the numbers */
    public static void mergeSort(int[] list) {
        if (list.length > 1) {

            // Merge sort the first half
            int[] firstHalf = new int[list.length / 2];
            System.arraycopy(list, 0, firstHalf, 0, list.length / 2);
            mergeSort(firstHalf);

            //Merge sort the second half
            int secondHalfLength = list.length - list.length / 2;
            int[] secondHalf = new int[secondHalfLength];
            System.arraycopy(list, list.length / 2, secondHalf, 0, secondHalfLength);
            mergeSort(secondHalf);

            //Merge firstHalf with secondHalf into list
            merge(firstHalf, secondHalf, list);
        }
    }

    /** Merge two sorted lists */
    public static void merge(int[] list1, int[] list2, int[] temp){
        int current1 = 0;//Current index in list1;
        int current2 = 0;//Current index in list2;
        int current3 = 0;//Current index in temp;

        while (current1 < list1.length && current2 < list2.length){
            if (list1[current1] <list2[current2]){
                temp[current3++] = list1[current1++];
            } else {
                temp[current3++] = list2[current2++];
            }

            while (current1 < list1.length) {
                temp[current3++] = list1[current1++];
            }

            while (current2 < list2.length) {
                temp[current3++] = list2[current2++];
            }
        }
    }

    /** A test method */
    public static void main(String[] args) {
        int[] list = {3,4,5,65,67,7,34,3,345,88,99,766,553};
        mergeSort(list);
        for (int e: list){
            System.out.print(e + " ");
        }
    }
}

归并排序的复杂度为 O(nlogn),由于 选择排序、插入排序和冒泡排序,因为这些排序算法的复杂度为 O(n^2)。

23.5 快速排序

快速排序算法在数组中选择一个称为主元(pivot)的元素,将数组分为两部分,使得第一部分中的所有元素都小于或等于主元,而第二部分中的所有元素都大于主元。

对第一部分递归地应用快速排序算法,然后对第二部分递归地应用快速排序算法。

public class QuickSort {
    public static void  quickSort(int[] list) {
        quickSort(list, 0, list.length - 1);
    }

    public static void quickSort(int[] list, int first, int last) {
        if (last > first) {
            int pivotIndex = partition(list, first, last);
            quickSort(list, first, pivotIndex - 1);
            quickSort(list, pivotIndex + 1, last);
        }
    }

    /** Partition the array list[first...last] */
    public static int partition(int[] list, int first, int last) {
        int pivot = list[first];
        int low = first + 1;
        int high =  last;

        while (high > low) {
            //Searching forward from left find the first number that is bigger than pivot
            while(low <= high && list[low] <= pivot){
                low++;
            }

            //Search backward from right find the first number that is smaller than pivot
            while (low <= high && list[high] > pivot){
                high--;
            }

            //Swap two elements in the list
            if (high > low) {
                int temp = list[high];
                list[high] = list[low];
                list[low] = temp;
            }
        }

        while (high > first && list[high] >= pivot) {
            high--;
        }

        //Swap pivot with list[high]
        if (pivot > list[high]){
            list[first] = list[high];
            list[high] = pivot;;
            return high;
        } else {
            return first;
        }
    }

    /**A test method */
    public static void main(String[] args) {
        int[] list = {2,3,2,5,6,1,-2,3,14,12};
        quickSort(list);
        for (int e: list){
            System.out.print(e + " ");
        }
    }
}

23.6 堆排序

实现 Heap 类:

public class Heap<E extends Comparable> {
    private java.util.ArrayList<E> list = new java.util.ArrayList<E>();
  
    /** Create a default heap */
    public Heap() {
    }
  
    /** Create a heap from an array of objects */
    public Heap(E[] objects) {
      for (int i = 0; i < objects.length; i++)
        add(objects[i]);
    }
  
    /** Add a new object into the heap */
    public void add(E newObject) {
      list.add(newObject); // Append to the heap
      int currentIndex = list.size() - 1; // The index of the last node
  
      while (currentIndex > 0) {
        int parentIndex = (currentIndex - 1) / 2;
        // Swap if the current object is greater than its parent
        if (list.get(currentIndex).compareTo(
            list.get(parentIndex)) > 0) {
          E temp = list.get(currentIndex);
          list.set(currentIndex, list.get(parentIndex));
          list.set(parentIndex, temp);
        }
        else
          break; // the tree is a heap now
  
        currentIndex = parentIndex;
      }
    }
  
    /** Remove the root from the heap */
    public E remove() {
      if (list.size() == 0) return null;
  
      E removedObject = list.get(0);
      list.set(0, list.get(list.size() - 1));
      list.remove(list.size() - 1);
  
      int currentIndex = 0;
      while (currentIndex < list.size()) {
        int leftChildIndex = 2 * currentIndex + 1;
        int rightChildIndex = 2 * currentIndex + 2;
  
        // Find the maximum between two children
        if (leftChildIndex >= list.size()) break; // The tree is a heap
        int maxIndex = leftChildIndex;
        if (rightChildIndex < list.size()) {
          if (list.get(maxIndex).compareTo(
              list.get(rightChildIndex)) < 0) {
            maxIndex = rightChildIndex;
          }
        }
  
        // Swap if the current node is less than the maximum
        if (list.get(currentIndex).compareTo(
            list.get(maxIndex)) < 0) {
          E temp = list.get(maxIndex);
          list.set(maxIndex, list.get(currentIndex));
          list.set(currentIndex, temp);
          currentIndex = maxIndex;
        }
        else
          break; // The tree is a heap
      }
  
      return removedObject;
    }
  
    /** Get the number of nodes in the tree */
    public int getSize() {
      return list.size();
    }
  }

实现堆排序:

public class HeapSort {
    /** Heap sosrt method */
    public static <E extends Comparable<E>> void heapSort(E[] list) {
        //Create a Heap of integers
        Heap<E> heap = new Heap<>();

        //Add elements to the heap
        for (int i = 0; i < list.length; i++) {
            heap.add(list[i]);
        }

        //Remove elements from the heap
        for (int i = list.length - 1; i >= 0; i--) {
            list[i] = heap.remove();
        }
    }

    /**A test method */
    public static void main(String[] args) {
        Integer[] list = {-44,-5,-3,3,3,33,1,-4,0,1,23,99};
        heapSort(list);
        for (int e: list) {
            System.out.print(e + " ");
        }
    }
}

堆排序的时间复杂度为 O(nlogn)。

23.7 桶排序和基数排序

桶排序和基数排序是 对整数 进行的排序的高效算法。

桶排序的时间复杂度为 O(n+t),基数排序的时间复杂度为 O(dn)。

23.8 外部排序

时间复杂度为 O(nlogn)。

import java.io.*;

public class SortLargeFile {
  public static final int MAX_ARRAY_SIZE = 43;
  public static final int BUFFER_SIZE = 100000;

  public static void main(String[] args) throws Exception {
    // Sort largedata.dat to sortedfile.dat
    sort("largedata.dat", "sortedfile.dat");

    // Display the first 100 numbers in the sorted file
    displayFile("sortedfile.dat");
  }
  
  /** Sort data in source file and into target file */
  public static void sort(String sourcefile, String targetfile)
      throws Exception {
    // Implement Phase 1: Create initial segments
    int numberOfSegments =
      initializeSegments(MAX_ARRAY_SIZE, sourcefile, "f1.dat");

    // Implement Phase 2: Merge segments recursively
    merge(numberOfSegments, MAX_ARRAY_SIZE,
      "f1.dat", "f2.dat", "f3.dat", targetfile);
  }

  /** Sort original file into sorted segments */
  private static int initializeSegments
      (int segmentSize, String originalFile, String f1)
      throws Exception {
    int[] list = new int[segmentSize];
    DataInputStream input = new DataInputStream(
      new BufferedInputStream(new FileInputStream(originalFile)));
    DataOutputStream output = new DataOutputStream(
      new BufferedOutputStream(new FileOutputStream(f1)));

    int numberOfSegments = 0;
    while (input.available() > 0) {
      numberOfSegments++;
      int i = 0;
      for ( ; input.available() > 0 && i < segmentSize; i++) {
        list[i] = input.readInt();
      }

      // Sort an array list[0..i-1]
      java.util.Arrays.sort(list, 0, i);

      // Write the array to f1.dat
      for (int j = 0; j < i; j++) {
        output.writeInt(list[j]);
      }
    }

    input.close();
    output.close();
    return numberOfSegments;
  }

  private static void merge(int numberOfSegments, int segmentSize,
      String f1, String f2, String f3, String targetfile)
      throws Exception {
    if (numberOfSegments > 1) {
      mergeOneStep(numberOfSegments, segmentSize, f1, f2, f3);
      merge((numberOfSegments + 1) / 2, segmentSize * 2,
        f3, f1, f2, targetfile);
    }
    else { // Rename f1 as the final sorted file
      File sortedFile = new File(targetfile);
      if (sortedFile.exists()) sortedFile.delete();
      new File(f1).renameTo(sortedFile);
    }
  }

  private static void mergeOneStep(int numberOfSegments,
      int segmentSize, String f1, String f2, String f3)
      throws Exception {
    DataInputStream f1Input = new DataInputStream(
      new BufferedInputStream(new FileInputStream(f1), BUFFER_SIZE));
    DataOutputStream f2Output = new DataOutputStream(
      new BufferedOutputStream(new FileOutputStream(f2), BUFFER_SIZE));

    // Copy half number of segments from f1.dat to f2.dat
    copyHalfToF2(numberOfSegments, segmentSize, f1Input, f2Output);
    f2Output.close();

    // Merge remaining segments in f1 with segments in f2 into f3
    DataInputStream f2Input = new DataInputStream(
      new BufferedInputStream(new FileInputStream(f2), BUFFER_SIZE));
    DataOutputStream f3Output = new DataOutputStream(
      new BufferedOutputStream(new FileOutputStream(f3), BUFFER_SIZE));

    mergeSegments(numberOfSegments / 2,
      segmentSize, f1Input, f2Input, f3Output);

    f1Input.close();
    f2Input.close();
    f3Output.close();
  }

  /** Copy first half number of segments from f1.dat to f2.dat */
  private static void copyHalfToF2(int numberOfSegments,
      int segmentSize, DataInputStream f1, DataOutputStream f2)
      throws Exception {
    for (int i = 0; i < (numberOfSegments / 2) * segmentSize; i++) {
      f2.writeInt(f1.readInt());
    }
  }

  /** Merge all segments */
  private static void mergeSegments(int numberOfSegments,
      int segmentSize, DataInputStream f1, DataInputStream f2,
      DataOutputStream f3) throws Exception {
    for (int i = 0; i < numberOfSegments; i++) {
      mergeTwoSegments(segmentSize, f1, f2, f3);
    }

    // f1 may have one extra segment, copy it to f3
    while (f1.available() > 0) {
      f3.writeInt(f1.readInt());
    }
  }

  /** Merges two segments */
  private static void mergeTwoSegments(int segmentSize,
    DataInputStream f1, DataInputStream f2,
    DataOutputStream f3) throws Exception {
    int intFromF1 = f1.readInt();
    int intFromF2 = f2.readInt();
    int f1Count = 1;
    int f2Count = 1;
  
    while (true) {
      if (intFromF1 < intFromF2) {
        f3.writeInt(intFromF1);
        if (f1.available() == 0 || f1Count++ >= segmentSize) {
          f3.writeInt(intFromF2);
          break;
        }
        else {
          intFromF1 = f1.readInt();
        }
      }
      else {
        f3.writeInt(intFromF2);
        if (f2.available() == 0 || f2Count++ >= segmentSize) {
          f3.writeInt(intFromF1);
          break;
        }
        else {
          intFromF2 = f2.readInt();
        }
      }
    }
  
    while (f1.available() > 0 && f1Count++ < segmentSize) {
      f3.writeInt(f1.readInt());
    }
  
    while (f2.available() > 0 && f2Count++ < segmentSize) {
      f3.writeInt(f2.readInt());
    }
  }
  
  /** Display the first 100 numbers in the specified file */
  public static void displayFile(String filename) {
    try {
      DataInputStream input =
        new DataInputStream(new FileInputStream(filename));
      for (int i = 0; i < 100; i++)
        System.out.print(input.readInt() + " ");
      input.close();
    }
    catch (IOException ex) {
      ex.printStackTrace();
    }
  }
}
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3165 引用 • 8206 回帖 • 1 关注

相关帖子

回帖

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...