第七章排序算法
前言
常用排序算法整理
提示:以下是本篇文章正文内容,下面案例可供参考
一、排序算法的基本介绍
1:排序算法的介绍
2:时间复杂度的计算
3:常见时间复杂度
4:平均时间复杂度和最坏时间复杂度
5:算法的空间复杂度简介
二、冒泡排序
舞动的排序算法-冒泡排序
https://www.bilibili.com/video/BV1xW411Y7VL?from=search&seid=15025507130363564352
1. bubbleSort分析
相邻的元素逆序就交换,每一趟确定一个位置的元素
1,一共进行(数组大小-1)次大的循环
2,如果发现在某一趟排序中,没有发生一次交换,可以提前结束冒泡排序(优化)
2.bubbleSort代码
public static void bubbleSort(int[] arr){
int temp=0;//临时变量
boolean flag=false;//标识变量,表示是否进行过交换
for(int j=0;j<arr.length-1;j++) {
//第一趟排序就是将最大的数排在最后
for (int i = 0; i < arr.length - 1-j; i++) {
//如果前面的数比后面的数大,就交换
if (arr[i] > arr[i + 1]) {
flag=true;//发生交换,flag置为true
temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
System.out.println("第"+(j+1)+"趟排序后的数组为:");
System.out.println(Arrays.toString(arr));
//如果发现在某一趟排序中,没有发生一次交换,可以提前结束冒泡排序(优化)
if(!flag){//在一趟排序中,一次交换都没有发生过
break;
}else{
flag=false;//重置flag进行下次判断
}
}
}
```c
3. bubbleSort小练
https://leetcode-cn.com/problems/move-zeroes/
三、选择排序
舞动的排序算法-选择排序
https://www.bilibili.com/video/BV1xW411Y738?from=search&seid=15025507130363564352
1. selectSort分析
依次将每轮最小的数据交换到前面的位置
(1)选择排序共有(数组大小-1)轮排序
(2)每一轮排序,找到这一轮中最小的数及其下标
找出这一轮中的最小的数
1,先假定当前这个数是最小数
2,然后和后面的每个数进行比较,如果发现有比当前数更小的数,就重新确定最小数,并得到其下标
3,当遍历到数组的最后时,就得到本轮最小数和下标
4,交换
2.selectSort代码
public static void selectSort(int[] arr) {
for (int i=0;i<arr.length-1;i++) {
int minIndex = i;
int min = arr[minIndex];
for (int j = minIndex + 1; j < arr.length; j++) {
if (min > arr[j]) {//说明假定的值并不是最小
//重置min和minIndex
min = arr[j];
minIndex = j;
}
}
//依次将每轮最小的数据交换到前面的位置
if(minIndex!=i) {
arr[minIndex] = arr[i];
arr[i] = min;
}
System.out.println("第"+(i+1)+"轮排序后的数组为:");
System.out.println(Arrays.toString(arr));
}
}
```c
3. selectSort小练
https://leetcode-cn.com/problems/sort-an-array/
四、插入排序
舞动的排序算法-插入排序
https://www.bilibili.com/video/BV1xW411Y73Z?from=search&seid=8147841895148488567
1. insertSort分析
每次插入时,从无序表中取出第一个元素,将它插入到有序表的适当位置
1,插入排序要进行(数组大小-1)次插入
2.insertSort代码
public static void insertSort(int[] arr){
for(int i=1;i<=arr.length-1;i++) {
//第一轮34.101,119,1
int insertVal = arr[i];//定义待插入的数
int insertIndex = i - 1;//要插入的索引
//给insertVal找到插入的位置,
//insertVal < arr[insertIndex]待插入的数还没有找到插入的位置
//insertIndex >= 0防止数组越界
while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
arr[insertIndex + 1] = arr[insertIndex];//将插入位置的元素后移
insertIndex--;
}
//当退出循环时,说明插入的位置找到,insertIndex+1
if(i!=insertIndex+1) {
arr[insertIndex + 1] = insertVal;
}
System.out.println("第"+i+"次插入后的数组为:" + Arrays.toString(arr));
}
}
```c
3. insertSort小练
https://leetcode-cn.com/problems/insertion-sort-list/submissions/
五、希尔排序(插入排序的优化版)
舞动的排序算法-希尔排序
https://www.bilibili.com/video/BV1xW411Y7gf?from=search&seid=2306591840291556619
1. shellSort分析
插入排序{2,3,4,5,6,1}当插入的数是较小的数时,后移的次数明显增多,对效率有影响
–》
希尔排序(shellSort)
[缩小增量排序,尽量把小的数后前面排,避免一个小的数在后面要移动很多次]:
1,按下标的一定增量分组,对每组使用直接插入排序算法排序
2,随增量的减少,每组元素数量增多,当增量减到1,整个数组就分成一组,此时接近有序数组
3,最后对这个有序数组进行插入排序
2.shellSort代码
//交换法
public static void shellSort(int[] arr){
int temp=0;
for(int gap=arr.length/2;gap>0;gap/=2) {
//希尔排序第一轮排序
//第一轮排序是将数组分成了5组,每组有两个元素,步长为5
for (int i = gap; i < arr.length; i++) {
//遍历各组中所有元素,步长为5
for (int j = i - gap; j >= 0; j -= gap) {
//如果当前元素大于加上增量后的那个元素,就交换
if (arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
System.out.println("希尔排序增值为"+gap+"的数组排序后的结果为:" + Arrays.toString(arr));
}
}
//由于直接插入是将要插入的数插入到有序表的适当位置,而上面的交换法,是将每组中的数据进行比较进行交换,对效率有影响
//为提高效率,对交换式的希尔排序进行优化-》移动法
public static void shellSortMove(int[] arr){
//增量为gap,并逐个的缩小增量
for(int gap=arr.length/2;gap>0;gap/=2){
//从第gap个元素,逐个对其所在的组进行直接插入排序
for(int i=gap;i<arr.length;i++){
//先保存要插入数的位置和值
int j=i;
int temp=arr[j];
//找到要插入位置
if(arr[j]<arr[j-gap]){
while(j-gap>=0&&temp<arr[j-gap]){
//就将这个数向前移动
arr[j]=arr[j-gap];
j-=gap;
}
//当退出while循环时,就找到temp要插入的位置
arr[j]=temp;
}
}
System.out.println("希尔排序增值为"+gap+"的数组排序后的结果为:" + Arrays.toString(arr));
}
}
```c
3. shellSort小练
https://leetcode-cn.com/problems/sort-an-array/submissions/
六、快速排序
舞动的排序算法-快速排序
https://www.bilibili.com/video/BV1xW411Y7g3?from=search&seid=2002458235042065823
1. quickSort分析
QuickSort(对冒泡排序的一种改进):
[分两半,左递归快排,右递归快排]
1,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分数据都比另一部分的所有数据都要小,
2,然后再按此方法对这两部分数据分别进行快速排序
3,整个排序过程可以递归进行
*/
2.quickSort代码
public static void quickSort(int[] arr,int left,int right){
//1,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分数据比另一部分数据要小
int r=right;//左下表和右下表
int l=left;
int pivot=arr[(left+right)/2];//中轴值
int temp=0;
while(l<r){
//在pivot的左边找,直到找到大于pivot的值才退出
while(arr[l]<pivot){
l+=1;
}
//在pivot的右边找,直到找到小于pivot的值才退出
while(arr[r]>pivot){
r-=1;
}
//如果l>=r说明左边的数都比右边的数小,就不用再交换数据
if(l>=r){
break;
}
//否则将数据进行交换
temp=arr[r];
arr[r]=arr[l];
arr[l]=temp;
//可能数组中有与pivot中轴相同的数,遇到相同的数,相同的数这一边位置不用移动,只需将另一边的位置推进,然后将这个相同的数一直交换
//直到交换到pivot旁边
if(arr[l]==pivot){
r-=1;
}
if(arr[r]==pivot){
l+=1;
}
}
//如果l==r,必须l++,r--,否则会出现栈溢出,死递归
if(l==r){
l+=1;
r-=1;
}
//2,左递归
if(left<r){
quickSort(arr,left,r);
}
//3,右递归
if(right>l){
quickSort(arr,l,right);
}
}
```c
3. quickSort小练
https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/submissions/
七、归并排序
舞动的排序算法-归并排序
https://www.bilibili.com/video/BV1xW411Y7gY?from=search&seid=16837856563663081315
1. mergeSort分析
mergeSort:采用分治策略,将问题分成一些小问题,然后递归求解
//合并的方法
/**
* 1,先把左右两边(有序)的数据按照规则填充到temp数组,直到左右两边的有序序列,有一边处理完毕为止
* 2,把有剩余数据的一边的数据依次全部填充到temp中
* 3,将temp数组的元素拷贝到arr
*/
2.mergeSort代码
//分+合方法
public static void mergeSort(int[] arr,int left,int right,int[] temp){
if(left<right){
int mid=(left+right)/2;//中间索引
//向左递归进行分解
mergeSort(arr,left,mid,temp);
//向右递归进行分解
mergeSort(arr,mid+1,right,temp);
//合并
merge(arr,left,mid,right,temp);
}
}
//合并的方法
/**
* @param temp 做中转的数组
* 1,先把左右两边(有序)的数据按照规则填充到temp数组,直到左右两边的有序序列,有一边处理完毕为止
* 2,把有剩余数据的一边的数据依次全部填充到temp中
* 3,将temp数组的元素拷贝到arr
*/
public static void merge(int[] arr,int left,int mid,int right,int[] temp){
int i=left;//左边有序序列的初始索引
int j=mid+1;//右边有序序列的初始索引
int t=0;//指向temp数组的当前索引
//*1
while(i<=mid&&j<=right){
//如果左边有序序列的当前元素<右边有序序列的当前元素,就将左边的元素加到temp数组
if(arr[i]<=arr[j]){
temp[t]=arr[i];
t+=1;
i+=1;
}else{
count+=(mid-i+1);//每次合并时,都会在此逆序,由于又都有序所以,该数后面的数与其结合也算逆序
//相反
temp[t]=arr[j];
t+=1;
j+=1;
}
}
//*2
//如果左边有序序列有剩余,就将其加到temp中
while(i<=mid){
temp[t]=arr[i];
t+=1;
i+=1;
}
//右边有剩余
while(j<=right){
temp[t]=arr[j];
t+=1;
j+=1;
}
//*3
//上面的递归,并不是每次都拷贝所有
t=0;
int tempLeft=left;
//{ 第三次合并[第一次合并(8,4),第二次合并(5,7)], 1,3,6,2}
//合并1:tempLeft=0 right=1
//合并2:tempLeft=2 right=3
//~
//合并7:tempLeft=0 right=7
System.out.println("tempLeft="+tempLeft+" right="+right);
while(tempLeft<=right){
arr[tempLeft]=temp[t];
t+=1;
tempLeft+=1;
}
}
```c
3. mergeSort小练
https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/
八、桶排序
桶排序讲解
https://www.bilibili.com/video/BV1v4411r74m?from=search&seid=9673918630871895125
1,初始化桶
2,读取数据,加入相应的桶中
3,遍历每个桶,依次读取数据即可
1. bucketSort分析
桶排序bucketSort:
(1)桶排序的思想是划分区间(桶),后一个区间中的数总是比前一个区间的大
(2)例如按薪水排序,处于月薪数W元区间的任何一个人的薪水肯定比处于月薪数K元区间的所有人多。
(3)分好桶后,再选择某一种算法对这个桶内的元素排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。最后根据桶的优先级依次取出元素,
1,初始化桶
2,读取数据,加入到相应的桶中
3,遍历每个桶,依次读取数据即可
2.bucketSort代码
//对每个桶进行初始化
int[] arr=new int[11];
int[] studentArr={1,3,5,3,8};//学生成绩
//将5个学生的分数加入到桶中
for(int i=0;i<5;i++){
arr[studentArr[i]]++;//将每个学生分数对应桶的序号加1
}
//遍历桶,依次读取即可
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr[i];j++){
System.out.print(i);
}
}
```c
3. bucketSort小练
https://leetcode-cn.com/problems/top-k-frequent-elements/submissions/
九、基数排序
基数排序讲解
https://www.bilibili.com/video/BV1E4411H73v?p=75
1. radixSort分析
radixSort:(桶排序的扩展)
1,它是通过键值各个位的值,将要排序的元素分配到某些桶中,达到排序
2, 将整数按位数切割成不同的数字,然后按每个位数分别比较
3,基本思想:
(1)将所有待比较数值统一为同样的数位长度,数位较短的数前面补零(007)
(2) 然后从最低位开始,依次进行一次排序,这样从最低位排序–>最高位排序完成,得到有序序列
4,步骤:
(1),将每个数放到桶中(针对每个元素的个位进行排序处理)
(2)按照这个桶的顺序(一维数组的下标)依次取出数据,放入原来数组
5,实际演示:int[] arr={53,3,542,748,14,214};
个位数:542, 53,03,14,214,748
十位数:003,014,214,542,748,053
百位数:003,014,053,214,542,748
数组排序完毕!
6,数组几轮排序演示
(1)数组第一轮排序
(2)数组第二轮排序
(3)数组第三轮排序
2.radixSort代码
//1,使用基数排序对数组进行排序
/**
1,将每个数放到桶中(针对每个元素的个位进行排序处理)
2,按照这个桶的顺序(一维数组的下标)依次取出数据,放入原来数组
*/
//求出最大位数,最大位数决定有几轮
int max=nums[0];
for(int i=0;i<nums.length;i++){
if(nums[i]>max){
max=nums[i];
}
}
int maxLength=(max+"").length();
//定义10个桶,每个桶就是一个一维数组
int[][] bucket=new int[10][nums.length];
//定义一个一维数组表示桶 bucketElementCounts[0]就代表bucket[0]桶中元素的数量
int[] bucketElementCounts=new int[10];
for(int k=0,n=1;k<maxLength;k++,n*=10){
//1*
for(int i=0;i<nums.length;i++){
//取个数组元素个位数的值
int digitOfElement=nums[i]/n%10;
//放入对应的数组中
bucket[digitOfElement][bucketElementCounts[digitOfElement]]=nums[i];
bucketElementCounts[digitOfElement]++;
}
//2*
int index=0;
for(int i=0;i<bucket.length;i++){
//如果桶中有数据,就将其加入到原数组中
if(bucketElementCounts[i]!=0){
for(int j=0;j<bucketElementCounts[i];j++){
nums[index++]=bucket[i][j];
}
}
//第一轮处理后要将桶中数据清空,便于接下来几轮的处理
bucketElementCounts[i]=0;
}
}
```c
3. radixSort小练
https://leetcode-cn.com/problems/maximum-gap/submissions/
十、常用排序算法总结和对比
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。