线性表(栈、队列、链表)
1. 栈、队列、链表的比较
属性 | 栈 Stack | 队列 Queue | 链表 LinkedList |
---|---|---|---|
进出顺序 | 先进后出 | 先进先出 | 无所谓,都可以 |
增加元素 | 压栈Push(进入栈顶) | 入队Enqueue(进入队尾) | 插入Insert(插入任意位置) |
减少元素 | 弹栈Pop(取出栈顶元素) | 出队Dequeue(取出队头元素) | 删除节点Delete(从任意位置删除) |
高级操作 | 深度优先搜索(DFS) | 广度优先搜索(BFS) | -------- |
2. 栈、队列、链表的基本操作
-
栈、队列、链表有多种实现方式,比如栈可以用数组或者链表实现。考试时,肯定只要求:采用数组实现栈和队列,用节点实现链表。因此,以下均采用数组实现栈和队列,采用节点实现链表。
-
此部分虽然简单,但属于重点内容,必考。
2.1 栈
class Stack{
// 用数组实现的栈
private int data[];
// 栈顶指针,指向栈顶部的元素,因此-1 <= top <= data.length - 1
private int top;
// 构造器
public Stack(int maxSize) {
data = new int[maxSize];
top = -1; // 注意这个-1,当空栈时,栈顶指针是-1,不是0
}
// 压栈
public void push(int x) throws Exception{
// 如果栈满了,那么就不能再压栈了,操作失败
if (top == data.length-1)
throw new Exception("Expection : Stack Overflow");
// 栈顶指针上移,然后在栈顶添加元素x
data[++top] = x;
}
// 弹栈
public int pop() throws Exception{
// 如果是空栈,那么不能弹栈
if (isEmpty())
throw new Exception("演示 Expection : Stack Underflow");
// 返回栈顶元素,栈顶指针下移
return data[top--];
}
// 判空操作
public boolean isEmpty(){
return top == -1;
}
}
2.2 队列
class Queue {
// 用数组实现队列
private double[] data;
// 需要两个指针,队头指针、队尾指针
private int head;
private int tail;
// 构造器
public Queue(int size){
data = new double[size];
head = -1; // 队列为空时,head记为-1
}
/*
* 设计思想 :
* 队头指针 ↓
* 数组下标 0 1 2 3 4 5
* 队尾指针 ↑
* 此时,队列内的元素从队头到队尾依次为:1,2,3。(不包含4,注意)
*/
// 判空
boolean isEmpty(){
return head == -1;
}
// 判满
public boolean isFull(){
return head == tail;
}
public void enqueue(double e) throws Exception{
// 如果队列满了,就不能入队了
if (isFull()){
throw new Exception("Queue overFlow !!!");
}
// 如果队列为空,那么此时队头为-1,我们需要把队头和队尾初始化为0
if (isEmpty()){
head = tail = 0;
}
if (tail != data.length-1){
// 在队尾入队
data[tail++] = e;
}
else {
// 如果队尾指针触及到数组右界,那么队尾接下来指向0
data[tail] = e;
tail = 0;
}
}
// 出队
public double dequeue() throws Exception{
//空队列不能出队
if (isEmpty()){
throw new Exception("Queue underFlow!!!");
}
// 出队元素的值
double res = data[head];
if (head != data.length - 1) head ++;
// 如果队头指针触及到数组有界,那么队头接下来指向0
else head = 0;
// 如果出队后队列空了,那么令head=-1,作为空队列的标记
if (head == tail) head = -1;
return res;
}
}
2.3 双向链表
class Node {
Node prev; // 前驱指针
Node next; // 后继指针
int key; // 节点的值
}
class LinkedList {
// 头节点
// 注意,头节点不存放元素
Node nil;
// 构造器
LinkedList() {
this.nil = new Node();
this.nil.prev = nil;
this.nil.next = nil;
}
// 在index位置插入元素key
void insert(int index, int key) {
Node node = new Node();
node.key = key;
Node x = nil.next; // 头节点不存放元素,因此直接从下一个元素开始
// 通过下面这个循环,前往链表中index的位置。因此在链表中插入元素的时间复杂度是O(n)
while(index > 0){
x = x.next;
index --;
}
// 看着上面的图片,好好想一想链表连接方式的变化
// 动手画一画,想不清楚再来问,这几行代码是靠理解的
x.prev.next = node;
node.prev = x.prev;
node.next = x;
x.prev = node;
}
// 删除节点元素e
void delete(int e) throws Exception{
Node node = nil.next; // 头节点不存放元素,因此直接从下一个元素开始
while(node != nil){
if (e == node.key){
// 看着图片,好好想一想链表连接方式的变化
node.prev.next = node.next;
node.next.prev = node.prev;
return;
}
node = node.next;
}
// 如果跳出循环都没有return,那么就是找不到元素e
throw new Exception("No such an element!!!" );
}
//搜索元素e
Node search(int e){
Node cur = nil.next;
return search(nil, e);// 调用下面的private Node search()函数
}
private Node search(Node cur, int e){
if (cur.key != e && cur !=nil){
//递归搜索
return search(cur.next,e);
}
else if (cur ==nil){
//达到链表尽头,却还是没找到元素e
return null;
}
//找到元素e了,返回
else return cur;
}
}
3. DFS与BFS
在二叉树中介绍。