1、栈Stack,栈也是一种线性结构,相比数组,栈对应的操作是数组的子集。栈只能从一端添加元素,也只能从同一端取出元素,这一端称为栈顶。栈是一种先进后出的或者后进先出的数据结构,也称为Last In First Out(LIFO)。
2、封装的数组的代码,可以实现增加,修改,删除,查询,动态扩容,缩容,判断是否为空等等方法。
1 package com.company; 2 3 4 /** 5 * 6 */ 7 public class Array<E> { 8 9 private E[] data;//定义数组 10 private int size;//数组实际的长度 11 12 /** 13 * 构造函数,传入数组的容量capacity构造Array 14 * 15 * @param capacity 初始化数据的容量长度 16 */ 17 public Array(int capacity) { 18 // 使用泛型,可以创建任意类型的数组类型 19 data = (E[]) new Object[capacity]; 20 // 初始化数组的实际内容的长度为0 21 size = 0; 22 } 23 24 /** 25 * 无参数的构造函数,默认数组的容量capacity=10 26 */ 27 public Array() { 28 // 无参构造函数,指定初始化数组容量长度为0 29 this(10); 30 } 31 32 /** 33 * 获取数组中的元素个数 34 * 35 * @return 36 */ 37 public int getSize() { 38 // 获取到数组中的元素个数 39 return size; 40 } 41 42 /** 43 * 获取数组的容量 44 * 45 * @return 46 */ 47 public int getCapacity() { 48 // 获取到数组的容量 49 return data.length; 50 } 51 52 /** 53 * 返回数组是否为空 54 * 55 * @return 56 */ 57 public boolean isEmpty() { 58 // 判断数组的长度是否为空 59 return size == 0; 60 } 61 62 63 /** 64 * 向所有元素后添加一个新元素 65 * 66 * @param e 67 */ 68 public void addLast(E e) { 69 // if (size == data.length) { 70 // throw new IllegalArgumentException("AddLast failed,Array is full......"); 71 // } else { 72 // 在数组的末尾添加一个元素 73 // data[size] = e; 74 // 添加元素以后数组长度加一 75 // size++; 76 // } 77 78 // 调用公共的方法,其实就是在数组的实际长度后面添加指定的元素的。 79 80 // 调用添加数组的方法,参数一,传入数组实际的长度,参数二,添加的元素 81 add(size, e); 82 } 83 84 /** 85 * 在第一个位置添加元素,在所有元素前添加一个新元素 86 * 87 * @param e 88 */ 89 public void addFirst(E e) { 90 // 在数组的第一个位置添加元素 91 add(0, e); 92 } 93 94 95 /** 96 * 在第index个位置插入一个新元素e 97 * 98 * @param index 在第index个位置 99 * @param e 添加的元素内容 100 */ 101 public void add(int index, E e) { 102 // 判断位置index是否小于0或者索引index是否大于数组的实际长度size 103 if (index < 0 || index > size) { 104 throw new IllegalArgumentException("add failed,Require index >= 0 and index <= size......."); 105 } 106 107 // 判断实际长度size是否等于数组的长度 108 if (size == data.length) { 109 // 可以直接抛出异常 110 // throw new IllegalArgumentException("add failed,Array is full......"); 111 // 调用数组扩容的方法,扩容的长度为元数组长度的2倍 112 resize(2 * data.length); 113 } 114 115 // 添加元素,就是在最后一个位置,将元素添加进去,即size的位置 116 // 从后向前移动,从后面的元素向后移动 117 // 如果传入的index是size,则初始化的位置是size-1,那么i的值大于等于传入的index(即size的值),i递减 118 119 // int i = size - 1是数组的实际长度的,即最后一个位置的元素 120 // i >= index,是在指定索引位置添加索引 121 // i--是为了将前面的元素向后面移动的。 122 for (int i = size - 1; i >= index; i--) { 123 // 后一个索引位置赋予前一个索引位置的值 124 data[i + 1] = data[i]; 125 } 126 // 将元素的内容插入到数组的index索引位置 127 data[index] = e; 128 // 数组的size递增 129 size++; 130 } 131 132 /** 133 * 私用扩容,扩容数组的长度 134 * 135 * @param newCapacity 136 */ 137 private void resize(int newCapacity) { 138 // 使用泛型创建对象,使用new Object的方法创建泛型的对象 139 E[] newData = (E[]) new Object[newCapacity]; 140 // 循环,将原数组里面的内容放入到新数组里面,新数组的长度为元素组的2倍 141 for (int i = 0; i < size; i++) { 142 // 将原数组的值赋值给新数组的值 143 newData[i] = data[i]; 144 } 145 // 将原数组的值指向新数组,此操作,不影响原数组的正常使用 146 data = newData; 147 } 148 149 /** 150 * 获取Index索引位置的元素 151 * 152 * @param index 153 * @return 154 */ 155 public E get(int index) { 156 // 如果索引index小于0或者索引的长度大于等于实际长度size,则抛出异常 157 if (index < 0 || index >= size) { 158 throw new IllegalArgumentException("add failed,Index is illegal......"); 159 } 160 // 返回指定索引位置的数组元素 161 return data[index]; 162 } 163 164 /** 165 * 获取到动态数组的第一个元素 166 * 167 * @return 168 */ 169 public E getFirst() { 170 return get(0); 171 } 172 173 /** 174 * 获取到数组的最后一个元素 175 * 176 * @return 177 */ 178 public E getLast() { 179 // 调用获取元素的方法 180 // 避免使用return data[size - 1],可能会出现size=0的情况 181 return get(size - 1); 182 } 183 184 /** 185 * 修改index索引位置的元素e 186 * 187 * @param index 188 */ 189 public void set(int index, E e) { 190 // 如果索引index小于0或者索引的长度大于等于实际长度size,则抛出异常 191 if (index < 0 || index >= size) { 192 throw new IllegalArgumentException("add failed,Index is illegal......"); 193 } 194 // 则将元素放入到数组的索引index位置 195 data[index] = e; 196 } 197 198 199 /** 200 * @return 201 */ 202 @Override 203 public String toString() { 204 // 封装数组遍历的内容 205 StringBuilder sb = new StringBuilder(); 206 sb.append(String.format("Array : size = %d,capacity = %d\n", size, data.length)); 207 sb.append('['); 208 for (int i = 0; i < size; i++) { 209 sb.append(data[i]); 210 if (i != size - 1) { 211 sb.append(", "); 212 } 213 } 214 sb.append(']'); 215 return sb.toString(); 216 } 217 218 /** 219 * 查找数据中是否包含元素e 220 * 221 * @param e 222 * @return 223 */ 224 public boolean contains(E e) { 225 // 查看是否包含元素,进行遍历 226 for (int i = 0; i < size; i++) { 227 // 如果数组元素等于传入的元素e 228 if (data[i].equals(e)) { 229 // 返回true 230 return true; 231 } 232 } 233 return false; 234 } 235 236 /** 237 * 查找数组中元素e所在的索引,如果不存在元素e,则返回-1 238 * 239 * @param e 240 * @return 241 */ 242 public int find(E e) { 243 // 查看是否包含元素,进行遍历 244 for (int i = 0; i < size; i++) { 245 // 如果数组元素等于传入的元素e 246 if (data[i].equals(e)) { 247 // 返回该索引位置的索引 248 return i; 249 } 250 } 251 return -1; 252 } 253 254 /** 255 * 从数组中删除index位置的元素,返回删除的元素 256 * 257 * @param index 此参数是索引参数 258 * @return 259 */ 260 public E remove(int index) { 261 // 如果索引位置小于等于0或者索引位置大于等于数组的实际长度size 262 if (index < 0 || index >= size) { 263 throw new IllegalArgumentException("remove failed,Index is illegal......"); 264 } 265 266 // 返回删除的元素,先获取到要删除的元素 267 E result = data[index]; 268 // System.out.println(result); 269 270 // 初始化值是要删除索引的位置加1,且i的值小于实际size的大小,i递减 271 272 // int i = index + 1,传入进去的删除索引位置的元素 273 // i < size,i小于数组的实际长度 274 // i++,i递增 275 for (int i = index + 1; i < size; i++) { 276 // 将数组元素的值赋值被删除元素的位置上面 277 // 即将数组元素向前移动,即第i位置的索引的数据移动到第i-1位置索引的数据 278 data[i - 1] = data[i]; 279 } 280 // 删除元素以后,数组长度size递减1 281 size--; 282 // 将不可访问的位置置空 283 data[size] = null; 284 285 // 避免出现复杂度震荡 286 // 删除数组长度,缩小容量 287 // data.length / 2 != 0,避免出现创建数组长度为0的数组 288 if (size == data.length / 4 && data.length / 2 != 0) { 289 // 缩容数组的长度 290 resize(data.length / 2); 291 } 292 return result; 293 } 294 295 /** 296 * 删除数组的第一个元素的值 297 * <p> 298 * 从删除中删除第一个元素,返回删除的元素 299 * 300 * @return 301 */ 302 public E removeFirst() { 303 // 删除数组的第一个位置的元素 304 return remove(0); 305 } 306 307 /** 308 * 从数组中删除最后一个元素,返回删除的元素。 309 * 310 * @return 311 */ 312 public E removeLast() { 313 // 删除数组最后一个位置的元素 314 return remove(size - 1); 315 } 316 317 /** 318 * 从数组中删除元素e 319 * 320 * @param e 321 */ 322 public void removeElement(E e) { 323 // 查看数组里面是否有该元素 324 int index = find(e); 325 // 如果查找到存在该元素 326 if (index != -1) { 327 // 调用删除数组元素的方法 328 remove(index); 329 } 330 } 331 332 public static void main(String[] args) { 333 // 数组添加元素 334 Array<Integer> array = new Array<>(20); 335 for (int i = 0; i < 10; i++) { 336 array.addLast(i); 337 } 338 339 System.out.println(array.toString()); 340 341 // 在指定位置添加元素 342 array.add(1, 110); 343 System.out.println(array.toString()); 344 345 // 在第一个位置添加元素 346 array.addFirst(-1); 347 System.out.println(array.toString()); 348 349 // 修改index索引位置的元素e 350 array.set(1, 120); 351 System.out.println(array.toString()); 352 353 // 是否包含某个元素 354 boolean contains = array.contains(9); 355 System.out.println(contains); 356 357 // 删除指定索引位置的元素 358 array.remove(2); 359 System.out.println(array); 360 361 // 删除第一个索引位置的元素 362 array.removeFirst(); 363 System.out.println(array); 364 365 // 删除最后一个位置的元素 366 array.removeLast(); 367 System.out.println(array); 368 369 // 从数组中删除元素e 370 array.removeElement(110); 371 System.out.println(array); 372 } 373 374 }
3、使用泛型,可以接受任何数据类型的。声明栈的接口。
1 package com.stack; 2 3 /** 4 * @param <E> 使用泛型,可以接受任何数据类型的 5 */ 6 public interface Stack<E> { 7 8 /** 9 * 获取到栈里面的大小 10 * 11 * @return 12 */ 13 public int getSize(); 14 15 /** 16 * 判断栈是否为空 17 * 18 * @return 19 */ 20 public boolean isEmpty(); 21 22 /** 23 * 向栈中添加一个元素,即入栈 24 * 25 * @param e 26 */ 27 public void push(E e); 28 29 /** 30 * 从栈中取出栈顶的元素,即出栈 31 * 32 * @return 33 */ 34 public E pop(); 35 36 /** 37 * 查看栈顶的元素 38 * 39 * @return 40 */ 41 public E peek(); 42 43 }
创建实现Stack自定义接口栈的类,ArrayStack栈,结合创建的Array<E>动态数组来实现栈的入栈,出栈,查看栈顶元素,判断栈是否为空等等操作。
1 package com.stack; 2 3 import com.company.Array; 4 5 /** 6 * 时间复杂度都是O(1)的 7 * <p> 8 * <p> 9 * 1)、void push(E),时间复杂度0(1),均摊时间复杂度。 10 * 2)、E pop(),时间复杂度0(1),均摊时间复杂度。 11 * 3)、E peek(),时间复杂度0(1)。 12 * 4)、int getSize(),时间复杂度0(1)。 13 * 5)、boolean isEmpty(),时间复杂度0(1)。 14 * <p> 15 * <p> 16 * 栈的应用。 17 * 1)、undo操作,编辑器。 18 * 2)、系统调用栈-操作系统。 19 * 3)、括号匹配-编辑器。 20 * 21 * @param <E> 22 */ 23 public class ArrayStack<E> implements Stack<E> { 24 25 public Array<E> array; 26 27 // Fn + Alt + Insert快捷键弹出构造函数 28 29 /** 30 * @param array 31 */ 32 public ArrayStack(Array<E> array) { 33 this.array = array; 34 } 35 36 /** 37 * @param capacity 动态数组的容量大小 38 */ 39 public ArrayStack(int capacity) { 40 // 初始化动态数组的初始化大小 41 array = new Array<>(capacity); 42 } 43 44 /** 45 * 无参的栈构造函数,创建一个默认容量大小的动态数组 46 */ 47 public ArrayStack() { 48 array = new Array<>(); 49 } 50 51 @Override 52 public int getSize() { 53 // 直接返回动态数组的大小即可 54 return array.getSize(); 55 } 56 57 @Override 58 public boolean isEmpty() { 59 // // 调用动态数组的是否为空,如果为空,则返回为空 60 // boolean empty = array.isEmpty(); 61 // if (empty) { 62 // return true; 63 // } 64 // return false; 65 return array.isEmpty(); 66 } 67 68 @Override 69 public void push(E e) { 70 // 调用向动态数组的末尾添加元素即可 71 array.addLast(e); 72 } 73 74 @Override 75 public E pop() { 76 // 从栈顶中取出元素 77 E e = array.removeLast(); 78 // 并将从栈顶中获取的元素取出,即移除栈顶中的元素 79 return e; 80 } 81 82 @Override 83 public E peek() { 84 // 获取栈顶的元素 85 return array.getLast(); 86 } 87 88 /** 89 * 判断栈的容量大小 90 * 91 * @return 92 */ 93 public int getCapacity() { 94 // 直接调用获取数组的容量大小 95 return array.getCapacity(); 96 } 97 98 /** 99 * 代码格式化快捷键Ctrl + Alt + l 100 * <p> 101 * 弹出构造方法的快捷键 102 * 103 * @return 104 */ 105 @Override 106 public String toString() { 107 StringBuffer sb = new StringBuffer(); 108 sb.append("Stack:"); 109 sb.append('['); 110 for (int i = 0; i < array.getSize(); i++) { 111 sb.append(array.get(i)); 112 if (i != array.getSize() - 1) { 113 sb.append(", "); 114 } 115 } 116 sb.append("] top"); 117 return sb.toString(); 118 } 119 120 public static void main(String[] args) { 121 // 声明一个ArrayStack的对象 122 ArrayStack<Integer> arrayStack = new ArrayStack<>(20); 123 for (int i = 0; i < 10; i++) { 124 // 入栈操作 125 arrayStack.push(i); 126 System.out.println(arrayStack.toString()); 127 } 128 129 // 出栈操作 130 arrayStack.pop(); 131 System.out.println("出栈操作: " + arrayStack.toString()); 132 133 // 查看栈的容量 134 int capacity = arrayStack.getCapacity(); 135 System.out.println("查看栈的容量: " + capacity); 136 137 // 获取到栈的大小 138 int size = arrayStack.getSize(); 139 System.out.println("获取到栈的大小: " + size); 140 141 // 获取到栈顶的元素内容 142 Integer peek = arrayStack.peek(); 143 System.out.println("获取到栈顶的元素内容: " + peek); 144 } 145 146 }
4、leetcode的一道算法题,结合栈实现的。题目如下所示:
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。有效字符串需满足:
1、左括号必须用相同类型的右括号闭合。
2、左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
1 package com.leetcode; 2 3 import java.util.Stack; 4 5 /** 6 * 栈顶元素反映了在嵌套的层次关系中,最近的需要匹配的元素。 7 */ 8 public class Solution { 9 10 public boolean isValid(String s) { 11 // 创建一个栈Stack对象 12 Stack<Character> stack = new Stack<>(); 13 // 遍历字符串 14 for (int i = 0; i < s.length(); i++) { 15 // 获取到每一个字符串的字符 16 char c = s.charAt(i); 17 // 判断,如果字符是左侧符号类型(、{、[这三类字符串,就将这三类字符串入栈操作 18 if (c == '(' || c == '{' || c == '[') { 19 // 如果是这三类字符,就进行入栈操作 20 stack.push(c); 21 } else { 22 // 否则,该字符是右侧的括号类型)、}、] 23 // 判断栈顶的字符和该字符是否匹配。 24 25 // 首先判断栈是否为空,如果为空,直接返回false 26 if (stack.isEmpty()) { 27 return false; 28 } else { 29 // 获取到栈顶的元素,开始进行判断栈顶的元素和该字符是否匹配 30 // stack.pop()方法调用就是将栈顶元素出栈的哦!,切记 31 char topChar = stack.pop(); 32 33 // 如果字符是右侧的)字符,并且栈顶的元素不等于左侧的(符号 34 if (c == ')' && topChar != '(') { 35 return false; 36 } 37 // 如果字符是右侧的]字符,并且栈顶的元素不等于左侧的[符号 38 if (c == ']' && topChar != '[') { 39 return false; 40 } 41 // 如果字符是右侧的}字符,并且栈顶的元素不等于左侧的{符号 42 if (c == '}' && topChar != '{') { 43 return false; 44 } 45 46 } 47 } 48 49 } 50 51 // 最后判断栈是否为空,如果不为空,则直接返回true,否则返回false 52 return stack.isEmpty(); 53 } 54 55 public static void main(String[] args) { 56 // 创建一个Solution对象 57 Solution solution = new Solution(); 58 // 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。 59 // 60 // 有效字符串需满足: 61 // 62 // 1、左括号必须用相同类型的右括号闭合。 63 // 2、左括号必须以正确的顺序闭合。 64 // 65 // 注意空字符串可被认为是有效字符串。 66 // String str = "()[]{}"; 67 String str = "([{}])"; 68 boolean valid = solution.isValid(str); 69 System.out.println(valid); 70 } 71 72 }
作者:别先生
博客园:https://www.cnblogs.com/biehongli/
如果您想及时得到个人撰写文章以及著作的消息推送,可以扫描上方二维码,关注个人公众号哦。