【算法】Java实现八种排序算法

162 阅读2分钟

巧记八种排序算法

通过自己编的一句话巧记八种排序算法:选择插入相同的哈值,为了尽快归来,减少垃圾堆积。分别对应着:

  • 冒泡排序
  • 选择排序
  • 插入排序
  • 希尔排序
  • 快速排序
  • 归并排序
  • 堆排序
  • 基数排序 下面将逐一介绍并代码实现。

冒泡排序

基本思想

两个数比较大小,较大的数下沉,较小的数冒起来。

步骤

(1)从后向前比较;

(2)比较相邻的两个数据,如果第二个数小,就交换位置;

(3)两层循环,内层通过flag,如果没有进行任何交换,则可以提前结束。

复杂度

时间复杂度为O(n2)

代码实现

package com.example.quickdemo.sort;

import java.util.Arrays;

public class BubbleSort {
   public static void main(String[] args) {
      int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
      bubbleSort(arr);
      System.out.println(Arrays.toString(arr));
   }

   private static void bubbleSort(int[] arr) {
      if (arr == null || arr.length <= 1) {
         return;
      }

      for (int i = 0; i < arr.length - 1; i++) {
         boolean flag = false;
         for (int j = arr.length - 1; j > i; j--) {
            if (arr[j] < arr[j - 1]) {
               int temp = arr[j];
               arr[j] = arr[j - 1];
               arr[j - 1] = temp;
               flag = true;
            }
         }
         if (!flag) {
            break;
         }
      }
   }
}

选择排序

基本思想

在长度为N的无序数组中,遍历一遍找到最小值,然后要第一个元素交换,不断重复

复杂度

时间复杂度为O(n2)

代码实现

package com.example.quickdemo.sort;

import java.util.Arrays;

public class SelectionSort {
   public static void main(String[] args) {
      int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
      selectionSort(arr);
      System.out.println(Arrays.toString(arr));
   }

   private static void selectionSort(int[] arr) {
      if (arr == null || arr.length <= 1) {
         return;
      }

      for (int i = 0; i < arr.length - 1; i++) {
         int minIndex = i;
         for (int j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[minIndex]) {
               minIndex = j;
            }
         }
         if (minIndex != i) {
            int temp = arr[minIndex];
            arr[minIndex] = arr[i];
            arr[i] = temp;
         }
      }
   }
}

插入排序

在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的,如此循环,直到全部排好顺序。

类似抓扑克牌时插入牌的过程。

复杂度

时间复杂度为O(n2)

代码实现

package com.example.quickdemo.sort;

import java.util.Arrays;

public class InsertSort {
   public static void main(String[] args) {
      int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
      insertSort(arr);
      System.out.println(Arrays.toString(arr));
   }

   private static void insertSort(int[] arr) {
      if (arr == null || arr.length <= 1) {
         return;
      }

      for (int i = 0; i < arr.length - 1; i++) {
         for (int j = i + 1; j > 0; j--) {
            if (arr[j] < arr[j - 1]) {
               int temp = arr[j];
               arr[j] = arr[j - 1];
               arr[j - 1] = temp;
            } else {
               break;
            }
         }
      }
   }
}

希尔排序

基本思想

在要排序的一组数中,根据某一增量分为若干子序列,并对子序列分别进行插入排序;

然后逐渐将增量减小,并重复上述过程,直至增量为1,此时数据序列基本有序,最后进行插入排序。

技巧

最外层是while(gap>=1);

里面三层for循环,第一层表示分为多少组,第二层和第三层是原来的插入排序。

代码实现

package com.example.quickdemo.sort;

import java.util.Arrays;

public class ShellSort {
   public static void main(String[] args) {
      int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
      shellSort(arr);
      System.out.println(Arrays.toString(arr));
   }

   private static void shellSort(int[] arr) {
      if (arr == null || arr.length <= 1) {
         return;
      }

      int gap = arr.length / 2;
      while (gap >= 1) {
         for (int i = 0; i < gap; i++) { // 分的组数
            for (int j = i + gap; j < arr.length; j += gap) {
               for (int k = j; k > i; k -= gap) {
                  if (arr[k] < arr[k - gap]) {
                     int temp = arr[k];
                     arr[k] = arr[k - gap];
                     arr[k - gap] = temp;
                  } else {
                     break;
                  }
               }
            }
         }
         gap /= 2;
      }
   }
}

快速排序

基本思想

是分治思想

步骤

(1)先从数列中取出一个数作为Key值(如取数组的第一个数);

(2)将比这个数小的数全部放在它的左边,大于或等于它的数放在它的右边,等于归属某一边;

(3)对左右两个小数列重复第二步,直至各区间只有一个数(即递归条件为left < right,采用前序遍历)

代码实现

package com.example.quickdemo.sort;

import java.util.Arrays;

public class QuickSort {
   public static void main(String[] args) {
      int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
      quickSort(arr);
      System.out.println(Arrays.toString(arr));
   }

   private static void quickSort(int[] arr) {
      if (arr == null || arr.length <= 1) {
         return;
      }
      sort(arr, 0, arr.length - 1);
   }

   private static void sort(int[] arr, int low, int high) {
      if (low >= high) {
         return;
      }

      int key = arr[low];
      int i = low;
      int j = high;
      while (i < j) {
         while (i < j && key <= arr[j]) {
            j--;
         }
         if (i < j) {
            arr[i] = arr[j];
            i++;
         }

         while (i < j && key > arr[i]) {
            i++;
         }
         if (i < j) {
            arr[j] = arr[i];
            j--;
         }
      }
      arr[i] = key;

      sort(arr, low, i - 1);
      sort(arr, i + 1, high);
   }
}

归并排序

基本思想

是分治思想,是一种建立在归并操作上的算法。

步骤

(1)提取方法实现两个有序的数组合并成一个数组:互相比较,谁小先取谁,指针后移一位,直到一个为空,然后把不为空的数组元素逐个添加到新数组上结束;

(2)通过递归,不断细分数组直到size为1,然后进行两两归并——后序遍历

代码实现

package com.example.quickdemo.sort;

import java.util.Arrays;

public class MergeSort {
   public static void main(String[] args) {
      int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
      mergeSort(arr);
      System.out.println(Arrays.toString(arr));
   }

   private static void mergeSort(int[] arr) {
      if (arr == null || arr.length <= 1) {
         return;
      }
      sort(arr, 0, arr.length - 1);
   }

   private static void sort(int[] arr, int low, int high) {
      if (low >= high) {
         return;
      }

      int mid = low + (high - low) / 2;
      sort(arr, low, mid);
      sort(arr, mid + 1, high);
      sortTwoArray(arr, low, mid, high);
   }

   private static void sortTwoArray(int[] arr, int low, int mid, int high) {
      int[] temp = new int[high - low + 1];
      int firstLow = low;
      int firstHigh = mid;
      int secondLow = mid + 1;
      int secondHigh = high;
      int index = 0;
      while (firstLow <= firstHigh && secondLow <= secondHigh) {
         if (arr[firstLow] < arr[secondLow]) {
            temp[index] = arr[firstLow];
            index++;
            firstLow++;
         } else {
            temp[index] = arr[secondLow];
            index++;
            secondLow++;
         }
      }
      while (firstLow <= firstHigh) {
         temp[index] = arr[firstLow];
         index++;
         firstLow++;
      }

      while (secondLow <= secondHigh) {
         temp[index] = arr[secondLow];
         index++;
         secondLow++;
      }

      for (int i = 0; i < index; i++) {
         arr[low + i] = temp[i];
      }
   }
}

堆排序

基本思想

将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;将堆顶元素与末尾元素交换,将最大元素“沉”到数组末端;重新调整结构,使其满足堆定义,然后交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

技巧

(1)构建堆时从arr.length/2 - 1的索引位置开始(即最后一个非叶子节点),然后从下到上,做右到左,也就是i--;

(2)节点i的左子节点索引为2i+1,右子结点索引为2i+2;

(3)进行元素交换之后,只需要进行一次调整就行(调整0的位置),不再需要完整的重建过程。

复杂度

时间复杂度O(NlogN),没有空间复杂度。

代码实现

package com.example.quickdemo.sort;

import java.util.Arrays;

public class HeapSort {
   public static void main(String[] args) {
      int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
      heapSort(arr);
      System.out.println(Arrays.toString(arr));
   }

   private static void heapSort(int[] arr) {
      if (arr == null || arr.length <= 1) {
         return;
      }

      // 构建堆,从最后一个非叶子结点开始,从下往上,从右往左
      for (int i = arr.length / 2 - 1; i >= 0; i--) {
         adjustHeap(arr, i, arr.length);
      }

      for (int i = arr.length - 1; i > 0; i--) {
         swap(arr, 0, i);
         adjustHeap(arr, 0, i);
      }
   }

   /**
    * @param arr    数组
    * @param parent 调整的结点
    * @param length 数组长度
    */
   private static void adjustHeap(int[] arr, int parent, int length) {
      int temp = arr[parent];
      int childIndex = parent * 2 + 1; // 左子结点
      while (childIndex < length) {
         // 选择左右子结点中较大的
         if (childIndex + 1 < length && arr[childIndex] < arr[childIndex + 1]) {
            childIndex++;
         }

         // 注意:一直都和初始值temp相比
         if (arr[childIndex] > temp) {
            arr[parent] = arr[childIndex];
            parent = childIndex;
            childIndex = parent * 2 + 1;
         } else {
            break;
         }
      }
      arr[parent] = temp;
   }

   private static void swap(int[] arr, int i, int j) {
      int temp = arr[i];
      arr[i] = arr[j];
      arr[j] = temp;
   }
}

基数排序

基本思想

按照位数进行排序,比如从低到高位进行排序,先根据最低位进行排序,排到最高位结束;需要找到最大值的位数是多少,即将会进行多少轮排序。

技巧

原数组放到临时数组时,记得从后往前排序

代码实现

package com.example.quickdemo.sort;

import java.util.Arrays;

public class RadixSort {
   public static void main(String[] args) {
      int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
      radixSort(arr);
      System.out.println(Arrays.toString(arr));
   }

   private static void radixSort(int[] arr) {
      if (arr == null || arr.length <= 1) {
         return;
      }

      // 找到最大值位数
      int bitCount = maxBit(arr);
      int length = arr.length;
      int[] temp = new int[length];
      int[] count = new int[10];
      int radix = 1;
      for (int bit = 1; bit <= bitCount; bit++) {
         // 初始化临时数组
         Arrays.fill(count, 0);

         // 计算元素归属于哪个桶
         for (int i = 0; i < length; i++) {
            int index = (arr[i] / radix) % 10;
            count[index]++;
         }

         // 将count的中值从第一个桶到当前桶的总数和
         for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
         }

         // 将原始数组元素根据当前位的排序结果放到临时数组中,从后往前面,因为后面的值大
         for (int i = length - 1; i >= 0; i--) {
            int index = (arr[i] / radix) % 10;
            count[index]--;
            temp[count[index]] = arr[i];
         }

         // 将临时数组的元素拷贝回原始数组
         for (int i = 0; i < length; i++) {
            arr[i] = temp[i];
         }

         radix *= 10;
      }
   }

   private static int maxBit(int[] arr) {
      int length = arr.length;
      int maxValue = arr[0];
      for (int i = 1; i < length; i++) {
         if (maxValue < arr[i]) {
            maxValue = arr[i];
         }
      }

      int indexCount = 1;
      while (maxValue > 10) {
         maxValue /= 10;
         indexCount++;
      }

      return indexCount;
   }
}

参考资料

排序算法总结

面试必备:八种排序算法原理及Java实现