目录
以下例子均为 从小到大 排序
一、直接选择排序
1.思路
选择选择——> 选择最小的是数据与当前位置交换
每一次从无序区间选出最小(或最大)的一个元素,存放在无序区间的最前(或最后),直到全部待排序的数据元素排完 。有序区间:[0,i) 无序区间: [ i,arr.lenth)
从第个1元素i开始,往后找出一个最小的数据,把该元素与第一个元素i交换;
从第二个,往后找。。。。。。;从第三个往后找。。。。。。
(即i++,从i处继续往后找一个最小的数据,交换)
2.代码
1.一个公共代码swap:交换数组两个下标对应的值
/**
* 交换
* @param arr
* @param i 下标
* @param j 下标
*/
private void swap(int[] arr,int i,int j){
int tmp=arr[i];
arr[i]=arr[j];
arr[j]=tmp;
}
2.代码实现
public void selectSort(int[] arr){
for(int i=0;i<arr.length;i++){
int j=i+1;
//记住最小值所在的下标,待会儿好交换
int minIndex=i;
for(;j< arr.length;j++){
if(arr[j]<arr[minIndex]){
minIndex=j;
}
}
//交换i和minIndex下标对应的值
swap(arr,i,minIndex);
}
}
3.复杂度分析
①时间复杂度:0(N^2)
N-1 + N-2 +N-3 +........+1=等差数列求和 ——> N^2
【不论有序还是无序,都是0(N^2):因为要一直往后找最小的,要全部找了,才能知道是不是最小的】
②空间复杂度:0(1)
4.稳定性:不稳定
二、双向选择排序
1. 思路
最左边一个数据下标L,最右边一个数据下标R,从L后第一个数据开始往后依次遍历,把找到的最大数的下标存入maxIndex,把找到的最小数的下标存入maxIndex
2.代码
1.易错点分析
第一行为排序前,第2行为排序后
错误原因分析
如图,此时L==maxIndex。根据代码,L和minIndex的值交换后,maxIndex的值到minIndex去了,所以此时下一行代码不应该是R和maxIndex的值交换,而应是R和minIndex的值交换(因为maxIndex的值到minIndex去了)。
更改后代码:
2.代码实现
/**
* 交换
* @param arr
* @param i 下标
* @param j 下标
*/
private void swap(int[] arr,int i,int j){
int tmp=arr[i];
arr[i]=arr[j];
arr[j]=tmp;
}
/**
* 双向选择排序
*/
public void selectSort2(int[] arr){
int L=0;
int R=arr.length-1;
while(L<R){
int minIndex=L;
int maxIndex=L;
for(int i=L+1;i<=R;i++){
if(arr[i]<arr[minIndex]){
minIndex=i;
}
if(arr[i]>arr[maxIndex]){
maxIndex=i;
}
}
//交换
swap(arr,L,minIndex); //minIndex和L换(指对应下标的数)
if(maxIndex==L){
maxIndex=minIndex;
}
swap(arr,R,maxIndex); //maxIndex和R换(指对应下标的数)
//迭代
L++;
R--;
}
}
3.时间复杂度:0(N^2)
4.稳定性:不稳定
从易错点分析第3个图可看出,minIndex和L下标对应的值交换后,两个6的相对位置发生了变化
三、堆排序
1.思路
1.总思路
以“从小到大”排序为例:
1.建立大根堆
2.每次和堆顶元素进行交换 [swap ( top , end )]
3.向下调整
4. 直到全部交换完成
总结:大根堆+交换+调整
2.每个步骤的具体思路 (从小到大为例)
①调整:(向下调整)
获取根节点(parent节点),判断是否有右子树。
若是有右子数节点,右子数和左子树哪个大,大的那个为child。
根节点parent和child比较,哪个大?若child大,则[parent]和[child]交换(总之使parent大)。
若是发生交换,则继续向下调整,迭代:parent=child,child=2*child+1。
循环以上操作,若是不发生交换,则break;
当child<=end(end表示逻辑上的最后一个节点的下标)
②建立大根堆:
从最后一个子树开始调整,依次往前一个树调整,直到整棵树都调整完
即获取最后一个子树的树根节点parent : parent=(child-1)/2,向下调整,parent--,循环直到parent=0
③交换:
堆顶值和最后一个元素end值交换,end--, 向下调整数组(该数组逻辑上从堆顶到end),循环该操作,直到end=0,此时堆顶和end相等
2.代码实现(详细)
/**
*
* @param arr
* @param parent
* @param end 表最后一个节点的下标
*/
public void shiftDown(int[] arr,int parent ,int end){
//child此时是左子树节点
int child=parent*2+1;
while(child<=end){
//是否存在右子树? child+1<=end 则存在
//右子树树和左子树谁大?
if(child+1<=end && arr[child]<arr[child+1]){
//有右子树且右子树比左子树大,则child改为右子树节点
child=child+1;
}
//若子树child大于根节点parent,则交换
if(arr[child]>arr[parent]){
swap(arr,parent,child);
}else{
break;
}
//迭代
parent=child;
child=2*child+1; //child此时是左子树节点
}
}
/**
* 创建一个大根堆
*/
public void createHeap(int[] arr){
//最后一个节点end
int end=arr.length-1;
//最后一颗树的父母节点
int parent=(end-1)/2;
//从最后一颗子树开始调整,依次往前一个树调整,直到所有树都调整完
while(parent>=0){
shiftDown(arr,parent,end);
parent--;
}
}
public void heapSort(int[] arr){
//获取一个大根堆
createHeap(arr);
int end=arr.length-1;
//交换
while(end>0){
//交换操作
swap(arr,0,end);
end--;
//向下调整
shiftDown(arr,0,end);
}
}