这周作业设计到的算法是有向无环图的最短路径算法,只需要按照顶点的拓扑顺序去放松顶点即可。而在这个题目中拓扑顺序就是按照行的顺序或列的顺序。
用到的数据结构为一个二维数组picture同来存储每个像素的颜色,一个二维数组energy用来存储每个像素的能量。开始我是用一个Picture类的对象来存储图像,但是在讨论区里发现用二维数组存储图像,可以占用更小的存储,且计算能量、removeseam时更快更方便。
在检验各像素能量时发现计算结果不正确,后来发现是运算符优先级的问题,((rgbLeft >> 16) & 0xFF) - ((rgbRight >> 16) & 0xFF),即‘ - ’的优先级大于‘ & ’的优先级,因此需要加括号。
在Checklist的Possible Progress Steps中发现计算seam以及removeseam时可以只写Horizontal和Vertical中的一个,然后另一个用矩阵转置的方法来完成。
第一次提交时memory没有通过,原因是把二维数组distTo和二维数组edgeTo放到了成员变量里,后来把这两个数组放到局部变量,就通过了memory测试。
1 import edu.princeton.cs.algs4.Picture; 2 3 public class SeamCarver { 4 private int[][] picture; 5 private double[][] energy; 6 private int width; 7 private int height; 8 9 public SeamCarver(Picture picture) // create a seam carver object based on the given picture 10 { 11 if (picture == null) 12 throw new IllegalArgumentException(); 13 width = picture.width(); 14 height = picture.height(); 15 energy = new double[width][height]; 16 this.picture = new int[width][height]; 17 18 for (int i = 0; i < width(); i++) 19 { 20 for (int j = 0; j < height(); j++) 21 this.picture[i][j] = picture.getRGB(i, j); 22 } 23 24 for (int i = 0; i < width(); i++) 25 { 26 for (int j = 0; j < height(); j++) 27 energy[i][j] = computeEnergy(i, j); 28 } 29 } 30 31 private double computeEnergy(int x, int y) 32 { 33 if (x == 0 || x == width() - 1 || y == 0 || y == height() - 1) 34 return 1000.0; 35 36 int rgbUp = picture[x][y - 1]; 37 int rgbDown = picture[x][y + 1]; 38 int rgbLeft = picture[x - 1][y]; 39 int rgbRight = picture[x + 1][y]; 40 double rx = Math.pow(((rgbLeft >> 16) & 0xFF) - ((rgbRight >> 16) & 0xFF), 2); 41 double gx = Math.pow(((rgbLeft >> 8) & 0xFF) - ((rgbRight >> 8) & 0xFF), 2); 42 double bx = Math.pow(((rgbLeft >> 0) & 0xFF) - ((rgbRight >> 0) & 0xFF), 2); 43 44 double ry = Math.pow(((rgbUp >> 16) & 0xFF) - ((rgbDown >> 16) & 0xFF), 2); 45 double gy = Math.pow(((rgbUp >> 8) & 0xFF) - ((rgbDown >> 8) & 0xFF), 2); 46 double by = Math.pow(((rgbUp >> 0) & 0xFF) - ((rgbDown >> 0) & 0xFF), 2); 47 48 return Math.sqrt(rx + gx + bx + ry + gy + by); 49 } 50 51 public Picture picture() // current picture 52 { 53 Picture pic = new Picture(width, height); 54 for (int i = 0; i < width; i++) 55 for (int j = 0; j < height; j++) 56 pic.setRGB(i, j, picture[i][j]); 57 58 return pic; 59 } 60 61 public int width() // width of current picture 62 { 63 return width; 64 } 65 66 public int height() // height of current picture 67 { 68 return height; 69 } 70 71 public double energy(int x, int y) // energy of pixel at column x and row y 72 { 73 if (x < 0 || x > width - 1 || y < 0 || y > height - 1) 74 throw new IllegalArgumentException(); 75 return energy[x][y]; 76 } 77 78 private void relaxvertical(double[][] distTo, int[][] edgeTo, int x, int y) 79 { 80 if (distTo[x][y + 1] > distTo[x][y] + energy[x][y + 1]) 81 { 82 distTo[x][y + 1] = distTo[x][y] + energy[x][y + 1]; 83 edgeTo[x][y + 1] = x; 84 } 85 if (x > 0 && distTo[x - 1][y + 1] > distTo[x][y] + energy[x - 1][y + 1]) 86 { 87 distTo[x - 1][y + 1] = distTo[x][y] + energy[x - 1][y + 1]; 88 edgeTo[x - 1][y + 1] = x; 89 } 90 if (x < width() - 1 && distTo[x + 1][y + 1] > distTo[x][y] + energy[x + 1][y + 1]) 91 { 92 distTo[x + 1][y + 1] = distTo[x][y] + energy[x + 1][y + 1]; 93 edgeTo[x + 1][y + 1] = x; 94 } 95 } 96 97 private void transpose() 98 { 99 int temp = width; 100 width = height; 101 height = temp; 102 103 double[][] energy2 = new double[width][height]; 104 int[][] picture2 = new int[width][height]; 105 106 for (int i = 0; i < width; i++) 107 { 108 for (int j = 0; j < height; j++) 109 { 110 energy2[i][j] = energy[j][i]; 111 picture2[i][j] = picture[j][i]; 112 } 113 } 114 115 energy = energy2; 116 picture = picture2; 117 } 118 119 public int[] findHorizontalSeam() // sequence of indices for horizontal seam 120 { 121 transpose(); 122 int[] array = findVerticalSeam(); 123 transpose(); 124 return array; 125 } 126 127 public int[] findVerticalSeam() // sequence of indices for vertical seam 128 { 129 int[] seam = new int[height]; 130 double[][] distTo = new double[width][height]; 131 int[][] edgeTo = new int[width][height]; 132 133 for (int i = 0; i < width; i++) 134 { 135 for (int j = 0; j < height; j++) 136 { 137 if (j == 0) distTo[i][j] = energy[i][j]; 138 else distTo[i][j] = Double.POSITIVE_INFINITY; 139 } 140 } 141 for (int j = 0; j < height - 1; j++) 142 { 143 for (int i = 0; i < width; i++) 144 { 145 relaxvertical(distTo, edgeTo, i, j); 146 } 147 } 148 149 double min = Double.MAX_VALUE; 150 int minIndex = 0; 151 for (int i = 0; i < width; i++) 152 { 153 if (distTo[i][height - 1] < min) 154 { 155 min = distTo[i][height - 1]; 156 minIndex = i; 157 } 158 } 159 160 seam[height - 1] = minIndex; 161 for (int j = height - 2; j >= 0; j--) 162 { 163 seam[j] = edgeTo[seam[j + 1]][j + 1]; 164 } 165 166 return seam; 167 } 168 169 public void removeHorizontalSeam(int[] seam) // remove horizontal seam from current picture 170 { 171 checkSeam(seam); 172 173 int min = Integer.MAX_VALUE; 174 int max = 0; 175 176 for (int i = 0; i < width; i++) 177 { 178 if (seam[i] > max) max = seam[i]; 179 if (seam[i] < min) min = seam[i]; 180 181 for (int j = seam[i]; j < height - 1; j++) 182 { 183 picture[i][j] = picture[i][j + 1]; 184 } 185 } 186 187 height--; 188 if (min > 0) min--; 189 if (max > height - 1) max = height - 1; 190 191 for (int i = 0; i < width; i++) 192 { 193 for (int j = min; j <= max; j++) 194 energy[i][j] = computeEnergy(i, j); 195 for (int j = max + 1; j < height - 1; j++) 196 energy[i][j] = energy[i][j + 1]; 197 } 198 199 } 200 201 private void checkSeam(int[] seam) 202 { 203 if (height <= 1 || seam == null || seam.length != width) 204 throw new IllegalArgumentException(); 205 for (int i = 0; i < width; i++) 206 { 207 if (seam[i] < 0 || seam[i] > height - 1) 208 throw new IllegalArgumentException(); 209 if (i > 0 && Math.abs(seam[i] - seam[i - 1]) > 1) 210 throw new IllegalArgumentException(); 211 } 212 } 213 public void removeVerticalSeam(int[] seam) // remove vertical seam from current picture 214 { 215 transpose(); 216 removeHorizontalSeam(seam); 217 transpose(); 218 } 219 }