算法导论第十章之基本数据结构-Java实现

前言

草草的学完了Java,就该实现了,不过还是从基层开始学习实现吧,首先是数据结构。
看的是 算法导论 这本书,然后打算首先从数据结构【书本第三部分,10~14章】入手,看懂的前提下,运用Java去实现。

基本数据结构

本章讨论如何通过使用指针【Java中即引用】的简单数据结构来表示动态集合。

栈和队列

栈(stack) 实现的是一种后进先出(last-in,first-out,LIFO) 策略。
队列(queue) 实现的是一种 先进先出(first-in,first-out,FIFO) 策略。

  1. 栈上的INSERT【插入】操作称为压入(PUSH) ,DELETE【删除】操作称为弹出(POP)
  2. 有一个属性s.top指向最新插入的元素。
  3. 空(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)


  • 队列
  1. 队列上的INSERT操作成为入队(ENQUEUE),DELETE操作称为出队(DEQUEUE);

  2. 队列有一个属性Q.head指向队头元素,而属性Q.tail则指向下一个新元素将要插入的位置。

  3. 注意:这里用的队列是环形队列。当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和其他数字表示第一个栈和第二个栈。


待更新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值