例如:待排序序列 5,4,3,7,2,7,(目的是升序排列)
排n趟,每趟只是将原序列变得相对有序,随着趟数的增加,有序性也得到提升,最终完全有序
5,4,3,7,2,7
第一趟,5>4,交换---》 4,5,3,7,2,7
5>3 ,交换---》 4,3,5,7,2,7
5<=7,不交换
7>2 ,交换,---》4,3,5,2,7,7
第一趟冒泡结果:4,3,5,2,7,7 , (其中 4,5,7,7有序)
第二趟冒泡结果(重复第一次的比较方式):3,4,2,5,7,7(其中3,4,5,7,7有序)
第三趟冒泡结果:3,2,4,5,7,7(其中3,4,5,7,7和2,4, 有序)
第四趟冒泡结果:2,3,4,5,7,7(其中2,3,4,5,7,7有序)
第五趟冒泡:没有发生交换,冒泡到此结束
规律:每趟比较都是将大的元素交换到后面
第一趟比较,就能将最大元素交换到最后
第二趟比较,就能将第二大元素交换到倒数第二个位置
******
那么每趟比较的元素数量是呈现 --1的趋势,这和插入排序有点像
public int [] testBubbleSort(int []data){
int maxPos=0;
for(int i=0;idata[j+1])
{
//将最大位置与i交换
int temp =data[j];
data[j]=data[j+1];
data[j+1]=temp;
}
}
}
return data;
}
/ 比如 初始是 1,-1,3,5,2,7,8,9
//第一趟扫描之后:-1,1,3,2,5 ,7,8,9 最后一次2交换位置是(下标) 3
//第二趟扫描之后: -1,1,2,3,5,7,8,9 最后一次2交换位置是2(下标) 扫描范围也应该是 [0,3] 而不是[0,到length]
优化版本java代码:
/**
* @author wangwei
* @date 2019/1/24 18:49
* @classDescription 冒泡排序:
* 思想:每趟比较,将较大的向后交换,有序子序列从后往前扩张
* 稳定性:稳定
*/
public class BubbleSort implements Sortable {
@Override
public void sort(int[] array) {
//这只是一个小小的优化
//优化1:冒泡排序结束的条件:最后一趟没有发生交换
//优化2:第i趟发生的最后一次交换位置未被记录,导致第i+1 趟会重复 扫描未上一次没有发生交换的位置
// 比如 初始是 1,-1,3,5,2,7,8,9
//第一趟扫描之后:-1,1,3,2,5 ,7,8,9 最后一次2交换位置是(下标) 3
//第二趟扫描之后: -1,1,2,3,5,7,8,9 最后一次2交换位置是2(下标) 扫描范围也应该是 [0,3] 而不是[0,到length]
boolean exchanged = true;
int m = array.length-1;
while (m > 1 && exchanged) {
exchanged = false;
int lastSwapIndex = 1;//记录最后一次交换位置,初始化为1
for (int j = 0; j <= m-1; j++) {
if (array[j] > array[j + 1]) {
ArrayUtil.swap(array, j, j + 1);
exchanged = true;
lastSwapIndex = j;
}
}
m = lastSwapIndex;
if (!exchanged) {
return;
}
}
}
}
使用一个单位的空间来存放当前比较对象序列中的第一个元素,该趟的目标是找出这段比较序列中的最小元素及其下标,将其交换到这段比较序列头部
第一趟:比较序列是 5,4,3,7,2,7
初始minPos=0;
minVal=array[0]=5
在这一趟比较中,会出现 4 3 2 第一趟比较完之后,将第一趟找出的最小元素交换到序列头部 也就是 第一趟比较之前: 5,4,3,7,2,7, 第一趟比较之后交换结果:2,4,3,7,5,7 第二趟比较序列是: 比较交换后结果: 2,3,4,7,5,7 第三趟比较序列是: 第i趟比较序列是: array[i-1],array[i],....,array[array.length-1] 每比较完一趟,下一趟比较的元素数量比上一趟少一个 模拟摸牌的过程,手中的牌始终是有序的,需要申请空间array.length(这个也是不一定的,如果直接将原数组在逻辑上分为有序与无序的两部分,也就不需要分配array.length 的空间来单独存储有序序列) 空间复杂度:o(1) 时间复杂度:o(n^2) 稳定性:稳定 生成随机数组的代码: 此时速率比较:快排>插入排序>选择排序>冒泡排序 20万条元素排序时间(毫秒): 插入: 5593 选择:1,6547(冒泡的改良,避免了大量无效的交换) 冒泡:8,2772(慢如蜗牛,主要原因是大量无效的交换) 快速:47(飞一样的感觉) 思想:将大规模的数组不断地二分,直到每个分区是一个元素,然后执行两个有序分区间的合并,直到合并成一个分区,完成排序 时间复杂度:o(nlgn) ,每趟归并时间复杂度是o(n) ,总共需要归并lgn 趟 空间复杂度:o(n) java代码实现: 注意这里使用了两个版本 第一个是直接每次merge时,申请空间,存储归并后的有序结果,然后拷贝至原数组对应区间 第二个版本是排序开始之前,就申请了一个与待排序数组等大的数组tempArrays,每次归并操作都是将有序结果合并在tempArrays然后将数据拷贝至原数组对应区间 前者需要的空间是:o(nlgn) 后者只需要:o(n) 两个标记i,和j分别指向数组两端 选取一个k值,表示比较的对象,枢纽 目的是为了将比k小的元素,放到k的左侧,比k大的元素放在k的右侧 待排序数组: i=0,j=5 k=array[i]=5; part1 从右往左比较: j=5时,array[ j ]=7>k,不交换 j--; j=4时,array[ j ]=2 目前数组: k=array[ j ] =5; part2 从左往右比较 此时:i=0;j=4 k=5>array[i]=2,不交换 i++ k=5>array[i]=array[1]=4,不交换 i++ k=5>array[i]=array[2]=3,不交换 i++ k=5<=array[i]=array[3]=7,交换 交换结果: 现在从右往左比较,回到part1 k=array[i]=array[3]=5 k=5 j-- 出现i和j碰头,结束以k=5的比较,对k=5这一元素的左右两侧分别执行上述操作,part1+part2 第一次快排的结果: 数组1:2,4,3 数组2:7,7 5,4,3,7,2,7, 快排的优化在于:枢纽的选取,尽可能居中,这样就能减少递归的次数 选取枢纽的方式:
*
* 需要注意是:优化需要考虑 "中间值"的选取方式
* 常用的选取方式:1,取首元素(不一定能取到靠中的位置,比如 1,2,3,4,5,6) 有序与逆序情况这是糟糕的
* 2,随机数法 (比较耗时)
* 3,三者取中(耗时短)
*
* 待排序数组的特点也会影响排序效率:如果数组接近有序,快排会退化为冒泡排序
*/
public class QuickSort implements Sortable {
@Override
public void sort(int[] array) {
qkSort(array, 0, array.length - 1);
// quickSort(array,0,array.length-1);
}
void qkSort(int[] array, int low, int high) {
if (low >= high) {
return;
}
//获取枢纽值,并将其放在当前待处理序列末尾
//这里的keyPos 是选取的三者取中的方法,当然也可以采用随机数,(耗时)),取首元素(对于完全有序或逆序,会很差的性能)
dealPivot(array, low, high);
int keyPos = high - 1;
System.out.println(keyPos);
int keyVal = array[keyPos];
int left = low +1;
int right = high-1;
while (left < right) {
//从左往右找到第一个比kVal大的元素 x1
while ((left < right) && array[left] <= keyVal) {
left++;
}
//从右往左找到第一个比kVal 小的元素 x2
//从右往左,大的往右交换
while ((left < right) && array[right] >= keyVal) {
right--;
}
//交换 x1,x2
if (left < right) {
ArrayUtil.swap(array, left, right);
}
}
//防止出现left与right碰头处元素大于kVal,如果不发生交换,此次排序相当于无用
if (array[right] > keyVal) {
ArrayUtil.swap(array, right, keyPos);
}
qkSort(array, low, right - 1);
qkSort(array, right + 1, high);
}
// 三者取中 将 两端 与中间位置元素比较,将中间大小的数据放在数组倒数第二个位置,最小放在开头,最大放在末尾
private void dealPivot(int[] array, int left, int right) {
//比如:1 4 2 4 7 3 8 首:1,中:4,尾:8 排序为 1,4,8 调整后数据变为 1,4,2,7,3,4,8
int mid = (left + right) / 2;
if (array[mid] > array[right]) {
ArrayUtil.swap(array, mid, right);
}
if (array[left] > array[right]) {
ArrayUtil.swap(array, left, right);
}
if (array[left] > array[mid]) {
ArrayUtil.swap(array, left, mid);
}
ArrayUtil.swap(array, mid, right - 1);
}
1,冒泡排序:每趟总是从左至右与相邻元素比较,一趟会将当前待排序序列中的最大的那个元素交换到最后 (注意:并不一定每次只是完成一个元素的排序,也就是该趟的最后一次交换位置,可以记住,减少下次排序规模) 2,选择排序:与冒泡类似,但是不会频繁的交换元素,使用一个空间记录当前待排序序列中的最大值,该趟扫描完,放到序列最后,待排序规模-1 3,插入排序:类比摸取扑克牌,申请一个输入规模一样大的空间(也可以不申请,只是在原数组逻辑上分为有序与无序区间),存储已排好序的元素 4,归并排序:将大数组不断二分,将各个有序子数组归并成一个有序数组,最终完成排序 5,快速排序:通过分而治之的思想排序。2, 4,3,7,5,72,3,4,7,5,7java代码实现
**
* @author wangwei
* @date 2019/1/24 10:47
* @classDescription 简单选择排序
* 排序思想:从无序子序列中"选择"关键字最小(升序排列)或最大的记录,并将其加入到有序子序列中,以此扩充有序子序列长度
* 排序稳定性:不稳定,由于使用了交换
*/
public class SimpleSelectSort implements Sortable{
@Override
public void sort(int[] array) {
for(int lastSortedIndex=0;lastSortedIndex
3,插入排序:
java代码实现:
/**
* @author wangwei
* @date 2019/1/20 13:26
* @classDescription 实现简单插入排序
* 排序思想:模拟摸牌,手中牌始终是有序的,新元素插入手中,需要判断是否需要挪动
* 空间:o(1)
* 时间:o(n^2)
* 稳定性:稳定
*/
public class SimpleInsertSort implements Sortable{
@Override
public void sort(int[] array) {
int currIndex=1;///标记有序和无序元素的分界点 [0,currIndex) 表示有序区间
//随着有序区间不断的扩增,直到完全覆盖整个数组,表示完成排序
//currIndex处元素即为待排序元素
for(;currIndex
public static int []getArrayRand(int length)
{
int []result=new int[length];
for(int i=0;i
4.归并排序
/**
* @author wangwei
* @date 2019/1/24 15:09
* @classDescription 归并排序
* 思想:将整体无序的原数组,分成若干规模小到可以直接排序的分组,每个分组完成排序后,两两合并(两个有序数组的合并)
* 最终达到整体有序的状态
*/
public class MergeSort implements Sortable {
@Override
public void sort(int[] array) {
int []tempArray=new int[array.length];
// mergeSort(array,0,array.length-1);
mergeSort2(array,tempArray,0,array.length-1);
}
void mergeSort(int[] array, int leftPos, int rightPos) {
if(leftPos
5.快速排序:
i
j
下标
0
1
2
3
4
5
元素
5
4
3
7
2
7
i
j
下标
0
1
2
3
4
5
元素
2
4
3
7
5
7
i
j
下标
0
1
2
3
4
5
元素
2
4
3
5
7
7
i
j
下标
0
1
2
3
4
5
元素
2
4
3
5
7
7
java代码实现
public void quickSort(int []array,int from,int to){
if(from<0||to>=array.length){
return;
}
if(from>=to){
return;
}
int kVal=array[from];
int kPos=from;
// int kPos=(from+to)/2;
// int kVal=array[kPos];
int low=from;
int high=to;
while(low
三者取中 java代码实现:
/**
* @author wangwei
* @date 2019/1/24 18:58
* @classDescription 快速排序 思想: 不断地选取枢纽("中间值"),大,小的元素分居两侧,
* 比如: 1,4,3,5,6,2
* 假设选取 3 为中间值
* 第一趟之后:1,2,3,5,6,4
* 稳定性:不稳定
*
总结: