前言
草草的学完了Java,就该实现了,不过还是从基层开始学习实现吧,首先是数据结构。
看的是 算法导论 这本书,然后打算首先从数据结构【书本第三部分,10~14章】入手,看懂的前提下,运用Java去实现。
基本数据结构
本章讨论如何通过使用指针【Java中即引用】的简单数据结构来表示动态集合。
栈和队列
栈(stack) 实现的是一种后进先出(last-in,first-out,LIFO) 策略。
队列(queue) 实现的是一种 先进先出(first-in,first-out,FIFO) 策略。
- 栈
- 栈上的INSERT【插入】操作称为压入(PUSH) ,DELETE【删除】操作称为弹出(POP) 。
- 有一个属性s.top指向最新插入的元素。
- 空(empty)、下溢(underflow)、上溢(overflow)
伪代码:
STACK-EMPTY(S)
if S.top == 0
return TRUE
else return FALSE
PUSH(S,x)
S.top = S.top +1
S[S.top] = x
POP(S)
if STACK_EMPTY(S)
error"undeflow"
else S.top = S.top-1
return S[S.top + 1]
三种栈操作的执行时间都为O(1)
Java实现:
package data_structures.realize;
import java.util.*;
public class Stack {
private int length = 0;// 栈容量
private int top = -1;// 栈顶,指向最新插入的元素
private ArrayList<Integer> stack = new ArrayList<>();
Stack(int length) {
this.length = length;
}
int getLength() {
return length;
}
boolean isStackEmpty() {//下溢
if (top == -1)
return true;
else
return false;
}
boolean isStackFull() { //上溢
if (top == length-1)
return true;
else
return false;
}
void push(int x) {
if (this.isStackFull()) {
System.out.println("overflow");
} else {
top++;
stack.add(x);
}
}
Integer pop() {
if (this.isStackEmpty()) { // 下溢
System.out.println("underflow");
return (Integer) null;
} else {
top--;
Integer a = stack.get(top+1);
stack.remove(top+1);
return a;
}
}
public static void main(String[] args) {
Stack s = new Stack(3);
s.pop();
s.push(5);
System.out.println(s.pop());
s.push(2);
s.push(0);
s.push(10);
s.push(1);
System.out.println(s.pop());
System.out.println(s.pop());
System.out.println(s.pop());
System.out.println(s.pop());
}
}
代码中的栈是定位为用来存储整型的,如果有其他型,可以把Integer改了,或者重构方法。
这里用的是ArrayList可变长度的数组,好像有点改了栈的一定长度的初衷了,可以试下用Integer数组,不用int数组的原因是因为当数组为空时不知道该返回什么。。(int不能为null)
- 队列
-
队列上的INSERT操作成为入队(ENQUEUE),DELETE操作称为出队(DEQUEUE);
-
队列有一个属性Q.head指向队头元素,而属性Q.tail则指向下一个新元素将要插入的位置。
-
注意:这里用的队列是环形队列。当Q.head=Q.tail 时,队列为空,初始时有Q.head=Q.tail=1。 当Q.head=Q.tail时,队列是满的。(书上的Q.head = Q.tail+1错了)
!!!注意:书写错了(算法导论也错。。。),环形队列中,空和满的时候, 头尾指针都是相等的,因为尾指针指的是下一个新元素将入插入的位置, 就是说,如果整个队列都满了,最后一个元素的下一个元素将又会是head指向的元素。
伪代码:
ENQUEUE(Q,x)
Q[Q.tail] = x
if Q.tail == Q.length
Q.tail = 1
else Q.tail = Q.tail +1
DEQUEUE(Q)
x = Q[Q.head]
if Q.head = Q.length
Q.head = 1
else Q.head = Q.head + 1
return x
先贴上我的错误代码:
package data_structures.realize;
import java.util.ArrayList;
public class Queue {
private int length;
private int head = 0;
private int tail = 0;
private ArrayList<Integer> queue = new ArrayList<>();
Queue(int length) {
this.length = length;
}
boolean isEmpty() { // 下溢
// if (this.head == this.tail)
// return true;
// else
// return false;
return ((head == tail)&&queue.isEmpty());
}
boolean isFull() {
// if(this.head == this.tail + 1)
// return true;
// else
// return false;
return ((head == tail)&& !queue.isEmpty());
}
void enqueue(int x) {
if (!this.isFull()) {
queue.add(x);
if (tail == this.length-1)
tail = 0;
else
tail = tail + 1;
} else
System.out.println("overflow");
}
Integer dequeue() {
if (this.isEmpty()) {
System.out.println("underflow");
return (Integer) null;
}
else {
int x = queue.get(head);
queue.remove(head);
if (head == length-1)
head = 0;
else
head = head + 1;
return x;
}
}
public static void main(String[] args) {
Queue q = new Queue(2);
q.dequeue();
q.enqueue(3);
q.enqueue(4);
q.enqueue(6);
System.out.println(q.dequeue());
System.out.println(q.dequeue());
System.out.println(q.dequeue());
}
}
- 错误的原因在于队列不能用ArrayList去写,ArrayList的内存是动态的(其实也可以,不过这时候的head和tail就得另外算,不能直接用作ArrayList里面的索引)。因为ArrayList每次get并remove之后,那后面的元素就会自动向前排。(debug发现的)。为什么栈可以呢?是因为在栈中,每次pop或者push都是对最后一个元素进行操作,从后往前操作,不影响前面。而在队列中,每次dequeue操作的元素都是最前面的(head)元素,如果remove第一个元素,后面所有元素的索引位置都向前了一位。
所以下面就来试下用常用数组解决,正好可以和上面 的栈中用的 ArrayList比较。
package data_structures.realize;
import java.util.ArrayList;
public class Queue {
private int length;
private int head = 0;
private int tail = 0;
private Object[] queue;
Queue(int length) {
this.length = length;
queue = new Object[length];
// queue = null;
}
int getLength() {
return length;
}
boolean isEmpty() { // 下溢
// if (this.head == this.tail)
// return true;
// else
// return false;
return ((head == tail)&&(queue[head] == null));
}
boolean isFull() {
// if(this.head == this.tail + 1)
// return true;
// else
// return false;
return ((head == tail)&& (queue[head] != null));
}
void enqueue(int x) {
if (!this.isFull()) {
queue[tail] = x;
System.out.println("enqueue success");
if (tail == length-1)
tail = 0;
else
tail = tail + 1;
} else
System.out.println("overflow");
}
Object dequeue() {
if (this.isEmpty()) {
System.out.println("underflow");
return null;
}
else {
Object x = queue[head];
queue[head] = null;
if (head == length-1)
head = 0;
else
head = head + 1;
return x;
}
}
public static void main(String[] args) {
Queue q = new Queue(2);
q.dequeue();
q.enqueue(3);
q.enqueue(4);
q.enqueue(6);
System.out.println(q.dequeue());
System.out.println(q.dequeue());
System.out.println(q.dequeue());
}
}
这里面用了 Object[]数组。有一点注意事项就是,每次dequeue之后,记得要把那一位置null.
练习
10.1-2 说明如何在一个数组A[1…n]中实现两个栈,使得当两个栈的元素个数之和不为n时,两者都不会发生上溢。要求PUSH和POP操作的运行时间为O(1)
我的想法是,一个栈底从1开始,另外一个栈底从n开始,当两个的栈顶top相差为1的时候,就会产生上溢了,下面试下用Java实现。
package data_structures.exercise;
//一个数组两个栈
public class oneArraytwoStack {
private int length = 0;// 数组容量n
private int top1 = -1;// 栈顶,指向最新插入的元素
private int top2;
Object[] a;
oneArraytwoStack(int length) {
this.length = length;
this.top2 = this.length;
a = new Object[length];
}
int getLength() {
return length;
}
boolean isStack1Empty() {// 栈1下溢
// if (top1 == -1)
// return true;
// else
// return false;
return top1 == -1;
}
boolean isStack2Empty() { // 栈2下溢
return top2 == length;
}
boolean isStackFull() { // 上溢
return (top2 - top1 == 1);
}
void push(int S, int x) {
if (this.isStackFull()) {
System.out.println("overflow");
} else {
if (S == 1)
a[++top1] = x;
else
a[--top2] = x;
System.out.println("push success");
}
}
Object pop(int S) {
Object x = null;
if (S == 1) {
if (this.isStack1Empty()) // 下溢
System.out.println("stack1 underflow");
else {
top1--;
x = a[top1 + 1];
a[top1 + 1] = null;
}
} else {
if (this.isStack2Empty())
System.out.println("stack2 underflow");
else {
top2++;
x = a[top2 - 1];
a[top2 - 1] = null;
}
}
return x;
}
public static void main(String[] args) {
oneArraytwoStack s = new oneArraytwoStack(3);
s.pop(1);
s.push(1, 5);
System.out.println(s.pop(1));
s.push(2, 2);
s.push(2, 0);
s.push(1, 0);
s.push(2, 1);
System.out.println(s.pop(1));
System.out.println(s.pop(2));
System.out.println(s.pop(2));
System.out.println(s.pop(2));
}
}
本来打算pop和push的时候直接用一条语句的,但是发现要将pop的语句置Null,不能直接用++,所以还是老实改回来吧。
push和pop相比较普通的栈都多了一个参数,第一个参数,是用来区分第一个或者第二个栈的,如果想用名字的话,可以另外定义一个栈类去实现,这里从简,用1和其他数字表示第一个栈和第二个栈。
待更新