您的位置:首页 > 理论基础 > 数据结构算法

数据结构学习记录(二)-----栈与队列(基于动态数组的实现)

2019-04-24 14:07 459 查看

栈(stack):只能在一端进行插入和删除操作操作的线性表。
其中,能够进行操作的一端称“栈顶(top)”,不能操作的一端称为“栈底(bottom)”。
也就是说插入与删除元素都要从栈顶开始。所有有了后进先出这一说。
注意点:
栈顶与栈底没有绝对的硬性要求,栈的顺序存储也是使用数组依次存储栈中数据元素,也就是说在数组的两端可以自己规定。
一般而言,大多数人还是喜欢把索引小的当做栈底。
最后要提醒的是,栈结构仅仅是一种思想,可以硬性的将规范化后的数组当做栈。

废话不多说,上代码!
目录结构:这里要说的是,这里实现的栈是基于数组底层实现的,没有直接调用jdk自带。

Array.java

public class Array<E> {

private E[] data;
private int size;

// 构造函数,传入数组的容量capacity构造Array
public Array(int capacity){
data = (E[])new Object[capacity];
size = 0;
}

// 无参数的构造函数,默认数组的容量capacity=10
public Array(){
this(10);
}

// 获取数组的容量
public int getCapacity(){
return data.length;
}

// 获取数组中的元素个数
public int getSize(){
return size;
}

// 返回数组是否为空
public boolean isEmpty(){
return size == 0;
}

// 在index索引的位置插入一个新元素e
public void add(int index, E e){

if(index < 0 || index > size)
throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");

if(size == data.length)
resize(2 * data.length);

for(int i = size - 1; i >= index ; i --)
data[i + 1] = data[i];

data[index] = e;

size ++;
}

// 向所有元素后添加一个新元素
public void addLast(E e){
add(size, e);
}

// 在所有元素前添加一个新元素
public void addFirst(E e){
add(0, e);
}

// 获取index索引位置的元素
public E get(int index){
if(index < 0 || index >= size)
throw new IllegalArgumentException("Get failed. Index is illegal.");
return data[index];
}

public E getLast(){
return get(size - 1);
}

public E getFirst(){
return get(0);
}

// 修改index索引位置的元素为e
public void set(int index, E e){
if(index < 0 || index >= size)
throw new IllegalArgumentException("Set failed. Index is illegal.");
data[index] = e;
}

// 查找数组中是否有元素e
public boolean contains(E e){
for(int i = 0 ; i < size ; i ++){
if(data[i].equals(e))
return true;
}
return false;
}

// 查找数组中元素e所在的索引,如果不存在元素e,则返回-1
public int find(E e){
for(int i = 0 ; i < size ; i ++){
if(data[i].equals(e))
return i;
}
return -1;
}

// 从数组中删除index位置的元素, 返回删除的元素
public E remove(int index){
if(index < 0 || index >= size)
throw new IllegalArgumentException("Remove failed. Index is illegal.");

E ret = data[index];
for(int i = index + 1 ; i < size ; i ++)
data[i - 1] = data[i];
size --;
data[size] = null; // loitering objects != memory leak

if(size == data.length / 4 && data.length / 2 != 0)
resize(data.length / 2);
return ret;
}

// 从数组中删除第一个元素, 返回删除的元素
public E removeFirst(){
return remove(0);
}

// 从数组中删除最后一个元素, 返回删除的元素
public E removeLast(){
return remove(size - 1);
}

// 从数组中删除元素e
public void removeElement(E e){
int index = find(e);
if(index != -1)
remove(index);
}

@Override
public String toString(){

StringBuilder res = new StringBuilder();
res.append(String.format("Array: size = %d , capacity = %d\n", size, data.length));
res.append('[');
for(int i = 0 ; i < size ; i ++){
res.append(data[i]);
if(i != size - 1)
res.append(", ");
}
res.append(']');
return res.toString();
}

// 将数组空间的容量变成newCapacity大小
private void resize(int newCapacity){

E[] newData = (E[])new Object[newCapacity];
for(int i = 0 ; i < size ; i ++)
newData[i] = data[i];
data = newData;
}
}

ArrayStack.java

public class ArrayStack<E> implements Stack<E> {

private Array<E> array;

public ArrayStack(int capacity){
array = new Array<>(capacity);
}

public ArrayStack(){
array = new Array<>();
}

@Override
public int getSize(){
return array.getSize();
}

@Override
public boolean isEmpty(){
return array.isEmpty();
}

public int getCapacity(){
return array.getCapacity();
}

@Override
public void push(E e){
array.addLast(e);
}

@Override
public E pop(){
return array.removeLast();
}

@Override
public E peek(){
return array.getLast();
}

@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append("Stack: ");
res.append('[');
for(int i = 0 ; i < array.getSize() ; i ++){
res.append(array.get(i));
if(i != array.getSize() - 1)
res.append(", ");
}
res.append("] top");
return res.toString();
}
}

Stack.java

public interface Stack<E> {

int getSize();
boolean isEmpty();
void push(E e);
E pop();
E peek();
}

Main.java

public class Main {

public static void main(String[] args) {

ArrayStack<Integer> stack = new ArrayStack<>();

for(int i = 0 ; i < 5 ; i ++){
stack.push(i);
System.out.println(stack);
}

stack.pop();
System.out.println(stack);
}
}

运行结果:

队列:限定只能在一端进行插入,而在另一端进行删除的线性表。

允许插入的一端叫队尾(rear),允许删除的一端称为对头(frond)。
一般选择下标小的为队首(frond),
队列也是基于数组的一种抽象数据类型。
进队列(插入)在队尾进行,出队列只在队头进行。
frond指示的位置=队头元素位置;
rear指示的位置=队尾元素所在位置的下一个元素。

当数据元素出队时,frond=frond+1,既frond后移一位,指向新的队头元素。
当元素进队时,rear=rear+1,既新元素放在rear,在将rear的索引后移一位!

下面看下实现代码p;
目录结构:

Array.java和上面的一样,这里就不粘贴复制啦!
ArrayQueeue.java

https://github.com/tiejiang-sunyubin/LooopQueue.git

由于代码太多,上传至github,以后有长代码都会上传至github。
这里有以下几点是最为重要的。

先是进队代码!进队时,会先判断是否为队满状态,如果队满,则扩充数组。
最后不要忘记维护数组的size。

public void enqueue(E e){
if((tail + 1) % data.length == front)
resize(getCapacity() * 2);
data[tail] = e;
tail = (tail + 1) % data.length;
size ++;
}

接下来是给数组扩容代码!tail的索引正好是size的数值。

private void resize(int newCapacity){
E[] newData = (E[])new Object[newCapacity + 1];
for(int i = 0 ; i < size ; i ++)
newData[i] = data[(i + front) % data.length];
data = newData;
front = 0;
tail = size;
}

接下来是,出队代码!出队代码,设置十分巧妙,如果发现为数组的容量在1/4处到1/2处,则将数组的容量给缩小一半。

public E dequeue(){

if(isEmpty())
throw new IllegalArgumentException("Cannot dequeue from an empty queue.");

E ret = data[front];
data[front] = null;
front = (front + 1) % data.length;
size --;
if(size == getCapacity() / 4 && getCapacity() / 2 != 0)
resize(getCapacity() / 2);
return ret;
}

下面是mian执行代码!这里的执行代码,完全展现了基于动态数组的队列。

public static void main(String[] args){

LoopQueue<Integer> queue = new LoopQueue<>();
for(int i = 0 ; i < 10 ; i ++){
queue.enqueue(i);
System.out.println(queue);

if(i % 3 == 2){
queue.dequeue();
System.out.println(queue);
}
}
}

以下是运行结果,从运行结果中,可以看出入队和出队的威力。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: