基本的排序算法
示例中都会用到的swap方法
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
冒泡排序
- 简单的冒泡排序
/**
* 冒泡排序
*
* @param nums 待排序序列
* @param left 待排序序列左端点
* @param right 待排序序列右端点
*/
public void bubbleSort(int[] nums, int left, int right) {
// 最外层for循环控制是第几轮排序
for (int i = left; i < right; i++) {
// 内层for循环,一轮比较下来将小数冒泡
for (int j = right; j > i; j--) {
// 依次两两比较
if (nums[j] < nums[j - 1]) {
swap(nums, j, j-1);
}
}
}
}
- 改进后的冒泡排序
/**
* 改进后的冒泡排序
*
* @param nums 待排序序列
* @param left 待排序序列左端点
* @param right 待排序序列右端点
*/
public void advancedBubbleSort(int[] nums, int left, int right) {
// 最外层for循环控制是第几轮排序
for (int i = left; i < right; i++) {
// 用来指示某次比较是否进行了交换
boolean exchanged = false;
// 内层for循环,一轮比较下来将小数冒泡
for (int j = right; j > i; j--) {
if (nums[j] < nums[j - 1]) {
swap(nums, j, j-1);
exchanged = true;
}
}
// 如果某次比较没有发生交换
// 则说明序列已经有序了,就退出
if (!exchanged) {
return;
}
}
}
插入排序
- 直接插入排序
/**
* 直接插入排序
*
* @param nums 待排序序列
* @param left 待排序序列左端点
* @param right 待排序序列右端点
*/
public void insertSort(int[] nums, int left, int right) {
// 从待排序列的第二个元素开始
for (int i = left + 1; i <= right; i++) {
// 如果待插入元素比已经排好的
// 有序序列的最后一个元素小
// 则进行插入
if (nums[i] < nums[i - 1]) {
int temp = nums[i];
int j = i - 1;
// 寻找插入位置
do {
nums[j + 1] = nums[j];
j--;
} while (j >= left && temp < nums[j]);
nums[j + 1] = temp;
}
}
}
- 二分插入排序
/**
* 二分插入排序
*
* @param nums 待排序序列
* @param left 待排序序列左端点
* @param right 待排序序列右端点
*/
public void binaryInsertSort(int[] nums, int left, int right) {
for (int i = left + 1; i <= right; i++) {
int temp = nums[i];
int low = left;
int high = i - 1;
// 二分搜索待插入元素应该
// 插入的位置
while (low <= high) {
int middle = (low + high) / 2;
// 搜索左区间
if (nums[middle] < temp) {
low = middle + 1;
} else {// 搜索右区间
high = middle - 1;
}
}
// 向后移动,空出low位置进行插入
for (int k = i - 1; k >= low; k--) {
nums[k + 1] = nums[k];
}
nums[low] = temp;
}
}
选择排序
/**
* 选择排序
*
* @param nums 待排序序列
* @param left 待排序序列左端点
* @param right 待排序序列右端点
*/
public void selectSort(int[] nums, int left, int right) {
for (int i = left; i < right; i++) {
int min = i;
// 寻找从当前位置到序列末尾
// 最小的元素
for (int j = i + 1; j <= right; j++) {
if (nums[j] < nums[min]) {
min = j;
}
}
// 和当前位置交换
if (min != i) {
swap(nums, i, min);
}
}
}
希尔排序
/**
* 希尔排序
*
* @param nums 待排序序列
* @param left 待排序序列左端点
* @param right 待排序序列右端点
*/
public void shellSort(int[] nums, int left, int right) {
// 计算初始步长
int gap = right - left + 1;
do {
// 按步长将序列划分成若干个子序列
// 对每个子序列进行直接插入排序
gap = (gap / 3) + 1;
for (int i = left + gap; i <= right; i++) {
if (nums[i] < nums[i - gap]) {
int temp = nums[i];
int j = i - gap;
do {
nums[j + gap] = nums[j];
j = j - gap;
} while (j >= left && temp < nums[j]);
nums[j + gap] = temp;
}
}
} while (gap > 1);
快速排序
- 基本的快速排序
/**
* 快速排序
*
* @param nums 待排序序列
* @param left 待排序序列左端点
* @param right 待排序序列右端点
*/
public void quickSort(int[] nums, int left, int right) {
if (left < right) {
// 计算基准位置,左侧的比基准元素小
// 右侧的比基准元素大
int pivotposition = partition(nums, left, right);
// 递归排序左子序列
quickSort(nums, left, pivotposition - 1);
// 递归排序右子序列
quickSort(nums, pivotposition + 1, right);
}
}
private int partition(int[] nums, int left, int right) {
// 简单的选取第一个元素为基准元素
int pivot = nums[left];
int pivotposition = left;
for (int i = left + 1; i <= right; i++) {
// 小于基准元素则交换
if (nums[i] < pivot) {
pivotposition++;
if (i != pivotposition) {
swap(nums, i, pivotposition);
}
}
}
// 基准元素归位
nums[left] = nums[pivotposition];
nums[pivotposition] = pivot;
return pivotposition;
}
- 快速排序的两种改进
private final static int MIN_ELEMENTS_SIZE = 25;
/**
* 快速排序和插入排序组合
* 解决当元素数量较小时
* 使用快速排序效率不高
* 的问题
*
* @param nums 待排序序列
* @param left 待排序序列左端点
* @param right 待排序序列右端点
*/
public void quickInsertSort(int[] nums, int left, int right) {
if (right - left < MIN_ELEMENTS_SIZE) {
insertSort(nums, left, right);
} else {
quickSort(nums, left, right);
}
}
/**
* 先进行快速排序
* 在对小区元素个数
* 比较小的区间进行
* 插入排序
*
* @param nums 待排序序列
* @param left 待排序序列左端点
* @param right 待排序序列右端点
*/
public void hybridSort(int[] nums, int left, int right) {
partQuickSort(nums, left, right);
insertSort(nums, left, right);
}
private void partQuickSort(int[] nums, int left, int right) {
if (right - left < MIN_ELEMENTS_SIZE) {
return;
}
int pivotposition = partition(nums, left, right);
partQuickSort(nums, left, pivotposition - 1);
partQuickSort(nums, pivotposition + 1, right);
}
堆排序
/**
* 堆排序
*
* @param nums 待排序序列
*/
public void heapSort(int[] nums) {
// 按增序排序需要构造最大堆
maxHeap(nums);
int length = nums.length;
for (int i = length - 1; i > 0; i--) {
// 第一元素即为最大元素
// 和当前元素交换
int temp = nums[0];
nums[0] = nums[i];
nums[i] = temp;
// 再次调整堆
siftDown(nums, 0, i - 1);
}
}
private void maxHeap(int[] nums) {
int size = nums.length;
int currentPosition = (size - 1) / 2;
while (currentPosition >= 0) {
// 从currentPosition到size-1
// 采用自顶向下构造最大堆
siftDown(nums, currentPosition, size - 1);
currentPosition--;
}
}
private void siftDown(int[] nums, int start, int end) {
int i = start;
int j = 2 * i + 1;
int temp = nums[i];
// 有子女
while (j <= end) {
// 有左右子女,取最大
if (j < end) {
if (nums[j] < nums[j + 1]) {
j = j + 1;
}
}
// 父结点比子女大不交换
if (temp > nums[j]) {
break;
} else {
nums[i] = nums[j];
i = j;
j = 2 * i + 1;
}
}
// 子女赋父结点的值
nums[i] = temp;
}
归并排序
/**
* 归并排序
*
* @param nums 待排序序列
* @param numsCopy 辅助数组
* @param left 待排序序列左端点
* @param right 待排序序列右端点
*/
public void mergeSort(int[] nums, int[] numsCopy, int left, int right) {
if (left >= right) {
return;
}
// 分割为子序列
int middle = (left + right) / 2;
mergeSort(nums, numsCopy, left, middle);
mergeSort(nums, numsCopy, middle + 1, right);
// 归并
merge(nums, numsCopy, left, middle, right);
}
private void merge(int[] nums, int[] numsCopy, int left, int middle, int right) {
// 初始化辅助数组
for (int i = left; i <= right; i++) {
numsCopy[i] = nums[i];
}
int s1 = left;
int s2 = middle + 1;
int t = left;
// 比较左子序列和右子序列
// 小的放入原始数组的相应位置
while (s1 <= middle && s2 <= right) {
if (numsCopy[s1] < numsCopy[s2]) {
nums[t++] = numsCopy[s1++];
} else {
nums[t++] = numsCopy[s2++];
}
}
// 剩余的左子序列直接追加
while (s1 <= middle) {
nums[t++] = numsCopy[s1++];
}
// 剩余的右子序列直接追加
while (s2 <= right) {
nums[t++] = numsCopy[s2++];
}
}