Leetcode 分治贪心算法
分治:
1. Pow(x, n)
50. Pow(x, n)
求x的n次方
x = n次方 等于 n为偶数的情况下 x 的 n/2次方 * x 的n/2次方
n为基数的情况下 x 的 n/2次方 * x 的 n / 2 次方 * x
public double myPow(double x, int n) {
long N = n;
if (N < 0) { //如果N < 0 需要计算x分之一的 -N次方
x = 1 / x;
N = - N;
}
return fastPow(x, N);
}
private double fastPow(double x, long n) {
if (n == 0.0) {
return 1.0;
}
double half = fastPow(x , n / 2);//计算x的 n / 2 次方
if (n % 2 == 0) {
return half * half;
}else {
return half * half * x;
}
}
2. x 的平方根
二分法
public int mySqrt(int x) {
if (x <= 1) {
return x;
}
int left = 1;
int right = x;
while (left <= right) {
int mid = left + (right - left) / 2; // 5 mid = 2 mid = 3
int sqrt = x / mid; // 8 / 5 = 1 8 / 2 = 4 8 / 3 = 2
if (sqrt == mid) {
return mid;
}else if (sqrt < mid) {
right = mid -1; //right = 4 right = mid - 1 = 3 - 1 = 2
}else {
left = mid + 1; // left = 3
}
}
return right; //循环退出的条件是left = right + 1 3 = 2(right) + 1 如果取得是上界就可以返回left
}
贪心算法
概念
贪心的意思在于做出选择的时候,每次都要选择对自身最为有利的结果,保证自身利益的最大化,贪心算法就是利用这种贪心算法就是利用这种贪心思想得出的一种算法
贪心算法作为五大算法之一,在数据结构中的应用十分广泛。
贪心算法可以简单的描述为:大事化小,小事化了。对于一个较大的问题,通过找到子问题的重叠,并且对于每个子问题进行选择,找出最优的值,进行处理,再找出最优的值,在处理。也就是贪心算法是一种在每一步选择都采取在当前状态下最好或者最优的选择,从而希望结果是最好和最优的算法
贪心算法在对问题进行求解的时候,总是做出在当前看来是最好的选择。也就是说,不从整体上最优上加一考虑,所以做出的是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解,或者是整体最优解的近似解。
算法的流程
- 建立数学模型来描述问题
- 把求解的问题分成若干个子问题
- 对每一子问题的求解,得到子问题的局部最优解
- 把子问题的局部最优解合成原来问题的一个借
3. 跳跃游戏
//从0位置开始跳,计算当前能调到的右边界,遍历当前位置到右边界之间的每个位置,选择最大的能够调到的位置,在更新右边界
public boolean canJump(int[] nums) {
if (nums == null || nums.length == 0)return false;
int right = nums[0];
for (int i = 0; i <= right; i++) {
if (right >= nums.length - 1) {
return true;
}
if (i + nums[i] > right) {
right = i + nums[i];
}
}
return false;
}
4. 跳跃游戏
public int jump(int[] nums) {
int end = 0;
int maxPosition = 0;
int steps = 0;
for (int i = 0; i < nums.length - 1; i++) {
maxPosition = Math.max(maxPosition, nums[i] + i); //找到能跳到的最远的位置
if (i == end) { //遇到边界,就需要更新边界的值
end = maxPosition;
steps++;
}
}
return steps;
}
方法2:我们知道最终都要到达最后一个位置,然后我们找前一个位置,遍历数组,找到能到达它的位置,离其最远的的就是要找的位置,然后继续找上上个位置,最后找到第0个位置就结束了。
public int jump(int[] nums) {
int position = nums.length - 1;//要找的位置:初始为最后一个位置
int steps = 0;
while (position != 0) { //是否到了第0位置
for (int i = 0; i < position; i++) {
if (nums[i] + i >= position) { //找到第一个能到达它的位置
position = i;//更新要找的位置
steps++;
break;
}
}
}
return steps;
}
5. 无重复字符的最长子串
3. 无重复字符的最长子串
假设子串里含有重复的字符,则父串一定含有重复的字符,单个子问题就可以决定父问题,和动态规划不同,动态规划里面,单个子问题只能影响父问题,不足以解决。
从左往右扫描,当遇到重复的字母的时候,以第一个重读的字母index + 1,作为新的搜索起始位置,直到最后一个字母。
public int lengthOfLongestSubstring(String s) {
if (s == null || s.length() == 0) return 0;
int left = 0, right = 0;
int res = 0;
int[] dict = new int[256];
while (right < s.length()) {
char c = s.charAt(right);
dict[c]++;
right++;
//遇到第一个重复的字符的时候,需要更新左边界。循环直到去掉第一个重复的字符;
while (dict[c] > 1) {
char c1 = s.charAt(left);
dict[c1]--;
left++;
}
res = Math.max(res, right - left);
}
return res;
}
6. 盛最多水的容器
public int maxArea(int[] height) {
if (height == null || height.length == 0) return 0;
int left = 0;//左边界
int right = height.length - 1; //右边界
int maxArea = 0;
while (left < right) {
int curArea = Math.min(height[left], height[right]) * (right - left); //取左右边界的最小值计算当前的容积
maxArea = Math.max(curArea, maxArea);
if (height[left] < height[right]) { //如果左边界小于右边界,固定右边界,更新左边界去寻找下一个较大的值,
left++;
}else if (height[left] > height[right]) {
right--;
}else {
left++;
right--;
}
}
return maxArea;
}
7、分发饼干
public int findContentChildren(int[] g, int[] s) {
if (g == null || s == null) return 0;
int res = 0;
Arrays.sort(g);
Arrays.sort(s);
int indexG = 0;
int indexS = 0;
while (indexG < g.length && indexS < s.length) {
if (g[indexG] <= s[indexS]) {
res++;
indexG++;
}
indexS++;
}
return res;
}
8. 无重叠区间
先计算最多能组成的不重叠区间个数,然后用区间总个数减去不重叠区间的个数。
在每次选择中,区间的结尾最为重要,选择的区间结尾越小,留给后面的区间的空间越大,那么后面能够选择的区间个数也就越大。
按区间的结尾进行排序,每次选择结尾最小,并且和前一个区间不重叠的区间。
public int eraseOverlapIntervals(int[][] intervals) {
if (intervals.length == 0) return 0;
Arrays.sort(intervals, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1] - o2[1];
}
});//以最小的上边界排序
int count = 1; //记录不重复的区间的个数,初始为1
int end = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] < end) { //出现了区间的重叠,就不计数
continue;
}
end = intervals[i][1];
count++;
}
return intervals.length - count;
}
9. 用最少数量的箭引爆气球
也是计算不重叠的区间个数,不过和 Non-overlapping Intervals 的区别在于,[1, 2] 和 [2, 3] 在本题中算是重叠区间。
public int findMinArrowShots(int[][] points) {
if (points == null || points.length == 0) return 0;
Arrays.sort(points, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1] - o2[1];
}
});
int count = 1;
int end = points[0][1];
for (int i = 1; i < points.length; i++) {
if (points[i][0] <= end) { //出现重叠了,可以戳破
continue;
}
end = points[i][1];
count++;
}
return count;
}
10. 非递减序列
一,当数组长度小于3时,最多需要调整一次就能满足条件
二,当数组长度大于等于3时,出现前一个元素y大于后一个元素z时,
如果y的前元素x不存在,让y=z即可;若x存在,会有以下情况
x y z
1 3 2
2 3 1
3 3 1
2 3 2
要满足条件,需要如下调整:
1,当x<z,让y=z
2,当x>z,让z=y
3, 当x=z,让y=z
综合以上可以得到:当x存在且x>z,就让z=y,否则让y=z
当变更超过2次就不再满足条件
public boolean checkPossibility(int[] nums) {
if(nums.length < 3){
return true;
}
int count = 0;
for(int i=0;i<nums.length-1;i++){
if(nums[i] > nums[i+1]){
count++;
if(count > 1){
break;
}
if(i-1 >=0&&nums[i-1] > nums[i+1]){
nums[i+1] = nums[i];
}else{
nums[i] = nums[i+1];
}
}
}
return count <= 1;
}