各路排序神仙理解以及Java实现

啊啊啊啊啊啊!大话数据结构终于看到了排序这一章章啦,然而各路排序神仙,真的是看懂了!仅仅是看懂了,所以打算用两天的时间比几个常见的算法写一遍啦,加深一下印象。嘤嘤嘤

参考博客
十大经典排序算法(动图演示

冒泡排序

这个算法我记得是上C语言就接触了,他的思想是,遇到逆序对,就进行交换啊,就这样慢慢的第n个有序,第n-1个有序.......全部有序。 关键点在于如果一次遍历,都没有交换操作的话,那说明已经有序啦,跳出循环。排序完成。

package com.it592.sort;

public class BubbleSort extends Sort {
    @Override
    public void sort(int nums[]) {
        int len = nums.length;
        for (int i = 0; i < len; i++){
            boolean flag = true;
            for (int j = 0; j < len - i -1; j++){
                if(nums[j] > nums[j+1]){ // 判断是否逆序,如果逆序,说明还没找到正确的位置,继续后移
                    nums[j] += nums[j+1];
                    nums[j+1] = nums[j] - nums[j+1];
                    nums[j] -= nums[j+1];
                    flag = false;
                }
            }
            if (flag)
                break;
        }
    }
}

选择排序

选择排序,按照大话数据结构的话说,其实和冒泡排序差不多,但是这两个算法的侧重点不同,冒泡排序每次都进行交换操作,其实有很多交换是没有必要的,毕竟交换也要费点时,选择排序他只是观察,找到适合某个位置的点,然后再进行交换,这就好比炒股票,冒泡排序是看到每次涨了之后,都卖出去,然后降了再买点回来,而选择排序是看到最高点,再抛售,这就省去了中间交易的开销,也就是冒泡排序时候交换的时候开销。

package com.it592.sort;

public class SelectSort extends Sort {
    @Override
    public void sort(int[] nums) {
        for (int i = 0; i < nums.length - 1; i++){
            int key = i;
            for (int j = i + 1; j < nums.length; j++){ //找到适合i的值。
                    if (nums[key] > nums[j])
                        key = j;
            }
            if(key != i){
                nums[i] += nums[key]; //让i变为有序的
                nums[key] = nums[i] - nums[key];
                nums[i] -= nums[key];
            }
        }
    }
}

插入排序

插入排序的思想是,假设序列已经有序了,那么我们只需要把当前元素插入到正确的位置即可。首先,对于第一个元素,因为只有一个元素,所以他是有序的,对于第二个元素,则与第一个元素进行判断,如果比第一个元素小,则插到第一个元素之前,第一个元素后移;对于第三个元素,则和前两个元素进行比较,找到合适的位置,插入即可。依次类推,最终有序。

package com.it592.sort;

public class InsertSort extends Sort {
    @Override
    public void sort(int[] nums) {
        int len = nums.length;
        for(int i = 0; i < len -1 ; i++){
            int tmp = nums[i+1];
            int j = i+1;
            for(; j > 0; j--){
                if(nums[j-1] > tmp){
                    nums[j] = nums[j-1];
                }else
                    break;
            }
            nums[j] = tmp;
        }
    }
}

希尔排序

在插入排序中,由于要把某个元素放到合适的位置上,这样可能就会导致其他元素偏离他本来的位置很远,希尔排序是对插入排序的一种改进,他可以将小的元素放到前面去,而大的元素集中在尾部。方法是,将数组进行分组,例如[2,5,1,4,5,55,11,1,15,19,2,33,55,8],我们设置space=3,那么则[2,11,19,55],[5,5,1,2,8],[1,55,15,33]为三个分组,使用插入排序对他们进行排序后,就会得到以下结果[2,1,1,11,2,15,19,5,33,55,5,55,8],通过这样一轮排序之后,就会发现小的部分相对集中在前面,而大数则集中在后面。最终循环完毕,有序

package com.it592.sort;

public class ShellSort extends Sort {
    @Override
    public void sort(int[] nums) {
        int gap = nums.length / 3 + 1;

        while (true){
                for (int i = 0; i < nums.length - gap; i += gap){
                    int tmp = nums[i+gap];
                    int j = i + gap;
                    for (; j > 0; j -= gap){
                        if(nums[j-gap] > tmp)
                            nums[j] = nums[j-gap];
                        else
                            break;
                    }
                    nums[j] = tmp;
                }
                if (gap == 1) // gap 等于 1的时候,说明进行了一次正常的插入排序,over,推出。
                    break;
                gap = gap / 3 + 1;

        }
    }
}

归并排序

归并排序,其实就是相当于划分成子问题,然后再合并起来,这玩意难点不在思路,递归实现贼方便,但是嘤嘤嘤,非递归就不那么容易啦。具体代码解释吧

package com.it592.sort;

public class MergeSort extends Sort {
    @Override
    public void sort(int[] nums) {
        mergeSort(nums);
    }

    private void mergeSort(int[] nums){
        /*
            主要包含两个部分,第一个分组,第二个是组内排序并合并
         */
        int space = 2; //space 用来设置当前划分的子问题的大小*2,
        while (space <= nums.length){
            int i = 0;
            while (i + space <= nums.length){
                    merge(nums,i,i + space / 2, i + space ); // 将子问题的解合并
                    i += space; //更新子问题的坐标,向后移动,继续解决其他子问题
            }
            if ((i + space / 2 - 1) < nums.length) //如果有剩余的没办法进行排序,那此次进行排序。
                merge(nums, i, i+space/2, nums.length);
            space *= 2;
        }
        merge(nums, 0, space / 2, nums.length); //从头到尾,合并一次。
    }

    private void merge(int nums[],int left, int center, int right){
        int temp[] = new int[right - left + 1];
        int cnt = 0;
        int i = left;
        int j = center;
        while (i < center && j < right){
            if (nums[i] > nums[j]){
                temp[cnt++] = nums[j++];
            }else
                temp[cnt++] = nums[i++];
        }
        while (i < center){
            temp[cnt++] = nums[i++];
        }
        while (j < right){
            temp[cnt++] = nums[j++];
        }
        cnt = 0;
        for (i = left; i < right; i++){
            nums[i] = temp[cnt++];
        }
    }
}

快速排序

快速排序的思想主要也是分治,主要是找到一个节点,把他放到合适的位置上,使左边的都比节点小(大),右边都比节点大(小)。他最关键的点就在找这个节点的合适位置,找法就是从右到左找到比节点小的点,交换,然后再从左往右找到合适的点,交换,直接i和j相等.具体看参考。
白话经典算法系列之六 快速排序 快速搞定

package com.it592.sort;

public class QuickSort extends Sort {
    @Override
    public void sort(int[] nums) {
            quick_sort(nums,0,nums.length-1);
    }

    private void quick_sort(int[] nums, int left, int right){
        int i = left;
        int j = right;
        if(left < right){
            int tmp = nums[i];
            while (i < j){
                while (i < j && nums[j] > tmp)
                    j--; // 找到第一个小数
                if(i < j)
                    nums[i++] = nums[j];
                while (i < j && nums[i] < tmp)
                    i++;
                if(i < j)
                    nums[j--] =  nums[i];
            }
            nums[i] = tmp;
            quick_sort(nums,left,i-1);
            quick_sort(nums,i+1,right);
        }
    }
}

posted @ 2018-10-10 08:10  罗梁  阅读(246)  评论(0编辑  收藏  举报