一.排序算法
就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
1.比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。
选择排序、冒泡排序、插入排序、希尔排序、归并排序、堆排序、快速排序
2.非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。
计数排序、桶排序、基数排序
3.稳定:如果a原本在b前面,而a==b,排序之后a仍然在b的前面。
4.不稳定:如果a原本在b的前面,而a==b,排序之后 a 可能会出现在 b 的后面。
5.时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
6.空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。
二.十大排序
1.选择排序
选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
时间复杂度:O(n^2) 空间复杂度: O(1) 稳定性: 不稳定
2.冒泡排序
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
时间复杂度:O(n^2) 空间复杂度:O(1) 稳定性: 稳定
3.插入排序
插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
时间复杂度:O(n^2) 空间复杂度:O(1) 稳定性: 稳定
4.希尔排序
1959年Shell发明,第一个突破O(n^2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
时间复杂度:O(n^1.3) 空间复杂度:O(1) 稳定性: 不稳定
5.归并排序
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2路归并。
时间复杂度:O(nlogn) 空间复杂度:S(n) 稳定性: 稳定
6.堆排序
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
时间复杂度:O(nlog2n) 空间复杂度:O(1) 稳定性: 不稳定
7.快速排序
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,比另一部分的关键字大,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
时间复杂度:O(nlogn) 空间复杂度:O(1) 稳定性:不稳定
8.计数排序
计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
时间复杂度:O(n+m) 空间复杂度:O(n+m) 稳定性:稳定
9.桶排序
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。
时间复杂度:O(n+m) 空间复杂度:O(n+m) 稳定性:稳定
10.基数排序
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。
时间复杂度:O(n+m) 空间复杂度:O(n+m) 稳定性:稳定
三.代码
1.Test
package p5.排序算法;
/*
算法的执行时间
除了跟算法的策略有关系之外
还跟数据分布情况有关系
数据分布情况:
完全随机 大致有序 大致平稳
选择 5 5 4
冒泡 4 4 5
插入 3 1 3
希尔 2 3 2
归并 1 2 1
单快 1+ 3+ 1+
双快 1+ 1+ 1+
三快 n n n
基排 3- 4 3-
桶排序 4+ 3- 4+
*/
public class TestSort {
public static void main(String[] args) {
ArrayData data = new ArrayData(0);
int[] arr = data.makeData();
test01(arr);
test02(arr);
test03(arr);
test04(arr);
test05(arr);
test06(arr);
test07(arr);
test08(arr);
test09(arr);
test10(arr);
}
private static void test10(int[] arr) {
BucketSort bucketSort = new BucketSort(arr);
Long start = System.currentTimeMillis();
bucketSort.sort();
Long end = System.currentTimeMillis();
System.out.println("桶排序:" + (end - start) + "ms");
}
private static void test09(int[] arr) {
RadixSort radixSort = new RadixSort(arr);
Long start = System.currentTimeMillis();
radixSort.sort();
Long end = System.currentTimeMillis();
System.out.println("基数排序:" + (end - start) + "ms");
}
private static void test08(int[] arr) {
QuickSort03 quickSort03 = new QuickSort03(arr);
Long start = System.currentTimeMillis();
quickSort03.sort();
Long end = System.currentTimeMillis();
System.out.println("三路快排:" + (end - start) + "ms");
}
private static void test07(int[] arr) {
QuickSort02 quickSort02 = new QuickSort02(arr);
Long start = System.currentTimeMillis();
quickSort02.sort();
Long end = System.currentTimeMillis();
System.out.println("双路快排:" + (end - start) + "ms");
}
private static void test06(int[] arr) {
QuickSort01 quickSort01 = new QuickSort01(arr);
Long start = System.currentTimeMillis();
quickSort01.sort();
Long end = System.currentTimeMillis();
System.out.println("单路快排:" + (end - start) + "ms");
}
private static void test05(int[] arr) {
MergeSort mergeSort = new MergeSort(arr);
Long start = System.currentTimeMillis();
mergeSort.sort();
Long end = System.currentTimeMillis();
System.out.println("归并排序:" + (end - start) + "ms");
}
private static void test04(int[] arr) {
ShellSort shellSort = new ShellSort(arr);
Long start = System.currentTimeMillis();
shellSort.sort();
Long end = System.currentTimeMillis();
System.out.println("希尔排序:" + (end - start) + "ms");
}
private static void test03(int[] arr) {
InsertionSort insertionSort = new InsertionSort(arr);
Long start = System.currentTimeMillis();
insertionSort.sort();
Long end = System.currentTimeMillis();
System.out.println("插入排序:" + (end - start) + "ms");
}
private static void test02(int[] arr) {
BubbleSort bubbleSort = new BubbleSort(arr);
Long start = System.currentTimeMillis();
bubbleSort.sort();
Long end = System.currentTimeMillis();
System.out.println("冒泡排序: " + (end - start) + "ms");
}
private static void test01(int[] arr) {
SelectionSort selectionSort = new SelectionSort(arr);
Long start = System.currentTimeMillis();
selectionSort.sort();
Long end = System.currentTimeMillis();
System.out.println("选择排序: " + (end - start) + "ms");
}
}
2.ArrayData
package p5.排序算法;
import java.util.Random;
public class ArrayData {
//产生数据 完全随机0 大致有序1 大致平稳2
private int type;
private Random random = new Random();
private int[] arr = new int[10000];
public ArrayData(int type) {
this.type = type;
}
public int[] makeData() {
if (type == 0) {
for (int i = 0; i < arr.length; i++) {
arr[i] = random.nextInt(10000);
}
} else if (type == 1) {
for (int i = 0; i < arr.length; i++) {
arr[i] = (i + 1) * 100 + random.nextInt(300);
}
} else {
for (int i = 0; i < arr.length; i++) {
arr[i] = 5000 + (i % 2 == 0 ? random.nextInt(500) : -random.nextInt(500));
}
}
return arr;
}
}
3.Sort
package p5.排序算法;
public abstract class Sort {
public int[] arr;
public Sort(){}
public Sort(int[] arr) {
this.arr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
this.arr[i] = arr[i];
}
}
public abstract void sort();
public void swap(int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
4.选择排序
package p5.排序算法;
//选择排序 O(n^2) S(1) 不稳定
public class SelectionSort extends Sort{
public SelectionSort(int[] arr) {
super(arr);
}
@Override
public void sort() {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
swap(i,j);
}
}
}
}
}
5.冒泡排序
package p5.排序算法;
//冒泡排序 O(n^2) S(1) 稳定
public class BubbleSort extends Sort{
public BubbleSort(int[] arr) {
super(arr);
}
@Override
public void sort() {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] < arr[j + 1]) {
swap(j,j + 1);
}
}
}
}
}
6.插入排序
package p5.排序算法;
//插入排序 O(n^2) S(1) 稳定
public class InsertionSort extends Sort{
public InsertionSort(int[] arr) {
super(arr);
}
@Override
public void sort() {
for (int i = 0; i < arr.length; i++) {
int e = arr[i];
int j = 0;
for (j = i; j > 0 && arr[j - 1] > e; j--) {
arr[j] = arr[j - 1];
}
arr[j] = e;
}
}
}
7.希尔排序
package p5.排序算法;
//希尔排序 O(n^1.3) S(1) 不稳定
public class ShellSort extends Sort{
public ShellSort (int[] arr) {
super(arr);
}
@Override
public void sort() {
int len = arr.length;
for (int gap = len / 2; gap > 0; gap = gap / 2) {
for (int i = gap; i < len; i++) {
int e = arr[i];
int j = i;
while (j - gap >= 0 && arr[j - gap] > e) {
arr[j] = arr[j - gap];
j = j - gap;
}
arr[j] = e;
}
}
}
}
8.归并排序
package p5.排序算法;
//归并排序 O(nlogn) S(n) 稳定
public class MergeSort extends Sort{
public MergeSort(int[] arr) {
super(arr);
}
@Override
public void sort() {
mergeSort(0, arr.length - 1);
}
private void mergeSort(int L, int R) {
if (L >= R) {
return;
}
int mid = (L + R) / 2;
//递归排序当前层级的左边
mergeSort(L, mid);
//递归排序当前层级的右边
mergeSort(mid + 1, R);
//左边排序完了 右边排序也完了
//将左右进行合并
//特殊 如果左边的最大值arr[mid]小于等于右边的最小值arr[mid+1] 则不需要合并
if (arr[mid] > arr[mid + 1]) {
merge(L, mid, R);
}
}
private void merge(int L, int mid, int R) {
int[] aux = new int[R - L + 1];
//复制当前层级中 原数组的内容给aux
for (int k = L; k <= R; k++) {
aux[k - L] = arr[k];
}
int i = L;
int j = mid + 1;
for (int k = L; k <= R; k++) {
if (i > mid) { //左边完毕
arr[k] = aux[j - L];
j++;
} else if (j > R) { //右边完毕
arr[k] = aux[i - L];
i++;
} else if (aux[i - L] < aux[j - L]) {
arr[k] = aux[i - L];
i++;
} else {
arr[k] = aux[j - L];
j++;
}
}
}
}
9.单路快速排序
package p5.排序算法;
import java.security.PublicKey;
//单路快速排序
public class QuickSort01 extends Sort{
public QuickSort01(int[] arr) {
super(arr);
}
@Override
public void sort() {
quickSort(0, arr.length - 1);
}
private void quickSort(int L, int R) {
if (L >= R) {
return;
}
//先对数组进行划分 并返回划分后的中点
int p = partition(L, R);
quickSort(L, p - 1);
quickSort(p + 1, R);
}
private int partition(int L, int R) {
//优化一下 随机让后面的数字和第一个数字换一下
//尽量避免极端情况
swap(L, (int) (Math.random() * (R - L + 1) + L));
int v = arr[L];
//arr[l+1 ~ j] < v < arr[j+1 ~ i)
int j = L;
for (int i = L + 1; i <= R; i++) {
if (arr[i] < v) {
swap(j + 1, i);
j++;
}
}
swap(L, j);
return j;
}
}
10.双路快速排序
package p5.排序算法;
//双路快速排序
public class QuickSort02 extends Sort{
public QuickSort02(int[] arr) {
super(arr);
}
@Override
public void sort() {
quickSort(0, arr.length - 1);
}
private void quickSort(int L, int R) {
if (L >= R) {
return;
}
//对数组进行划分 并返回划分后的中点
int p = partition(L, R);
quickSort(L,p - 1);
quickSort(p + 1, R);
}
private int partition(int L, int R) {
//优化一下 随机让后面的数字和第一个数字换一下
//尽量避免极端情况(升序情况)
swap(L, (int) (Math.random() * (R - L + 1) + L));
int v = arr[L];
int i = L + 1;
int j = R;
while (true) {
while (i <= R && arr[i] < v) {
i++;
}
while (j >= L + 1 && arr[j] > v) {
j--;
}
if (i > j) {
break;
}
swap(i, j);
i++;
j--;
}
swap(L, j);
return j;
}
}
11.三路快速排序
package p5.排序算法;
//三路快速排序
public class QuickSort03 extends Sort {
public QuickSort03(int[] arr) {
super(arr);
}
@Override
public void sort() {
quickSort(0, arr.length - 1);
}
private void quickSort(int L, int R) {
if (L >= R) {
return;
}
swap(L, (int) (Math.random() * (R - L + 1) + L));
int v = arr[L];
int lt = L;
int gt = R + 1;
int i = L + 1;
while (i < gt) {
if (arr[i] < v) {
swap(i, lt + 1);
lt++;
i++;
} else if (arr[i] > v) {
swap(i, gt - 1);
gt--;
} else {
i++;
}
}
swap(L, lt);
quickSort(L,lt - 1);
quickSort(gt, R);
}
}
12.桶排序
package p5.排序算法;
import java.util.ArrayList;
import java.util.Comparator;
//桶排序
public class BucketSort extends Sort {
public BucketSort(int[] arr) {
super(arr);
}
@Override
public void sort() {
//1.找到最大值和最小值
int max = arr[0];
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min) {
min = arr[i];
}
}
//2.确定桶的个数并创建桶
int bucketNum = (max - min) / arr.length + 1;
ArrayList list[] = new ArrayList[bucketNum];
for (int i = 0; i < list.length; i++) {
list[i] = new ArrayList<>();
}
//3.遍历源数据 将数据进行分类处理
for (int i = 0; i < arr.length; i++) {
list[(arr[i] - min) / arr.length].add(arr[i]);
}
//4.对每一个桶进行排序
for (int i = 0; i < list.length; i++) {
list[i].sort(new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 -o2;
}
});
// System.out.println("第" + (i+1) + "个桶:" + list[i].toString());
}
//5.将所有桶中的数据依次返回到原数组中即可
int index = 0; //原数组的角标
for (int i = 0; i < list.length; i++) {
for (int j = 0; j < list[i].size(); j++) {
arr[index++] = list[i].get(j);
}
}
// System.out.println(Arrays.toString(arr));
}
}
13.基数排序
package p5.排序算法;
import java.util.LinkedList;
//基数排序
public class RadixSort extends Sort {
public RadixSort(int[] arr) {
super(arr);
}
@Override
public void sort() {
//1.找 分类-收集 的轮数(最大值的长度)
int radix = getRadix();
//2.创建桶 list所有桶的集合 每一个桶是LinkedList当成队列来用
LinkedList[] list = new LinkedList[10];
for (int i = 0; i < list.length; i++) {
list[i] = new LinkedList<>();
}
//3.开始 分类-收集
for (int r = 1; r <= radix; r++) {
//分类过程
for (int i = 0; i < arr.length; i++) {
list[getIndex(arr[i], r)].offer(arr[i]);
}
int index = 0; //遍历arr原数组
//收集的过程
for (int i = 0; i < list.length; i++) {
while (!list[i].isEmpty()) {
arr[index++] = list[i].poll();
}
}
}
}
private int getIndex(int num, int r) {
int ret = 0;
for (int i = 1; i <= r; i++) {
ret = num % 10;
num /= 10;
}
return ret;
}
private int getRadix() {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return (max + "").length();
}
}