Java排序算法
1.插入排序 稳定
原理:从有序序列中选择合适的位置进行插入
复杂度:最好 - 最坏 - 平均 O(n) - O(n^2) - O(n^2)
public void insertSort(int[] a) {
if (null ==a || a.length < 2) {
return;
}
for (int i = 1; i < a.length; i++) {
int temp = a[i]; // 暂存
int j = i - 1;
while (j >= 0 && temp < a[j]) {
a[j+1] = a[j];
j--;
}
a[j+1] = temp;
}
}
2.冒泡排序 稳定
原理:相邻两个元素比较大小进行交换,一趟冒泡后会有一个元素到达最终位置
复杂度:最好 - 最坏 - 平均 O(n) - O(n^2) - O(n^2)
public void bubbleSort(int[] a) {
if (null == a || a.length < 2) {
return;
}
boolean flag;
for (int i = 0; i < a.length - 1; i++) {
flag = false;
for (int j = 0; j < a.length - 1 - i; j++) {
if (a[j] > a[j+1]) {
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
flag = true;
}
if (flag == false) {
return;
}
}
}
}
3.希尔排序(缩小增量排序) 不稳定
按步长进行分组,组内直接插入,缩小增量再次进行此步骤,增量为1时相当于一次直接插入。
复杂度:最好O(n) - 最坏O(n^s 1<s<2) - 平均O(n^1.3)
public void shellSort(int[] a) {
if (null == a || a.length < 2) {
return;
}
for (int d = a.length/2; d > 0; d/=2) {
for (int i = d; i < a.length; i++) {
// 内部直接插入
int temp = a[i];
int j = i - d;
while (j >= 0 && temp < a[j]) {
a[j+d] = a[j];
j -= d;
}
a[j+d] = temp;
}
}
}
4.选择排序 不稳定
原理:每次从无序序列选择一个最小的
复杂度:最好O(n^2) - 最坏O(n^2) - 平均O(n^2)
public void selectSort(int[] a) {
if (null == a || a.length < 2) {
return;
}
for (int i = 0; i < a.length; i++) {
int k = i;
for (int j = i + 1; j < a.length; j++) {
if (a[j] < a[k]) {
k = j;
}
}
if (k != i) {
int temp = a[k];
a[k] = a[i];
a[i] = temp;
}
}
}
5.快速排序 不稳定
原理:分治+递归
复杂度:最好O(nlgn) - 最坏O(n^2) - 平均O(nlgn)
public void quickSort(int[] a, int low, int high) {
if (low < high) {
int mid = partition(a, low, high);
quickSort(a, low, mid - 1);
quickSort(a, mid + 1, high);
}
}
private int partition(int[] a, int low, int high) {
int pivot = a[low];
while (low < high) {
while (low < high && a[high] >= pivot) {
high--;
}
a[low] = a[high];
while (low < high && a[low] <= pivot) {
low++;
}
a[high] = a[low];
}
a[low] = pivot;
return low;
}
选取pivot的方式:固定基准元 随机基准 三数取中
快排的优化:针对随机数组+有序数组+重复数组
1.当待排序序列的长度分割到一定大小后,使用插入排序<三数取中+插入排序>:效率提高一些,但是都解决不了重复数组的问题。
2.在一次分割结束后,可以把与Key相等的元素聚在一起,继续下次分割时,不用再对与key相等元素分割 <三数取中+插排+聚集相同元素>
6.归并排序 稳定
原理:两个有序序列的合并,方法:分治 + 递归
复杂度:最好O(nlgn) - 最坏O(nlgn) - 平均O(nlgn)
public void mergeSort(int[] a, int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
//左边
mergeSort(a, low, mid);
//右边
mergeSort(a, mid + 1, high);
//有序序列归并
merge(a, low, mid, high);
}
}
private void merge(int[] a, int low, int mid, int high) {
int[] temp = new int[high - low + 1];
// 左指针
int i = low;
// 右指针
int j = mid + 1;
// 临时数组指针
int k = 0;
while (i <= mid && j <= high) {
if (a[i] < a[j]) {
temp[k++] = a[i++];
} else {
temp[k++] = a[j++];
}
}
//左边剩余
while (i <= mid) {
temp[k++] = a[i++];
}
//右边剩余
while (j <= high) {
temp[k++] = a[j++];
}
// 倒出
for (int t = 0; t < temp.length; t++) {
a[t + low] = temp[t];
}
}
7. 堆排序
原理:利用堆的特性
复杂度:O(nlogn) [平均 - 最好 - 最坏]
// 堆排序
public void heapSort(int[] a) {
if (null == a || a.length < 2) {
return;
}
buildMaxHeap(a);
for (int i = a.length - 1; i >= 0; i--) {
int temp = a[0];
a[0] = a[i];
a[i] = temp;
adjustHeap(a, i, 0);
}
}
// 建堆
private void buildMaxHeap(int[] a) {
for (int i = a.length/2; i >= 0; i--) {
adjustHeap(a, a.length, i);
}
}
// 调整堆
private void adjustHeap(int[] a, int size, int parent) {
int left = 2 * parent + 1;
int right = 2 * parent + 2;
int largest = parent;
if (left < size && a[left] > a[largest]) {
largest = left;
}
if (right < size && a[right] > a[largest]) {
largest = right;
}
if (parent != largest) {
int temp = a[parent];
a[parent] = a[largest];
a[largest] = temp;
adjustHeap(a, size, largest);
}
}
8.基数排序[稳定]
原理:分配加收集
复杂度: O(d(n+r)) r为基数d为位数 空间复杂度O(n+r)
// 基数排序
public void radixSort(int[] a, int begin, int end, int digit) {
// 基数
final int radix = 10;
// 桶中的数据统计
int[] count = new int[radix];
int[] bucket = new int[end-begin+1];
// 按照从低位到高位的顺序执行排序过程
for (int i = 1; i <= digit; i++) {
// 清空桶中的数据统计
for (int j = 0; j < radix; j++) {
count[j] = 0;
}
// 统计各个桶将要装入的数据个数
for (int j = begin; j <= end; j++) {
int index = getDigit(a[j], i);
count[index]++;
}
// count[i]表示第i个桶的右边界索引
for (int j = 1; j < radix; j++) {
count[j] = count[j] + count[j - 1];
}
// 将数据依次装入桶中
// 这里要从右向左扫描,保证排序稳定性
for (int j = end; j >= begin; j--) {
int index = getDigit(a[j], i);
bucket[count[index] - 1] = a[j];
count[index]--;
}
// 取出,此时已是对应当前位数有序的表
for (int j = 0; j < bucket.length; j++) {
a[j] = bucket[j];
}
}
}
// 获取x的第d位的数字,其中最低位d=1
private int getDigit(int x, int d) {
String div = "1";
while (d >= 2) {
div += "0";
d--;
}
return x/Integer.parseInt(div) % 10;
}
}