题目:
给你一个整数数组 nums,请你将该数组升序排列。
冒泡排序优化
概念:把最大的换到最后一位,第二大的换到倒数第二位;
冒泡写法:
冒泡原始写法
public int[] maopao(int[] nums){
for(int i=0;i<nums.length-1;i++){
//int max = 0;
for(int j=i+1;j<nums.length;j++){
if(nums[i]>nums[j]){
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
}
return nums;
}
优化方法一
//内循环处理,每次只交换一次,这种方法其实就是选择排序
public int[] maopao(int[] nums){
for(int i=0;i<nums.length-1;i++) {
int max = 50000, idx = 0;
for (int j = i + 1; j < nums.length; j++) {
if (nums[j] < max) {
max = nums[j];
idx = j;
}
}
if(nums[i]>max){
int tmp = nums[i];
nums[i] = nums[idx];
nums[idx] = tmp;
}
}
return nums;
}
优化方法二
/对已经有序的序列停止排序
public static void MaoPao(int[] arr) {
int length = arr.length;
boolean flag = true;
//外层循环控制轮数:
for (int i = 0; i < length - 1; i++) {
flag = true;
//内存循环进行比较:
//如果内循环整个一轮下去,一次交换也没有发生,
//说明此时0~length-1-i之间的元素都已经是有序(升序)的了,
// 而且我们也知道length-1-i~length-1之间的元素也是刚刚冒泡排序好的有序序列,
// 那么此时说明,整个数组其实都已经是有序的了,就没有必要再继续遍历和比较下去了。
for (int j = 0; j < length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = false;
}
}
//所以我们设置一个flag,如果内循环整个一轮下去,一次交换也没有发生的时候,flag为true,跳出所有循环;
if (flag) {
break;
}
}
}
上面是最原始的冒泡和对冒泡优化后的两种算法,极端情况下这两种算法在对数组排序后都会超时;这时候就要考虑同为交换排序的快速排序;
快速排序
快排原始写法
public void quickSort(int[] nums,int left,int right) {
if(left>=right){
return;
}
int le = left;
int ri = right;
//getMid(nums,left,right);
int povit = nums[left];
while (le< ri) {
while (povit <= nums[ri] && le < ri) ri--;
swap(nums,le,ri);
while (nums[le] <= povit && le < ri) le++;
swap(nums,le ,ri);
}
nums[le] = povit;
quickSort(nums,left,le-1);
quickSort(nums,le+1,right);
}
public void swap(int[] nums,int a,int b){
int tmp = 0;
if(nums[a]>nums[b]){
tmp = nums[a];
nums[a] = nums[b];
nums[b] = tmp;
}
}
优化方法一
三数取中法
三数取中即在数据中取第一个数、最后一个数和中间一个数,在这三个数据得到一个中数,以这个中数作为标准值进行排序
优点:这种方法完美解决了当标准值过大或者过小时造成数据全在标准值的左边或者全在右边,从而使得快速排序退化为冒泡排序。
缺点:当数列特别大时这种三数取中的方法的效果会变小,因为可能取到的三个数都非常小,这种时候就需要多取一些值,比如采取9数取中法;
九数取中:把数组分为前中后三段,每段都采用三值取中得到三个值,然后再从这三个数中去中数作为标准值,这样既能避免标准值偏大或者偏小的情况又能避免数组过大时取到的中值不够均匀
代码实现:
//三数取中法
public void getMid(int[]nums ,int left,int right){
int mid=(left+right)/2;
if (nums[left]>nums[right])swap(nums,left,right);
if (nums[mid]>nums[right]) swap(nums,mid,right);
if (nums[left]>nums[mid]) swap(nums,left,mid);
}
public void swap(int[] nums,int a,int b){
int tmp = 0;
if(nums[a]>nums[b]){
tmp = nums[a];
nums[a] = nums[b];
nums[b] = tmp;
}
}
//优化后的三数取中法
public void quickSort(int[] nums,int left,int right) {
if(left>=right){
return;
}
int le = left;
int ri = right;
getMid(nums,left,right);
int povit = nums[left];
System.out.println("povit="+povit);
while (le< ri) {
while (povit <= nums[ri] && le < ri) ri--;
nums[le] = nums[ri];
while (nums[le] <= povit && le < ri) le++;
nums[ri] = nums[le];
}
nums[le] = povit;
quickSort(nums,left,le-1);
quickSort(nums,le+1,right);
}
优化方法二:
把交换改成赋值
在快速排序中只有最后一次交换是有效的,即之前的操作都是将较小值放在标准值的左边,较大值放在标准值右边;
public void quickSort(int[] nums,int left,int right) {
if(left>=right){
return;
}
int le = left;
int ri = right;
int povit = nums[left];
//防止里面while满足le<ri后进行了 le++操作使得le==ri
while (le< ri) {
//采用写覆盖的方法把右边小值放在左边
while (povit <= nums[ri] && le < ri) ri--;
nums[le] = nums[ri];
//采用写覆盖的方法把左边大值放在右边
while (nums[le] <= povit && le < ri) le++;
nums[ri] = nums[le];
}
nums[le] = povit;
quickSort(nums,left,le-1);
quickSort(nums,le+1,right);
}
优化方法三
减少递归操作
当待排序的序列及其不平衡的时候,递归的深度趋近于n,而不是平衡时的log(n)。而且栈的空间而是有限的,每次递归调用都会耗费一定的栈空间,函数的参数越多,每次递归消耗的空间也就越多,如果减少了递归的操作,就能大大提升性能。所以可以使用尾递归来优化性能
public void quickSort(int[] nums,int left,int right) {
int povit;
while (left< right) {
povit = partition1(nums,left,right);
quickSort(nums,left,povit-1);
left = povit+1;
}
}
public int partition1(int[] nums,int lo,int hi){
//以第一个值为基准值,当然你也可以3取1,
int key=nums[lo];
while(lo<hi){
while(nums[hi]>key&&hi>lo){//从后半部分向前扫描
hi--;
}
nums[lo]=nums[hi];
while(nums[lo]<=key&&hi>lo){//从前半部分向后扫描
lo++;
}
nums[hi]=nums[lo];
}
nums[hi]=key;
return hi;
}
简单插入排序
简单插入排序原始写法
public void chaRu(int[] nums){
int l = nums.length;
for(int i =0 ;i<l;i++){
int tmp = nums[i];
int j = i-1;
while(j>=0 && nums[j]>tmp){
nums[j+1] = nums[j];
j--;
}
nums[j+1] = tmp;
}
}
优化方法一
//折半优化排序
public void MyBinaryInsertSort(int[] nums){
for(int i=0;i<nums.length;i++){
int tmp = nums[i];
int j = i-1;
int le =0 ,ri =i-1,mid = 0;
while(le<=ri){
mid = (le+ri)/2;
if(tmp >nums[mid])
le = mid+1;
else
ri = mid -1;
}
for(;j>ri;j--)
nums[j+1] = nums[j];
nums[j+1] = tmp;
}
}
优化方法二
//希尔排序
public void xier(int[] nums){
int l = nums.length;
for(int dk= l/2;dk>=1;dk = dk/2){
for(int i =dk ;i<l;i++){
int tmp = nums[i];
int j = i-dk;
while(j>=0 && nums[j]>tmp){
nums[j+dk] = nums[j];
j=j-dk;
}
nums[j+dk] = tmp;
}
}
}
总结优化过程
冒泡------选择排序------内部有序判断
快排------三值取中------交换改赋值------减少递归次数
简单插入排序------折半优化插入排序------希尔排序