数据结构之栈

爱⌒轻易说出口 提交于 2020-02-26 20:10:46

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/

如果您想及时得到个人撰写文章以及著作的消息推送,可以扫描上方二维码,关注个人公众号哦。

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!