一、插入排序
1.直接插入排序
1.1 算法思想:
每次从待排序的数列中按顺序拿出一个数,在已经排好序的有序序列中比对其大小,插入到对应位置,形成一个新的有序序列,全部插入完毕后形成的有序序列就是排好序的序列。
1.2 算法图示:
1.3 特性总结:
1. 元素集合越有序,时间效率越高
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:稳定
1.4代码实现:
public static void insertSort(int[] array){
for(int i=0,len = array.length; i<len; i++){
int temp = array[i];//待插入的数据
int j=i-1;
for(; j>=0 && array[j] > temp;j--){
array[j+1] = array[j];//后移为带插入的数据挪出位置
}
array[j+1] = temp;//放入数据
}
}
2.希尔排序(缩小增量排序)
2.1算法思想:
将整个无序列分割成若干小的子序列分别进行插入排序的一种方法,是直接插入排序算法的一种更高效的改进版。
先选定一个增量gap,把待排序文件中所有距离为gap的记录分在同一组内,并对每一组内的记录进行插入排序。然后将增量减少,重复上述分组和排序的工作。当增量减少到1时,整个文件恰被分成一组,算法便终止 。
2.2 算法图示:
2.3 特性总结:
1. 希尔排序是对直接插入排序的优化。
2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序,这样直接插入排序就会很快。
3. 希尔排序的平均时间复杂度: O(N^1.3—N^2)
4. 稳定性:不稳定
2.4代码实现:
public static void shellSort(int[] array){
int[] drr = {5,3,1};//增量
for(int i=0,len = drr.length; i<len; i++){
shellHelper(array, drr[i]);//进行直接插入排序
}
}
private static void shellHelper(int[] array, int gap){
for(int i=gap,len = array.length; i<len ;i++){
int temp = array[i];//待插入数据
int j=i-gap;
for(; j>=0 && array[j] > temp; j-=gap){
array[j+gap] = array[j];//移出位置
}
array[j+gap] = temp;//放入数据
}
}
二、选择排序
1.直接选择排序
1.1算法思想:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部将待排序的数据元素排完
1.2 算法图示:
1.3 特性总结:
1. 直接选择排序效率不是很好,实际中很少使用
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:不稳定
1.4代码实现:
public static void selectSort(int[] array){
for(int i=0,len = array.length; i<len-1; i++){
for(int j=i+1; j<len; j++){
if(array[j] < array[i]){
int temp = array[j];
array[j] = array[i];
array[i] = temp;
}
}
}
}
2. 堆排序
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法。它是通过堆来进行选择数据。排升序要建大根堆,排降序建小根堆。
2.1算法思想:
将待排序序列构造成一个大根堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
2.2 算法图示:
2.3 特性总结:
1. 堆排序使用堆来选数,效率就高了很多。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(1)
4. 稳定性:不稳定
2.4代码实现:
public static void heapSort(int[] array) {
for(int i=(array.length-1-1)/2; i>=0; i--){
adjust(array,i,array.length-1);
}
//上面的代码使得整棵树成为大根堆
for(int j=0; j<array.length-1; j++){
//交换
int temp = array[0];
array[0] = array[array.length-1-j];
array[array.length-1-j] = temp;
//再次调整
adjust(array,0,array.length-1-j-1);
}
}
public static void adjust(int[] array,int start,int end){
int temp = array[start];
for(int i=2*start+1; i<=end; i=2*i+1){
if((i<end) && array[i]<array[i+1]){
i++;//最大值的下标
}
if(array[i] > temp){
array[start] = array[i];
start = i;
}else if(array[i] < temp){
break;
}
}
array[start] = temp;
}
三、交换排序
所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
1.冒泡排序
1.1算法思想:
将键值较大/小的记录向序列的尾部/前部移动。
1.2 算法图示:
1.3 特性总结:
1. 时间复杂度:O(N^2)
2. 空间复杂度:O(1)
3. 稳定性:稳
定
1.4代码实现:
public static void bubbleSort(int[] array){
//外层循环控制趟数;
for (int i = 0,len=array.length; i < len - 1; i++){
//内层循环控制趟数,第i趟比较len-i-1次;
for (int j = 0; j < len - i-1; j++){
if (array[j] > array[j + 1]){ //将大数沉底
int temp = array[j+1];
array[j+1] = array[j];
array[j] = temp;
}
}
}
}
2.快速排序
2.1算法思想:
任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
将区间按照基准值划分为左右两半部分的常见方式有:
1. hoare版本
2. 挖坑法
3. 前后指针版本
2.2 算法图示:
2.3 特性总结:
1. 时间复杂度:O(N*logN)
2. 空间复杂度:O(logN)
3. 稳定性:不稳定
2.4 代码实现(优化后):
2.4.1 递归实现:
public static void quickSort(int[] array){
quick(array,0,array.length-1);
}
public static void quick(int[] array,int low,int high){
if(low == high){
return;
}
//优化:插入排序
if(high-low+1 <= 10){
insertSort1(array,low,high);
return;
}
//优化:三数取中
takeThreeNumber(array,low, high);
int par = partion(array,low,high);
//递归左边
if(par >low+1){
quick(array,low,par-1);
}
//递归右边
if(par < high-1){
quick(array,par+1,high);
}
}
public static int partion(int[] array,int low,int high){
int tmp = array[low];
while(low < high){
while((low < high) && array[high] >= tmp){
high--;
}
if(low == high){
break;
}else{
array[low] = array[high];
}
while((low < high) && array[low] <= tmp){
low++;
}
if(low == high){
break;
}else{
array[high] = array[low];
}
}
array[low] = tmp;
return low;
}
public static void insertSort1(int[] array,int low, int high){
for(int i=low+1; i<=high; i++){
int temp = array[i];
int j=i-1;
for(; j>low && array[j] > temp;j--){
array[j+1] = array[j];
}
array[j+1] = temp;
}
}
public static void swap(int[] array,int low, int high){
int temp = array[low];
array[low] = array[high];
array[high] = temp;
}
public static void takeThreeNumber(int[] array,int low, int high){
int mid = (low +high)>>1;
if(array[mid] >array[low]){
swap(array,low,mid);
}
if(array[mid] >array[high]){
swap(array,low,high);
}
if(array[low] >array[high]){
swap(array,low,high);
}
}
2.4.2 非递归实现:
public static void quickSort(int[] array){
Stack<Integer> stack = new Stack<>();
int low = 0;
int high = array.length-1;
int par = partion(array,low,high);
//左边有两个数据元素以上
if(par > low+1){
stack.push(low);
stack.push(par-1);
}
//右边有两个元素以上
if(par < high-1){
stack.push(par+1);
stack.push(high);
}
while(!stack.empty()){
high = stack.pop();
low = stack.pop();
par= partion(array,low,high);
if(par > low+1){
stack.push(low);
stack.push(par-1);
}
if(par < high-1){
stack.push(par+1);
stack.push(high);
}
}
}
public static int partion(int[] array,int low,int high){
int tmp = array[low];
while(low < high){
while((low < high) && array[high] >= tmp){
high--;
}
if(low == high){
break;
}else{
array[low] = array[high];
}
while((low < high) && array[low] <= tmp){
low++;
}
if(low == high){
break;
}else{
array[high] = array[low];
}
}
array[low] = tmp;
return low;
}
四、归并排序
1.算法思想:
该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
2.算法图示:
3.特性总结:
1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(N)
4. 稳定性:稳定
4.代码实现:
4.1递归实现
public static void mergeSort(int[] array,int start,int end){
if(start >= end){
return;
}
int mid = (start+end)>>1;
mergeSort(array,start,mid);
mergeSort(array,mid+1,end);
merge(array,start,mid,end);
}
//合并
public static void merge(int[] array,int start,int mid,int end){
int[] tmpArr = new int[array.length];
int tmpIndex = start;//指的是tmpArr的下标
int start2 = mid+1;//第二个归并段的开始
int i = start;
while(start <= mid && start2<=end){
if(array[start] <= array[start2]){
tmpArr[tmpIndex++] = array[start++];
}else{
tmpArr[tmpIndex++] = array[start2++];
}
}
if(start2<=end){
System.arraycopy(array,start2,tmpArr,tmpIndex,end-start2+1);
}else if(start <= mid){
System.arraycopy(array,start,tmpArr,tmpIndex,mid-start+1);
}
System.arraycopy(tmpArr,i,array,i,end-i+1);
}
4.2非递归实现
public static void mergeSort(int[] array){
for(int i=1; i<array.length; i*=2){
merge(array,i);
}
}
/*
gap:每组的个数
*/
public static void merge(int[] array,int gap){
int[] tmpArr = new int[array.length];
int i=0;
int start1 = 0;
int end1= start1+gap-1;
int start2 =end1+1;
int end2 = start2+gap-1 >array.length-1 ? array.length-1 : start2+gap-1;
//保证有两个归并段
while (start2 < array.length){
while(start1<=end1 && start2<=end2){
if(array[start1] <=array[start2]){
tmpArr[i++] = array[start1++];
}else{
tmpArr[i++] = array[start2++];
}
}
while (start1<=end1){
tmpArr[i++] = array[start1++];
}
while (start2<=end2){
tmpArr[i++] = array[start2++];
}
start1 = end2+1;
end1 = start1+gap-1;
start2 = end1+1;
end2 = start2+gap-1 >array.length-1 ? array.length-1 : start2+gap-1;
}
while(start1 <= array.length-1){
tmpArr[i++] = array[start1++];
}
System.arraycopy(tmpArr,0,array,0,array.length);
}