【算法】差分数组-基础篇

差分数组

本质上说,就是一个数组,但可以在O(1)的时间复杂度处理区间修改;

形式

假设有一数组a,差分数组为d,有:任一 i ∈ [ 2 , n ] i \in[2,n] i[2,n],都有 d [ i ] = a [ i ] − a [ i − 1 ] d[i] = a[i]-a[i-1] d[i]=a[i]a[i1],则可以发现有以下性质:原数组第a[n]项为: a [ n ] = ∑ i = 0 n d [ i ] a[n]=\sum_{i=0}^{n}d[i] a[n]=i=0nd[i],原数组前n项和Sum为: S u m ( n ) = ∑ i = 0 n a [ i ] = ∑ i = 0 n ∑ i = 0 n d [ i ] = ∑ i = 0 n ( n − i + 1 ) d [ i ] Sum(n) = \sum_{i=0}^{n}a[i]=\sum_{i=0}^{n}\sum_{i=0}^{n}d[i]=\sum_{i=0}^{n}(n-i+1)d[i] Sum(n)=i=0na[i]=i=0ni=0nd[i]=i=0n(ni+1)d[i],

用法

主要支持两种操作:1.区间修改 2.单点查询

  • 快速处理区间加减操作:对数列区间[L,R]中的每个数都加上x,根据性质一,只需要将查分数组中的d[L]+x,而a[R+1]后的数没有加上x,则需要将d[R+1]-x,这样对后面的数就没有影响;
  • 询问区间和问题:根据性质二我们知道前缀和Sum,区间和[L,R]的和为:Sum® -Sum(L);

举例

原数组:3,6,8,7,4,3,2,9 ;查分数组:3,3,2,-1,-3,-1,-1,7

区间加减:比如对下标[3,6]之间的数字都加上2,原数组变为:3,6,8,9,6,5,4,9。而查分数组变为:3,3,2,1,-3,-1,-1,5

力扣题目(LeetCode)

1.1109.航班预定统计
2.798. 得分最高的最小轮调
3. 1094. 拼车

mycode and analysis

1109.航班预定统计

   //简单思路版本,执行耗时:799 ms,击败了15.08% 的Java用户,
    public int[] corpFlightBookings(int[][] bookings, int n) {
        int[] res  = new int[n];
        int size = bookings.length;
        for (int i = 0; i < size; i++) {
            int l = bookings[i][0],r= bookings[i][1],seats= bookings[i][2];
            for (int j = l; j <= r; j++) {
                res[j-1]+=seats;
            }
        }
        return res;
    }

//差分数组版本:执行耗时:2 ms,击败了100.00% 的Java用户
    public int[] corpFlightBookings(int[][] bookings, int n) {
        int[] d = new int[n];
        int[] res=new int[n];
        int size = bookings.length;
        for (int i = 0; i < size; i++) {
            int l = bookings[i][0],r = bookings[i][1],seats =bookings[i][2];
            d[l-1]+=seats;
            if(r<n)d[r]-=seats;
        }
        res[0]=d[0];
        for (int i = 1; i < n; i++) {
            res[i]=res[i-1]+d[i];
        }
        return res;
    }

[798. 得分最高的最小轮调]

 public int bestRotation(int[] nums) { //执行耗时:6 ms,击败了84.56% 的Java用户
        //思路在于:我们可以找到nums中每个数 轮转得分的范围,然后再范围内都加一;
        // 范围内加上一个固定的数,便可以想到使用 差分数组;
        int res,n=nums.length;
        int[] points = new int[n];

        for (int i = 0; i < n; i++) {
            //计算每个数 能得分的k 的范围
            // 比如100个数,第5个数为34,能得分的下标范围为[34,99],那么移动的k值应该为[6,66]
            // 比如 ,第26个数为5,能得分的下标范围为:[5,99],移动k值的范围为:[0,21]\[27,99];
            // 那么一般情况下:nums[i] 能得分的范围为:[0,i],移动次数的范围要分类讨论
            if(nums[i]>i){
                //k值取值范围应该为:
                int l =i+1,r=(n-nums[i]+i)%n;
                points[l]+=1;
                if(r+1 <n) points[r+1]-=1;
            }else if(nums[i]<=i) { //两个区间
                points[0]+=1;
                int r = i-nums[i];
                if(r+1<n) {
                    points[r + 1] -= 1;
                    //另外一个区间为[n-nums[i]+i,n-1]
                    if (i + 1 < n) points[i + 1] += 1;
                }
            }
        }

//遍历获取最小的k值;
        res= points[0];
        int max = points[0];
        int index=0;
        for (int i = 1; i < n; i++) {
            res+=points[i];
            if(res>max){
                max=res;
                index = i;
            }else if(res==max){
                if(index >i) index=i;
            }
        }
        return index;

    }

1094. 拼车

    public boolean carPooling(int[][] trips, int capacity) {
        int[] station = new int[1000+1]; //差分数组
        int maxMile =0; //记录最远距离;
        int n = trips.length;
        for (int i = 0; i < n; i++) {  
            int addPersons = trips[i][0]; //跟上面几题一样;
            if(maxMile<trips[i][2]) maxMile = trips[i][2];
            station[trips[i][1]]+=addPersons;
            station[trips[i][2]]-=addPersons;
        }

        int personCar =0;
        for (int i = 0; i <= maxMile; i++) {
            personCar +=station[i];
            if(personCar>capacity) return false;
        }
        return true;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 可以使用 GROUP BY 子句来进行分组查询,例如: ``` SELECT column1, COUNT(column2) FROM table_name GROUP BY column1; ``` 其中,column1 是要分组的列,COUNT(column2) 是对 column2 列进行计数。这条 SQL 语句会返回每个 column1 值对应的 column2 计数。 如果需要对多个列进行分组,可以在 GROUP BY 子句中指定多个列,例如: ``` SELECT column1, column2, COUNT(column3) FROM table_name GROUP BY column1, column2; ``` 这条 SQL 语句会返回每个 column1 和 column2 组合值对应的 column3 计数。 ### 回答2: Java中可以使用SQL语句来进行分组查询,主要通过SELECT语句的GROUP BY子句实现。 首先,我们需要连接到数据库,可以使用Java中的JDBC来实现。通过JDBC连接到数据库后,可以使用PreparedStatement对象来执行SQL语句。 以下是一个简单的示例,演示了如何在Java中使用分组查询: ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class GroupByQueryExample { public static void main(String[] args) { Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; try { // 连接到数据库 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "username", "password"); // 编写SQL语句 String sql = "SELECT category, COUNT(*) FROM products GROUP BY category"; // 创建PreparedStatement对象并设置参数 stmt = conn.prepareStatement(sql); // 执行查询 rs = stmt.executeQuery(); // 处理查询结果 while (rs.next()) { String category = rs.getString("category"); int count = rs.getInt(2); System.out.println("分类:" + category + ",数量:" + count); } } catch (SQLException e) { e.printStackTrace(); } finally { // 关闭连接和资源 try { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } if (conn != null) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } } ``` 上述示例是一个简单的Java程序,通过JDBC连接到数据库,执行了一个分组查询的SQL语句,并打印结果。 需要注意的是,上述示例中的数据库连接信息(URL、用户名、密码)需要根据实际情况进行修改,确保与数据库的连接信息一致。同时要确保相关的JDBC驱动程序已被正确引入项目中。 ### 回答3: Java 分组查询是通过使用 SQL 语句的 GROUP BY 子句来实现的。GROUP BY 子句用于将结果集分组并根据分组生成汇总信息。 在 Java 中,可以使用 JDBC 来执行 SQL 查询。首先创建一个 Connection 对象,然后通过该对象创建一个 Statement 对象。接下来,使用 Statement 对象执行 SQL 查询,并将结果保存在 ResultSet 对象中。最后,使用 ResultSet 对象遍历结果并处理分组数据。 首先,编写 SQL 查询语句,包含 GROUP BY 子句和选择需要的列。例如,假设有一个名为 students 的表,其中包含学生的姓名和分数信息。想要按照分数进行分组,并计算每个分组的平均分。SQL 查询可以如下所示: SELECT score, AVG(score) FROM students GROUP BY score; 接下来,在 Java 中执行该查询。首先创建 Connection 和 Statement 对象,并连接到数据库。然后,使用 Statement 的 executeQuery() 方法执行查询,并将结果保存在 ResultSet 对象中。最后,使用 ResultSet 对象遍历结果并处理分组数据。具体代码示例如下: ```java import java.sql.*; public class GroupByExample { public static void main(String[] args) { Connection connection = null; Statement statement = null; ResultSet resultSet = null; try { // 创建连接 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "username", "password"); // 创建 Statement 对象 statement = connection.createStatement(); // 执行查询 resultSet = statement.executeQuery("SELECT score, AVG(score) FROM students GROUP BY score"); // 遍历结果集 while (resultSet.next()) { // 获取分组数据 int score = resultSet.getInt(1); double averageScore = resultSet.getDouble(2); // 处理分组数据 System.out.println("Score: " + score + ", Average Score: " + averageScore); } } catch (SQLException e) { e.printStackTrace(); } finally { // 关闭连接、Statement 和 ResultSet 对象 try { if (resultSet != null) { resultSet.close(); } if (statement != null) { statement.close(); } if (connection != null) { connection.close(); } } catch (SQLException e) { e.printStackTrace(); } } } } ``` 以上就是使用 Java 进行分组查询的简单示例。根据实际需求和数据表结构,可以编写不同的 SQL 查询语句来实现更复杂的分组查询功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

格局不能小

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值