前提引入:
在阅读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源码