因为面试美团的时候,面试官让我写个快排我20分钟都没写出来,后面经高人指点,把十大经典排序算法抄十遍,因此有了这篇博客的出现。
1. 冒泡排序
//平均n2,最好n,最坏n2,空间1,稳定
class Solution {
public int[] buubleSort(int[] nums) {
if (nums == null || nums.length == 0) return nums;
for (int i = 0; i < nums.length; i++) {
//要-1是因为这里j+1会溢出
//因为每遍历一次,都能够保证数组最后一个元素是最大,因此j要-i
for (int j = 0; j < nums.length - i - 1; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
return nums;
}
}
2. 选择排序
//平均n2,最好是n,最差是n2,空间1,不稳定
class Solution {
public int[] selectionSort(int[] nums) {
if (nums == null || nums.length == 0) return nums;
//找最小的
for (int i = 0; i < nums.length; i++) {
int minIndex = i;
for (int j = i; j < nums.length; j++) {
//有更小的就改变标记
if (nums[j] < nums[minIndex]) {
minIndex = j;
}
}
//交换
int temp = nums[minIndex];
nums[minIndex] = nums[i];
nums[i] = temp;
}
return nums;
}
}
3. 插入排序
//平均n2,最好n,最坏n2,空间1,稳定
//从未排序中依次选择插入
class Solution {
public int[] insertionSort(int[] nums) {
if (nums == null || nums.length == 0) return nums;
for (int i = 0; i < nums.length - 1; i++) {
int current = i;
int base = nums[i + 1];
while (current >= 0 && nums[current] > base) {
nums[current + 1] = nums[current];
current--;
}
nums[current + 1] = base;
}
return nums;
}
}
4. 希尔排序
//平均nlog2n,最好nlog2n,最坏nlog2n,空间1,不稳定
class Solution {
public int[] shellSort(int[] nums) {
if (nums == null || nums.length == 0) return nums;
int gap = nums.length / 2;
while (gap > 0) {
for (int i = gap; i < nums.length; i++) {
int temp = nums[i];
int sorted = i - gap;
//分组进行插入排序
while (sorted >= 0 && nums[sorted] > temp) {
nums[sorted + gap] = nums[sorted];
sorted -= gap;
}
//这里 + gap是因为上面多进行了一次sorted -= gap
nums[sorted + gap] = temp;
}
gap /= 2;
}
return nums;
}
}
5. 归并排序
import java.util.Arrays;
//平均nlogn,最好nlogn,最坏nlogn,空间n,稳定,外排序
//用到了递归思想
class Solution {
public int[] mergeSort(int[] nums) {
//如果长度小于2则结束递归拆分
if (nums.length < 2) return nums;
//if (nums == null || nums.length == 0) return nums;
int mid = nums.length / 2;
//进行拆分
int[] left = Arrays.copyOfRange(nums, 0, mid);
int[] right = Arrays.copyOfRange(nums, mid, nums.length);
return helper(mergeSort(left), mergeSort(right));
}
//进行归并
private int[] helper(int[] left, int[] right) {
int[] result = new int[left.length + right.length];
int i = 0, j = 0;
for (int index = 0; index < result.length; index++) {
//这里顺序很重要,因为if和elseif只会执行一个,所以先判断是否溢出
if (i >= left.length) result[index] = right[j++];
else if (j >= right.length) result[index] = left[i++];
else if (left[i] > right[j]) result[index] = right[j++];
else result[index] = left[i++];
}
return result;
}
}
6. 快速排序
//平均nlogn,最好nlogn,最坏n2,空间logn,不稳定
class Solution {
public int[] fastSort(int[] nums) {
if (nums == null || nums.length == 0) return nums;
return helper(nums, 0, nums.length - 1);
}
private int[] helper(int[] nums, int low, int high) {
//if (nums.length <= 2) return nums;
int base = nums[low];
int i = low, j = high;
while (i < j) {
while (i < j && base <= nums[j]) j--;
while (i < j && base >= nums[i]) i++;
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
nums[low] = nums[j];
nums[j] = base;
if (i - low > 1) helper(nums, low, i);
if (high - j > 2) helper(nums, j + 1, high);
return nums;
}
}
7. 堆排序
//平均nlogn,最好nlogn,最坏nlogn,空间1,不稳定
class Solution {
int len;
public int[] sortArray(int[] nums) {
len = nums.length;
if (len < 1) return nums;
//构建一个最大堆
buildMaxHeap(nums);
//循环将堆首位(最大值)与末位交换,然后在重新调整最大堆
while (len > 0) {
swap(nums, 0, len - 1);
len--;
//调整成为最大堆
adjustHeap(nums, 0);
}
return nums;
}
private void adjustHeap(int[] nums, int i) {
int maxIndex = i;
//如果有左子树,且左子树大于父节点,则将最大指针指向左子树
if (i * 2 + 1 < len && nums[i * 2 + 1] > nums[maxIndex])
maxIndex = i * 2 + 1;
//如果有右子树,且右子树大于父节点,则将最大指针指向右子树
if (i * 2 + 2 < len && nums[i * 2 + 2] > nums[maxIndex])
maxIndex = i * 2 + 2;
//如果父节点不是最大值,则将父节点与最大值交换,并且递归调整与父节点交换的位置。
if (maxIndex != i) {
swap(nums, maxIndex, i);
adjustHeap(nums, maxIndex);
}
}
private void buildMaxHeap(int[] nums) {
//从最后一个非叶子节点开始向上构造最大堆
for (int i = (len/2 - 1); i >= 0; i--) {
adjustHeap(nums, i);
}
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
8. 计数排序
import java.util.Arrays;
//平均n+k,最好n+k,最坏n+k,空间k,外排序,稳定
class Solution {
public int[] sortArray(int[] nums) {
if (nums.length == 0) return nums;
int bias, min = nums[0],max = nums[0];
//标记最大和最小的元素
for (int i = 1; i < nums.length; i++) {
if (nums[i] > max) max = nums[i];
if (nums[i] < min) min = nums[i];
}
bias = 0 - min;
int[] bucket = new int[max - min + 1];
Arrays.fill(bucket, 0);
//统计数组中各个值出现的次数
for (int i = 0; i < nums.length; i++) {
bucket[nums[i] + bias]++;
}
int index = 0, i = 0;
//进行反填充
while (index < nums.length) {
if (bucket[i] != 0) {
nums[index] = i - bias;
bucket[i]--;
index++;
} else {
i++;
}
}
return nums;
}
}
9. 桶排序
import java.util.ArrayList;
import java.util.Collections;
//平均n+k,最好n+k,最差n2,空间n+k,外排序,稳定
/**桶的大小和桶的个数可以这样确定
* 桶的大小gap,桶的个数k,nums长度len,最小元素min,最大元素max
* gap = (max - min) / len + 1
* k = (max - min) /gap + 1
* 同一个桶中元素相差最多为gap-1
* 对于nums[i],放入哪个桶的公式为(nums[i] - min) / gap
* */
class Solution {
public int[] sortArray(int[] nums) {
if (nums == null || nums.length < 2) return nums;
int len = nums.length;
int max = nums[0], min = nums[0];
//确认最大最小值
for (int i = 0; i < len; i++) {
if (nums[i] < min) min = nums[i];
if (nums[i] > max) max = nums[i];
}
int gap = (max - min) / len + 1; //桶的大小
int k = (max - min) / gap + 1; //桶的个数
ArrayList<ArrayList<Integer>> bucketList = new ArrayList<>(k);
//ArrayList<Integer> result = new ArrayList<>();
//创建桶链表
for (int i = 0; i < k; i++) {
bucketList.add(new ArrayList<Integer>());
}
//遍历nums元素,加入桶链表中
for (int i = 0; i < len; i++) {
//先get要加入的桶的位置,再add
bucketList.get((nums[i] - min) / gap).add(nums[i]);
}
//对桶中元素进行排序
for (int i = 0; i < k; i++) {
Collections.sort(bucketList.get(i));
}
int j = 0;
for (int i = 0; i < len; i++) {
int index = 0;
while (bucketList.get(j).get(index) != null) {
nums[i] = bucketList.get(j).get(index);
}
j++;
}
return nums;
}
}
10. 基数排序
import java.util.ArrayList;
//平均n*k,最好n*k,最坏n*k,空间n+k,外排序,稳定
class Solution {
public int[] sortArray(int[] nums) {
if (nums == null || nums.length < 2) return nums;
//算出最大数的位数
int max = nums[0];
for (int i = 0; i < nums.length; i++) {
if (nums[i] > max) max = nums[i];
}
int maxDigit = 0;
while (max != 0) {
max /= 10;
maxDigit++;
}
int mod = 10, div = 1;
ArrayList<ArrayList<Integer>> bucketList = new ArrayList<ArrayList<Integer>>();
for (int i = 0; i < 10; i++)
bucketList.add(new ArrayList<Integer>());
for (int i = 0; i < maxDigit; i++, mod *= 10, div *= 10) {
for (int j = 0; j < nums.length; j++) {
int num = (nums[j] % mod) / div;
bucketList.get(num).add(nums[j]);
}
int index = 0;
for (int j = 0; j < bucketList.size(); j++) {
for (int k = 0; k < bucketList.get(j).size(); k++)
nums[index++] = bucketList.get(j).get(k);
bucketList.get(j).clear();
}
}
return nums;
}
}