上节讲的归并排序运行速度比简单排序块,但是它需要的空间是原始数组空间的两倍;通常这是一个严重的缺点
希尔排序,希尔排序的时间复杂度是O(N*(logN)^2)。希尔排序是基于插入排序的。希尔排序又叫缩小增量排序,它是基于插入排序的增强版。
基本思想:把记录按步长进行分组,对每组记录采用直接插入的方法进行排序。随着步长的缩小,所分成的组包含的记录就越来越多,当步长的值减小到1时
,整个数据合成一组,构成一组有序的记录,则完成排序。
过程如下图所示:
具体的执行步骤是:
1,在第一趟排序过程中,我们不妨设gap1=N/2=5,即为相隔距离为5的元素组成一组,可以分为5组。接下来,按照直接插入排序的方法对每个组进行排序。
2,第二趟排序中,我们把上次的gap缩小一半,即为gap2=gap1/2=2(取整数)。这样每相隔距离为2的元素组成一组,可以分为2组。按照直接插入排序的方法
对每个组进行排序。
3,在第三趟排序中,再次把gap缩小一半,即为gap3=gap2/2=1。这样相隔距离为1的元素组成一组,即只有一组。按照直接插入排序的算法对每个组进行排序。此时排序结束。
注意希尔排序是不稳定的排序。
核心代码:
毫无疑问,快速排序是最流行的排序算法,大多是情况下,快速排序都是最快的,执行时间是O(N*logN)级别。它是一种既不浪费空间也不浪费时间的排序算法。
方法其实很简单:给一个序列“6 1 2 7 9 3 4 5 10 8”两端开始“探测”。以第一个数作为基准数(6),先从右向左找一个小于6的数,再从左向右找一个大于6的数,然后交换他们。这里可以用两个变量i和j,分别指向最左边和最右边。我们为这两个变量起一个好听的名字“哨兵i”和“哨兵j”。刚开始的时候让哨兵i指向>序列的最左边(即为i=0),指向数字6.让哨兵j指向序列的最右边(即为i=9),指向数字8.如图:
首先哨兵j开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵j先出动,这一点非常重要。哨兵j一步一步的向左挪动(即为j--),直到找到一个小于6的数停下来。接下来哨兵i再一步一步向右挪动(即为i++),直到找到一个大于6的数停下来。最后哨兵j停在了数字5面前,哨兵i停在了数字7面前。如图:
6 1 2 5 9 3 4 7 10 8
到此,第一次交换结束。接下来开始哨兵j继续向左挪动(注意,每次必须数哨兵j先出发)。他发现了4(比基准数6要小,满足要求)之后停了下来。哨兵i也继续向右挪动,他发现了9(比基准数6大,满足要求)之后停了下来。如下图:
6 1 2 5 4 3 9 7 10 8
第二次交换结束后,“探测”继续。哨兵j继续向左挪动,他发现了3(比基准数6小,满足要求)之后停了下来。哨兵i继续向右挪动,糟糕!此时哨兵i和哨兵
j相遇,哨兵i和哨兵j都走到了3面前。说明此时“探测”结束。我们将基准数6和3进行交换。如下图:
3 1 2 5 4 6 9 7 10 8
此时,第一轮“探测结束”。此时以基准数6为分界点,6左边的数小雨等于6,6右边的数大于等于6.
将以6为分界点的两个子序列:3 1 2 5 4和9 7 10 8再次进行快速排序.整个图的处理过程如下图所示:
注意:快速排序的最差时间复杂度为O(N^2),它的平均复杂度为O(NlogN),它是基于“二分”的思想。
代码实现:
希尔排序,希尔排序的时间复杂度是O(N*(logN)^2)。希尔排序是基于插入排序的。希尔排序又叫缩小增量排序,它是基于插入排序的增强版。
基本思想:把记录按步长进行分组,对每组记录采用直接插入的方法进行排序。随着步长的缩小,所分成的组包含的记录就越来越多,当步长的值减小到1时
,整个数据合成一组,构成一组有序的记录,则完成排序。
过程如下图所示:

具体的执行步骤是:
1,在第一趟排序过程中,我们不妨设gap1=N/2=5,即为相隔距离为5的元素组成一组,可以分为5组。接下来,按照直接插入排序的方法对每个组进行排序。
2,第二趟排序中,我们把上次的gap缩小一半,即为gap2=gap1/2=2(取整数)。这样每相隔距离为2的元素组成一组,可以分为2组。按照直接插入排序的方法
对每个组进行排序。
3,在第三趟排序中,再次把gap缩小一半,即为gap3=gap2/2=1。这样相隔距离为1的元素组成一组,即只有一组。按照直接插入排序的算法对每个组进行排序。此时排序结束。
注意希尔排序是不稳定的排序。
核心代码:
public void shellsort(int[] list){
int gap=list.length/2;
while(gap>=1){
for(int i=gap;i<list.length;i++){
int j=i-gap;
int temp=list[i];
if(j>=0&&list[j]>temp){
list[i]=list[j];
list[j]=temp;
}
}
gap=gap/2;
}
printAll(list);
}
毫无疑问,快速排序是最流行的排序算法,大多是情况下,快速排序都是最快的,执行时间是O(N*logN)级别。它是一种既不浪费空间也不浪费时间的排序算法。
方法其实很简单:给一个序列“6 1 2 7 9 3 4 5 10 8”两端开始“探测”。以第一个数作为基准数(6),先从右向左找一个小于6的数,再从左向右找一个大于6的数,然后交换他们。这里可以用两个变量i和j,分别指向最左边和最右边。我们为这两个变量起一个好听的名字“哨兵i”和“哨兵j”。刚开始的时候让哨兵i指向>序列的最左边(即为i=0),指向数字6.让哨兵j指向序列的最右边(即为i=9),指向数字8.如图:
首先哨兵j开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵j先出动,这一点非常重要。哨兵j一步一步的向左挪动(即为j--),直到找到一个小于6的数停下来。接下来哨兵i再一步一步向右挪动(即为i++),直到找到一个大于6的数停下来。最后哨兵j停在了数字5面前,哨兵i停在了数字7面前。如图:
6 1 2 5 9 3 4 7 10 8
到此,第一次交换结束。接下来开始哨兵j继续向左挪动(注意,每次必须数哨兵j先出发)。他发现了4(比基准数6要小,满足要求)之后停了下来。哨兵i也继续向右挪动,他发现了9(比基准数6大,满足要求)之后停了下来。如下图:
6 1 2 5 4 3 9 7 10 8
第二次交换结束后,“探测”继续。哨兵j继续向左挪动,他发现了3(比基准数6小,满足要求)之后停了下来。哨兵i继续向右挪动,糟糕!此时哨兵i和哨兵
j相遇,哨兵i和哨兵j都走到了3面前。说明此时“探测”结束。我们将基准数6和3进行交换。如下图:
3 1 2 5 4 6 9 7 10 8
此时,第一轮“探测结束”。此时以基准数6为分界点,6左边的数小雨等于6,6右边的数大于等于6.
将以6为分界点的两个子序列:3 1 2 5 4和9 7 10 8再次进行快速排序.整个图的处理过程如下图所示:
注意:快速排序的最差时间复杂度为O(N^2),它的平均复杂度为O(NlogN),它是基于“二分”的思想。
代码实现:
public void quicksort(int[] n,int left,int right){
int dp;
if(left<=right){
dp=partion(n,left,right);
quicksort(n,left,dp-1);
quicksort(n,dp+1,right);
}
}
public void partion(int[] n,int left,int right){
int pro=n[left];
int start=left;
int temp=0;
while(left<right){
while(left<right&&n[right]>=pro){
right--;
}
if(left<right){
temp=n[right];
}
while(left<right&&n[left]<=pro){
left++;
}
if(left<right){
n[right--]=n[left]
n[left++]=temp
}
}
n[start]=n[left];
n[left]=pro;
return left;
}
转载自:
静默虚空、
坐在马桶上看算法:快速排序