记录一下Java经典的排序算法,需要时不时来看一下
1. 插入排序
思想
将数组分成无序和有序两个部分,通常左边为有序区,然后从左到右遍历数组,将元素逐个插入到左边有序区中。
重点第一个元素我们将它看为是有序的,所以需要从第二个元素开始进行排序。
源码
// 插入排序
static int[] insertSort(int[] arr) {
// 默认第一个元素有序,从第二个开始循环
for (int i = 1; i < arr.length; i++) {
int key = arr[i];// 基准元素
for (int j = i; j > 0 && arr[j - 1] > key; j--) {
// 遇到比基准元素大的就交换
arr[j] = arr[j - 1];
arr[j - 1] = key;
}
System.out.println("本趟排序结果:" + Arrays.toString(arr));
}
return arr;
}
结果
如果要换乘从大到小排,就把跟哨兵的比较换成 < 即可
优化
可以先进行比较,最后把元素插入到合适的位置
static void insertSort(int [] arr){
for (int i = 1; i < arr.length; i++) {
int j = i;
int tmp = arr[i];
while (j > 0 && arr[j - 1] > tmp) {
arr[j] = arr[j - 1];
j--;
}
arr[j] = tmp;
}
}
2. 冒泡排序
思想
将数组分成有序和无序,通常我们将元素冒泡到数组右侧既右侧为有序,左侧为无序。
源码
// 冒泡排序
int[] bubbleSort(int[] arr) {
// 第一层交换 n-1 次
for (int i = 0; i < arr.length - 1; i++) {
// 第二重需要减去已排好的元素
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
System.out.println("本趟排序结果:" + Arrays.toString(arr));
}
return arr;
}
void swap(int[] arr, int x, int y) {
int temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
如果从大到小排序,则值的比较换成<即可
结果
3. 快速排序
思想
主要是递归和分治,将序列选定第一个元素作为基准元素,然后分别两侧开始往中间移动:当右侧遇到比基准元素小的就停止移动,并把数据赋值给左侧。同理左侧遇到比基准元素大的就停止移动,并把数据赋值给右侧;当左右下标相遇的时候,这个时候就把基准元素赋值给这个下标,这时左侧的都比基准小,右侧的都比基准大,一趟排序就完成了;
之后使用分治思想,分别递归基准左边的元素,以及基准右边的元素,最后整个序列就会变成有序的。
源码
// 快速排序
Integer[] quickSort(Integer[] arr, int left, int right) {
if (arr.length == 0) {
return null;
}
if (left < right) {
// 选择第一个元素为基准元素
int key = arr[left];
int l = left;
int r = right;
while (l < r) {
// 先移动右边的下标
while (l < r && arr[r] >= key) {
r--;
}
// 如果找到比基准数据小的,停止while循环,并把他的值赋值给最左侧
arr[l] = arr[r];
// 再移动左边的下标
while (l < r && arr[l] <= key) {
l++;
}
// 如果找到比基准数据大的,停止循环,并把他的值赋值给最右侧
arr[r] = arr[l];
}
// 当 l=r 的时候,说明这一趟循环结束,这时左边的元素都比他小,右边的元素都比他大,l就是中间位置的下标
arr[l] = key;
quickSort(arr, left, l - 1);
quickSort(arr, l + 1, right);
}
return arr;
}
结果
4. 归并排序
思想
归并字面意思就可以理解为合并,将数组不断的进行左右拆分,直到拆分成一个元素时再对数组进行排序合并,然后不断的合并两个有序的数组,最终整个数组就是有序的。
源码
// 归并排序
int[] mergeSort(int[] arr) {
if (null == arr || arr.length < 2) {
return arr;
}
int center = arr.length >> 1;
int[] leftArr = Arrays.copyOfRange(arr, 0, center);
int[] rightArr = Arrays.copyOfRange(arr, center, arr.length);
System.out.println("拆分数组结果: " + Arrays.toString(leftArr) + " <---> " + Arrays.toString(rightArr));
// 递归拆分数组并合并
return merge(mergeSort(leftArr), mergeSort(rightArr));
}
// 归并排序,合并两个数组
private int[] merge(int[] arrLeft, int[] arrRight) {
int i = 0, j = 0, k = 0;
// 存放合并后的数组
int[] newArr = new int[arrLeft.length + arrRight.length];
// 两个数组都从第一个元素开始进行比较,将小的放入
while (i < arrLeft.length && j < arrRight.length) {
if (arrLeft[i] <= arrRight[j]) {
newArr[k++] = arrLeft[i++];
} else {
newArr[k++] = arrRight[j++];
}
}
// 如果左侧数组有剩余,证明他比新数组中的元素都大,那么把它的元素全部追加在新数组中
while (i < arrLeft.length) {
newArr[k++] = arrLeft[i++];
}
// 同样如果右侧数组有剩余,证明他比新数组中的元素都大,那么把它的元素全部追加在新数组中
while (j < arrRight.length) { //序列2中多余的元素移入新数组
newArr[k++] = arrRight[j++];
}
System.out.println("合并结果: " + Arrays.toString(newArr));
return newArr;
}
结果
5. 选择排序
思想
将数组分为有序去和无序区,每次从无序区中找到最小的元素然后和无序区第一个元素交换;然后将有序区扩大一位,直到全部变成有序的。
源码
// 选择排序
int[] selectSort(int[] arr) {
if (null == arr) {
return null;
}
for (int i = 0; i < arr.length - 1; i++) {
// 假设无序区第一个元素是最小的
int min = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[min]) {
min = j;
}
}
swap(arr, i, min);
System.out.println("本趟排序结果:" + Arrays.toString(arr));
}
return arr;
}
void swap(int[] arr, int x, int y) {
int temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
结果
6. 希尔排序
思想
希尔排序其实就是插入排序的变种。思想就是每次取一个增量步长,针对此步长间隔开的子序列进行插入排序,然后逐渐缩短这个步长的值,直到最后步长为一,树列就变成有序数列。
重点一般初次的步长为数组的二分之一,之后每次再减半,直到增量为1。
源码
// 希尔排序
int[] hillSort(int[] arr){
int j;
int tmp;
// 步长每次除以2
for (int gap = arr.length / 2; gap >0; gap /= 2) {
for (int i = gap; i < arr.length;i++) {
tmp = arr[i];
// 对在这个步长上的数组进行插入排序
for (j = i; j >= gap && tmp< arr[j - gap]; j -= gap) {
arr[j] = arr[j - gap];
}
arr[j] = tmp;
System.out.println("本趟排序结果:" + Arrays.toString(arr));
}
}
return arr;
}
结果