手撕一个二分排序算法2-深入Arrays.sort源码2

前提引入:

在阅读Arrays.sort()源码的时候发现里面有二分法,于是试着去手撕一个,发现还是需要一些构思能力的。

先做完了一个leetcode题再来手撕会好些,具体先看这个文章:35搜索插入位置-Java-手撕二分法系列1-深入Arrays.sort源码1

一、思路

实现结果:这里对于二分排序,是这样的,先输入一个数组进去再输出排序好了的数组出来。

那么就要开始构思了。

主要思路:

1、开辟一个数组myRes,输入的数组nums一样长度。

2、先存一个元素到myRes的第一个位置里。此时myRes的实际长度为1.

3、遍历nums剩下的元素,每个元素插入myRes的合适位置:

  • ①此时要将nums[1]插入myRes里,首先实际长度是1,那么也就是插入myRes的后面或者前面;
  • 如果要插入到后面,也就是直接放到myRes的【1】这个索引的位置;
  • 如果要插入到前,也就是直接放到myRes的【0】这个索引的位置,插入前需要挪位。
  • 此时怎么挪?对数组某一范围的索引元素往后挪,也就是要将[0,0]这个范围的数据整体往后挪一位。设计一个挪位的方法吧。

可以放到35搜索插入位置来提交测试:

class Solution {
    public int[] binarySort(int[] nums) {
        if (nums == null || nums.length == 0) return nums;

        int[] myRes = new int[nums.length];
        myRes[0] = nums[0];//先存一个进去
        int len = 1;//
        for (int i = 1; i < nums.length; i++) {
            fillInArray(myRes, len, nums[i]);
            len++;
        }
        return myRes;
    }

    private void fillInArray(int[] myRes, int len, int target) {
        int leftIndex = 0;
        int rightIndex = len - 1;
        if (myRes[0] >= target) {//判断是不是要插入到第一个位置
            moveArray(myRes, leftIndex, rightIndex);//将索引访问为[leftIndex,rightIndex]的元素整体右移一位
            myRes[0] = target;//插入元素
            return;
        }
        if (myRes[rightIndex] <= target) {
            myRes[len] = target;//直接在末尾插入元素
            return;
        }

        while (leftIndex <= rightIndex) {
            int haft = (leftIndex + rightIndex) / 2;
            if (myRes[haft] == target) {
                moveArray(myRes, haft + 1, len - 1);
                myRes[haft + 1] = target;
                return;
            } else if (myRes[haft] > target) {
                rightIndex = haft - 1;
            } else {
                leftIndex = haft + 1;
            }
        }

        moveArray(myRes, leftIndex, len - 1);//因为往leftIndex位置插入元素,于是要对[leftIndex,len - 1]的元素整体右移
        myRes[leftIndex] = target;//插入
    }

    /**
     * @param array
     * @param left  索引左边界
     * @param right 索引右边界
     */
    private void moveArray(int[] array, int left, int right) {
        for (int i = right + 1; i > left; i--) {
            if (i >= array.length) continue;
            array[i] = array[i - 1];
        }
    }

    public int searchInsert(int[] nums, int target) {
        int[] arr = new int[nums.length + 1];
        for (int i = 0; i < nums.length; i++) arr[i] = nums[i];
        arr[arr.length - 1] = target;
        int[] ints = binarySort(arr);
        for (int i = 0; i < ints.length; i++) {
            if (ints[i] == target) return i;
        }
        return 0;
    }
}

或者说先在本地测试再放到leetcode里测试也可以:

@Test
public void testBinarySortByLC() {//nums = [1,3,5,6], target = 5
    int[][] arrays = {
            {1, 3, 5, 6},//2
            {1, 3, 5, 6},//1
            {1, 3, 5, 6},//4
            {3, 5, 7, 9, 10},//3
            {1},

    };
    int[] targets = {
            5,
            2,
            7,
            8,
            0
    };

    for (int i = 0; i < arrays.length; i++) {
        System.out.println("nums is " + Arrays.toString(arrays[i]) +
                " , targetInput is " + targets[i] +
                " ,OutPut = " + searchInsert(arrays[i], targets[i]));
    }
}

测试全通过了!

二、具体实现:

public class MyBinarySort {
    public int[] binarySort(int[] nums) {
        if (nums == null || nums.length == 0) return nums;

        int[] myRes = new int[nums.length];
        myRes[0] = nums[0];//先存一个进去
        int len = 1;//
        for (int i = 1; i < nums.length; i++) {
            fillInArray(myRes, len, nums[i]);
            len++;
        }
        return myRes;
    }

    private void fillInArray(int[] myRes, int len, int target) {
        int leftIndex = 0;
        int rightIndex = len - 1;
        if (myRes[0] >= target) {//判断是不是要插入到第一个位置
            moveArray(myRes, leftIndex, rightIndex);//将索引访问为[leftIndex,rightIndex]的元素整体右移一位
            myRes[0] = target;//插入元素
            return;
        }
        if (myRes[rightIndex] <= target) {
            myRes[len] = target;//直接在末尾插入元素
            return;
        }

        while (leftIndex <= rightIndex) {
            int haft = (leftIndex + rightIndex) / 2;
            if (myRes[haft] == target) {
                moveArray(myRes, haft + 1, len - 1);
                myRes[haft + 1] = target;
                return;
            } else if (myRes[haft] > target) {
                rightIndex = haft - 1;
            } else {
                leftIndex = haft + 1;
            }
        }

        moveArray(myRes, leftIndex, len - 1);//因为往leftIndex位置插入元素,于是要对[leftIndex,len - 1]的元素整体右移
        myRes[leftIndex] = target;//插入
    }

    /**
     * @param array
     * @param left  索引左边界
     * @param right 索引右边界
     */
    private void moveArray(int[] array, int left, int right) {
        for (int i = right + 1; i > left; i--) {
            if (i >= array.length) continue;
            array[i] = array[i - 1];
        }
    }
}

三、优化:

阅读了Arrays.sort的源码后进行了优化

优化1:

小改:使用native方法调用c++方法对数据进行数据搬迁,使用Arrays.sort()优秀的思想之一。

class Solution {
    public int[] binarySort(int[] nums) {
        if (nums == null || nums.length == 0) return nums;

        int[] myRes = new int[nums.length];
        myRes[0] = nums[0];//先存一个进去
        int len = 1;//
        for (int i = 1; i < nums.length; i++) {
            fillInArray(myRes, len, nums[i]);
            len++;
        }
        return myRes;
    }

    private void fillInArray(int[] myRes, int len, int target) {
        int leftIndex = 0;
        int rightIndex = len - 1;
        if (myRes[0] >= target) {//判断是不是要插入到第一个位置
            moveArray(myRes, leftIndex, rightIndex);//将索引访问为[leftIndex,rightIndex]的元素整体右移一位
            myRes[0] = target;//插入元素
            return;
        }
        if (myRes[rightIndex] <= target) {
            myRes[len] = target;//直接在末尾插入元素
            return;
        }

        while (leftIndex <= rightIndex) {
            int haft = (leftIndex + rightIndex) / 2;
            if (myRes[haft] == target) {
                moveArray(myRes, haft + 1, len - 1);
                myRes[haft + 1] = target;
                return;
            } else if (myRes[haft] > target) {
                rightIndex = haft - 1;
            } else {
                leftIndex = haft + 1;
            }
        }

        moveArray(myRes, leftIndex, len - 1);//因为往leftIndex位置插入元素,于是要对[leftIndex,len - 1]的元素整体右移
        myRes[leftIndex] = target;//插入
    }

    /**
     * @param array
     * @param left  索引左边界
     * @param right 索引右边界
     */
    private void moveArray(int[] array, int left, int right) {


        System.arraycopy(array,left,array,left + 1,right - left + 1);
    }

    public int searchInsert(int[] nums, int target) {
        int[] arr = new int[nums.length + 1];
        for (int i = 0; i < nums.length; i++) arr[i] = nums[i];
        arr[arr.length - 1] = target;
        int[] ints = binarySort(arr);
        for (int i = 0; i < ints.length; i++) {
            if (ints[i] == target) return i;
        }
        return 0;
    }
}

优化2:

大改,使用native方法

可以用leetcode35搜索插入位置来测试下:

class Solution {
    public int searchInsert(int[] nums, int target) {
        int[] arr = new int[nums.length + 1];
        for (int i = 0; i < nums.length; i++) arr[i] = nums[i];
        arr[arr.length - 1] = target;
        int[] ints = binarySort(arr);
        for (int i = 0; i < ints.length; i++) {
            if (ints[i] == target) return i;
        }
        return 0;
    }

    public int[] binarySort(int[] nums) {
        if (nums == null || nums.length == 0) return nums;

        int[] myRes = new int[nums.length];
        myRes[0] = nums[0];//先存一个进去
        for (int i = 1; i < nums.length; i++) {
            int left = fillInArray(myRes, i, nums[i]);
            int n = i - left;
            moveArray(myRes,left,n,nums[i]);
        }
        nums = myRes;
        return nums;
    }

    private int fillInArray(int[] myRes, int len, int target) {
        int leftIndex = 0;
        int rightIndex = len - 1;
        if (myRes[0] >= target) {//判断是不是要插入到第一个位置
            return 0;
        }
        if (myRes[rightIndex] <= target) {
            return len;
        }

        while (leftIndex <= rightIndex) {
            int haft = (leftIndex + rightIndex) / 2;
            if (myRes[haft] == target) {
                return haft + 1;
            } else if (myRes[haft] > target) {
                rightIndex = haft - 1;
            } else {
                leftIndex = haft + 1;
            }
        }
        return leftIndex;
    }

    private void moveArray(int[] array, int left, int n,int target) {
        switch (n) {
            case 2: array[left + 2] = array[left + 1];
            case 1: array[left + 1] = array[left];
                break;
            default:
                System.arraycopy(array, left, array, left + 1, n);
        }
        array[left] = target;
    }
}

通过了,于是可以这样优化:

优化2:

public class MyBinarySort3 {
    public int[] binarySort(int[] nums) {
        if (nums == null || nums.length == 0) return nums;
        int[] myRes = new int[nums.length];
        myRes[0] = nums[0];//先存一个进去
        for (int i = 1; i < nums.length; i++) {
            int left = fillInArray(myRes, i, nums[i]);
            int n = i - left;
            moveArray(myRes,left,n,nums[i]);
        }
        nums = myRes;
        return nums;
    }

    private int fillInArray(int[] myRes, int len, int target) {
        int leftIndex = 0;
        int rightIndex = len - 1;
        if (myRes[0] >= target) {//判断是不是要插入到第一个位置
            return 0;
        }
        if (myRes[rightIndex] <= target) {
            return len;
        }

        while (leftIndex <= rightIndex) {
            int haft = (leftIndex + rightIndex) / 2;
            if (myRes[haft] == target) {
                return haft + 1;
            } else if (myRes[haft] > target) {
                rightIndex = haft - 1;
            } else {
                leftIndex = haft + 1;
            }
        }
        return leftIndex;
    }

    private void moveArray(int[] array, int left, int n,int target) {
        switch (n) {
            case 2: array[left + 2] = array[left + 1];
            case 1: array[left + 1] = array[left];
                break;
            default:
                System.arraycopy(array, left, array, left + 1, n);
        }
        array[left] = target;
    }
   
}

四、优化参考:

Arrays.sort源码阅读:深入Arrays.sort源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值