物理结构和逻辑结构
物理结构就是物质层次上的结构,是在内存中实实在在存在的存储结构。
逻辑结构是抽象的结构,不在现实中存在,主要是以来物理结构而存在。
物理结构主要就是数组和链表
逻辑结构就有顺序表、栈、队列和树、图等。
栈
栈是一种线性结构,特点是先进后出,,first in last out,FILO。既是先进者后出,后进者先出。最早进入的元素存放的位置叫栈底,最后进入的元素叫做栈顶,
栈的理解
从栈的特性上看,栈是一种受限制的线性表,只允许在一端插入和删除数组,另一端是密封的。
这样一个受限制的数据结构明显没有数组和链表灵活,但为什么还要有这个数据结构呢。
特定的数据结构是对特定场景的抽象,且数组和链表 暴露太多接口了,操作上的灵活但是使用的时候有不可控性,更容易出错。
当某个数据集合只涉及在一端插入和删除数据,且满足先进后出、后进先出的特性,这个时候首先要选择栈结构。
栈 结构的实现
package java_test;
public class MyStack {
private String[] arr;//数组
private int count;//栈元素个数
private int n ; //栈大小
public MyStack(int n){
this.arr = new String[n];
this.n = n;
this.count = 0;
}
public boolean push(String item){
if (count==n) return false;
arr[count] = item;
++count;
return true;
}
public String pop(){
if (count==0) return null;
String tmp = arr[count-1];
--count;
return tmp;
}
public void output(){
for (int i=0;i<count;++i){
System.out.println(arr[i]);
}
}
public static void main(String[] args) {
MyStack myStack = new MyStack(3);
myStack.push("A");
myStack.push("B");
myStack.push("C");
myStack.output();
System.out.println("------------------");
myStack.pop();
myStack.output();
}
}
无论是顺序栈还是链式栈,都是存储一个n的数组,在入栈出栈的时候,只需要一两个临时变量存储空间,所以空间复杂度是O(1)。空间复杂度是指除了原本的数据存储空间外,算法运行还需要额外的存储空间。
动态扩容数组栈的时间复杂度分析
扩容栈不常用,这里只是分析时间复杂度,
1.出栈的话,时间复杂度O(1)。
2.当栈还有剩余空间的时候,入栈时间复杂度也是O(1)
3.但是栈没有空间的时候,需要申请内存和数据迁移,时间复杂度变成了O(n)。
所以最好复杂度是O(1),最坏是O(n)
当当前栈大小为K,且满了,当有新数据入栈时候,需要申请2倍大小的内存,并做k个数据的迁移,然后入栈。但是剩下的K-1个数据入栈的时候,不需要申请内存和迁移数据,所以k-1次入栈都只要O(1)的复杂度。
即 k次入栈操作,总共涉及了k个数据的迁移和k个数据的入栈操作。将k个数据迁移均摊到k次入栈操作,那么没次入栈操作只要一个数据迁移和一个数据入栈操作。所以入栈操作的均摊时间复杂度就是O(1),接近O(1)。
栈的经典应用场景
栈在函数调用的应用
操作系统 给每个线程分配了一个独立的空间,这个空间是栈的结构,用于存储函数调用的临时变量。没进入一个函数,就会将临时变量作为栈帧入栈,当变量被函数执行完成后,出栈。
例如
int main() {
int a = 1;
int ret = 0;
int res = 0;
ret = add(3, 5);
res = a + ret;
printf("%d", res);
reuturn 0;
}
int add(int x, int y) {
int sum = 0;
sum = x + y;
return sum;
}
main方法先定义变量,然后调用add()函数,获取结果后然后与临时变量a相加,打印res。
链在表达式中的应用
例如34+13*9+44-12/3
编译器会实现两个栈,一个栈保存操作数的栈,一个是保存运算符的栈,
从左到右,遇到数字,压入操作数栈,遇到运算符,先与运算符栈的栈顶元素比较。
如果比栈顶元素的优先级高,压入栈中。
如果比栈顶元素的优先级低,从栈顶中取出栈顶运算符,从操作数栈中取出两个操作数,进行计算,然后把结果压入操作数栈中,继续比较。
链在括号匹配的应用
一组字符串,如何检验是否合法。
用栈来保存未匹配的左括号,从左到右扫描字符串,当扫到左括号,压入栈。
扫描到右括号,从栈顶取出左括号。
如果能匹配,则继续扫描,如果不能匹配或者栈为空,则说明是非法格式。
当所有括号扫描完成后,栈为空,则说明是合法字符串。
栈来实现浏览器的前进、后退功能
使用两个栈,x,y。 首次浏览的页面压入x,点击后退的时候,从x取出栈,再把取出的页面放入y中。
点击前进的时候,从y中取出页面,压入x中。当x没数据的时候,就没有页面可以后退,当y没数据,就没页面可以前进。