概念:
所谓贪心,总是做出在当前看来是最好的选择,也就是说,不从整体最优进行考虑,算法得到的是在某种意义上的局部最优解,比如当我们初中考试的时候,如果我们每科都考的是最优的,那么总成绩相对于而言也是最优的,这就是贪心算法的核心
部分题目分析:
1.最大乘积差
public int maxProductDifference(int[] nums) {
if(nums==null||nums.length==0){
return 0;
}
int n=nums.length;
fastSort(nums,0,n-1);
return (nums[n-1]*nums[n-2]-nums[0]*nums[1]);
}
//快排,将数组进行排序
public void fastSort(int[] nums,int left,int right){
if(nums==null||nums.length==0){
return;
}
if(left>=right){
return;
}
int i=left;
int j=right;
int pvoit=nums[left];
while(i<j){
//在右边找到第一个小的
while(i<j&&nums[j]>pvoit){
j--;
}
if(i<j){
nums[i++]=nums[j];
}
//在左边找第一个大的
while(i<j&&nums[i]<pvoit){
i++;
}
if(i<j){
nums[j--]=nums[i];
}
}
nums[i]=pvoit;
fastSort(nums,i+1,right);
fastSort(nums,left,i-1);
}
-
先对数组进行排序(这里大家可以顺便温习一下之前讲的算法)
- 利用贪心,直接取最大的两个数和最小的两个数
2.三角形的最大周长
public int largestPerimeter(int[] nums) {
if(nums==null||nums.length==0){
return 0;
}
int n=nums.length;
fastSort(nums,0,n-1);
for(int i=n-1;i>=2;i--){
if(nums[i-2]+nums[i-1]>nums[i]){
return nums[i]+nums[i-1]+nums[i-2];
}
}
return 0;
}
//快排,将数组进行排序
public void fastSort(int[] nums,int left,int right){
if(nums==null||nums.length==0){
return;
}
if(left>=right){
return;
}
int i=left;
int j=right;
int pvoit=nums[left];
while(i<j){
//在右边找到第一个小的
while(i<j&&nums[j]>pvoit){
j--;
}
if(i<j){
nums[i++]=nums[j];
}
//在左边找第一个大的
while(i<j&&nums[i]<pvoit){
i++;
}
if(i<j){
nums[j--]=nums[i];
}
}
nums[i]=pvoit;
fastSort(nums,i+1,right);
fastSort(nums,left,i-1);
}
-
利用三角形两边之和大于第三边的性质,假设小的两条边为a和b,大的那条边为c,必须满足等式a+b>c
- 这个问题是要求最大的三角形周长,那么势必最最大的那条边越大越好,所以我们可以枚举将所有的边按照递增排序,然后逆序枚举最大的那条边c,去剩下的边里找另外的两条边,最好的情况肯定是比c小的最大和次大的最优,那么这两边都不能满足条件,剩下的边也肯定不能满足条件(递增排序)
3.数组拆分
public int arrayPairSum(int[] nums) {
if(nums==null||nums.length==0){
return 0;
}
fastSort(nums,0,nums.length-1);
int res=0;
for (int i = 0; i <nums.length;i++) {
if(i%2==0){
res+=nums[i];
}
}
return res;
}
//快排,将数组进行排序
public void fastSort(int[] nums,int left,int right){
if(nums==null||nums.length==0){
return;
}
if(left>=right){
return;
}
int i=left;
int j=right;
int pvoit=nums[left];
while(i<j){
//在右边找到第一个小的
while(i<j&&nums[j]>pvoit){
j--;
}
if(i<j){
nums[i++]=nums[j];
}
//在左边找第一个大的
while(i<j&&nums[i]<pvoit){
i++;
}
if(i<j){
nums[j--]=nums[i];
}
}
nums[i]=pvoit;
fastSort(nums,i+1,right);
fastSort(nums,left,i-1);
}
-
对数组进行排序
- 选择偶数项进行相加
4.救生艇
public int numRescueBoats(int[] people, int limit) {
int left=0;
int right=people.length-1;
Arrays.sort(people);
int count=0;
while(left<=right){
if(left==right){
count++;
break;
}else if(people[left]+people[right]>limit){
right--;
count++;
}else{
count++;
right--;
left++;
}
}
return count;
}
- 对数组进行排序
- 如果只剩下一个人,那么直接加上一只船,并且跳出循环
- 如果最重的那个人和最轻的那个人加起来不能做同一个船,那么最重的那个人势必只能一个人坐船
- 如果最重的那个人可以和最轻的那个人一起坐船,那就顺带捎上
5.分发饼干
//贪心算法 尽量满足胃口大的孩子
public int findContentChildren(int[] g, int[] s) {
//对入参进行判断
if(g==null||g.length==0||s==null||s.length==0){
return 0;
}
Arrays.sort(g);
Arrays.sort(s);
//先遍历胃口大的孩子 然后在遍历饼干
int index=s.length-1;
int count=0;
for (int i =g.length-1; i>=0; i--) {
if(index>=0&&s[index]>=g[i]){
index--;
count++;
}
}
return count;
}
-
分别对胃口值和饼干都进行递增排序
- 然后将两个指针指向胃口值和饼干值的末尾
- 如果一个饼干和胃口值满足s[index]>=g[i],那么指针都往前移动一格,且答案+1
- 否则,胃口值的指针往前一移动一格,寻找更加容易满足条件的值,直到饼干或者胃口枚举完成
6.最少操作使数组递增
public int minOperations(int[] nums) {
int ans=0; int pre=nums[0];
for(int i=1;i<nums.length;i++){
//保证当前数一定比前面那个数多1或者本身就比那个数大
pre=Math.max(pre+1,nums[i]);
ans+=pre-nums[i];
}
return ans;
}
-
如果当前的数比前一个数大,则没有任何的消耗,并且重置前一个数为当前数+1,进行下一轮判断
- 否则,需要把当前的数变为前一个数+1
7.有效三角形的个数
//输出可围成的三角形
public int triangleNumber(int[] nums) {
//对入参进行判断
if(nums==null||nums.length<3){
return 0;
}
Arrays.sort(nums);
int n=nums.length;
int count=0;
for(int i=n-1;i>=2;i--){
//第三条边
int x=nums[i];
int left=0;
int right=i-1;
while(left<right){
if(nums[left]+nums[right]<=x){
left++;
}else{
count+=right-left;
right--;
}
}
}
return count;
}
- 首先将数组元素进行递增排序
- 利用for循环,不断的枚举最长的那条边
- 利用前后指针的思想去寻找合适的解
- 因为nums[right]+nums[left]>=x,因为数组是递增的,所以left+1~right中的数字都满足这个条件,所以在[left,right]范围内可以形成三角形的解的个数为right(包含关系)-left=count
leetcode题单:
public int maximum69Number (int num) {
//利用栈进行操作
Stack<Integer> stack=new Stack<>();
while(num!=0){
int str=num%10;
stack.push(str);
num/=10;
}
int count=0;
int sum=0;
while(!stack.isEmpty()){
int num1=stack.pop();
if(num1==6&&count==0){
num1=9;
count++;
}
sum=sum*10+num1;
}
return sum;
}
public int partitionString(String s) {
int[] hash=new int[26];
int count=0;
for(char c:s.toCharArray()){
if(hash[c-'a']==1){
count++;
Arrays.fill(hash,0);
}
hash[c-'a']++;
}
return count+1;
}
public int partitionString(String s) {
int hash=0;
int count=0;
for(char c:s.toCharArray()){
if((hash>>(c-'a')&1)==1){
count++;
hash=0;
}
hash|=1<<(c-'a');
}
return count+1;
}
//贪心问题,每次按照最大的去找
public boolean lemonadeChange(int[] bills) {
if(bills==null||bills.length==0){
return true;
}
int five=0;
int ten=0;
//遍历账单
for(int i=0;i<bills.length;i++){
//如果收取的是5,直接收入囊中
if(bills[i]==5){
five++;
//如果有5,直接找零
}else if(bills[i]==10){
ten++;
five--;
}else{
if(ten>0){
ten--;
five--;
}else{
five-=3;
}
}
if(five<0||ten<0){
return false;
}
}
return true;
}
public int splitArray(int[] nums, int k) {
return shipWithinDays(nums,k);
}
public int shipWithinDays(int[] weights, int days) {
if(weights==null||weights.length==0){
return 0;
}
//二分查找的范围
//left是数组中的最大值
//right是数组的全部元素之和
int left=0;
int right=0;
for(int w:weights){
left=Math.max(left,w);
right+=w;
}
while(left<right){
int mid=left+(right-left)/2;
if(f(weights,mid)<=days){
right=mid;
}else{
left=mid+1;
}
}
return left;
}
//当运输能力为x时,需要f(x)天运输完成
public int f(int[] weights,int x){
int day=0;
for (int i = 0; i <weights.length;) {
int cap=x;
while(i<weights.length){
if(cap<weights[i]){
break;
}else{
cap-=weights[i];
i++;
}
}
day++;
}
return day;
}