1、冒泡排序
思想:升序:从数组第2个数开始与前一个数比较,前大于后则交换位置,然后再比较下一位与前一位,即第三位与第2位,遍历比较一次后最末尾为最大值。然后重新遍历比较,依次选择最大值。最后即为升序数组。
时间复杂度:最好:O(n)一次交换都没有发生,最坏:O(n^2)
最好情况是第一轮比较发现无需交换,则代表数组有序。所以时间复杂度此时为O(n)
最坏则是每一轮都需要比较n次,比较n轮、则时间复杂度为O(n^2)
空间复杂度:O(1)
不需要额外空间
稳定性:是
代码:
public class BubbleSort {
public static void main(String[] args) {
int[] a = {1,3,2,5,8,33,77,4,2,4};
new BubbleSort().sort(a);
System.out.println(Arrays.toString(a));
}
public int[] sort(int[] a){
int len = a.length;
int temp = 0;
boolean flag = true;
for (int i = 0;i < len;i++){
for (int j =0; j < len-1;j++){
if (a[j+1] < a[j]){
flag = false;
temp = a[j+1];
a[j+1] = a[j];
a[j] = temp;
}
}
if (flag == true)
break;
}
}
}
输出结果:[1, 2, 2, 3, 4, 4, 5, 8, 33, 77]
2、插入排序
思想:两个数组,一个数组存放无序数据,依次从无序数组中取出值放入另一数组,放入时遍历另一数组找到合适位置插入。
时间复杂度:最好O(n),每次遍历都只执行一次,最坏:O(n^2)每次都要遍历全部数据。
空间复杂度:O(1),,空间未增删。
稳定性:是。
代码:
public class InsertSort {
public static void main(String[] args) {
int[] a = {1,3,2,5,8,33,77,4,2,4};
new InsertSort().insertSort(a);
System.out.println(Arrays.toString(a));
}
public int[] insertSort(int[] a){
int len = a.length;
int[] b = new int[len];
b[0] = a[0];
for (int i = 1;i < len;i++){
for (int j = 0;j < i;j++){
if (a[i] < b[j]){
int lenb=i;
for (int z = j;z < i;z++){
b[lenb] = b[lenb-1];
lenb--;
}
b[j] = a[i];
break;
}else if (j == i-1){
b[i] = a[i];
}
}
}
}
}
结果:[1, 2, 2, 3, 4, 4, 5, 8, 33, 77]
注:
1、可不用重新创建b数组来存放a数组的值,可直接将a数组分为两部分,左边部分相当于b数组且初始大小为0,随着从右边的数组中拿一个放到左边且排序后,左边有序数组大小就变为1了。
2、在执行插入操作时,需要注意当要插入的数值a与b数组中值全部比较一遍而未发生插入时,代表a值大于b数组中所有值,此时,需要将a值直接放入b数组末尾,而不用执行插入操作。
3、在执行插入操作后需要结束当前循环,因为我们在b数组里循环遍历合适插入的位置后,如位置为3、那么将数值插入后,如果循环不结束而是继续遍历至末尾,是会发生错误的以及是额外的浪费。所以插入操作完成后需要结束此次遍历的循环。
4、上述代码虽然有3个for循环,好像最坏的时间复杂度应该是n ^ 3,实际上,最后两个循环执行的次数总和恒定为n次,如最好情况就是不执行插入操作,而是直接添加到末尾,那么相当于有一个关于插入的循环没有使用(即第三个循环),如最坏情况,需要插入在最前面,那么我们只需要执行n+1次,n次为插入操作时所有数据都需要后移一位,1次是第二个循环刚开始判断的一次,但在时间复杂度运算中,n+1次就相当于n次,所以最坏时间复杂度为O(n ^ 2)。
5、我们可把插入操作封装成方法,方便复用。
3、选择排序
思想:每次从无序数组中选择一个最大值/最小值放入到另一数组中,以达到排序目的
时间复杂度:最好和最坏:O(n^2)
最好、最坏:选择排序恒定需在无序数组遍历一遍选最大值且放入另一数组中,遍历n边,所以最好最坏都一样O(n ^ 2)、但如果添加判断无序数组是否本来就有序、则只需遍历一遍,即为O(n)(此情况不做代码演示)
空间复杂度:O(1)
稳定性:否
代码:
public class SelectSort {
public static void main(String[] args) {
int[] a = {1,3,2,5,8,33,77,4,2,4};
new SelectSort().sort(a);
System.out.println(Arrays.toString(a));
}
public int[] sort(int[] a){
int len = a.length;
int min = 0,minX = 0;
for (int i = 0; i < len-1;i++){
min = a[i];
minX = i;
for (int j = i+1;j < len;j++){
if (min > a[j]) {
min = a[j];
minX = j;
}
}
a[minX] = a[i];
a[i] = min;
}
}
}
输出结果:
[1, 2, 2, 3, 4, 4, 5, 8, 33, 77]
注:
1、我们在存放最小值下标时要考虑到如果遍历一整轮下来都没有发生交换时,即此时我们最初赋值给min的值为最小值,那么在赋值给min时也要考虑到将赋值的下标赋给minX,也将最小值下标初始化一下。
4、归并排序
思想
准备好无序数组与一个空数组,两者空间大小相同。将无序数组两两细分为若干个子数组保证最终子数组只有一个数(如:有8个数,则先分为4、 4两子数组,之后2,2,2,2四个子数组,最终1,1,1,1…八个子数组),然后将子数组两两有序合并、即判定两个子数组头部数字的数值大小,若小则放入等同空间(即两子数组空间和的大小)的空数组中,然后依次判断放入,最终合并为个数为2的四个子数组,然后重复以上方法,直到合并为一个数组,即为排序完成。
时间复杂度:最好与最坏:O(nlog2^n)
空间复杂度:O(n)
稳定性:是
代码:
public class MergeSort {
public static void main(String[] args) {
int[] a = {34,22,77,5,77,43,11,23,4,2,0};
int[] b = new int[a.length];
new MergeSort().mergeSort(a,0,a.length-1,b);
System.out.println(Arrays.toString(a));
}
public void merge(int[] a,int left,int mid,int right,int[] b){
int i = 0;
int j = left,k = mid+1;
while (j <= mid && k <= right){
if (a[j] <= a[k])
b[i++] = a[j++];
else
b[i++] = a[k++];
}
while (j <= mid)
b[i++] = a[j++];
while (k <= right)
b[i++] = a[k++];
for (int t = 0;t < i;t++){
a[left+t] = b[t];
}
}
public void mergeSort(int[] a,int left,int right,int[] b){
if (left < right){
int mid = (left+right)/2;
mergeSort(a,left,mid,b);
mergeSort(a,mid+1,right,b);
merge(a,left,mid,right,b);
}
}
}
结果:
[0, 2, 4, 5, 11, 22, 23, 34, 43, 77, 77]
5、快速排序
思想:我们取数组中一个值为基准值,小于它的放左边,大于放右边。然后在左右两边的数中各取一个值为基准值,再进行小左大右,此时最开始的基准值不参与运算,隐藏细分,直到无值可作为基准值代表排序完成。
时间复杂度:最好:O(nlog2^n),最坏:O(n ^ 2)
空间复杂度:O(log2^n)~O(n)
稳定性:否
public class QuickSort {
public static void main(String[] args) {
int a[] = {30,40,60,10,20,50};
QuickSort.sort(a,0,a.length-1);
System.out.println(Arrays.toString(a));
}
public static void sort(int[] a,int l,int r){
if (l < r){
int x,i,j;
i = l;
j = r;
x = a[i];
while (i < j){
while (i < j && a[j] > x)
j--;
if (i < j)
a[i++] = a[j];
while(i < j && a[i] < x)
i++;
if(i < j)
a[j--] = a[i];
}
a[i] = x;
sort(a, l, i-1);
sort(a, i+1, r);
}
}
}
输出结果:
[10, 20, 30, 40, 50, 60]
6、希尔排序
思想:设定一个增量(N)为数组大小的一半、将数组分为 N 个子数组,然后子数组进行直接插入排序、一轮后将增量(N)重新设置为 N/2 即一半,并按增量大小重新分割数组、且子数组进行直接插入排序。如此循环往复、最终增量变为1时,即此时分割的子数组数为1时,数组已大致有序,此时进行最后一次直接插入排序即可。因在数组有序时,直接插入排序效率非常高。
时间复杂度:最好:O(n^1.3)、最坏:O(n ^2)
空间复杂度:O(1)
稳定度:否
|