1.简介
八大排序算法,可以将其归为五种类型:1.交换排序,包含冒泡排序和选择排序;2.插入排序,包含直接插入排序和希尔排序;3.选择排序,包含简单选择排序和堆排序(需要用到二叉树相关的知识点,放到后面进行叙述);4.归并排序;5.基数排序。
2.代码及解释说明
2.1 选择排序
2.1.1 冒泡排序
原理:比较两个相邻的元素,将值大的元素交换到右边。
思路:
(1)第一次比较:首先比较第一和第二个数,将小数放在前面,将大数放在后面。
(2)比较第2和第3个数,将小数 放在前面,大数放在后面。
…
(3)如此继续,知道比较到最后的两个数,将小数放在前面,大数放在后面,重复 步骤,直至全部排序完成。
(4)在上面一趟比较完成后,最后一个数一定是数组中最大的一个数,所以在比较第二趟的时候,最后一个数是不参加比较的。
(5)在第二趟比较完成后,倒数第二个数也一定是数组中倒数第二大数,所以在第三趟的比较中,最后两个数是不参与比较的。
(6)依次类推,每一趟比较次数减少依次
代码:
public class bubbleSort {
public static void main(String[] args) {
int[] arr = new int[]{1,48,8,90,3,5,18,12,28};
bubble(arr);
}
public static void bubble(int[] arr){
int temp=0;
int len=arr.length;
//控制比较多少轮
for(int j=0;j<len-1;j++){
//控制每一个数字和后面一个数字的比较
// ,j表示已经进行过比较得出的大数,无需再进行比较
for(int i=0;i<len-1-j;i++){
if(arr[i]>arr[i+1]){
temp=arr[i+1];
arr[i+1]=arr[i];
arr[i]=temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
}
2.1.2 快速排序
算法思想:基于分治的思想,是冒泡排序的改进型。首先在数组中选择一个基准点(在以下代码中选择第一个数为基准),然后分别从数组的两端扫描数组,设两个指示标志(low指向起始位置,high指向末尾)。首先从后半部分开始,如果发现有元素比该基准点的值小,将high位置的值赋值给low位置;如果元素大于基准数,则high–。然后从前半部分开始扫秒,发现有元素大于基准点的值,则将low位置的数赋值给high位置,如果元素小于基准数,则low++。如此往复循环,直到low>=high,然后把基准点的值放到low这个位置。一次排序就完成了。以后采用递归的方式分别对前半部分和后半部分排序,当前半部分和后半部分均有序时该数组就自然有序了。
代码如下:
public class QuickSort {
public static void main(String[] args) {
int[] arr = new int[]{40,48,8,90,3,5,18,12,28};
quickSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr,int start,int end){
if(start<end){
//将数组中的第0个数字设置为标准数
int stard = arr[start];
//记录排序的下标
int low = start;
int high = end;
//循环查找比标准数大的数和比标准数小的数
while(low<high){
//右边的数大于标准数,则右边的指针向左移动一位
while(low<high&&stard<arr[high]){
high--;
}
//否则,右边的数小于标准数,则将右边的数赋值给左边
arr[low]=arr[high];
//左边的数小于标准数,则左边的指针向右移动一位
while(low<high&&stard>arr[low]){
low++;
}
//否则,左边的数大于标准数,则将左边的数赋值给右边
arr[high]=arr[low];
}
//循环结束,此时,low==high
arr[low]=stard;
//递归,处理所有比标准数小的数字
quickSort(arr,start,low);
//递归,处理所有比标准数大的数字
quickSort(arr,low+1,end);
}
}
}
2.2 插入排序
2.2.1 直接插入排序
算法思想:从第二个元素开始,若当前的数小于前面的数,则:将当前数取出,前面的数依次向后移动,直到这个当前数大于前面的数为止。
public class inserSort {
public static void main(String[] args) {
int[] arr = new int[]{1,14,18,11,9,2,8,5,8,10};
insertSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void insertSort(int[] arr){
//对整个数组进行循环遍历
for(int i=1;i<arr.length;i++){
//保存当前的数据
int temp=arr[i];
int j;
//对当前数据前面的数据进行循环判断,满足当前数字小于前一个数字
for(j=i-1;j>=0&&temp<arr[j];j--){
//则把前一个数字赋值给后一个数字
arr[j+1]=arr[j];
}
//把临时变量(外层for循环的当前数字)赋给不满足条件的最后一个元素
arr[j+1]=temp;
}
}
}
2.2.2 希尔排序
希尔排序是直接插入排序的一种改进,试想这样一种情况:有一个数组为3 4 5 6 2,若进行直接插入排序,则最后一位的2需要与前面的所有的数进行比较,所以这样效率会变得很低。
希尔排序的思想是:在数组中采用跳跃式分组的策略,通过某个增量将数组元素划分为若干组,然后分组进行插入排序,随后逐步缩小增量,继续按组进行插入排序操作,直至增量为1。希尔排序通过这种策略使得整个数组在初始阶段达到从宏观上看基本有序,小的基本在前,大的基本在后。然后缩小增量,到增量为1时,其实多数情况下只需微调即可,不会涉及过多的数据移动。在此我们选择增量d=length/2,缩小增量继续以d = d/2的方式减小,直到d=1。
代码如下:
public class shellSort {
public static void main(String[] args) {
int[] arr=new int[]{4,5,8,6,3,2,1};
shellSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void shellSort(int[] arr){
//遍历所有的步长
for(int d=arr.length/2;d>0;d/=2){
//遍历所有的元素
for(int i=d;i<arr.length;i++){
//遍历本组中的所有元素(d作为步长)
for(int j=i-d;j>=0;j-=d){
//对本组中的所有元素进行希尔排序
if(arr[j]>arr[j+d]){
int temp = arr[j+d];
arr[j+d]=arr[j];
arr[j]=temp;
}
}
}
}
}
}
2.3 选择排序
2.3.1 简单选择排序
算法思想:在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
代码如下:
public class selectSort {
public static void main(String[] args) {
int[] arr =new int[]{1,2,8,5,6,3,4,7,8,9};
seleSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void seleSort(int[] arr){
//遍历数组中的所有元素
for(int i=0;i<arr.length;i++){
//保存最小值的下标
int minIndex=i;
//把当前的数和后面的所有数进行比较,并记录最小的下标
for(int j=i+1;j<arr.length;j++){
//如果后面的数比记录的数小
if(arr[minIndex]>arr[j]){
//记录最小的数的下标
minIndex=j;
}
}
//如果最小的数和当前遍历数的下标不一致,说明下标为minIndex的数比当前遍历的数更小
if(i!=minIndex){
int temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
}
}
}
2.3.2 堆排序
后面会专门写一篇文章对这种排序方式进行说明。
2.4 归并排序
算法思想:初始时数组开始和结尾为start和end,取数组的中间的下标为middle(middle=(start+end)/2),将数组分成start–middle和middle+1–end两个部分。首先设置一个指针low和high,初始时low指向start,high指向middle+1。设置一个临时数组temp,如果low处的值小于middle+1处的值,则将low的值存入temp中,low向右移动一位;否则将high的值存入temp中,high向右移动一位。直到low等于middle或者high等于end,结束操作。将原来数组中剩下的元素(low等于middle的时候,high有可能没有移动到end的位置,就会存在没有被遍历到的元素)赋值给temp,然后将temp赋值给原数组。然后令end等于middle(此时的数组变成了start–middle),start等于middle+1(此时的数组变成了middle+1–end),将一个数组分成了两个数组,进行分别的递归和归并操作,直到low>high为止。
代码如下:
public class mergeSort {
public static void main(String[] args) {
int[] arr = new int[]{1,14,18,11,9,2,8,5,8,10};
mergeSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void mergeSort(int[] arr,int low,int high){
if(low<high){
int middle = (low+high)/2;
//处理左边
mergeSort(arr,low,middle);
//处理右边
mergeSort(arr,middle+1,high);
//上面的作用是将数组进行切割,直到左边为一个数
// ,右边也为一个数
//进行归并操作,从里向外嵌套运行出去(递归的思想)
merge(arr,low,middle,high);
}
}
public static void merge(int[] arr,int low,int middle,int high){
//思想:将数组分成两部分(low--middle和middle+1--high),
// 如果左边的第一个数小于右边第一个数,
// 则左边存入数组存入数组,左边指针加一,
// 再进行比较,直到有某一边达到最大值
//临时数组
int[] temp = new int[high-low+1];
//用于记录左边的下标
int i=low;
//用于记录右边的下标
int j=middle+1;
//用于记录临时数组的下标
int index=0;
//遍历两个数组取出小的数字,放入临时数组中
while(i<=middle&&j<=high){
if(arr[i]<=arr[j]){
temp[index] = arr[i];
i++;
index++;
}else{
temp[index] = arr[j];
j++;
index++;
}
}
//如果左边的取完了,右边的还没有取完,
// 则将剩下的放进临时数组中
while(j<=high){
temp[index] = arr[j];
j++;
index++;
}
//如果右边的取完了,左边的还没取完,
// 则将剩下的放入临时数组中
while(i<=middle){
temp[index] = arr[i];
i++;
index++;
}
//将临时数组(排好顺序的)中的数,放入原数组中
for(int k=0;k<temp.length;k++){
arr[low+k]=temp[k];
}
}
}
2.5 基数排序
基数排序比较适用于数组中的某些数差距较大,差了几个数量级的情况,比如:2,386,34587,50,89,111,1234。
算法思想(以最大数为三位数举例,其余情况皆可以类推):
1.先找出数组中最大的数,决定了一共需要进行排序的轮数。
2.第一次按照个位进行排序,分别存入序号为0,1,2,3,…,9的10个数组中。将个位取出的方法为:进行模10操作。然后按照先进先出的方式,依次将数取出放回到原数组中。
3.第二次按照十位进行排序,分别存入10个数组中。然后先进先出的方式将数取出,放回原数组中。将十位取出的方法为:先除以10,再进行模10操作。
4.第三次按照百位进行排序,分别存入10个数组中。然后先进先出的方式将数取出,放回原数组中。将十位取出的方法为:除以100。
代码如下:
public class RedixSort {
public static void main(String[] args) {
int[] arr = new int[]{158,2,54,69,9948,335,10000,5,7};
redixSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void redixSort(int[] arr){
//先将最大的数设置为最小
int max = Integer.MIN_VALUE;
//循环查找并记录最大的数
for(int i=0;i<arr.length;i++){
if(arr[i]>max){
max = arr[i];
}
}
//找出最大的数为几位数字
int length = (max + "").length();
//用于临时保存数据
int[][] temp = new int[10][arr.length];
//用于记录在temp中相应数组中存放的数字的数量
int[] count = new int[10];
//记录余数
int ys=0;
//循环将arr中的数字按照各个数位的数字放入相应的数组中
for(int j=0,n=1;j<length;j++,n*=10){
//存入数据
for(int l=0;l<arr.length;l++){
ys = arr[l]/n%10;
temp[ys][count[ys]]=arr[l];
count[ys]++;
}
//将临时数组中的数字取出来,放进原数组中
int h=0;
for(int k=0;k<count.length;k++){
if(count[k]!=0){
for(int m=0;m<count[k];m++){
arr[h]=temp[k][m];
h++;
}
}
//将记录数量的数组置零
count[k]=0;
}
}
}
}
基数排序取出数据的方式是先进先出,于是可以利用队列这种数据结构进行基数排序。
队列结构的代码:
public class myQueue {
private int[] arr;
public myQueue(){
arr = new int[0];
}
@Override
public String toString() {
return "myQueue{" +
"arr=" + Arrays.toString(arr) +
'}';
}
//往队列中添加元素:与往栈中压入元素的方法类似
public void add(int element){
int[] arrNew = new int[arr.length+1];
for(int i=0;i<arr.length;i++){
arrNew[i]=arr[i];
}
arrNew[arr.length]=element;
arr=arrNew;
}
//出队:创建一个长度为arr,length-1的新数组,
//将原来数组的第1-(arr.length-1)个元素取出,放入新数组
public int poll(){
if(arr.length==0){
return -1;
}else{
int ele = arr[0];
int[] arrNew = new int[arr.length-1];
for(int i=0;i<arrNew.length;i++){
arrNew[i] = arr[i+1];
}
arr=arrNew;
return ele;
}
}
//判断队列是否为空
public boolean isEmpty(){
return arr.length==0;
}
}
使用队列结构的基数排序的代码:
import shujujiegou.myQueue;
import java.util.Arrays;
public class RedixQueueSort {
public static void main(String[] args) {
int[] arr = new int[]{158,20,54,69,9948,335,10000,5,7};
redixSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void redixSort(int[] arr){
//先将最大的数设置为最小
int max = Integer.MIN_VALUE;
//循环查找并记录最大的数
for(int i=0;i<arr.length;i++){
if(arr[i]>max){
max = arr[i];
}
}
//找出最大的数为几位数字
int length = (max + "").length();
//用于临时保存数据
myQueue[] temp = new myQueue[10];
for(int kk=0;kk<temp.length;kk++){
temp[kk] = new myQueue();
}
//记录余数
int ys=0;
//循环将arr中的数字按照各个数位的数字放入相应的数组中
for(int j=0,n=1;j<length;j++,n*=10){
//存入数据
for(int l=0;l<arr.length;l++){
ys = arr[l]/n%10;
temp[ys].add(arr[l]);
}
//将临时数组中的数字取出来,放进原数组中
int h=0;
for(int k=0;k<temp.length;k++){
while(!temp[k].isEmpty()){
arr[h] = temp[k].poll();
h++;
}
}
}
}
}