暴躁算法(剑指系列)-每日一练4.18
2022.4.18
分治法实现二分查找
这两个经常使用,属于基础算法
就放在练习的前面
分治法实现快速排序
建立基本的代码库,用于以后便于使用。
快速排序-分解
以q为基准元素将a:p:r划分成3段a:p:q-1、q和q+1:r,使得a:q-1中任何元素小于q ,q+1:r中任何元素大于q ;
下标q在划分过程中确定。
递归求解
递归调用快速排序算法对ap:q-1和aq+1:r进行排序;
二分查找:
public int BinarySearch(int[] A,int target){
int start = 0;
int end = A.length;
while(start<=end){
//初始化中间指针
int mid = (start+end)/2;
//根据mid与target的大小变化start或end
if(A[mid]==target){
return mid;
}
if(A[mid]>target){
end = mid-1;
}else {
start= mid+1;
}
}
//返回非法下标表示查找失败
return -1;
}
快速排序:
public static int Partition(int[] A,int start,int end){
int temp = A[start];
while(start<end) {
while (A[end] >= temp && end >start) {
end--;
}
A[start] = A[end];
while (A[start] <= temp && start<end) {
start++;
}
A[end] = A[start];
}
A[start] = temp;
return start;
}
public static void QuickSort(int[] A,int s,int t){
int i;
if(s<t){
i = Partition(A,s,t);
QuickSort(A,s,i-1);
QuickSort(A,i+1,t);
}
}
剑指 Offer 10- II. 青蛙跳台阶问题
难度简单273
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n
级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
简单的动态规划问题。
动态规划5部曲
数组的定义:是d[i]代表走到第i个台阶的方法种数。
初始化数组:第0阶是1,第一阶1,第二节为2
明确递推公式:任何一阶的种数取决于他前两阶。比如第三个台阶,只能是由第一阶走一步(两阶)或者第二阶作为起点走一阶。
所以d[i] = d[i-1]+d[i-2]。(在决定好起点与终点后,走法只有一种)
咳咳,这个递推公式很明显嘛:斐波那契(这个就有一点取巧了。。)
所以直接反手把之前写的斐波那契数列的最优写法cv上去(在4.16好那篇里面)
public int numWays(int n) {
if(n==0){
return 1;
}
if(n==1){
return 1 ;
}
if(n==2){
return 2 ;
}
long pre = 1L;
long cur = 1L;
long next = 2L;
long temp;
while(n>=3){
temp =(cur+next)%1000000007;
pre = cur;
cur = next;
next=temp;
n--;
}
return (int)next;
}
运算结果:
执行结果:
通过
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:38 MB, 在所有 Java 提交中击败了62.13%的用户
勉勉强强嘛.
阿巴阿巴。。
这个就是正儿八经动态规划写法。
public int fib(int n) {
if(n==0){
return 1;
}
if(n==1){
return 1 ;
}
//比起斐波那契,这里的初始值要稍微改一下01123——》1123
long[] fbs = new long[n+1];
fbs[0] = 1L;
fbs[1]=1L;
fbs[2]=2L;
for(int i =3;i<fbs.length;i++){
fbs[i]=(fbs[i-1]+fbs[i-2])%1000000007;
}
return (int)fbs[n];
}
执行结果:
通过
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:38 MB, 在所有 Java 提交中击败了71.75%的用户
通过测试用例:51 / 51
这里的效果也是不错的
剑指 Offer 11. 旋转数组的最小数字
难度简单602
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
给你一个可能存在 重复 元素值的数组 numbers
,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2]
为 [1,2,3,4,5]
的一次旋转,该数组的最小值为 1。
注意,数组 [a[0], a[1], a[2], ..., a[n-1]]
旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]
。
public int minArray(int[] numbers) {
int cur=0;
//特殊情况处理
if(numbers.length==1){
return numbers[0];
}
//判断当前序列是不是升序。
while(numbers[cur]<=numbers[cur+1] && cur<numbers.length-2){
cur++;
}
//排除全部是升序,最小数在下标为0的地方这种情况
if(numbers[cur]>numbers[cur+1]){
return numbers[cur+1];
}
return numbers[0];
}
执行结果:
通过
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:41.4 MB, 在所有 Java 提交中击败了11.76%的用户
~
优化:二分查找
最小值左边的值任何一个都会比右边所有值大。
public int minArray(int[] numbers) {
//初始化
int low = 0;
int high = numbers.length - 1;
//这里出现三种情况
while (low < high) {
int pivot = low + (high - low) / 2;
//舍弃右边的值
if (numbers[pivot] < numbers[high]) {
high = pivot;
}
//舍弃左边的值
else if (numbers[pivot] > numbers[high]) {
low = pivot + 1;
}
//如果相等,移动坐标再一次比较
else {
high -= 1;
}
}
//结束的时候low就是最小值
return numbers[low];
}
int pivot = low + (high - low) / 2;
这一个很妙,一般写的是a+b)/2,这样写,用减法可以避免在相加的时候溢出。
执行结果:
通过
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:40.8 MB, 在所有 Java 提交中击败了77.48%的用户
其他写法
- 1、分治:「二分的减治思想本来就是分治思想的特殊情况」
- 2、递归