目录
一、排序基础
排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
稳定性:关键值相等的元素,排序之后相对位置不变,如果不变则是稳定的,改变则是不稳定的。但是稳定性并不是判断排序算法是否优秀的指标。
二、内排序
1.插入排序
基本思想:每步讲一个待排的对象,按照关键码的大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止。即便插入边排序。
直接插入排序
按照顺序依次寻找插入位置
public static void insertSort(int[] array){
for (int i = 1; i < array.length; i++) {
int tmp = array[i];
int j = i-1;
for (; j >= 0; j--) {
if (array[j] > tmp) {
array[j + 1] = array[j];
} else {
break;
}
}
array[j+1] = tmp;
}
}
适用范围:顺序表&链表
时间复杂度:O(N^2)
空间复杂度:O(1),没有开辟额外的空间
从右往前,每次与已经有序的最右边元素比较
如果序列本身有序,则时间复杂度可以达到O(N),所以适用于基本有序数据量不大的排序表。
原本逆序,最快情况变为O(N^2)
希尔排序
将待排序列分成若干序列,分别对每个序列进行直接插入排序,直到增量为1基本有序。分成若干序列保证了数据量不大,不断地缩小增量,同时保证这些范围的数据基本有序,基于这两点使用直接插入排序。
public static void shellSort(int[] array) {
int gap = array.length;
while(gap > 1){
shell(array,gap);
gap /= 2;
}
shell(array,gap);
}
public static void shell(int[] array,int gap){
for (int i = gap; i < array.length; i++) {
int tmp = array[i];
int j = i - gap;
for (; j >=0 ; j-=gap) {
if(array[j] > tmp){
array[j+gap] = array[j];
}else{
break;
}
}
array[j+gap] = tmp;
}
}
第一轮增量一般取k/2,为素数,第二轮在除以2,以此类推。希尔排序是基于直接插入排序改进的,所以属于插入排序。
适用范围:顺序表
空间复杂度:O(1)
2. 交换类排序
基本思想:两个元素比较,如果不符合排序则交换顺序。
冒泡排序
从前往后或者从后往前,两两元素比较元素大小, 若逆序则交换。从前往后会把最大的元素冒泡到最后,从后往前会把最小的元素冒泡到最前。所以每趟会将一个元素放在它最终的位置。知道某一趟排序不交换任何数据了,说明表已经有序,冒泡排序结束。
public static void bubbleSort(int[] array){
for (int i = 0; i < array.length-1; i++) {
boolean flg = false;
for (int j = 0; j < array.length-1-i; j++) {
if(array[j] > array[j + 1]){
swap(array,j,j+1);
flg = true;
}
}
if (flg == false){
return;
}
}
}
空间复杂度:O(1)
时间复杂度:O(N^2)
稳定性:稳定
快速排序
在待排表中选一个元素pivot作为枢纽(一般取第一个),通过一趟排序将表划分为两个独立的部分,pivot左边部分比他小,右边部分比他大,则pivot放在了最终位置。然后分别递归的对左右两个子序列进行快速排序。
//挖坑法
public static void quickSort(int[] array){
quick(array,0,array.length-1);
}
private static void quick(int[] array, int start, int end){
if(start >= end){
return ;
}
int pivot = partition(array, start, end);
quick(array, start, pivot-1);
quick(array, pivot+1, end);
}
private static int partition(int[] array, int left, int right){
int tmp = array[left];
while(left < right){
while(left < right && array[right] >= tmp){
right--;
}
array[left] = array[right];
while(left < right && array[left] <= tmp){
left++;
}
array[right] = array[left];
}
array[left] = tmp;
return left;
}
//Hoare法
private static int partition1(int[] array, int left, int right){
int tmp = array[left];
int i = left;
while(left < right){
while(left < right && array[right] >= tmp){
right--;
}
while(left < right && array[left] <= tmp){
left++;
}
swap(array, left, right);
}
swap(array, left, i);
return left;
}
每次排序完都能确定中间枢纽的最终位置。
判断是否是快速排序第几趟结果:从头到尾一个个查看,是否满足一趟排序后的结果
空间复杂度:O(1)
时间复杂度:O(Nlogn),最坏情况下O(N^2)
稳定性:不稳定
3. 选择排序
简单选择排序
算法思想:第i趟从一个无序子列选择最小的元素与第i个元素交换,则第i趟结束后前i个元素都有序,以此类推,直到n-1趟后,待排元素只剩一个
public static void selectSort(int[] array){
for (int i = 0; i < array.length; i++) {
int minIndex = i;
int j = i + 1;
for (; j < array.length; j++) {
if(array[j] < array[minIndex]){
minIndex = j;
}
}
swap(array,i,j);
}
}
private static void swap(int[] array,int i,int j){
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
空间复杂度:O(1)
时间复杂度:O(N^2)
稳定性:不稳定
堆排序
大小根堆的建立:首先对一个无序的序列进行完全二叉树的建立,然后从下往上,从右往左找到第一个非叶子结点开始比较,与该节点的孩子结点进行比较,必须满足大于所有孩子结点或者小于所有孩子结点。
堆排序:每次将根结点和剩余树中最后一个结点进行交换并且输出最后一个结点,交换完之后将根结点与左右子树根结点比较,并与较小者(较大者)进行交换,重复直到叶子结点,得到新的堆。
空间复杂度:O(1)
时间复杂度:O(nlogn)
稳定性:不稳定
适用于n较大的情况
堆相当于完全二叉树,最后一个非叶子结点是n/2
堆删除根结点元素,最后一个结点进行补位,然后进行向下调整。
4. 归并排序
将两个或两个以上的有序表组合成一个新的有序表
public static void mergeSort(int[] array){
mergeSortFunc(array,0,array.length-1);
}
private static void mergeSortFunc(int[] array,int left, int right){
if(left > right){
return;
}
int mid = (left + right) / 2;
mergeSortFunc(array,left,mid);
mergeSortFunc(array,mid+1,right);
merge(array,left,right,mid);
}
private static void merge(int[] array, int start, int end, int mid){
int s1 = start;
int s2 = mid+1;
int[] tmp = new int[end-start+1];
int k = 0;
while(s1 <= mid && s2 <=end){
if(array[s1] <= array[s2]){
tmp[k++] = array[s1++];
}else{
tmp[k++] = array[s2++];
}
}
while(s1 < mid){
tmp[k++] = array[s1++];
}
while(s2 < end){
tmp[k++] = array[s2++];
}
for (int i = 0; i < tmp.length; i++) {
array[i+start] = tmp[i];
}
}
空间复杂度:O(n)
时间复杂度:O(nlogn)
稳定性:稳定
归并排序在排序过程中比较次数的数量级与序列初始状态无关
归并排序每次未必能选出一个元素放到最终位置上