一、栈
栈是限定仅在表尾进行插入和删除操作的线性表。允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为空栈。栈又称为后进先出的线性表。其实栈可以理解成前一节讲的顺序表和单链表,只不过是不能从中间插入或者删除元素,和指针指向是相反的,由栈顶逐个指向栈底.
栈的应用:逆波兰表达式。
1.栈的实现
栈有两种实现方式:数组(顺序表)和链表。
使用数组实现比较复杂,需要限定大小,会出现栈溢出的问题。android源码的Stack就是用数组实现的。
使用链表实现比较简单,不会限定大小,内存占用较高。
链表实现的入栈操作:
代码:
s.data = e;
s.next = stack.top;
stack.top = s;
count++;
链表实现的出栈操作:
代码:
p = stack.top;
stack.top = p.next;
free(p);
stack.count--;
因为只能在尾部进行插入和删除,所以这两种方式的增删改查效率接近。
二、逆波兰表达式
1.定义
逆波兰表达式又叫做后缀表达式。在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,这种表示法也称为中缀表示。波兰逻辑学家J.Lukasiewicz于1929年提出了另一种表示表达式的方法,按此方法,每一运算符都置于其运算对象之后,故称为后缀表示。
2.用途
逆波兰表达式是一种十分有用的表达式,它将复杂表达式转换为可以依靠简单的操作得到计算结果的表达式。例如(a+b)(c+d)转换为ab+cd+。
3.实现
将中缀表达式转化为后缀表达式,需要通过栈实现,将字符入栈时需要遵循以下规则:
- 如果是数字,直接取出
- 如果栈顶为空,直接入栈
- 如果是加减乘除,则与栈顶比较,优先级高于栈顶,直接入栈;优先级低于栈顶,先把栈顶出栈,然后与新的栈顶比较;直到优先级高于栈顶,然后入栈;
- 如果是'(',直接入栈;
- 如果是')',匹配栈顶里面'(',然后把其中的运算符都出栈.
4.得到后缀表达式,如何计算出结果
1.拿到后缀,数字入栈
2.运算符号就取栈顶两个数字计算再入栈,注意计算结果时,取出的两个数字分别从右向左放到运算符两次.
三 递归
程序调用自身的编程技巧就是递归(recursion). 递归作为一种算法在程序设计语言广泛运用.一个过程或者函数在其定义或说明中有直接或者间接调用自身的方法 他通常把一个大型的复杂的问题层层转化为原问题相似规模较小的问题来解决求解.递归的策略只需要少量程序就可以描述解题过程中所需要的多次重复计算,大大地减少了程序的代码量. 调用递归方法时,方法后面的代码是内压入方法栈中,当最后一次递归不满足条件跳出递归时,在从栈顶逐一出栈.递归要有出口,不然会造成栈内存溢出
1.执行顺序:
2.一些经典案例
汉诺塔算法:
/**
* 汉诺塔问题解决方法
* @param plateNum 盘子数目
* @param start 第一个柱子
* @param middle 第二个柱子
* @param end 第三个柱子
*/
public void hanoiTower(int plateNum,int start,int middle,int end){
if (plateNum==1){
System.out.println(start+"--------"+end);
}else {
hanoiTower(plateNum-1,start,end,middle);
System.out.println(start+"--------"+end);
hanoiTower(plateNum-1,middle,start,end);
}
}
四、队列
队列是只允许在一端进行插入操作、而在另一端进行删除操作的线性表。插入的一端称为队尾,删除的一端称为队头。
1.实现
队列也有两种实现:数组和链表。
数组实现:出队复杂度高0(n);容易假溢出。
链表实现:实现简单,内存较高。
注:假溢出是指队列的头部还有空位,尾部已经满了,不再再往尾部插入元素,造成溢出的假象。
2.常见的队列
循环队列
头尾相接的顺序存储结构的队列。解决了假溢出的问题。
双端队列(Deque)
一种具有栈和队列性质的数据结构。双端队列中的元素限定从表的两端插入和删除。
应用:LinkedList/ArrayDeque/ LinkedBlockingDeque(阻塞的双向队列)
优先级队列
优先级队列和通常的栈和队列一样,只不过里面的每一个元素都有一个”优先级”,在处理的时候,首先处理优先级最高的。如果两个元素具有相同的优先级,则按照他们插入到队列中的先后顺序处理。
应用:MessageQueue