一、算法时间复杂度
二、个人感想
个人时间测量-各种排序方法排序若干个随机数
冒泡排序: 8万条数据 — 10秒
选择排序: 8万条数据 — 2秒
直接插入: 20万条数据 — 3秒
希尔排序: 4000万条 — 15秒
快速排序: 4000万条 — 4秒
归并排序: 4000万条 —5秒
基数排序: 4000 万条 —2秒
堆排序 : 4000万条 — 11秒
冒泡和选择较为基础,不再赘述。(选择比冒泡快点)
插入排序比以上两者都快
希尔排序可以说是对插入排序开始时的一种优化,比如说如果在插入排序的右边有一个最小数min,那么min想要到达最左边需要和许多的元素进行比较。而如果在开始时就尽可能的把那些小的数放到左边就可以减少很多次的比较,这就是希尔排序的原理了。(希尔排序的位移式比插入快,希尔排序的交换式比插入慢)
快速排序,速度略快与归并,先选一个数,比它小的放左边,大的放右边,再在左边找一个数,比它小的放左边,大的放右边,不断递归
归并排序速度快,用到了分而治之的思想,先把数组中每个元素变为一个个的个体,再两两排序比较合为一个,再把两个合为一个的数组再合为一个,不断递归
基数排序速度最快,空间换时间的经典,一个数组需要用到而外的10个等长度数组来排序
基数排序的时间复杂度在数据数较大时(logn > 10),比快速和归并小
反之比它们大
堆排序升序用大顶堆,降序用小顶堆,要求树是完全二叉树,但其实用的还是数组,重要的就是知道如果目前的节点下标为 i ,那么左子树下标就是 i x 2 + 1,右子树下标就是 i x 2 + 2
代码
1.冒泡排序
package 排序.冒泡;
public class BubbleSort {
public static void main(String[] args) {
int[] arr = {1,5,8,-4,6};
for (int i = 0; i < arr.length-1; i++) {
boolean flag = false;//用于判断是否已经排好序,这是冒泡优化
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j+1]){
int temp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp;
flag = true;
}
}
if (!flag){
break;
}
for (int k = 0; k < arr.length; k++) {
System.out.print(arr[k]+" ");
}
System.out.println();
}
}
}
2.插入排序
2.1直接插入
package 排序.插入.直接插入;
public class InsertSort {
public static void main(String[] args) {
int[] arr = {5, 1, 3, 2, 4};
insertSort(arr);
}
//插入排序
public static void insertSort(int[] arr) {
int insertVal;
int insertIndex = 0;
for (int i = 1; i < arr.length; i++) {
insertVal = arr[i];
insertIndex = i - 1;
while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
//没越界并且当前比左边那个小,左边右移
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
//插入,如果比排好序的所有都大的话就不用插了
if (insertIndex + 1 != i){
arr[insertIndex + 1] = insertVal;
}
}
}
}
2.2希尔排序
交换式(较慢,不建议使用)
public static void shellSort(int[] arr) {
int temp = 0;
for (int gap = (arr.length / 2); gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
for (int j = i - gap; j >= 0; j -= gap) {
if (arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
// System.out.println(Arrays.toString(arr));
}
}
位移式(快)
public static void shellSort2(int[] arr) {
for (int gap = (arr.length / 2); gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
int j = i;
int temp = arr[j];
if (arr[j] < arr[j - gap]) {
while (j - gap >= 0 && temp < arr[j - gap]) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
}
// System.out.println(Arrays.toString(arr));
}
}
3.选择排序
package 排序.选择;
public class SelectSort {
public static void main(String[] args) {
int[] arr = {1,34,119,101,5,78,-5};
for (int i = 0; i < arr.length - 1; i++) {
int min = arr[i];
int minIndex = i;
for (int j = i+1; j < arr.length ; j++) {
if (arr[j] < min){
min = arr[j];
minIndex = j;
}
}
if (i != minIndex){
arr[minIndex] = arr[i];
arr[i] = min;
}
//这种方法比较麻烦,不如直接看最大下标是否为当前最后一个(这里用的是先找最大)
// if (arr[arr.length - 1 -i] < max){
// arr[maxIndex] = arr[arr.length-1-i];
// arr[arr.length-1-i] = max;
// }
for (int j = 0; j < arr.length; j++) {
System.out.print(arr[j]+" ");
}
System.out.println();
}
}
}
4.快速排序
package 排序.快速;
public class QuickSort {
public static void main(String[] args) {
int[] arr = {-9, 12, 0, 5, 0, 23, -5, 14, -6, 4};
quickSort(arr, 0, arr.length - 1);
}
public static void quickSort(int[] arr, int left, int right) {
int l = left;
int r = right;
int pivot = arr[(left + right) / 2];//中轴值
int temp;
while (l < r) {
while (arr[l] < pivot) {
l += 1;
}
while (arr[r] > pivot) {
r -= 1;
}
if (l >= r) {
break;
}
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
// System.out.println(Arrays.toString(arr));
if (arr[l] == pivot) {
r--;
}
if (arr[r] == pivot) {
l++;
}
}
if (l == r) {
l++;
r--;
}
if (left < r) {
quickSort(arr, left, r);
}
if (right > l) {
quickSort(arr, l, right);
}
}
}
5.归并排序
package 排序.归并;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MergerSort {
public static void main(String[] args) {
int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
int[] temp = new int[arr.length];
mergeSort(arr,0,arr.length-1,temp);
System.out.println(Arrays.toString(arr));
}
//分+合并
public static void mergeSort(int[] arr,int left,int right,int[] temp){
if (left < right){
int mid = (left + right)/2;
mergeSort(arr,left,mid,temp);
mergeSort(arr,mid+1,right,temp);
//合并
merge(arr,left,mid,right,temp);
}
}
//合并
/**
* @param arr 排序的原始数组
* @param left 左边有序数组的初始索引
* @param mid 中间索引
* @param right 右边索引
* @param temp 中转数组
*/
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left;
int j = mid + 1;
int t = 0;//temp用的索引
//左右两边的数据填充到temp
//直到左右两边有一边处理完毕
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[t] = arr[i];
t++;
i++;
} else {
temp[t] = arr[j];
t++;
j++;
}
}
//把剩下的都填充过去
//左边满了
while (j <= right) {
temp[t] = arr[j];
t++;
j++;
}
//右边满了
while (i <= mid) {
temp[t] = arr[i];
t++;
i++;
}
//把temp填回原数组
//不是每次都拷贝所有
t = 0;
int tempLeft = left;
while (tempLeft <= right) {
arr[tempLeft] = temp[t];
t++;
tempLeft++;
}
}
}
6.基数排序
package 排序.基数;
import java.text.SimpleDateFormat;
import java.util.Date;
public class RadixSort {
public static void main(String[] args) {
// int[] arr = {53, 3, 542, 748, 14, 214,0};
int[] arr = new int[40000000];//2秒
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * 800000);
}
int[] temp = new int[arr.length];
Date date1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time1 = simpleDateFormat.format(date1);
System.out.println(time1);
radixSort(arr);
Date date2 = new Date();
String time2 = simpleDateFormat.format(date2);
System.out.println(time2);
}
//基数排序方法
public static void radixSort(int[] arr) {
int max = arr[0];
//得到最大值
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
//获取最大值长度
int maxLength = (max + "").length();//这种比下面的那种更巧妙
// while (Math.abs(max) / 10 > 0) {
// max /= 10;
// weishuForMax++;
// }
int[][] bucket = new int[10][arr.length];
int[] index = new int[10];//记录放了几个进桶
int no = 0;//原数组第几个数据
int count = 0;//该轮位数上的数是什么
for (int time = 0, n = 1; time < maxLength; time++, n *= 10) {
for (int i = 0; i < arr.length; i++) {
count = arr[i] / n % 10;
bucket[count][index[count]] = arr[i];
index[count]++;
}
no = 0;
//把桶的数据弄回原数组
for (int i = 0; i < 10; i++) {
for (int j = 0; j < index[i]; j++) {
arr[no++] = bucket[i][j];
}
index[i] = 0;//索引重新指向0
}
// System.out.println(Arrays.toString(arr));
}
//打印桶内信息
// for (int i = 0; i < 10; i++) {
// for (int j = 0; j < index[i]; j++) {
// System.out.print("第"+i+"个桶"+bucket[i][j]+" ");
// }
// System.out.println();
// }
}
}
7.堆排序
package 二叉树.堆排序;
public class HeapSort {
public static void main(String[] args) {
//升序排序,大顶堆
int[] arr = {4, 6, 8, 5, 9};
heapSort(arr);
}
//编写一个堆排序
public static void heapSort(int[] arr) {
int temp = 0;
//变成大顶堆
for (int i = arr.length / 2 - 1; i >= 0; i--) {
adjustHeap(arr, i, arr.length);
}
//排序:最大的放后面,然后再变成大顶堆,大的再放后面....
for (int i = arr.length - 1; i > 0 ; i--) {
temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
adjustHeap(arr,0,i);
}
}
/**
* 将以i对应的非叶子节点的树调整成一个大顶堆
* 例子: {4,6,8,5,9} --> i = 1 --->{4,9,8,5,6} --> i = 0 --->{9,6,8,5,4}
*
* @param arr 待调整数组
* @param i 非叶子节点再数组中的索引
* @param length 表示对多少个元素继续调整
*/
public static void adjustHeap(int[] arr, int i, int length) {
int temp = arr[i];
//k = i * 2 + 1, k是i节点的左子节点
for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
if (k + 1 < length && arr[k] < arr[k + 1]) {
k++;//指向右节点
}
if (arr[k] > temp) {//如果子节点大于父节点,较大的值给夫节点
arr[i] = arr[k];
i = k;//i指向k继续循环
} else {//直接退出是因为排序是从左往右,从下往上的,不用担心下面还有更大的
break;
}
}
//把原父节点放在调整后的位置
arr[i] = temp;
System.out.println(Arrays.toString(arr));
}
}