贪心算法的概念
- 贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解 。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择 。
算法思路
- 建立数学模型来描述问题。
- 把求解的问题分成若干子问题。
- 对每个子问题求解,等到子问题的局部最优解。
- 把子问题的解局部最优解合成原来解问题的一个解。
个人理解
- 我们可以将一个问题拆分成各个子问题进行解决,在每个子问题中我们选择一种贪心策略,选出最优解,最终将这些子问题的最优解组合起来,就是整个问题的最优解。
例题
- 在开发购物车功能时,我们可以有这种规则的限定。每个用户可以有多个订单,每个订单上面包含多个商品,(每一个订单上的商品归属于一个用户),而且每笔订单的交易商品的总价格不能超过1000元。当用户购买完商品时,现要求生成的订单数量最少。
根据上面问题我们创建一下模型
1.首先从上面问题,我们可以将商品类和订单类创建出来,其中商品类Item包含价格 price,用户id sellerId。订单类Order 包含总价格 totalPrice,这个订单归属用户的id sellerId,以及商品列表 orderItems.
- 订单类
import lombok.Data;
import java.util.List;
/**
* 订单类
*/
@Data
public class Order {
/**
* 该订单对应的商品
*/
List<Item> orderItems;
/**
* 该订单金额,单位分
*/
Double totalPrice;
/**
* 该订单对应的卖家userId
*/
long sellerId;
}
- 商品类
import lombok.Data;
/**
* 商品类
*/
@Data
public class Item {
/**
* 卖家用户id
*/
long sellerId;
/**
* 商品价格,单位分
*/
Double price;
}
2.其次我们分析 而且每笔订单的交易商品的总价格不能超过1000元。当用户购买完商品是,现要求生成的订单数量最少 一下该规则要求。可以设计出一种贪心策略。
- 用户购买的商品可能超过1000,也可能超不过1000 ,超不过1000元的我们只需要一个订单就可以了。超过1000的的,我们应如何保持订单最少呢?
- 当超过1000时,我们将该用户的商品列表List进行降序排列,然后从头往下去取数据,加入一个订单中,当一个订单中的总价格大于1000元时,我们就不在取数据了,将剩余的商品列表中的数据,在加入另外的订单中,直到该用户的商品类表为空。
- 此时我们已经获得了一个用户的订单列表,此时你就以为获得了最少数量的订单类吗?
** 我们看一下下面的一组数据
** 用户a 购买了 888 950 112 40 四种价格的商品。
** 按我们上面的思路,首先对着四种商品进行排序变为 950 888 112 40
** 我们首先将最大的 950加入到一个订单,在加入888,因为950+888>1000,因此我们不能将其加入进去,因此 我们将 888 和112 封装成一个订单 40有封装成了一个订单。但是我们可以发现我们只需要两个订单就可以了,现在出现了3个,因此我们还需要对订单类表进行优化
** 因此,我们还需要对订单进行合并,将950和40合并成一个即可,订单如何合并呢?
** 首先我们根据订单的总价格进行排序,将总价格相加起来小于1000的进行排序,思想和对商品加入订单的思路相同。
运行结果
核心代码
public static List<Order> packageItemsToOrders(List<Item> items) {
//定义函数返回的订单
List<Order> orders = new ArrayList<>();
//根据用户id对items进行分组,为实现:每笔交易订单的商品只能是同一个卖家
Map<Long, List<Item>> listItemMap = items.stream()
.collect(Collectors.groupingBy(Item::getSellerId));
//得到该商品列表中所以普的用户id/初步优化
Object sellerIds[] = listItemMap.keySet().toArray();
for (int i = 0; i < sellerIds.length; i++) {
//定义用户的订单列表
List<Order> addOrder=new ArrayList<>();
//获取到用户所有的商品列表
List<Item> getItems = listItemMap.get((long) sellerIds[i]);
//对商品列表进行排序
getItems = getItems
.stream()
.sorted(Comparator.comparing(Item::getPrice).reversed())
.collect(Collectors.toList());
/**
* 将用户的商品列表的数据,
* 取出来封装成订单,
* 加入用户的订单列表,
* 用户的商品列表为空结束循环
*/
while (getItems.size() > 0) {
double countPrice = 0.0;
//新建一个订单接收商品
Order o = new Order();
o.setSellerId((long) sellerIds[i]);
int j = 0;
for (; j < getItems.size(); j++) {
countPrice += getItems.get(j).getPrice();
if (countPrice > 1000.0) {
break;
}
}
List<Item> removers=new ArrayList<>();
//记录已经加入到了订单中的商品,等一下将他们删除
removers = getItems.subList(0, j );
if(removers.size()>0){
//加入到订单中的闪频列表大于0,说明订单中有值
o.setTotalPrice(removers.stream().map(Item::getPrice)
.reduce(Double::sum).get());
List<Item> items1=new ArrayList<>();
removers.forEach(remover->{
items1.add(remover);
}
);
o.setOrderItems(items1);
//我们将订单,加入用户的订单列表中
addOrder.add(o);
//将加入到订单的商品的数据,从订单列表中删除
getItems.removeAll(removers);
}
}
//对用户的订单列表进行排序
List<Order> orderList=addOrder.parallelStream()
.sorted(Comparator.comparing(Order::getTotalPrice).reversed())
.collect(Collectors.toList());
int k =0;
//进行订单的合并操作
for(;k<orderList.size();k++){
double countPrice = 0.0;
int j = k;
for (; j < orderList.size(); j++) {
countPrice += orderList.get(j).getTotalPrice();
if (countPrice > 1000.0) {
break;
}
}
if(j-k>1){
//j-k>1代表有订单可以合并
//需要合并的订单,也是需要将该订单移除出点订单列表的订单
List<Order> orders1=orderList.subList(k,j);
List<Order> removeOrder=new ArrayList<>();
orders1.parallelStream().forEach(
o->{
removeOrder.add(o);
}
);
//定义一个新订单,用户合并其他订单
Order order=new Order();
order.setSellerId(orders1.get(0).sellerId);
List<Item> itemList=new ArrayList<>();
orders1.forEach(o->{
itemList.addAll(o.getOrderItems());
});
order.setOrderItems(itemList);
order.setTotalPrice(itemList.parallelStream().map(Item::getPrice)
.reduce(Double::sum).get());
//订单列表移除出合并订单
orderList.removeAll(removeOrder);
//订单列表加入新的订单
orderList.add(order);
//对合并后的新的订单列表进行重新排序
orderList=orderList.parallelStream()
.sorted(Comparator.comparing(Order::getTotalPrice).reversed())
.collect(Collectors.toList());
//将k值归0
k=0;
}
}
orders.addAll(orderList);
}
return orders;
}
Main函数
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Item> items = new ArrayList<>();
// TODO Auto-generated method stub
for (int i = 0; i < 20; i++) {
Item item = new Item();
Random random = new Random();
item.price = random.nextInt(999)+random.nextDouble();
item.sellerId = random.nextInt(10) + 100;
items.add(item);
}
List<Order> orders = packageItemsToOrders(items);
for (Order order : orders) {
System.out.println("订单价格" + order.getTotalPrice() + "元,"
+ " 商品:" + order.getOrderItems()
+ "商家id" + order.getSellerId());
}
}