3.栈:
-
栈是一种基于先进后出(FILO)的数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出
的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一
个数据被第一个读出来)。
-
我们称数据进入到栈的动作为压栈,数据从栈中出去的动作为弹栈
-
栈API设计:
类名 Stack 构造方法 Stack:创建Stack对象 成员方法 1.public boolean isEmpty() 2.public int size() 3.public T pop() 4.public void push(T t) 成员变量 1.private Node head:记录首结点 2.private int N:当前栈的元素个数 -
利用链表实现。入栈:在第一个结点处插入元素,出栈:依次弹出第一个结点
-
栈的链式代码实现:
public class aStack<T> implements Iterable<T> { //记录首结点 private Node head; //栈中元素的个数 private int N; public aStack(){ head = new Node(null,null); N=0; } //判断当前栈中元素个数是否为0 public boolean isEmpty(){ return N==0; } //把t元素压入栈 public void push(T t){ Node oldNext = head.next; Node node = new Node(t, oldNext); head.next = node; //入栈元素与头结点相连,入栈元素指向上一个结点 //个数+1 N++; } //弹出栈顶元素,弹出的是头结点的下一个结点 public T pop(){ Node oldNext = head.next; if (oldNext==null){ return null; } //删除首个元素 head.next = head.next.next; //个数-1 N--; return oldNext.item; } //获取栈中元素的个数 public int size(){ return N; } @Override public Iterator<T> iterator() { return new SIterator(); } private class SIterator implements Iterator<T>{ private Node n = head; @Override public boolean hasNext() { return n.next!=null; } @Override public T next() { Node node = n.next; n = n.next; return node.item; } } private class Node{ public T item; public Node next; public Node(T item, Node next) { this.item = item; this.next = next; } } } -
测试:
public class test { public static void main(String[] args) { aStack<String> stack=new aStack<>(); stack.push("a"); stack.push("b"); stack.push("c"); stack.push("d"); for (String str : stack) { System.out.print(str+" "); } System.out.println("-----------------------------"); String result = stack.pop(); System.out.println("弹出了元素:"+result); System.out.println(stack.size()); } }
1.括号匹配问题:
-
问题描述:
给定一个字符串,里边可能包含"()"小括号和其他字符,请编写程序检查该字符串的中的小括号是否成对出现。 例如: "(上海)(长安)":正确匹配 "上海((长安))":正确匹配 "上海(长安(北京)(深圳)南京)":正确匹配 "上海(长安))":错误匹配 "((上海)长安":错误匹配 -
如果遇到 '(' ,将它存入栈中;如果遇到 ')',将 '(' 出栈。
-
两种情况错误匹配,遇到 ')' 栈中弹出的 ')' 为null;循环完后,栈中还有元素
-
括号匹配问题的代码实现:
public class BracketsMatch { public static void main(String[] args) { String str = "(上海(长安)())"; boolean match = isMatch(str); System.out.println(str+"中的括号是否匹配:"+match); } public static boolean isMatch(String str){ Stack<Character> stack= new Stack<>(); for(int i=0;i<str.length();i++){ char c=str.charAt(i); if(c=='('){ stack.push(c); } if(c==')'){ Character pop = stack.pop(); if(pop==null){ return false; } } } if(stack.size()==0) { return true; }else{ return false; } } }
2.逆波兰表达式问题:
-
逆波兰表达式求值问题是我们计算机中经常遇到的一类问题,要研究明白这个问题,首先我们得搞清楚什么是逆波
兰表达式?要搞清楚逆波兰表达式,我们得从中缀表达式说起。
-
中缀表达式:
-
中缀表达式就是我们平常生活中使用的表达式,例如:1+3*2,2-(1+3)等等,中缀表达式的特点是:二元运算符总
是置于两个操作数中间。
中缀表达式是人们最喜欢的表达式方式,因为简单,易懂。但是对于计算机来说就不是这样了,因为中缀表达式
运算顺序不具有规律性。不同的运算符具有不同的优先级,如果计算机执行中缀表达式,需要解析表达式语义,做大量的优先级相关操作。
-
-
逆波表达式(后缀表达式):
-
逆波兰表达式是波兰逻辑学家J・卢卡西维兹(J・ Lukasewicz)于1929年首先提出的一种表达式的表示方法,后缀表达式的特点:运算符总是放在跟它相关的操作数之后。
中缀表达式 逆波兰表达式 a+b ab+ a+(b-c) abc-+ a+(b-c)*d abc-d*+ a*(b-c)+d abc-*d+ -
逆波兰表达式求值问题代码实现:
将操作数压入栈中,将操作符当做条件判断
public class ReversePolishNotation { public static void main(String[] args) { //中缀表达式3*(17-15)+18/6的逆波兰表达式如下 String[] notation = {"3", "17", "15", "-", "*","18", "6","/","+"}; int result = caculate(notation); System.out.println("逆波兰表达式的结果为:"+result); } public static int caculate(String[] notaion){ Stack<Integer> stack=new Stack<>(); Integer pop1; Integer pop2; Integer result; for(String s:notaion){ switch (s){ case "+": pop1 = stack.pop(); pop2 = stack.pop(); result=pop2+pop1; stack.push(result); break; case "-": pop1 = stack.pop(); pop2 = stack.pop(); result=pop2-pop1; stack.push(result); break; case "*": pop1 = stack.pop(); pop2 = stack.pop(); result=pop2*pop1; stack.push(result); break; case "/": pop1 = stack.pop(); pop2 = stack.pop(); result=pop2/pop1; stack.push(result); break; default: stack.push(Integer.valueOf(s)); break; } } return stack.pop(); } } -
3.链式栈的实际应用:
//创建对象
Stack<String> stack=new Stack<>();
//方法:
isEmpty() 判断当前栈是否为空
peek() 获得当前栈顶元素
pop() 获得当前栈顶元素并删除
push(E object) //将元素加入栈顶
search(Object o) //查找元素在栈中的位置,由栈低向栈顶方向数
isEmpty() //判断是否为空
4.队列:
-
队列是一种基于先进先出(FIFO)的数据结构,是一种只能在一端进行插入,在另一端进行删除操作的特殊线性表,它
按照先进先出的原则存储数据,先进入的数据,在读取数据时先读被读出来。
-
队列的API设计:
类名 Queue 构建方法 Queue():创建Queue对象 成员方法 1.public boolean isEmpty() :判断队列是否为空,是返回true,否返回false 2.public int size() :获取队列中元素的个数 3.public T dequeue() :从队列中拿出一个元素 4.public void enqueue(T t) :往队列中插入一个元素 成员变量 1.private Node head:记录首结点 2.private int N:当前栈的元素个数 3.private Node last:记录最后一个结点 -
队列链表实现:
public class Queue<T> implements Iterable<T> { //记录首结点 private Node<T> head; //记录最后一个结点 private Node<T> last; //记录队列中元素的个数 private int N; public Queue() { head = new Node(null,null); last=null; N=0; } //判断队列是否为空 public boolean isEmpty(){ return N==0; } //返回队列中元素的个数 public int size(){ return N; } //向队列中插入元素t public void enqueue(T t){ if (last==null) { last = new Node(t, null); head.next = last; }else{ Node oldLast = last; last = new Node(t,null); oldLast.next=last; } //个数+1 N++; } //从队列中拿出一个元素 public T dequeue(){ if (isEmpty()){ return null; } Node oldFirst = head.next; head.next = oldFirst.next; N--; if (isEmpty()){ last=null; } return (T) oldFirst.item; } @Override public Iterator<T> iterator() { return new QIterator(); } private class QIterator implements Iterator<T>{ private Node n = head; @Override public boolean hasNext() { return n.next!=null; } @Override public T next() { Node node = n.next; n = n.next; return (T) node.item; } } //结点类 private static class Node<T> { //存储元素 public T item; //指向下一个结点 public Node next; public Node(T item, Node next){ this.item=item; this.next=next; } } } -
测试:
public class test {
public static void main(String[] args) {
Queue<String> queue = new Queue<>();
queue.enqueue("a");
queue.enqueue("b");
queue.enqueue("c");
queue.enqueue("d");
for (String str : queue) {
System.out.print(str+" ");
}
System.out.println();
System.out.println("-----------------------------");
String result = queue.dequeue();
System.out.println("出列了元素:"+result);
System.out.println(queue.size());
}
}
1.链式队列实际应用:
//创建对象
Queue<Integer> queue=new LinkedList<>();
queue.add(1); //往队列尾部插入元素
queue.poll(); //出队列
queue.size(); //队列长度
queue.isEmpty() //判断队列是否为空
queue.peek() //返回队首元素,但不出队
queue.offer(3) //往队列尾部插入元素
1.优先队列:
- 底层使用堆实现,利用堆排序的原理
-
优先队列按照其作用不同,可以分为以下两种:
- 最大优先队列:可以获取并删除队列中最大的值
- 最小优先队列:可以获取并删除队列中最小的值
1.最大优先队列:
-
最大优先队列API设计:
类名 MaxPriorityQueue 构造方法 MaxPriorityQueue(int capacity):创建容量为capacity的MaxPriorityQueue对象 成员方法 1.private boolean less(int i,int j):判断堆中索引i处的元素是否小于索引j处的元素 2.private void exch(int i,int j):交换堆中i索引和j索引处的值 3.public T delMax():删除队列中最大的元素,并返回这个最大元素 4.public void insert(T t):往队列中插入一个元素 5.private void swim(int k):使用上浮算法,使索引k处的元素能在堆中处于一个正确的位置 6.private void sink(int k):使用下沉算法,使索引k处的元素能在堆中处于一个正确的位置 7.public int size():获取队列中元素的个数 8.public boolean isEmpty():判断队列是否为空 成员变量 1.private T[] imtes : 用来存储元素的数组 2.private int N:记录堆中元素的个数 -
最大优先队列代码实现:
public class 最大优先队列<T extends Comparable<T>> {
//存储堆中的元素
private T[] items;
//记录堆中元素的个数
private int N;
public 最大优先队列(int capacity) {
items = (T[]) new Comparable[capacity+1];
N = 0;
}
//获取队列中元素的个数
public int size() {
return N;
}
//判断队列是否为空
public boolean isEmpty() {
return N == 0;
}
//判断堆中索引i处的元素是否小于索引j处的元素
private boolean less(int i, int j) {
return items[i].compareTo(items[j]) < 0;
}
//交换堆中i索引和j索引处的值
private void exch(int i, int j) {
T tmp = items[i];
items[i] = items[j];
items[j] = tmp;
}
//往堆中插入一个元素
public void insert(T t) {
items[++N] = t; swim(N);
}
//删除堆中最大的元素,并返回这个最大元素
public T delMax() {
T max = items[1];
//交换索引1处和索引N处的值
exch(1, N);
//删除最后位置上的元素
items[N] = null;
N--;
//个数-1
sink(1);
return max;
}
//使用上浮算法,使索引k处的元素能在堆中处于一个正确的位置
private void swim(int k) {
//如果已经到了根结点,就不需要循环了
while (k > 1) {
//比较当前结点和其父结点
if (less(k / 2, k)) {
//父结点小于当前结点,需要交换
exch(k / 2, k);
}
k = k / 2;
}
}
//使用下沉算法,使索引k处的元素能在堆中处于一个正确的位置
private void sink(int k) {
//如果当前已经是最底层了,就不需要循环了
while (2 * k <= N) {
//找到子结点中的较大者
int max = 2 * k;
if (2 * k + 1 <= N) {
//存在右子结点
if (less(2 * k, 2 * k + 1)) {
max = 2 * k + 1;
}
}
//比较当前结点和子结点中的较大者,如果当前结点不小,则结束循环
if (!less(k, max)) {
break;
}
//当前结点小,则交换,
exch(k, max);
k = max;
}
}
}
-
测试:
public class 最大测试 { public static void main(String[] args) { String[] arr = {"S", "O", "R", "T", "E", "X", "A", "M", "P", "L", "E"}; 最大优先队列<String> maxpq = new 最大优先队列<>(20); for (String s : arr) { maxpq.insert(s); } System.out.println(maxpq.size()); String del; while(!maxpq.isEmpty()){ del = maxpq.delMax(); System.out.print(del+","); } } }
2.最小优先队列:
- 构建大根堆,然后和尾元素交换
3.索引优先队列:
4.优先队列的实例应用:
1.创建优先队列:
//创建优先队列(自然排序)
Queue<Integer> Queue = new PriorityQueue<>();
//创建优先队列(自定义排序)
static Comparator<Integer> cmp = new Comparator<Integer>() {
public int compare(Integer e1, Integer e2) {
return e2 - e1;
}
Queue<Integer> Queue = new PriorityQueue<>(cmp);
//从大到小排序
PriorityQueue<Integer> q = new PriorityQueue<>((o1,o2)->o2-o1);
2.常用方法:
1. peek()//返回队首元素
2. poll()//返回队首元素,队首元素出队列
3. add()//添加元素
4. size()//返回队列元素个数
5. isEmpty()//判断队列是否为空,为空返回true,不空返回false
3.实例代码:
//自定义比较器,降序排列 |
| - | -------------------------------------------------------------- |
| | static Comparator<Integer> cmp = new Comparator<Integer>() { |
| | public int compare(Integer e1, Integer e2) { |
| | return e2 - e1; |
| | } |
| | }; |
| | public static void main(String[] args) { |
| | //不用比较器,默认升序排列 |
| | Queue<Integer> q = new PriorityQueue<>(); |
| | q.add(3); |
| | q.add(2); |
| | q.add(4); |
| | while(!q.isEmpty()) |
| | { |
| | System.out.print(q.poll()+" "); |
| | } |
| | /** |
| | * 输出结果 |
| | * 2 3 4 |
| | */ |
| | //使用自定义比较器,降序排列 |
| | Queue<Integer> qq = new PriorityQueue<>(cmp); |
| | qq.add(3); |
| | qq.add(2); |
| | qq.add(4); |
| | while(!qq.isEmpty()) |
| | { |
| | System.out.print(qq.poll()+" "); |
| | } |
| | /** |
| | * 输出结果 |
| | * 4 3 2 |
| | */ |
| | }