昨天刚把这三个排序算法复习了一遍,其中归并排序和快速排序特别的重要,一定要熟练并理解透彻!
以下排序的结果都默认为非递减
堆排序的思想:首先构建一个完全二叉树,从最大的非叶子结点,如果该结点小于孩子结点,则把该结点与最大的孩子结点交换,使该结点不断的往下沉到合适位置。然后又从第二大的非叶子结点开始,不断循环下去直到根节点,这时候便构造出了大顶堆,最后根结点就是堆中最大的节点。交换根结点(arc[1])与最后一个结点(arc[N--]),再使新的根节点不断往下沉到合适位置,并形成新的大顶堆,又让根结点(arc[1])与最后一个(arc[N--])交换...不断循环至整个数组有序。
看看具体的代码:
public void heapSort(int arc[]){
int N =arc.length-1;
for(int i=N/2;i>=1;i--){ //从最大的非叶子结点开始,构造一个大顶堆
sink(arc,N,i);
}
while(N>=1){
each(arc,1,N--); //交换啊arc[1]与arc[N--],注意是N--而不是N
System.out.println(arc[N+1]); //输出根结点,即堆中最大的结点
sink(arc,N,1); //使新的根结点往下沉到合适的位置
}
}
private void sink(int[] arc,int N,int i) { //使结点沉到合适的位置
int j;
while(2*i<=N){
j = 2*i;
if(jarc[j]){ //如果父结点比两个子结点都大,则跳出循环
break;
}
each(arc,i,j); //交换父子结点
i=j;
}
}
private void each(int[] arc, int i, int j) { //两个结点交换
int t;
t = arc[i];
arc[i] = arc[j];
arc[j] = t;
}
堆排序算法的几个特点:
1.时间复杂度为O(nlogn);
2.不稳定
3.适用于大量数据选出小于k(k>0)的元素
归并排序思想:应用了分治思想,即不断的将大问题变小问题,通过解决小问题来解决整个大问题。
自上而下归并排序思想:不断地将无序数组用递归方法划分为两个数组,直到数组变的很小时(如划分到每个数组只有1个元素时)再进行两两归并,最后使得整个数组有序。
看看具体的代码
public void mergeSort(int arc[],int low,int hight){
int mid=low+(hight-low)/2;
if(low>=hight){ //递归的结束条件
return;
}
mergeSort(arc,low,mid); //将数组划分为两个数组
mergeSort(arc,mid+1,hight);
merge(arc,low,mid,hight); //将两个数组归并
}
private void merge(int[] arc, int low, int mid,int hight) {
int a;
int i=low,j=mid+1; //注意i=low,而不是i=0
for(int k=low;k<=hight;k++){//k=low,而不是k=0,k<=hight,而不是k<=arc.lenght-1
aux[k] = arc[k];
}
for(a=low;a<=hight;a++) //a=low,而不是a=0,a<=hight,而不是a<=arc.lenght-1
if(i>mid){ //如第一个数组元素全部已经归并,则将第二个数组元素直接归并到arc[]
arc[a] = aux[j++];
}else if(j>hight){ //如第二个数组元素全部已经归并,则将第一个数组元素直接归并到arc[]
arc[a] = aux[i++];
}else if(aux[i]
注:数组aux[]声明为全局变量,如果作为局部变量的话,在递归过程中会不断实例化新的数组,这样子会增加算法的空间复杂度
自下而上归并排序思想:直接将数组进行两两(每个数组为1个元素)归并,然后四四(每个数组为2个元素)归并,八八(每个数组为4个元素)归并,一直循环下去,直到将整个数组归并。
看看具体的代码
public void mergeSort2(int arc[]){
int N = arc.length;
int low;
for(int sz=1;sz
注:之前一直对merge(arc,low,low+sz-1,Math.min(low+2*sz-1, N-1));中的low+sz-1和low+2*sz-1的“-1”难以理解,网上又找不到对它的解释,现在就将我的理解分享出来吧
(sz即为两两、四四、八八归并时的变量,这里不多解释)。假设a指向的元素和a左边的所有元素称为a拥有的元素,因此low+sz-1和low+2*sz-1中的 “-1”是因为low和sz或者low和2*sz拥有的元素中,low指向的那个元素重复了,既然重复那么就要-1。
归并排序算法的几个特点:
1.时间复杂度为O(nlogn);
2.稳定
3.自上而下的归并排序需要创建辅助数组
快速排序的思想:找一个切点,将数组切分为两部分,小于切点的元素交换到切点的左边,大于切点的元素交换到切点的右边,并返回切点下标,再把切点左边和切点右边分别作为一个数组,递归下去,直到low>=high。
看看具体的代码
public void qSort(int arc[],int low,int high){
if(low>=high){ //递归的结束条件
return;
}
int pivot = partiton(arc,low,high); //返回切点
qSort(arc,low,pivot); //将切点左边的数组作为整体递归
qSort(arc,pivot+1,high); //将切点右边的数组作为整体递归
}
private int partiton(int[] arc, int low, int high) { //将数组切分为两部分,小于切点的元素交换到切点的左边,
// 大于切点的元素交换到切点的右边,并返回切点下标
int pivotkey = arc[low]; //默认数组的第一个元素为切点
while(low=pivotkey){
high--;
}
ench(arc,low,high); //元素的交换
while(low
快速排序算法的几个特点:
1.时间复杂度为O(nlogn);
2.不稳定
1.堆排序适用于大量数组选出小于k(k>0)的元素的应用,过程是不断向堆尾添加数据,使新添加的不断往上浮到合适位置,并把根结点(值最大)去掉,重新调整为大顶堆,不断循环,就可以找到小于k的元素。这样大量数据就需要一次性调入内存进行排序,而是调用一部分作为堆,并不断向堆添加数据和删除不符合条件的数据。
2.归并排序分为自上而下和自下而上两种,即递归和非递归。
3.虽然快速排序和归并排序的时间复杂度都一样(nlogn),但是由于硬件原因,快速排序的平均速度要快于归并排序