附:Java标准库已经内置了排序功能Arrays.sort(ns);
1.冒泡排序
思想:像气泡一样,小的冒上去
每一轮循环后,最小的一个数被交换到开始,因此,下一轮循环就可以“刨除”最开始的数,每一轮循环都比上一轮循环的结束位置靠后一位。
代码:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
int tmp;
// 排序前:
System.out.println(Arrays.toString(ns));
for(int i = 0; i < ns.length-1; i++) {//一共要进行n-1次比较
for(int j = ns.length-1; j > i; j--) {//每次要比较第ns.length-1到i的数
if(ns[j] < ns[j-1]){
tmp = ns[j];
ns[j] = ns[j-1];
ns[j-1] = tmp;
}
}
}
//排序后
System.out.println(Arrays.toString(ns));
}
}
算法分析:
- 平均时间复杂度:o(n^2),嵌套双循环
- 最好时间复杂度:o(n),如果数组有序,循环一次就可以了
- 最坏时间复杂度:o(n^2)
- 空间复杂度:o(1),只使用了常数空间
- 稳定性:稳定(相同数字比较过程不会发生交换)
优化: 数据排好序后,冒泡算法仍会进行下一轮的比较,直到ns.length-1次结束。后面的比较是没有意义的。
可以设置标志位flag,如果发生了交换就设置flag为true,否则为false。
如果一轮交换后flag仍然为false说明数组已经有序,不需要再进行下去了。
public class Main {
public static void main(String[] args) {
int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
int tmp;
System.out.println(Arrays.toString(ns));
for(int i = 0; i < ns.length-1; i++) {
boolean flag = false;//优化的地方[1]
for(int j = ns.length-1; j > i; j--) {
if(ns[j] < ns[j-1]){
tmp = ns[j];
ns[j] = ns[j-1];
ns[j-1] = tmp;
flag = true;
}
}
if(!flag) {//优化的地方[2]
System.out.println("数组在第"+(i+1)+"次排序后有序");
break;
}
}
System.out.println(Arrays.toString(ns));
}
}
2.快速排序
思想(分治法):1.设置一个基准值(一般设置为数组第一个数),标签low和high。
2.从high开始,当high位置的书比基准值大,high--;比基准值小,把high位置的书放到low的位置,low++
low位置的数比基准值小,low++;比基准值大,把low 位置的数放到high,high--
直到low=high
(最后比基准值小的书都在它左边,比基准值大的数都在它右边)
3.对左右两个小数列重复第二步,直到各个区间都只有一个数
Java代码:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
// 排序前:
System.out.println(Arrays.toString(ns));
//排序
QuickSort(0,ns.length-1,ns);
//排序后
System.out.println(Arrays.toString(ns));
}
public static void QuickSort(int low, int high, int[] ns) {
while(low < high) { //所以有传参的函数一定要先有个判断条件!!!!!
int pivo = ns[low];//将第一个值作为基准赋给pivo暂存
int slow = low;//记录本次排序的初始值(后面会发生改变)
int shigh = high;
while(low < high) {
while(low < high && ns[high] >= pivo) {//左边
high--;
}
ns[low] = ns[high];
while(low < high && ns[low] <= pivo) {//右边
low++;
}
ns[high] = ns[low];
}
ns[low] = pivo;
// System.out.println("初始slow是 = "+slow+" , shigh是 = "+shigh+" ,基准值 = ns[" +slow+"] = "+pivo);
// System.out.println("排序一趟后的基准位置low=high = ns["+low+"] = "+ns[high]);
// System.out.println("最终排序结果: "+Arrays.toString(ns)+"\n");
// System.out.println("上次排序结果: "+Arrays.toString(ns));
QuickSort(slow,low-1,ns);
QuickSort(low+1,shigh,ns);
}
}
}
Python代码:
def partition(arr,low,high):
pivot = arr[low]
while (low < high):
while arr[high] > pivot and low < high:
high -=1
arr[low] = arr[high]
while arr[low] < pivot and low < high:
low += 1
arr[high] = arr[low]
arr[high] = pivot
return high
def quicksort(arr, low, high):
if low < high:
next = partition(arr, low, high);
quicksort(arr, low, next-1)
quicksort(arr, next+1, high)
arr = [8,6,1,9,4,5,10,14,3]
length = len(arr)-1
quicksort(arr, 0, length)
print(arr)
算法分析:
先说主定理:
(a>=1,b>1) 其中a是划分后的子问题数,b是划分后问题的规模。对于快速排序,a=b=2
f(n)=O(n)是划分问题的时间复杂度,快速排序为n
所以快速排序公式然后迭代计算,随后第次划分时 = 后的哪一项只有一个T(1)=1了,此时
所以平均时间复杂度为
最好的情况:快速排序的性能取决于快速排序递归树。递归算法执行情况可以用递归树来描述。
下图是数组{50,10,90,30, 70,40,80,60,20}在快速排序的递归过程,枢轴值是50正好递归树是平衡的,性能比较好。
在最优情况下,Partition每次都划分得很均匀,如果排序n个关键字,其递归树的深度就为.log2n.+1
最坏情况:
在最坏的情况下,待排序的序列为正序或者逆序,每次划分只得到一个比上一次划分少一个记录的子序列,注意另一个为空。如果递归树画出来,它就是一棵斜树。此时需要执行n‐1次递归调用,且第i次划分需要经过n‐i次关键字的比较才能找到第i个记录,也就是枢轴的位置,因此比较次数为 ,最终其时间复杂度为O(n2)。
另一种说法:T[n] = T[n-1] + T[1] + O(n),
问题来了,这一次的划分白玩了,划分之后一边是一个,一边是n-1个,这种极端情况的时间复杂度就是O(n2).
3.堆排序
思想:
代码:
算法分析:
优化: