目录
一、插入排序
1:直接插入排序
2:折半插入排序
3:希尔排序
二、交换排序
1:冒泡排序
2:快速排序
三、归并排序
四、堆排序
一、插入排序
1:直接插入排序
思想:在有序序列中插入元素,有移动法和交换法两种实现
时间复杂度:O(n2)
稳定性:稳定
/**
* 移动法:先找到插入位置,然后向后移动元素
*/
public static void sort1(int arr[]) {
for (int i = 1; i < arr.length; i++) {
if (arr[i] >= arr[i - 1]) continue;
int j = i;
int temp = arr[i];
//找到插入位置
while (j > 0 && temp < arr[j - 1]) j--;
//向后移动
for (int k = i; k > j; k--) arr[k] = arr[k - 1];
//插入到指定位置
arr[j] = temp;
}
}
/**
* 交换法:当前元素和之前元素比较,如果比前面元素小则交换
*/
public static void sort2(int arr[]) {
for (int i = 1; i < arr.length; i++) {
if (arr[i] >= arr[i - 1]) continue;
int j = i;
while (j > 0 && arr[j] < arr[j-1]){
int temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
j--;
}
}
}
2:折半插入排序
思想:是对直接插入排序的改进,在寻找插入点时,直接插入排序是和前面有序元素一个一个比较,折半插入排序是使用折半的思路寻找插入点
/**
* 折半查找插入位置
*
* @return
*/
private static int zbFind(int arr[], int low, int high, int x) {
int mid = 0;
//如果有序序列中存在元素x,找到直接返回下标
while (low <= high) {
mid = (low + high) / 2;
if (arr[mid] == x) return mid;
if (x > arr[mid]) low = mid + 1;
else high = mid - 1;
}
//循环结束如果没有结束,则序列中没有x,此时区间长度为2,mid取小值,比mid值小则返回mid,否则返回mid+1
if (x < arr[mid]) return mid;
else return mid + 1;
}
//找到插入位置后和直接插入排序的思想一致
private static void sort(int arr[]) {
for (int i = 1; i < arr.length; i++) {
if (arr[i] >= arr[i - 1]) continue;
int temp = arr[i];
//找到插入位置
int j = zbFind(arr, 0, i-1, arr[i]);
//向后移动
for (int k = i; k > j; k--) arr[k] = arr[k - 1];
//插入到指定位置
arr[j] = temp;
}
}
折半排序减少了查找插入位置的次数,移动次数和直接插入排序一样,所以时间复杂度也是O(n2),稳定的排序算法
3:希尔排序
思想:对序列进行分组,分组之后使用直接插入排序
private static void sort(int arr[]) {
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
if (arr[i] >= arr[i - gap]) continue;
int j = i;
int temp = arr[i];
//找到插入位置
while (j - gap >= 0 && temp < arr[j - gap]) j -= gap;
//向后移动
for (int k = i - gap; k >= j; k -= gap) {
arr[k + gap] = arr[k];
}
arr[j] = temp;
}
}
}
时间复杂度:O(n^(1.3—2))
稳定性:不稳定
二、交换排序
1:冒泡排序
思想:相邻两个元素进行比较(j,j + 1),如果arr[j] > arr[j+1]则交换两元素,每次循环从下标0开始,一趟后最大的排到最后,第二趟后次大的排到倒数第二位....
private static void sort(int arr[]) {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
时间复杂度:O(n2)
稳定性:稳定
2:快速排序
思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分所有数据小,然后再按此方法对这两部分数据分别进行快速排序
private static void sort(int arr[], int low, int high) {
//递归出口
if (low >= high) return;
int i = low;
int j = high;
//取区间第一个元素为基点
int key = arr[low];
while (i < j) {
//从后往前找一个比基点小的元素
while (i < j && arr[j] >= key) j--;
//从前往后找一个比基点大的元素
while (i < j && arr[i] <= key) i++;
//交换
if (i < j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
arr[low] = arr[i];
arr[i] = key;
sort(arr, low, i - 1);
sort(arr, i + 1, high);
}
时间复杂度:O(nlogn)
稳定性:不稳定
三、归并排序
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
第一步:递归拆分子序列
第二步:合并有序序列(期间需要一个临时数组保存数据)
private static void sort(int arr[], int temp[], int left, int right) {
if (left >= right) return;
int mid = (left + right) / 2;
sort(arr, temp, left, mid);
sort(arr, temp, mid + 1, right);
merge(arr, temp, left, mid, right);
}
private static void merge(int[] arr, int[] temp, int left, int mid, int right) {
int i = left;
int j = mid + 1;
int t = 0;
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) temp[t++] = arr[i++];
else temp[t++] = arr[j++];
}
while (i <= mid) temp[t++] = arr[i++];
while (j <= right) temp[t++] = arr[j++];
t = 0;
while (left <= right) arr[left++] = temp[t++];
}
时间复杂度:O(nlogn)
稳定性:稳定
四、堆排序
堆本质是一个完全二叉树,分为最大堆(根不小于孩子)和最小堆(根不大于孩子)
第一步:初始化堆
第二步:将根(第一个元素)和最后一个元素交换,重新调整堆
/**
* 推排序
* @param array
*/
public static void sort(int[] array) {
//初始化堆
for (int i = array.length / 2 - 1; i >= 0; i--) {
adjustHeap(array, i, array.length);
}
// 开始排序
for (int j = array.length - 1, i = 0; j > 0; j--) {
// 元素交换
swap(array, 0, j);
i++;
int start = (array.length - i) / 2 - 1; //最后一个非叶子节点的下标
int length = array.length - i; //需要调整序列的长度
//从最后一个非叶子节点开始调整
for (int k = start; k >= 0; k--) {
adjustHeap(array, k, length);
}
}
}
/**
* 堆内调整
* @param array
* @param i
* @param length
*/
public static void adjustHeap(int[] array, int i, int length) {
int temp = array[i];
for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
// 得到最大的节点的下标
if (k + 1 < length && array[k] < array[k + 1]) {
k++;
}
// 如果发现子节点更大,则进行值的交换
if (array[k] > temp) {
swap(array, i, k);
//调整后更新下标,继续判断是否满足堆的条件
i = k;
} else {
break;
}
}
}
/**
* 交换数组元素
* @param arr
* @param a
* @param b
*/
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
时间复杂度:O(nlogn)
稳定性:不稳定