八大排序
1.交换排序:
a.冒泡排序(相邻交换,最大在后)
外循环表示交换多少轮,内循环表示一轮交换多少次,两次循环
时间复杂度为O(n^2)
规律:
(1)外循环一共进行数组大小-1次循环(每循环一次排序一次)
(2)内循环每一次排序次数都在逐渐减小,(因为最大值已经在最后)
优化:如果发现某次排序一次交换都没有,那这个就是排序好的
// 优化
public static void sortwin(int a[]){
int zhuan=0;
// 标记后面一轮是否有过交换
boolean T=false;
// 所有论
for (int j = 0; j <a.length-1; j++) {
// 一轮循环
for (int i = 0; i <a.length-1-j ; i++) {
if (a[i]>a[i+1]){
T=true;
zhuan=a[i];
a[i]=a[i+1];
a[i+1]=zhuan;
}
}
if (!T){
// 如果没有交换那证明已经排序完成
break;
}else{
// 重置
T=false;
}
}
// System.out.println(Arrays.toString(a));
}
b.快速排序(重点)
基础思想:
1.首先,会有一个基准值和一个左指针以及右指针(并且要有两个变量保存指针)
2.指针跟基准值比较,小的放在基准值左边,大的放右边(指针移动)
3.这样就会分成左边一个值小的分区和右边值大的分区
4.使用递归重复1,2的操作,直到每一个小分区只有1个数据
public static void quicksot(int nums[],int left,int right){
// 递归退出条件
if (left>right){
return;
}
// 因为后面交换中会更改两个指针,所以先保存
int l=left;
int r=right;
// 定义基准值
int pivot=nums[left];
while (l!=r){
// 45 ,30 , 61 , 82 , 74 , 12 , 26 , 49
// 45 ,30 , 26 , 82 , 74 , 12 , 61 , 49
// 45 ,30 , 26 , 12 , 74 , 82 , 61 , 49
// 12 ,30 , 26 , 45 , 74 , 82 , 61 , 49
// --------------------
// 12 ,30 , 26 , 45 , 74 , 82 , 61 , 49
// 从右边找到小于基准的值
while (nums[r]>=pivot && l<r ){
r--;
// 6,5,4,3
// ---------
//
}
// 从左边找到大于基准的值
while ( nums[l]<=pivot && l<r ){
l++;
// 2,3
}
// 注:在这里还需要判断一次l<r
// 交换
if (l<r){
int temp=nums[l];
nums[l]=nums[r];
nums[r]=temp;
}
}
// 当l=r的时候,交换此时的值和基准的值
nums[left]=nums[l];
nums[l]=pivot;
// 递归重复
// 此时的r是基准值的位置
quicksot(nums,left,r-1);
quicksot(nums,r+1,right);
}
2.插入排序
a.直接插入排序
内循环是移动左分区,外循环移动右分区,两次循环,时间复杂度为O(n^2)
规律:1.将整个数组分为两个分区,left有序区,和right无序区
2.左分区的数跟右分区一个一个比较,小于左分区,就左分区比较的那个值向后挪动
3.循环结束证明右分区的值大于左分区,此时leftindex+1才是插入的下标位置
注:下图中right保存了值,因为下面向后挪动会把值给挤掉
优化:当要插入的位置就是当前位置就不要插入
public void insert(int nums[]){
for (int j = 1; j <nums.length; j++) {
//定义右边无序列表要比较的值
int right=nums[j];
// 左边有序列表要跟右边表比较的那个值的下标
int leftindex=j-1;
while (leftindex>=0 && nums[leftindex]>right){
// 向后挪动,为插入让出位置,之前理解有误,这里只是值往后挪,不是下标挪
nums[leftindex+1]=nums[leftindex];
// 有序列表向左移动,继续比较
leftindex--;
}
// 1,-1,2,4
// leftindex是要跟右分区比较值的下标,,但是由于要往左边移动,此时+1才是插入的下标
// 优化,如果=j证明插入的位置就是当前位置
if (leftindex+1!=j){
nums[leftindex+1]=right;
}
}
// System.out.println(Arrays.toString(nums));
}
b.希尔排序
希尔排序其实也是一种插入排序,是在插入排序的基础上优化的一种排序算法
有两种能够实现方式能够实现希尔排序:
原理:a.不断分组分到最后一组再进行其他排序
b.分组不断/2,直到为1
(1)交换实现希尔
(2)插入移动实现:在插入外面套一个循环分组
public void winxier(int num[]){
// 跟插入排序区别不大,外面加了个循环分组
// 分组
for (int fenzhu = num.length/2; fenzhu >0 ;fenzhu/=2) {
// 中层循环表示分成几个小组,且j=i指每个小组的后一个
for (int zhu = fenzhu; zhu<num.length; zhu++) {
// 根据插入排序,这里保存的右边无序分区
int right=num[zhu];
// 左边有序分区跟右边比较的指
int leftindex=zhu-fenzhu;
// leftindex>=0 防止数组越界 num[leftindex]>right只有右边分区小于左边才会进去
while (leftindex>=0 && num[leftindex]>right){
// 左边分区值向后挪动
num[leftindex+fenzhu]=num[leftindex];
// 当前下标值比较完后,下标向左移动,又因为不属为i
leftindex-=fenzhu;
}
// 优化:当判断满足时证明leftindex移动了
if (leftindex-fenzhu!=zhu){
num[leftindex+fenzhu]=right;
}
}
}
// System.out.println(Arrays.toString(num));
}
3.归并排序
使用归并思想实现的排序方法,采用分治的策略,将问题分成多个小问题进行递归求解---分而治之
基本思路:合并次数等于数组长度-1
1.整体分为分 - 治
2.分,怎么分?通过递归向下分,到只有两个数比较为止
3.治,如何治?递归回溯向上合并,在分到最底部开始,
a.比较两个数大小,并使用一个临时数组temp临时储存
b.如果两个序列(最少有1个值)比较完,一方序列还有值,就将剩余的全部放到临时数组temp
c.将临时数组的值再copy到原数组
假如有此数组int nums[]={8,4,5,7,1,3,6,2};
会实现合并7次
//拆分在合并
public static void diguisort(int left,int right,int nums[],int temp[]){
if (left<right){
int mid=(left+right)/2;
// 向左递归
diguisort(left,mid,nums,temp);
// 向右递归
diguisort(mid+1,right,nums,temp);
guisort(left,mid,right,nums,temp);
}
}
// 合并方法
public static void guisort(int left,int mid,int right,int nums[],int temp[]){
// 左边序列的初始值
int l=left;
// 右边序列的初始值
int r=mid+1;
// 因为我们比较两个序列会把小的值暂存另一个数组,所以要有这个计数器
int t=0;
// (1) 左右两边值的比较
while (l<=mid && r<=right){
// 左小于右
if (nums[l]<=nums[r]){
// 左值赋给暂存数组
temp[t]=nums[l];
// 暂存数组后移
t+=1;
// 左序列后移
l+=1;
}else{
temp[t]=nums[r];
t+=1;
r+=1;
}
}
// (2) 如果有一方剩余数组将剩余数组全部放到暂存数组
// 左边序列有剩余
while (l<=mid){
temp[t]=nums[l];
t+=1;
l+=1;
}
// 右边序列有剩余
while (r<=right){
temp[t]=nums[r];
t+=1;
r+=1;
}
// 将temp数组copy到原数组
t=0;
System.out.println("left:"+left+" "+"right:"+right);
while (left<=right){
nums[left]=temp[t];
t+=1;
left+=1;
}
}
4.基数排序
基础思路:(用空间换时间)
1.首先有10个桶,因为用的是十进制数
2.每一趟呢就按位数存在桶里,取回原数组
3.会有多少趟?看最大数的位数,假如最大数为345此时最大位数n=3,有3趟
5.选择排序
a.简单选择排序
跟冒泡有丢相似的地方,冒泡大的往后冒,选择是小的往前选
外循环表示交换多少轮,内循环表示找到最小值,两次循环
时间复杂度为O(n^2)
规律:
(1)有数组大小-1轮排序
(2)每一轮排序又会通过循环找到一个最小值
(3)如第一轮找到最小值(1--length)与第一个值a[0]交换,第二轮排序就从
(2--length)里找最小值,找到后与a[1]交换
(4)初始化时定义一个最小值和最小值下标
优化:定义的下标值改变后才交换,没有改变就是当前就是最小值
注:如下图A步骤那里刚开始时一直想着感觉就是保存了最小值,可以拿到下面B步骤那里一起交换,后来发现一直不行,debug半天才发现,里面那个循环,如果不重置最小值的话就会找到最后一个比min小的值而不是最小值,因为最小值min一直没有重置
public static void jishu(int nums[]){
// 求最大数
int max=nums[0];
for (int i = 1; i < nums.length ; i++) {
if (max<nums[i]){
max=nums[i];
}
}
// 求出最大数的位数
int maxnum=String.valueOf(max).length();
// 定义10个桶,每个桶的最大值是原数组的最大值,所以当要排序的原数组很大时,10个桶就会要消耗很大的内存
int bucket[][]=new int[10][nums.length];
// 定义一个记录每一个桶中装了多少元素的数组
int bucketmany[]=new int[10];
// 将原数组的值放入到桶中
// 排序第z趟
for (int z=0;z<maxnum;z++){
// 每一轮
for (int i = 0; i < nums.length ; i++) {
// 根据z求出当前是多少位数的值
int k= (int)(nums[i]/Math.pow(10,z)%10);
// 几位数是多少就放到那个桶里,并放在那个桶里的第几个位置上
bucket[k][bucketmany[k]]=nums[i];
bucketmany[k]++;
}
int index=0;
// 将每个桶中的值顺序放回原数组
// 遍历所有的桶
for (int i = 0; i < bucket.length ; i++) {
// 如果当前桶个数不为0
if (bucketmany[i]!=0) {
for (int j = 0; j < bucketmany[i]; j++) {
// 把桶里的数据放回原数组
nums[index]=bucket[i][j];
index++;
}
}
// 第i轮,将当前桶置空,因为上面已经将值赋给原数组了
bucketmany[i]=0;
}
// System.out.println(Arrays.toString(nums));
}
b.堆排序
浙公网安备 33010602011771号