1.选择排序
1.1冒泡排序
package com.sort.swapsort;
//冒泡排序
/*
* 冒泡排序
* 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
* 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
* 针对所有的元素重复以上的步骤,除了最后一个。
* 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
* @param numbers 需要排序的整型数组
*/
//时间复杂度:最好情况:O(n) 最差情况:O(n*n) 平均:O(n*n ) 稳定
public class BubbleSort {
public static void bubbleSort(int nums[]){
int temp=0; //交换的中间变量
int size=nums.length; //数组的长度
for(int i=0;i<size-1;i++){
for(int j=0;j<size-1-i;j++){ //为什么j<size-1-i
if(nums[j]>nums[j+1]){ //如果前一个元素比后一个元素大,就交换
temp=nums[j];
nums[j]=nums[j+1];
nums[j+1]=temp;
}
}
}
}
}
算法优化:
若某一趟排序中未进行一次交换,则排序结束
public static void bubbleSort(int[] array) {
int len = array.length;
boolean flag = true;
while (flag) {
flag = false;
for (int i = 0; i < len - 1; i++) {
if (array[i] > array[i + 1]) {
int temp = array[i + 1];
array[i + 1] = array[j];
array[i] = temp;
flag = true;
}
}
len--;
}
}
1.为什么j<size-1-i?
因为当i=0时,最差的情况是j要从第一个数遍历到最后一个数,刚好遍历size-1次,
当i=1时,最差的情况时j遍历到倒数第二个数,因为倒数第一个数已经是最大了,不需要与之比较,增加运算量,所以遍历size-1-1次;
所以当i=i时,j最多遍历的次数是size-1-i,即j<=size-1-i;
2.时间复杂度如何计算的?
最好情况:所有的数都是按照从小到大的顺序排好,所以比较两个相邻的元素时你会发现只用把每个元素与之后面相邻的元素比较一次就可以得出结果来,所以时间复杂度是O(N);
最差情况:所有的数据都是按照从大到小的顺序排好:则需要比较:(n-1)+(n-2)+(n-3)......+1;化简得到:n*n+常数,所以时间复杂度为:O(N*N)
一般我们说的复杂度是它的平均复杂度:[O(N)+O(N*N)]/2,利用复杂度的计算规则来讲:省略常数和只看最高次幂,所以时间复杂度为:O(N*N)
3.稳定性是如何判断的?
排序过程中只交换相邻两个位置的元素,因此,当两个元素相等的时候,是没必要交换两个数的位置的,所以他们的相对位置并没有改变,冒泡排序的算法是稳定的。
1.2快速排序
(a)一趟排序的过程:
(b)排序的全过程:
把整个序列看做一个数组,把第零个位置看做中轴,和最后一个比,如果比它小交换,比它大不做任何处理;交换了以后再和小的那端比,比它小不交换,比他大交换。这样循环往复,一趟排序完成,左边就是比中轴小的,右边就是比中轴大的,然后再用分治法,分别对这两个独立的数组进行排序。
package com.sort.swapsort;
//快速排序
/**
* 把整个序列看做一个数组,把第零个位置看做中轴,
* 和最后一个比,如果比它小交换,比它大不做任何处理;
* 交换了以后再和小的那端比,比它小不交换,比他大交换。
* 这样循环往复,一趟排序完成,左边就是比中轴小的,
* 右边就是比中轴大的,然后再用分治法,分别对这两个独立的数组进行排序。
*/
public class QuickSort {
public static void quickSort(int a[], int left, int right) {
if (left>= right) //当设置一个左边数大于右边的数时,返回
return;
int i = left;//一般以a[0]为中轴起始分界线
int j = right;
int key = a[left];//选择第一个数为key
while (i < j) {
while (i < j && a[j] >= key) //从右向左找第一个小于key的值
j--;
if (i < j) {
a[i] = a[j];
i++;
}
while (i < j && a[i] < key)//从左向右找第一个大于key的值
i++;
if (i < j) {
a[j] = a[i];
j--;
}
}
//i == j
a[i] = key;
quickSort(a, left, i - 1);//递归调用
quickSort(a, i + 1, right);//递归调用
}
}
核心思想:将小的部分放在左边,大的部分放在右边,实现分割。
时间复杂度:
为了分析快速排序的时间复杂度,请先看下面的主定理:
主定理: T [n] = aT[n/b] + f (n)
其中 a >= 1 and b > 1 是常量 并且 f (n) 是一个渐近正函数, 为了使用这个主定理,您需要考虑下列三种情况:
快速排序的每一次划分把一个 问题分解成两个子问题,其中的关系可以用下式表示:
T[n] = 2T[n/2] + O(n) 其中O(n)为PARTITION()的时间复杂度,对比主定理,
T [n] = aT[n/b] + f (n)
我们的快速排序中:a = 2, b = 2, f(n) = O(n)
那么为什么还有最坏情况呢?
考虑如下极端情况,
T[n] = T[n-1] + T[1] + O(n),
问题来了,这一次的划分白玩了,划分之后一边是一个,一边是n-1个,这种极端情况的时间复杂度就是O(n2).