排序算法
冒泡排序
时间和空间复杂度
要点
- 每轮冒泡不断地比较比较相邻的两个元素,如果它们是逆序的,则需要交换它们的位置
- 下一轮冒泡,可以调整未排序的右边界,减少不必要比较
代码
public static int[] test(int[] array) {
// 外层循环控制遍历次数
for (int i = 0; i < array.length; i++) {
// 内层循环控制每轮比较和交换
for (int j = 0; j < array.length - i - 1; j++) {
// 比较相邻两元素大小
if (array[j] > array[j + 1]) {
// 交换元素位置
int temp = array[j + 1];
array[j + 1] = array[j];
array[j] = temp;
}
}
}
// 返回排序后的数组
return array;
}
测试结果
public static void main(String[] args) {
int[] array = {3,1,2,4,7,9};
int[] ary = test(array);
System.out.println(Arrays.toString(ary));
}
选择排序
时间和空间复杂度
要点
- 每一轮选择,找出最大(最小)的元素,并把它交换到合适的位置
代码
public static int[] test(int[] array) {
// 外循环遍历数组元素
for (int i = 0; i < array.length; i++) {
// 内循环从当前元素的下一个元素开始比较
for (int j = i+1; j < array.length; j++) {
// 如果前一个元素大于后一个元素,交换它们的位置
if(array[i] > array[j]){
int temp = array[j];
array[j] = array[i];
array[i] = temp;
}
}
}
// 返回排序后的数组
return array;
}
测试结果
public static void main(String[] args) {
int[] array = {3,1,2,4,7,9};
int[] ary = test(array);
System.out.println(Arrays.toString(ary));
}
堆排序
时间和空间复杂度
要点(如果这里看不懂的话,是需要去了解大顶堆这个数据结构的)
- 建立大顶堆
- 每次将堆顶元素最大值,交换到末尾,调整堆顶元素,让它重新符合大顶堆特性
代码
import java.util.Arrays;
public class Heap {
int[] array; // 存储堆元素的数组
int size; // 堆的大小
public Heap(int capacity) {
this.array = new int[capacity];
}
public Heap(int[] array){
this.array = array;
this.size = array.length;
heapify(); // 调用堆化方法,构建堆
}
/**
* 建立堆
*/
private void heapify() {
// 建立堆的过程实际上是找到最后一个非叶子节点
for (int i = size/2-1; i >=0 ; i--) {
// 接下来的循环中,i 是当前非叶子节点的索引
down(i); // 对当前非叶子节点进行下潜操作,保证满足堆的性质
}
}
// 下潜操作
private void down(int i) {
int left = i * 2 + 1; // 左子节点索引
int right = left + 1; // 右子节点索引
int max = i; // 初始化最大值索引为当前节点
// 判断左子节点是否存在且大于当前节点
if (left < size && array[left] > array[max]) {
max = left;
}
// 判断右子节点是否存在且大于当前节点
if (right < size && array[right] > array[max]) {
max = right;
}
if (max != i) {
// 交换当前节点与最大值节点
swap(max, i);
// 递归调用下潜操作,保证交换后的子树仍然满足堆的性质
down(max);
}
}
// 交换数组中两个位置的元素
private void swap(int a, int b) {
int temp = array[a];
array[a] = array[b];
array[b] = temp;
}
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6,7};
Heap heap = new Heap(array);
// 堆排序
while (heap.size > 1) {
// 将堆顶元素与堆尾元素交换
heap.swap(0, heap.size - 1);
// 堆大小减一
heap.size--;
// 对交换后的堆顶元素进行下潜操作,保证仍然满足堆的性质
heap.down(0);
}
System.out.println(Arrays.toString(heap.array));
}
测试结果
插入排序
时间和空间复杂度
要点
- 将数组分为两部分[0----low-1][low----a.length-1]
- 左边[0-----low-1]是已排序部分
- 右边[low------a.length-1]是未排序部分
- 每次从未排序区域取出low位置的元素,插入到已排序区域
代码
public static int[] test(int[] array) {
// 外层循环,从数组第二个元素开始
for (int low = 1; low < array.length; low++) {
// 记录当前元素值
int t = array[low];
// 从当前元素的前一个位置开始向前查找插入位置
int i = low - 1;
while (i >= 0 && t < array[i]) {
// 如果当前元素小于已排序元素,将已排序元素后移一位
array[i + 1] = array[i];
i--;
}
// 找到插入位置,将当前元素插入
if (i != low - 1) {
array[i + 1] = t;
}
}
// 返回排序后的数组
return array;
}
测试结果
public static void main(String[] args) {
int[] array = {3,1,2,4,7,9};
int[] ary = test(array);
System.out.println(Arrays.toString(ary));
}
希尔排序
时间和空间复杂度
要点
- 简单来说,就是分组实现插入,魅族元素间隙称为hap
- 每轮排序之后gap逐渐变小,直到gap为1完成排序
- 对插入排序的优化,让元素更快地交换到最终位置
代码
public static int[] test(int[] array) {
// 此时的gap就是间隙
for (int gap = array.length >> 1; gap >= 1 ; gap = gap >> 1) {
for (int low = gap; low < array.length ; low++) {
// 记录当前位置的元素值
int t = array[low];
// 初始化比较位置为当前位置的前一个位置
int i = low - gap;
// 当比较位置合法且当前元素值小于比较位置的元素值时进行插入排序
while (i >= 0 && t < array[i]){
// 将比较位置的元素往后移动gap个位置
array[i + gap] = array[i];
// 更新比较位置为前一个gap位置
i -= gap;
}
// 如果实际插入位置不是当前位置的前一个gap位置,则进行插入操作
if (i != low - gap){
array[i + gap] = t;
}
}
}
// 返回排序后的数组
return array;
}
测试结果
public static void main(String[] args) {
int[] array = {3,1,2,4,7,9};
int[] ary = test(array);
System.out.println(Arrays.toString(ary));
}