数据结构算法第一篇排序算法常见

排序算法讲解:

选择排序:
   选择排序的原理十分简单直观,通常使用两层 for 循环来实现:第一层 for 循环依次选定数组从 0 到 N 的每一个索引位置的值,
   第二层 for 循环将该索引后的每个值依次与该索引的值进行比较,将较小值交换到第一层循环索引所在的位置。
   这就使得第一层 for 每一次循环都是在将剩余数组的最小值排列在剩余数组的最前列,最终实现升序排列。
时间复杂度为 O(N^2)

冒泡排序:
冒泡排序同样使用双层 for 循环,第二层循环从 0 到 N 将相邻索引位置的值两两对比,将较大值交换到后一位,
第二层循环遍历完成,就将无序区的最大值推到了数组最后。
冒泡排序顾名思义逐渐将最大的值冒出来
时间复杂度为 O(N^2)

插入排序:
插入排序的原理是将数组中索引 0 既定为有序区,然后把索引 1 的值取出设定为插入值,
将插入值和索引 0 的值对比,如果索引 0 的值较大,则把索引 0 的值往后移动一位,插入值到空出的位置,则索引 0 和索引 1 就是有序的。
接着把索引 2 的值作为插入值依次往前对比,如果前面的值较大,则该值往后移动一位,直到找到一个更小值,将插入值放置到更小值之后,又形成了一个有序区。
以此类推,最终整个数组都变成有序。
时间复杂度为 O(N^2)

快速排序:
快速排序的原理是在数组中随机取一个基准数,一个最左索引 i 和一个最右索引 j 。
从索引 j 开始往左找到第一个小于基准值的值,将其赋值给 arr[i] ,然后将 i 往右移动一格;
接着从索引 i 开始往右找到第一个大于基准值的值,将其赋值给 arr[j] ,并把 j 往左移动一格。
依此类推,当 i 和 j 索引重合时停止,最后将基准值赋值给 arr[i] ,
至此 i 左边的值都比 i 小,i 右边的值都比 i 大 ,接着使用递归将左右两边的子数组分别进行快速排序,最终得到一个有序数组。
时间复杂度为 O(N*logN)

快速排序原始:
6  1  2  7  9  3  4  5  10  8  

第一次:i=0  j=9  inde =a[i]=6
从右边 找小于index的数  j--  i=0 j=7  a[j]=5<index=6 a[i]=a[j]=5 i++ i=1 j=7
这时数组是
5  1  2  7  9  3  4  5  10  8  
从左边 找大于index的数  i++  i=3 j=7  a[3]=7>index=6 a[j]=a[i]=7  j-- i=3 j=6
这时数组是
5  1  2  7  9  3  4  7  10  8  
i=3   j=6  i<j 继续
从右边 找小于index的数  j--  i=3 j=6  a[6]=<index=6 a[i]=a[j]=4  i++ i=4 j=6
这时数组是
5  1  2  4  9  3  4  7  10  8  
从左边 找大于index的数  i=4  j=6  a[5]=9>index=6 a[j]=a[i]=9  j-- i=4 j=5
这时数组是
5  1  2  4  9  3  9  7  10  8  
从右边 找小于index的数   i=4 j=5  a[5]=3<index=6 a[i]=a[j]=3  i++  i=5 j=5
这时数组是
5  1  2  4  3  3  9  7  10  8  
i=5 j=5 i<j不成立 a[i]=index=6
第一次遍历排序后为:
5  1  2  4  3  6  9  7  10  8  

堆排序
堆排序的原理是建立一个完全二叉树,将数组的值填入其中,然后从右往左从下往上依次调整堆结构,
使父节点的值不小于其两个子节点的值,一轮调整过后,根结点的键值就是所有堆结点键值中最大者。
接着将根节点的值与最后一个叶子节点的值互换,并将得到最大值的叶子节点从二叉树中取出,置入数组的最后,然后再次调整堆结构,
依次类推,最终得到一个有序数组。

堆有最大堆也有最小堆
大根堆:根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。
小根堆:根结点的键值是所有堆结点键值中最小者,且每个结点的值都比其孩子的值小。


排序算法稳定性:
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,
这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在
排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳
定的。

 

代码案例:

public class 排序算法 {
    public static Logger logger = Logger.getLogger(排序算法.class);

    /**
     * 选择排序
     *
     * @param arr 数组
     */
    public static void selectSort(int[] arr) {
        logger.info("测试选择排序");
        logger.info("排序算法原始数组:");
        Arrays.stream(arr).mapToObj(item -> item + "  ").forEach(System.out::print);
        System.out.println("");
        long starttime = Calendar.getInstance().getTimeInMillis();
        //第一层for循环的arr[i]每次循环之后都会填入i之后的位置的最小值
        for (int i = 0; i < arr.length - 1; i++) {//有序区
            //将arr[i]之后每一个值依次与arr[i]比较,若较小则交换,保证arr[i]的值最小
            for (int j = i + 1; j < arr.length; j++) {//无序区
                if (arr[i] > arr[j]) {
                    swap(arr, i, j);
                }
            }
            logger.info("第" + i + "次排序后数组:");
            Arrays.stream(arr).mapToObj(item -> item + "  ").forEach(System.out::print);
            System.out.println("");
        }
        long endtime = Calendar.getInstance().getTimeInMillis();
        System.out.println("冒泡排序总用时:" + (endtime - starttime) + "毫秒");
    }

    /**
     * 冒泡排序
     *
     * @param arr
     */
    public static void bubbleSort(int[] arr) {
        logger.info("测试冒牌排序");
        logger.info("排序算法原始数组:");
        Arrays.stream(arr).mapToObj(item -> item + "  ").forEach(System.out::print);
        System.out.println("");
        long starttime = Calendar.getInstance().getTimeInMillis();
        for (int i = 0; i < arr.length - 1; i++) {
            //将无序区的最大值移动到最右的有序区
            for (int j = 0; j < arr.length - 1 - i; j++) {
                //如果左边的值大于右边的值,则互换位置
                if (arr[j] > arr[j + 1]) {
                    swap(arr, i, j);
                }
            }
            logger.info("第" + i + "次排序后数组:");
            Arrays.stream(arr).mapToObj(item -> item + "  ").forEach(System.out::print);
            System.out.println("");
        }
        long endtime = Calendar.getInstance().getTimeInMillis();
        System.out.println("冒泡排序总用时:" + (endtime - starttime) + "毫秒");
    }

    /**
     * 插入排序
     *
     * @param arr
     */
    public static void insertSort(int[] arr) {
        logger.info("测试插入排序");
        logger.info("排序算法原始数组:");
        Arrays.stream(arr).mapToObj(item -> item + "  ").forEach(System.out::print);
        System.out.println("");
        long starttime = Calendar.getInstance().getTimeInMillis();
        // j:插入位置的指针,insertNote:要插入的数据
        int j, insertNote;
        // 从数组的第二个元素开始循环将数组中的元素插入
        for (int i = 1; i < arr.length; i++) {
            // 设置数组中的第2个元素为第一次循环要插入的数据
            insertNote = arr[i];
            j = i - 1;
            while (j >= 0 && insertNote < arr[j]) {
                // 如果要插入的元素小于第j个元素,就将第j个元素向后移动
                arr[j + 1] = arr[j];
                // 将j-1,开始判断前一位与插入值的大小
                j--;
            }
            // 直到要插入的元素不小于第j个元素,将insertNote插入到数组中
            arr[j + 1] = insertNote;
            logger.info("第" + i + "次排序后数组:");
            Arrays.stream(arr).mapToObj(item -> item + "  ").forEach(System.out::print);
            System.out.println("");
        }
        long endtime = Calendar.getInstance().getTimeInMillis();
        System.out.println("插入排序总用时:" + (endtime - starttime) + "毫秒");
    }

    /**
     * 快速排序
     *
     * @param arr
     */
    public static void quickSort(int[] arr) {
        logger.info("测试快速排序");
        logger.info("排序算法原始数组:");
        Arrays.stream(arr).mapToObj(item -> item + "  ").forEach(System.out::print);
        System.out.println("");
        if (arr.length == 0) return;
        long starttime = Calendar.getInstance().getTimeInMillis();
        quickSortArr(arr, 0, arr.length - 1);
        long endtime = Calendar.getInstance().getTimeInMillis();
        System.out.println("快速排序总用时:" + (endtime - starttime) + "毫秒");
    }

    private static void quickSortArr(int[] arr, int low, int high) {
        if (low >= high) return;
        //取左边的数作为基准数
        int i = low, j = high, index = arr[i];
        //i==j时跳出循环
        while (i < j) {
            //从最右开始向左寻找第一个小于index的数
            while (i < j && arr[j] >= index) j--;
            //将arr[j]放入arr[i],并将i右移
            if (i < j) arr[i++] = arr[j];
            //从最左开始向右寻找第一个大于index的数
            while (i < j && arr[i] <= index) i++;
            //将arr[i]放入arr[j],并将j左移
            if (i < j) arr[j--] = arr[i];
            logger.info("i=="+i+ "   j=="+j);
            logger.info("这时数组为:");
            Arrays.stream(arr).mapToObj(item -> item + "  ").forEach(System.out::print);
            System.out.println("");
        }
        //最后将基数填入i
        arr[i] = index;
        logger.info("排序后数组:");
        Arrays.stream(arr).mapToObj(item -> item + "  ").forEach(System.out::print);
        System.out.println("");
        //至此 , i左边都比i小 ,i右边都比i大
        quickSortArr(arr, low, i - 1);//递归,分治i左边
        quickSortArr(arr, i + 1, high);//递归,分治i右边
    }

    /**
     * 堆排序  大顶堆
     *
     * @param arr
     */
    public static void heapSort(int[] arr) {
        logger.info("测试堆排序");
        logger.info("排序算法原始数组:");
        Arrays.stream(arr).mapToObj(item -> item + "  ").forEach(System.out::print);
        System.out.println("");
        //1.构建大顶堆
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            //从第一个非叶子结点从下至上,从右至左调整结构
            adjustHeap(arr, i, arr.length);
        }
        //2.调整堆结构+交换堆顶元素与末尾元素
        for (int j = arr.length - 1; j > 0; j--) {
            swap(arr, 0, j);//将堆顶元素与末尾元素进行交换
            adjustHeap(arr, 0, j);//重新对堆进行调整
        }
    }

    //调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
    public static void adjustHeap(int[] arr, int i, int length) {
        logger.info("调整小顶堆");
        int temp = arr[i];//先取出当前元素i
        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {//从i结点的左子结点开始,也就是2i+1处开始
            if (k + 1 < length && arr[k] < arr[k + 1]) {//如果左子结点小于右子结点,k指向右子结点
                k++;
            }
            if (arr[k] > temp) {//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
                arr[i] = arr[k];
                i = k;
            } else {
                break;
            }
        }
        arr[i] = temp;//将temp值放到最终的位置
        logger.info("排序后数组:");
        Arrays.stream(arr).mapToObj(item -> item + "  ").forEach(System.out::print);
        System.out.println("");
    }

    //交换元素
    public static void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }


    /**
     * 快排
     *
     * @param arr
     * @param left
     * @param right
     */
    public static void quickSortArr_bak(int[] arr, int left, int right) {
        logger.info("排序算法原始数组:");
        Arrays.stream(arr).mapToObj(item -> item + "  ").forEach(System.out::print);
        System.out.println("");
        int pivot;
        if (left < right) {
            pivot = partition(arr, left, right);
            quickSortArr_bak(arr, left, pivot - 1);
            quickSortArr_bak(arr, pivot + 1, right);
            logger.info("排序后数组:");
            Arrays.stream(arr).mapToObj(item -> item + "  ").forEach(System.out::print);
            System.out.println("");
        }
    }

    /**
     * 将数组分成两部分,pivot之前的数都比key小,之后的都比key大
     *
     * @param arr
     * @param left
     * @param right
     * @return pivot 中轴角标
     */
    public static int partition(@NotNull int[] arr, int left, int right) {
        //以数组的第一位作为比较基准值
        int key = arr[left];
        //循环遍历,直到left和right重合
        while (left < right) {
            //从后向前遍历,直到找到比key值小的数停止
            while (left < right && arr[right] >= key) {
                right--;
            }
            //将找到的数赋值给left角标
            arr[left] = arr[right];
            //从前向后遍历,直到找到比key值大的数停止
            while (left < right && arr[left] <= key) {
                left++;
            }
            //将找到的数赋值给此时的right角标
            arr[right] = arr[left];
        }
        //此时left位是空缺的,将key值赋值给left
        arr[left] = key;
        return left;
      }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员路同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值