Basic Algorithm(1)--Sort Algorithm(1)

笔者写到现在大约20多篇blog,开始写写算法系列的blog了。算法这东西如果不是专职做以下工作或比赛的,包括:ACM比赛、AI算法工程师、算法岗职业和做计算机技术研究的等等,那么主要就是了解主要的思想、懂大概流程、能够自主实现经典的实例和过得了笔试就差不多了(仅代表笔者个人的观点)。
言归正传,今天笔者的blog主要是冒择路兮快中的冒泡排序(bubble sort)、选择排序(select sort)、插入排序(insert sort)和归并排序(merge sort)4种。

1.Bubble Sort

冒泡在工程上基本不会有太多用武之地,而作为教学典例会被经常提及,因此笔者在这就不过多赘述了。
这里写图片描述
上图省略了一些步骤。

Bubble.java

class Bubble{
    public static void bubbleSort(int[] arr){
        if(null == arr || arr.length < 2) return;
        for(int end = arr.length - 1; end >= 0; end--){
            for(int start = 0; start < end; start++){
                if(arr[start] > arr[start + 1])
                    swap(arr, start, start + 1);
                System.out.println(Arrays.toString(arr));
            }
        }
    }

    public static void swap(int[] arr, int i, int j){
        arr[i] = arr[i] ^ arr[j];//这里用的是二进制中异或运算的特性:1 = 1 ^ 0 ^ 0 
        arr[j] = arr[i] ^ arr[j];//一个数连续异或另一个数两次其值仍为此数本身
        arr[i] = arr[i] ^ arr[j];//这种方式不能乱用
    }

    public static void main(String[] args) {
        int[] arr = {4, 3, 2, 1};
        bubbleSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

这里写图片描述

Bubble Sort Big O

这里写图片描述
bubble sort的时间复杂度是属于T(N)=a(N^2)+bN+c的O(N^2)。

2.Select Sort

select sort和bubble sort是有一点点的相似的,它们在时间复杂度上都是O(N^2),下面给出笔者的一点点分析。
这里写图片描述
下面是对应的代码。

Select.java

class Select{
    public static void selectSort(int[] arr){
        if(null == arr || arr.length < 2) return;
        for(int end = arr.length - 1; end >= 0; end--){
            int maxIndex = end;
            for(int start = end - 1; start >= 0; start--){
                maxIndex = arr[maxIndex] < arr[start] ? start : maxIndex;
            }
            swap(arr, maxIndex, end);
            System.out.println(Arrays.toString(arr));
        }
    }

    public static void swap(int[] arr, int i, int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void main(String[] args) {
        int[] arr = {4, 3, 2, 1};
        selectSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

上面的这个select sort的实现只是为了作证下笔者对于select sort和bubble sort的相似的想法。

Select Sort Big O

这里写图片描述
下面给出比较经典的写法。

Select.java version1.1

class Select{
    public static void selectSort(int[] arr){
        if(null == arr || arr.length < 2) return;
            for(int start = 0; start < arr.length; start++){
            int minIndex = start;
            for(int next = start + 1; next < arr.length; next++){
                minIndex = arr[minIndex] > arr[next] ? next : minIndex;
            }
            swap(arr, start, minIndex);
        }
    }

    public static void swap(int[] arr, int i, int j){
        int temp = arr[i];//这里不要使用上面的异或来进行交换
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void main(String[] args) {
        int[] arr = {4, 3, 2, 1};
        selectSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

select sort和bubble sort一样现在最多也只是成为教学典例而已,因为其性能太差了,任然摆脱不了无用的比较。

3.Insert Sort

insert sort较之前面两种排序方式其实也并没有过多的性能上的提升,仍然是O(N^2)的复杂度,也仍然和数据的规模和数据的好坏情况有莫大关联。
这里也不多赘言,我们的重点是第4和第5点。
这里写图片描述
这里右边已排序元素只会有一个,而且当当前此次排序完成时它会立即成为参照元素,代替原本的参照元素,当有新的元素插入时,参照元素的右边是不会存在已排序的元素的。

Insert.java

class Insert{
    public static void insertSort(int[] arr){
        if(null == arr || arr.length < 2) return;
                for(int start = 1; start < arr.length; start++){
            for(int tar = start; tar > 0 && arr[tar] < arr[tar - 1]; tar--)
                swap(arr, tar, tar - 1);
        }
    }

    public static void swap(int[] arr, int i, int j){
        int temp = arr[i];//这里不要使用上面的异或来进行交换
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void main(String[] args) {
        int[] arr = {4, 3, 2, 1};
        insertSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

Insert Sort Big O

这里写图片描述
接下来才是我们重点!

4.Merge Sort

merge sort的实现和上面的三种颇为不同,它是通过递归的方式和分治策略(Master)实现的。说道递归或许留给各位最深印象就是“递归不就是自己调用自己吗?”,在具体讨论merge sort前,笔者先说说递归的实现。

递归

递归是通过stack(栈)实现的,在上一层递归方法(父方法)调用下一层同一递归方法(子方法)时,系统会先把上一层递归方法的所有信息保存在栈中,包括当前运行后PC指向的行号、参数列表以及方法体内的局部变量等,当子方法执行到递归结束条件返回退出时,父方法就能获取其子方法返回的值,当此父方法也处理完毕也返回了,那么它也将把值传递给它父方法,就这样层层调用和返回直到结束。
现在笔者来一个简单的例子:使用递归的方式来寻找数组中的最大值。

Test.java

package com.unicorn.base_01;

/**
 * @author Unicorn
 * @descrition
 * @created 2018-05-05-8:55
 */

public class Test {
    public static Integer findMax(int[] arr, int l, int r){
        if(null == arr) return null;
        if(l == r) return arr[l];

        int mid = l + ((r - l) >> 1);
        int leftMax = findMax(arr, l, mid);
        int rightMax = findMax(arr, mid + 1, r);
        return leftMax > rightMax ? leftMax : rightMax;
    }

    public static void main(String[] args) {
        int[] arr = new int[]{1001, 4, 55, 8, 12, 99, 101, 22, 688};
        System.out.println(findMax(arr, 0, arr.length - 1));
    }
}

Analysis

这里写图片描述
这里写图片描述
接下来给出merge sort的实现

Merge.java

class Merge{
    public static void mergeSort(int[] arr){
        if(null == arr || arr.length < 2)
            return;
        mergeSort(arr, 0, arr.length - 1);
    }

    private static void mergeSort(int[] arr, int l, int r) {
        if(l == r)
            return;
        int mid = l + ((r - l) >> 1);
        mergeSort(arr, l, mid);
        mergeSort(arr, mid + 1, r);
        merge(arr, l, mid, r);
    }

    private static void merge(int[] arr, int l, int mid, int r) {
        int[] help = new int[(r - l) + 1];
        int pl = l, pr = mid + 1;
        int index = 0;
        while(pl <= mid && pr <= r){
            help[index++] = arr[pl] > arr[pr] ? arr[pr++] : arr[pl++];
        }

        while(pl <= mid){
            help[index++] = arr[pl++];
        }

        while (pr <= r){
            help[index++] = arr[pr++];
        }

        for (int i = 0; i < index; i++) {
            arr[l + i] = help[i];
        }
    }
    public static void main(String[] args) {
        int[] arr = {4, 3, 2, 1};
        mergeSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

这里写图片描述

Analysis

这里写图片描述
这里写图片描述

5.Test our sorted methods

到现在笔者完成了4种排序,以上的排序都经过50万次长度为100的随机数组的排序测试,现在笔者就来讲讲如何编写这个随机测试。
编写随机测试时我们要满足以下的几点要求:

  1. 要有一个正确的排序方法,作为排序结果的参照,这个正确的排序方法不需要管其性能的好坏,只要保证正确就可以了(其实不正确的也没关系,迭代代码直到正确为止就好)
  2. 需要一个产生随机数组的方法
  3. 需要一个比较自行编写的排序方法和正确的排序方法所排序的结果
  4. 测试不正确的时候几时停止并打印结果,便于修改

    Test.java

package com.unicorn.base_01;

import com.sun.xml.internal.bind.v2.model.core.ID;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * @author Unicorn
 * @descrition
 * @created 2018-05-05-8:55
 */

public class Test {
    //for test
    public static void comparator(int[] arr){
        Arrays.sort(arr);
    }

    public static int[] generateRandomArray(int maxSize, int maxValue){
        int[] arr = new int[(int)((maxSize + 1) * Math.random())];
        for(int i = 0; i < arr.length; i++){
            arr[i] = (int)((maxValue + 1) * Math.random()) - (int)((maxValue + 1) * Math.random());
        }
        return arr;
    }

    public static int[] copyArray(int[] arr){
        if(null == arr) return null;
        int[] res = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            res[i] = arr[i];
        }
        return res;
    }

    public static boolean isEqual(int[] arr1, int[] arr2){
        if((null == arr1 && null != arr2) || (null != arr1 && null == arr2))
            return false;

        if(null == arr1 && null == arr2)
            return true;

        if(arr1.length != arr2.length)
            return false;

        for (int i = 0; i < arr1.length; i++) {
            if(arr1[i] != arr2[i]) return false;
        }
        return true;
    }

    public static void printArray(int[] arr){
        if(null == arr) return;
        System.out.println(Arrays.toString(arr));
    }

    public static void main(String[] args) {
        int testTimes = 500000;
        int maxSizes = 100;
        int maxValue = 100;
        boolean succeed = true;
        for (int i = 0; i < testTimes; i++) {
            int[] arr1 = generateRandomArray(maxSizes, maxValue);
            int[] arr2 = copyArray(arr1);
            Merge.mergeSort(arr1);
            comparator(arr2);
            if(!isEqual(arr1, arr2)){
                succeed = false;
                printArray(arr1);
                printArray(arr2);
                break;
            }
        }
        System.out.println(succeed ? "pass" : "shit!");
    }
}

各位可以自行测试上面的排序结果,如果出错了恳请各位及时联系笔者,笔者会即使进行改正.
感谢阅读到末尾的各位的支持!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值