[例子和习题出自数据结构(严蔚敏版), 本人使用java进行实现. 转载请注明作者和出处, 如有谬误, 欢迎在评论中指正. ]
栈的实现
栈是一种先进后出的数据结构, 首先定义了栈需要实现的接口:
1. public interface
2. /**
3. * 判断栈是否为空
4. */
5. boolean
6. /**
7. * 清空栈
8. */
9. void
10. /**
11. * 栈的长度
12. */
13. int
14. /**
15. * 数据入栈
16. */
17. boolean
18. /**
19. * 数据出栈
20. */
21. T pop();
22. }
栈的数组实现, 底层使用数组:
1. public class MyArrayStack<T> implements
2. private Object[] objs = new Object[16];
3. private int size = 0;
4.
5. @Override
6. public boolean
7. return size == 0;
8. }
9.
10. @Override
11. public void
12. // 将数组中的数据置为null, 方便GC进行回收
13. for (int i = 0; i < size; i++) {
14. null;
15. }
16. 0;
17. }
18.
19. @Override
20. public int
21. return
22. }
23.
24. @Override
25. public boolean
26. // 判断是否需要进行数组扩容
27. if
28. resize();
29. }
30. objs[size++] = data;
31. return true;
32. }
33.
34. /**
35. * 数组扩容
36. */
37. private void
38. new Object[objs.length * 3 / 2 + 1];
39. for (int i = 0; i < size; i++) {
40. temp[i] = objs[i];
41. null;
42. }
43. objs = temp;
44. }
45.
46. @SuppressWarnings("unchecked")
47. @Override
48. public
49. if (size == 0) {
50. return null;
51. }
52. return
53. }
54.
55. @Override
56. public
57. new
58. "MyArrayStack: [");
59. for (int i = 0; i < size; i++) {
60. sb.append(objs[i].toString());
61. if (i != size - 1) {
62. ", ");
63. }
64. }
65. "]");
66. return
67. }
- }
栈的链表实现, 底层使用链表:
1. public class MyLinkedStack<T> implements
2. /**
3. * 栈顶指针
4. */
5. private
6. /**
7. * 栈的长度
8. */
9. private int
10.
11. public
12. null;
13. 0;
14. }
15.
16. @Override
17. public boolean
18. return size == 0;
19. }
20.
21. @Override
22. public void
23. null;
24. 0;
25. }
26.
27. @Override
28. public int
29. return
30. }
31.
32. @Override
33. public boolean
34. new
35. node.data = data;
36. node.pre = top;
37. // 改变栈顶指针
38. top = node;
39. size++;
40. return true;
41. }
42.
43. @Override
44. public
45. if (top != null) {
46. Node node = top;
47. // 改变栈顶指针
48. top = top.pre;
49. size--;
50. return
51. }
52. return null;
53. }
54.
55. /**
56. * 将数据封装成结点
57. */
58. private final class
59. private
60. private
61. }
62. }
两种实现的比较, 主要比较数据入栈和出栈的速度:
1. @Test
2. public void
3. new
4. int num = 10000000;
5. long
6. for (int i = 0; i < num; i++) {
7. new Person("xing", 25));
8. }
9. long
10. "push time: "
11. while (stack.pop() != null)
12. ;
13. "pop time: "
14. }
MyArrayStack中入栈和出栈10,000,000条数据的时间:
push time: 936
pop time: 47
将MyArrayStack改为MyLinkedStack后入栈和出栈的时间:
push time: 936
pop time: 126
可见两者的入栈速度差不多, 出栈速度MyArrayStack则有明显的优势.
为什么测试结果是这样的? 可能有些朋友的想法是数组实现的栈应该具有更快的遍历速度, 但增删速度应该比不上链表实现的栈才对. 但是栈中数据的增删具有特殊性: 只在栈顶入栈和出栈. 也就是说数组实现的栈在增加和删除元素时并不需要移动大量的元素, 只是在数组扩容时需要进行复制. 而链表实现的栈入栈和出栈时都需要将数据包装成Node或者从Node中取出数据, 还需要维护栈顶指针和前驱指针.
栈的应用举例
1. 将10进制正整数num转换为n进制
1. private String conversion(int num, int
2. new
3. Integer result = num;
4. while (true) {
5. // 将余数入栈
6. myStack.push(result % n);
7. result = result / n;
8. if (result == 0) {
9. break;
10. }
11. }
12. new
13. // 按出栈的顺序倒序排列即可
14. while ((result = myStack.pop()) != null) {
15. sb.append(result);
16. }
17. return
18. }
2. 检验符号是否匹配. '['和']', '('和')'成对出现时字符串合法. 例如"[][]()", "[[([]([])()[])]]"是合法的; "([(])", "[())"是不合法的.
遍历字符串的每一个char, 将char与栈顶元素比较. 如果char和栈顶元素配对, 则char不入栈, 否则将char入栈. 当遍历完成时栈为空说明字符串是合法的.
1. public boolean
2. new
3. char[] arr = str.toCharArray();
4. for (char
5. Character temp = myStack.pop();
6. // 栈为空时只将c入栈
7. if (temp == null) {
8. myStack.push(c);
9. }
10. // 配对时c不入栈
11. else if (temp == '[' && c == ']') {
12. }
13. // 配对时c不入栈
14. else if (temp == '(' && c == ')') {
15. }
16. // 不配对时c入栈
17. else
18. myStack.push(temp);
19. myStack.push(c);
20. }
21. }
22. return
23. }
3. 行编辑: 输入行中字符'#'表示退格, '@'表示之前的输入全都无效.
使用栈保存输入的字符, 如果遇到'#'就将栈顶出栈, 如果遇到@就清空栈. 输入完成时将栈中所有字符出栈后反转就是输入的结果:
1. private
2. new
3. char[] arr = input.toCharArray();
4. for (char
5. if (c == '#') {
6. myStack.pop();
7. else if (c == '@') {
8. myStack.clear();
9. else
10. myStack.push(c);
11. }
12. }
13.
14. new
15. null;
16. while ((temp = myStack.pop()) != null) {
17. sb.append(temp);
18. }
19. // 反转字符串
20. sb.reverse();
21. return
22. }