9种排序算法

工具类准备好

public class ArrayUtils
{
    /**
     * 打印 数组
     * @param arr
     */
    public static void print(int arr[])
    {
        System.out.print ("arr:[");
        for(int ele:arr)
        {
            System.out.print (ele+" ");
        }
        System.out.print ("]\n");
    }

    /**
     * 交换两个元素
     * @param arr 数组
     * @param aIndex    下表索引
     * @param bIndex
     */
    public static void swap(int arr[],int aIndex,int bIndex)
    {
        int temp = arr[aIndex];
        arr[aIndex] = arr[bIndex];
        arr[bIndex] = temp;
    }
}

一. 冒泡排序算法

顾名思义: 思想在于两两比较交换

import util.ArrayUtils;

/**
 * 冒泡排序
 */
public class MaoPaoSort
{
    public static void main(String[] args)
    {
        int arr[]={5,3,1,4,2,8,6,9,8,7};
        System.out.println ("排序前的数组!");
        ArrayUtils.print (arr);

        /**
         * 思想:冒泡排序 在于从前往后 两两比较,如果前面的大于后面 则交换
         * 最终两两交换 只需要 length-1 趟即可
         */
        for(int i=0;i<arr.length-1;i++)
        {
            //注意  i 只管的是 几趟
            // 最终目的是把 最小值 排在最前面 以此往后推
            //注意这里的 j>i  否则j和j-1 会造成 数组下标越界
            for(int j=arr.length-1;j>i;j--)
            {
                if(arr[j-1]>arr[j])
                {
                    ArrayUtils.swap (arr,j,j-1);
                }
            }
        }


        System.out.println ("排序后的数组!");
        ArrayUtils.print (arr);
    }
}

二. 选择排序算法

思想: 从后续剩余的数组中选择最小的,插入到前面已经排序好的数组中

/**
 * 简单选择排序 //不稳定的排序算法
 */
public class SelectSort
{
    public static void main(String[] args)
    {
        int arr[]={5,3,1,4,2,8,6,9,8,7};
        System.out.println ("排序前的数组!");
        ArrayUtils.print (arr);

        /**
         * 思想: 就是每趟从剩余元素中选择一个最小值 ,然后与 前面指定的
         * arr【i】交换,一共只需要 (length-1) 躺
         */
        for(int i=0;i<arr.length-1;i++)
        {
            //int minIndex=i; //这个为每次查找到的最小值的下表
            for(int j=i+1;j<arr.length;j++)
            {
                if(arr[i]>arr[j])
                {
                    ArrayUtils.swap (arr,i,j);
                }
            }
        }

        System.out.println ("排序后的数组!");
        ArrayUtils.print (arr);
    }
}

三. 直接插入排序算法

思想:从甚于的数种,依次选择一个,插入到前面已经排序好的数组当中

/**
 * 直接插入排序
 */
public class InsertSort
{
    public static void main(String[] args)
    {
        int arr[]={5,3,1,4,2,8,6,9,8,7};
        System.out.println ("排序前的数组!");
        ArrayUtils.print (arr);

        //从第二个元素开始 依次往前插入排序
        //所以这里的 i=1 开始
        for(int i=1;i<arr.length;i++)
        {
            //这里代表  从i开始依次往前两两比较,如果不符合顺序,则交换
            for(int j=i;j>0;j--)
            {
                if(arr[j-1]>arr[j])
                {
                    ArrayUtils.swap (arr,j-1,j);
                }
            }
        }

        System.out.println ("排序后的数组!");
        ArrayUtils.print (arr);
    }
}

四. 希尔排序

内部其实就是 直接插入排序,只不过 我们把整个数组分为了 几个数组进行直接插入排序,最终 步长为一时,就时直接插入排序

/**
 * 希尔排序
 */
public class ShellSort
{
    public static void main(String[] args)
    {
        int arr[]={5,3,1,4,2,8,6,9,8,7};
        System.out.println ("排序前的数组!");
        ArrayUtils.print (arr);

        //这里的gap(步长) 相当于 希尔数组 d[5,3,1]最后一个元素必定要为1
        for(int gap=arr.length/2;gap>0;gap/=2)
       {
            //(重点记忆)希尔排序内部 就是一个 插入排序
            for(int i=1;i<arr.length;i++)
            {
                for(int j=i;j-gap>0;j-=gap)  //这里跳着循环比较
                {
                    if(arr[j-gap]>arr[j])
                    {
                        ArrayUtils.swap (arr,j-gap,j);
                    }
                }
            }

       }
        System.out.println ("排序后的数组!");
        ArrayUtils.print (arr);
    }
}

五.快速排序

思想在于:以数组第一个元素为分界线,左边都是小于它的,右边都是大于它的元素分割成两个数组,然后再分别以 数组第一个元素进行以上操作,最终达到排序目的。

**
 * 第一种快速 排序,即 单轴排序
 */
public class QuickSortMain
{
    //用于存放 关键字的
    int key;
    @Test
    public void QuickSortTest()
    {
        int arr[]={5,3,1,4,2,10,6,9,8,7};
        System.out.println ("排序前的数组!");
        ArrayUtils.print (arr);

        quickSort(arr,0,arr.length-1);

        System.out.println ("排序后的数组!");
        ArrayUtils.print (arr);
    }

    private void quickSort(int[] arr, int low, int high)
    {
        if(low<high)
        {
            int index = partition (arr,low,high); //对第一个 关键字 进行一趟快速排序,返回他的下标所在位置
            quickSort (arr,low,index-1);  //对左边部分进行 快速排序
            quickSort (arr,index+1,high);  //对右边部分进行 快速排序
        }
    }

    //分割
    private int partition(int arr[],int low,int high)
    {   //空间复杂度为 1
        key = arr[low];
        if(low < high)
        {   //这里必须是while   //注意 这里是把 key 和数组元素比较
            while(low < high && key<=arr[high])
            {
                high--;
            }
            arr[low] = arr[high];
            while(low < high && key>arr[low])
            {
                low++;
            }
            arr[high] = arr[low];
        }
        arr[low] = key;
        return low;
    }
}

六.二路归并排序

在于 两两归并,最终形成一个有序数组

public class MergeSortMain
{
    //辅助数组,归并排序的空间复杂度为 n
    public  int temp[] ;

    @Test
    public  void MyTest()
    {
        int arr[]={5,3,1,4,2,8,6,9,8,7};
        System.out.println ("排序前的数组!");
        ArrayUtils.print (arr);
        initUtilArray(arr.length);

        MergeSort(arr,0,arr.length-1);

        System.out.println ("排序后的数组!");
        ArrayUtils.print (arr);
    }

    //递归遍历 采用二路归并
    public void MergeSort(int arr[],int low,int high)
    {
        if(low<high)
        {
            int mid = (low + high) / 2;
            MergeSort (arr,0,mid);
            MergeSort (arr,mid+1,high);
            Merge (arr,low,mid ,high);
        }
    }

    //合并操作,作用是将两个有序的 序列合并成一个
    public void Merge(int arr[],int low,int mid,int high)
    {
        //条件不符合时候
        if(low>=mid || low>=high || mid>= high)
        {
            return;
        }

        int i = low; //用于指向最一个数组开始
        int j= mid+1; //用于指向 第二个数组开始
        int k = 0;//k用于 辅助数组下表 每趟merge 都要这般操作

        //用于比较 两个有序的 数组
        while(i <= mid && j <= high) //这里的 i<=mid  保证了 数据的稳定性
        {
            if(arr[i] <= arr[j])
            {
                temp[k] = arr[i];
                k++;
                i++;
            }
            else
            {
                temp[k] = arr[j];
                k++;
                j++;
            }
        }

        //代表 第一个有序序列没排序完
        while(i<=mid)
        {
            temp[k] = arr[i];
            k++;
            i++;
        }

        //代表 第二个有序序列没排序完
        while(j<=high)
        {
            temp[k] = arr[j];
            k++;
            j++;
        }

        //最后一步 非常重要 将 在temp中排序好的数组 再次放入 原数组中 位置从 low 到 high
        for (k=0,i=low;i<=high;i++,k++)
        {
            arr[i] = temp[k];
        }
        //清空辅助数组 都为0,因为每次都要有一个新的空的数组
        freeUtilArr();
    }

    //用于初始化 辅助数组
    public  void initUtilArray(int lenght)
    {
        this.temp = new int[lenght];
    }
    //用于将数组中的元素 都清空 为0
    public void freeUtilArr()
    {
        Arrays.fill (this.temp,0);
    }
}

七.堆排序

public class HeadSort
{
    @Test
    public void HeadSortTest()
    {
        int arr[]={0,5,3,1,4,2,8,6,9,10,7};//第一个位置空着
        System.out.println ("排序前的数组!");
        ArrayUtils.print (arr);

        headSortMain (arr,arr.length-1);

        System.out.println ("排序后的数组!");
        ArrayUtils.print (arr);
    }

    /**
     * 堆排序的 算法
     * @param arr
     * @param n
     */
    private void headSortMain(int arr[],int n)
    {
        int i=0;

        //建立初始堆 依次 从最后一个父节点 向上调整
        for(i=n/2;i>=1;i--)
        {
            sift (arr,i,n);
        }

        //这个循环 主要实现的就是  每次 把最后一个元素和 第一个元素交换  然后调用向下调整
        for(i = n;i>=2;i--)
        {
            //把 最后一个元素 和 第一个交换位置
            ArrayUtils.swap (arr,i,1);
            //然后向下调整
            sift (arr,1,i-1);
        }
    }



    /**
     * 本函数主要完成 从arr[low] 到 arr[high] 上的   在对arr[low] 上的堆向下调整
     * @param arr
     * @param low
     * @param high
     */
    private void sift(int arr[],int low,int high)
    {
        int i = low;
        int j = 2 * i ; //这个是 左孩子

        //如果 不是叶子节点   也就是所谓的 左孩子  就向下调整
        while(j<=high)
        {
            //用于 比较左孩子 和 右孩子的 大小
            if(j <high && arr[j] < arr[j+1])
            {
                j++;
            }
            //如果 父亲节点小于 孩子节点 ,就交换位置;
            if(arr[low] < arr[j])
            {
                ArrayUtils.swap (arr,low,j);
                i = j;                //下边这两句话,是要继续向下调整
                j = 2 * i ;
            }
            else  //如果任何问题 都没有,就直接结束循环
            {
                break;
            }
        }
    }
}

八.基数排序

/**
 * 基数排序,多关键字排序
 * 思想: 先 在个位数排序,然后 十位数排序,然后百位数排序 依次下去,直到最高位
 */
public class RadixSort
{
    @Test
    public void  RadixSortTest()
    {
        int arr[]={423,123,685,1489,587,158,698,247,358};
        System.out.println ("排序前的数组!");
        ArrayUtils.print (arr);

        int result[] = sort(arr);

        System.out.println ("排序后的数组!");
        ArrayUtils.print (result);
    }

    /**
     * 基数排序核心算法
     * @param arr  传入要排序的数组
     * @return  一个排序好的新数组
     */
    private int[] sort(int[] arr)
    {
        int result[] = new int[arr.length];
        //count用于保存 0 - 9的位数
        int count[] = new int[10];
        //division 就是最高位数
        int maxPos = findMaxPosition (arr);
        System.out.println ("最高位: " +maxPos);
        //System.out.println ((int)Math.pow (10,2));
       for (int i=0;i<maxPos;i++)
        {
            /**
             *  division 就是倍数(10的几次方)  如果是 10 0 ->1   10 1->100   10 3 -> 1000
             */
            int division = (int)Math.pow(10,i);

            //存入 计数数组 count
            for(int j=0;j<arr.length;j++)
            {
                //这句话就是 取个位数,十位数 或者百位数上的数字
                //比如 421 取十位数  ->  421 / 10 %10 =2
                int num = arr[j]/division%10;
                count[num]++;
            }

            //这个 跟计数数组一样 小技巧  可以 得到每个 数字的位置
            for(int m=1; m <count.length;m++)
            {
                count[m] = count[m] + count[m-1];
            }


            //接下来操作就是  存放到result 当中  从后往前,可以保证 数据的稳定性
            for(int n = arr.length-1;n>=0;n--)
            {
                //根据 个位 或者十位上的数字 查找 在当前的位置
                int num = arr[n]/division%10;
                int position =--count[num];//得到 这个数字 要存放在的下标索引
                result[position] = arr[n]; //存进去
            }

            //把一次循环完的(比如个位数排序好的数字 再次放入 arr 中 进行十位数的比较)
            System.arraycopy (result,0,arr,0,arr.length);
            //把计数 数组 count 清空,用于下次 十位数字比较
            Arrays.fill (count,0);
        }
        return result;
    }

    //查找  一组关键字的 最高位数
    //比如123  是三位  5689  4位等等...
    private int findMaxPosition(int arr[])
    {
        int maxPosition = 0;
        for(int i=0;i<arr.length;i++)
        {
            //这里每步 都要初始化 j;
            int j=10;
            int position = 1;
            while(arr[i]/j != 0)
            {
                position++;
                j *=10;
            }

            if(position > maxPosition)
            {
                maxPosition = position;
            }
        }
        return maxPosition;
    }

}

九.计数排序

用于统计 比如考研成绩人数统计

9.1 不稳定的计数排序

/**
 * 计数排序
 * 用处:比如高考成绩排序   0 到 750 人多 基数大   但是范围小
 * 思想: 把每个成绩 的个数统计在一个数组中,然后 最终按个数 插入新数组中 返回
 *
 * 空间复杂度 n+k
 */
public class CountSort
{
   @Test
    public void  CountSortTest()
   {
       int arr[]={2,4,2,3,7,1,1,0,0,5,6,9,8,5,7,4,0,9};
       System.out.println ("排序前的数组!");
       ArrayUtils.print (arr);

       int result[] = sort(arr);

       System.out.println ("排序后的数组!");
       ArrayUtils.print (result);
   }

   //计数排序 核心函数
    private int[] sort(int[] arr)
    {
        int result[] = new int[arr.length];

        int count[] = new int[10]; //统计的个数在 0 -9  之间

        //统计每种元素的个数  放入count中
        for(int i=0;i<arr.length;i++)
        {
            count[arr[i]]++;
        }

        //开始返回
        for(int i=0,j=0;i<count.length;i++)
        {
            while(count[i]-- >0)
            {
                result[j] = i;
                j++;
            }
        }

        return result;
    }
}

9.2稳定的计数排序

public class CountSort稳定的
{
    @Test
    public void  CountSortTest01()
    {
        int arr[]={2,4,2,3,7,1,1,0,0,5,6,9,8,5,7,4,0,9};
        System.out.println ("排序前的数组!");
        ArrayUtils.print (arr);

        int result[] = sort(arr);

        System.out.println ("排序前的数组!");
        ArrayUtils.print (result);
    }

    //计数排序 核心函数
    private int[] sort(int[] arr)
    {
        int result[] = new int[arr.length];

        int count[] = new int[10]; //统计的个数在 0 -9  之间

        //统计每种元素的个数  放入count中
        for(int i=0;i<arr.length;i++)
        {
            count[arr[i]]++;
        }
        System.out.println ("修改前的count数组");
        ArrayUtils.print (count);


        // 改进的计数排序
        //比如之前的count数组为  arr:[3 2 2 1 2 2 1 2 1 2 ]
        //现在变为[3 5 7 8 10 12 13 15 16 18 ]代表着每种元素 下标的最后一个位置
        // 这样可以 从后循环遍历 arr数组,然后 一个一个添加,然后实现稳定性
        for(int i=1;i<count.length;i++)
        {
            count[i] = count[i]+count[i-1];
        }
        System.out.println ("修改后的count数组");
        ArrayUtils.print (count);


        //实现稳定性插入 到result数组中
        for (int i=arr.length-1;i>=0;i--)
        {
            //arr[i] 如果是 2  那么count[arr[i]] 就是在count中查找 2元素的最后一个位置
             //然后  count[arr[i]]-1  因为2元素 要插入 的 下标最后一个位置
            result[count[arr[i]]-1] = arr[i];
            count[arr[i]]--;
        }

        return result;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值