一、插入排序
1.1基本思想
1.2直接插入排序
示例代码如下:
class Sort{
public static void insertSort(int[] array){
//排序完成条件
for (int i = 1; i < array.length ; i++) {
//用于比较大小
int tmp = array[i];
//j的起始位置
int j =i-1;
//单次比较的结束条件
for(;j>=0;j--){
if(array[j]>tmp){
array[j+1]=array[j];
}else{
//由于前面的元素经排序后已经有序,故直接将tmp的值放入对应位置后,j无需在遍历前面的元素进行比较,
//直接跳出循环即可。
array[j+1]=tmp;
break;
}
}
array[j+1]=tmp;
}
}
}
1.3希尔排序(缩小增量排序)
思路有了,代码如何实现?如何分组?
代码如下:
public static void shellSort(int[] array){
int gap=array.length;
while(gap>1){
gap/=2;
shell(array,gap);
}
}
private static void shell(int[] array, int gap) {
for (int i = gap; i <array.length ; i++) {
//增量为i++,使排序过程分组交替进行
int tmp = array[i];
int j = i-gap;
for (; j >=0 ; j-=gap) {
if(array[j]>tmp){
array[j+gap]=array[j];
}else{
array[j+gap]=tmp;
break;
}
}
array[j+gap]=tmp;
}
}
二、选择排序
2.1直接选择排序
2.1.1基本思想
2.2.2代码实现
图示:
代码演示:
public static void selectSort(int[] array){
for (int i = 0; i < array.length; i++) {
//每轮比较默认i下标的值为最小值
int minIndex=i;
for (int j =i+1; j <array.length ; j++) {
if(array[j]<array[minIndex]){
minIndex=j;
}
}
int tmp=array[i];
array[i]= array[minIndex];
array[minIndex]=tmp;
}
}
2.2堆排序
堆排序思路在前面已经了解到,详情见文章中“堆的应用”部分。
https://blog.csdn.net/zhakakqns/article/details/141531282?spm=1001.2014.3001.5501https://blog.csdn.net/zhakakqns/article/details/141531282?spm=1001.2014.3001.5501
public static void heapSort(int[] array){
createHeap(array);
int end=array.length-1;
while(end>0) {
int tmp = array[0];
array[0] = array[end];
array[end] = tmp;
siftDown(array, 0, end);
end--;
}
}
private static void createHeap(int[] array){
//创建堆
for (int parent = (array.length-1-1)/2; parent >=0 ; parent--) {
siftDown(array,parent,array.length);
}
}
private static void siftDown(int[] array,int parent,int length){
//向下调整
int child = parent+1;
while(child<length){
if(child+1<length && array[child]<array[child+1]){
child++;
}
if(array[child]>array[parent]){
int tmp=array[parent];
array[parent]=array[child];
array[child]=tmp;
parent=child;
child=2*parent+1;
}else{
break;
}
}
}
三、交换排序
3.1冒泡排序
3.1.1普通冒泡排序
public static void bubbleSort(int[] array){
for (int i = 0; i <array.length-1 ; i++) {
for (int j = 0; j < array.length-i-1; j++) {
if(array[j]>array[j+1]){
int tmp=array[j];
array[j]=array[j+1];
array[j+1]=tmp;
}
}
}
}
3.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-i-1; j++) {
if(array[j]>array[j+1]){
int tmp=array[j];
array[j]=array[j+1];
array[j+1]=tmp;
flg=true;//发生交换,置为true
}
}
if(!flg){
break;
}
}
}
!!!一般我们所说的冒泡排序的时间复杂度是指未优化过的冒泡排序。
3.2快速排序
3.2.1快速排序
基本思想:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
3.2.1.1Hoare版
代码实现:
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){
//以left下标的值作为基准
int key = left;
while(left<right) {
//如果数据为3,4,5,6那么right一直--也找不到比3大的数据
//,会导致越界,因此必须右left<right这个条件
while (left<right && array[right] >= array[key]) {
right--;
}
while (left<right && array[left] <= array[key]) {
left++;
}
int tmp = array[right];
array[right] = array[left];
array[left] = tmp;
}
int tmp = array[left];
array[left]=array[key];
array[key]=tmp;
return left;
}
3.2.1.2挖坑法
递归的思路和Hoare法一样,也就是说只需要修改Hoare法的partition方法即可。
代码如下:
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;
}
3.2.2快速排序优化
当一组数据有序时,快速排序的时间复杂度会达到O(N^2),并且当数据量过多时还会导致栈溢出,因此我们右以下几种优化方法:
(1)三数取中法选key
下面是寻找中间大小数据的方法:
private static int getMiddleNum(int[] array,int left,int right){
int mid = (left+right)/2;
if(array[left]<array[right]){
if(array[mid]<array[left]){
return left;
}else if(array[mid]>array[right]){
return right;
}else{
return mid;
}
}else{
if(array[mid]>array[left]){
return left;
}else if(array[mid]<array[right]){
return right;
}else{
return mid;
}
}
(2)递归到小的子区间时,可以考虑使用插入排序 ,但是在使用插入排序时,应注意排序的范围
i要从start+1开始,当j<start时结束该轮插入。
代码如下:
private static void quick(int[] array,int start,int end){
if(start>=end){
return;
}
//当数据小于10个时,使用直接插入排序
if(end-start+1<10){
insertSortRange(array,start,end);
return;
}
int pivot = partition(array,start,end);
quick(array,start,pivot-1);
quick(array,pivot+1,end);
}
private static void insertSortRange(int[] array, int start, int end) {
for (int i = start+1; i <=end ; i++) {
int tmp=array[i];
int j=i-1;
for(;j>=start;j--){
if(array[j]>tmp){
array[j+1]=array[j];
}else{
array[j+1]=tmp;
break;
}
}
array[j+1]=tmp;
}
}
3.2.3快速排序非递归
代码如下:
public static void quickNor(int[] array,int start,int end){
//首先先用pivot将两个子数组的start,end放入栈中
int pivot = partition(array,start,end);
Stack<Integer> stack = new Stack<>();
//如果pivot<=start+1,pivot>=end-1说明只剩下一个数据,则已经有序,不用再排序
if(pivot>start+1){
stack.push(start);
stack.push(pivot-1);
}
if(pivot<end-1){
stack.push(pivot+1);
stack.push(end);
}
while(!stack.isEmpty()){
end=stack.pop();
start=stack.pop();
pivot=partition(array,start,end);
if(pivot>start+1){
stack.push(start);
stack.push(pivot-1);
}
if(pivot<end-1){
stack.push(pivot+1);
stack.push(end);
}
}
}
3.2.4快速排序总结
四、归并排序
public static void mergeSort(int[] array){
mergeSortTmp(array,0,array.length-1);
}
private static void mergeSortTmp(int[] array, int left, int right) {
if (left >= right) {
return;
}
int mid = (left+right)/2;
mergeSortTmp(array,left,mid);
mergeSortTmp(array,mid+1,right);
//分解完毕,开始合并
merge(array,left,mid,right);
}
private static void merge(int[] array, int left, int mid, int right) {
//相当于合并连个有序数组
int[] tmp=new int[right-left+1];
int k=0;
int s1=left;
int e1=mid;
int s2=mid+1;
int e2=right;
while(s1<=e1 && s2<=e2){
if(array[s1]<=array[s2]){
tmp[k++]=array[s1++];
}else{
tmp[k++]=array[s2++];
}
}
while(s1<=e1){
tmp[k++]=array[s1++];
}
while(s2<=e2){
tmp[k++]=array[s2++];
}
for (int i = 0; i < k; i++) {
array[i+left]=tmp[i];
}
}
4. 稳定性:稳定