绘制直线。有个单色屏幕存储在一个一维数组中,使得32个连续像素可以存放在一个 int 里。屏幕宽度为w,且w可被32整除(即一个 int 不会分布在两行上),屏幕高度可由数组长度及屏幕宽度推算得出。请实现一个函数,绘制从点(x1, y)到点(x2, y)的水平线。
给出数组的长度 length,宽度 w(以比特为单位)、直线开始位置 x1(比特为单位)、直线结束位置 x2(比特为单位)、直线所在行数 y。返回绘制过后的数组。
示例1:
输入:length = 1, w = 32, x1 = 30, x2 = 31, y = 0
输出:[3]
说明:在第0行的第30位到第31为画一条直线,屏幕表示为[0b000000000000000000000000000000011]
示例2:
输入:length = 3, w = 96, x1 = 0, x2 = 95, y = 0
输出:[-1, -1, -1]
分析:
方法:模拟
可以通过 w * y + x1 的方式,将二维平面转化为一维,那么对目标范围的直线的情况进行讨论即可:


- 目标直线不在当前范围内,如 [ 0,32 ) 和 [ 128,160 ),此时全 0。
- 左边界在当前范围内,如图1 [ 32,64 ),此时 left 左边全 0,右边全 1。
- 当前范围在目标直线内,如图1 [ 64,96 ),此时全 1。
- 右边界在当前范围内,如图1 [ 96,128 ),此时 right 左边全 1,右边全 0。
- 目标直线都在当前范围内,如图2 [ 65,96 ),此时 [ left,right ] 全 1,其余全 0。
时间复杂度:O(n) n 为 length
空间复杂度:O(n)
public int[] drawLine(int length, int w, int x1, int x2, int y) {
//结果数组
int[] res = new int[length];
//定义左右边界
int left = w * y + x1, right = w * y + x2;
//遍历,记录每32位的数字
for(int i = 0; i <length; ++i){
//当前范围左右边界
int cur = i * 32, next = cur + 31;
//目标范围都不在范围里
//当前范围在目标范围外
if(next < left || cur > right){
continue;
}
//当范围在目标范围内
if(cur >= left && next <= right){
res[i] = -1;
continue;
}
//目标范围仅在当前范围里
if(cur <= left && right <= next){
res[i] = -1 >>> left % 32 & -1 << 31 - right % 32;
continue;
}
//左边界部分在当前范围内
if(cur <= left){
res[i] = -1 >>> left % 32;
continue;
}
//右边界部分在当前范围内
res[i] = -1 << 31 - right % 32;
}
return res;
}
优化:
因为Java 整形数组默认为 0,索引只需要将直线中间的范围赋为 -1,然后单独计算左右边界即可。
时间复杂度:O(n) 最坏情况,全是1
空间复杂度:O(n)
class Solution {
public int[] drawLine(int length, int w, int x1, int x2, int y) {
//结果数组
int[] res = new int[length];
//定义左右边界
int left = (w * y + x1) / 32, right = (w * y + x2) / 32;
//对中间进行赋值
for(int i = left + 1; i <= right; ++i){
res[i] = -1;
}
//左边界
res[left] = -1 >>> x1 % 32;
//右边界
res[right] = res[right] & Integer.MIN_VALUE >> x2 % 32;
return res;
}
}
题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/draw-line-lcci