算法与数据结构的关系是密不可分的。在很多复杂的算法实现过程中,我们都会先考虑如何设计它的数据结构使得算法实现变得简单,如何组织数据能够获得最佳的性能,所以这里准备写一些算法当中使用到的基础的数据结构,它们可以说是进一步学习算法的基石。
本文首先介绍一下线性数据结构,其余数据结构后续会继续进行补充
线性数据结构的存储方式分为顺序存储和链式存储,对于顺序存储一般对应的也就是程序中的数组,链式存储又分为单向链表、双向链表和循环链表,这里以单向链表为例,单向链表节点除了存储数据本身以外还会存储一个指向下一个节点的引用,这是一种递归的数据结构,通过引用的一次次链接最终形成一个链表。具体应该如何选用数据的存储方式应该根据实现的具体实例来决定。
下面介绍一下常见的线性数据结构实例,并列举他们的实现。
1、背包
针对背包这种数据结构,我们可以这样理解,有一个漫画收藏家,他会将喜欢的漫画都装进自己的背包当中。背包可以说是最简单的数据结构,我们设计的api如下:
public void add(T t):该方法为将数据添加到背包当中
public Iterator iterator():我们的背包是一个可迭代的背包,我们会实现Iterable接口
这里我们采取单链表的方式进行存储,具体实现如下:
public class Bag<T> implements Iterable<T> {
private class Node{
T t;
Node next;
}
private Node first;
public void add(T t){
Node oldFirst = first;
Node node = new Node();
node.t = t;
node.next = oldFirst;
first = node;
}
@Override
public Iterator iterator() {
return new BagIterator();
}
private class BagIterator implements Iterator<T>{
private Node cursor = first;
@Override
public boolean hasNext() {
return cursor!=null;
}
@Override
public T next() {
T retVal = cursor.t;
cursor = cursor.next;
return retVal;
}
}
}
2、栈
栈的性质是先进后出或者说后进先出,设计的api如下:
public void push(T t):入栈操作
public T pop():出栈操作
public T peek():返回栈顶元素但并不删除
public boolean isEmpty():栈是否为空
public int size():大小
public Iterator iterator():同样栈可迭代,实现Iterable接口
这里我们采用自动扩容数组、单链表两种方式来实现:
/**
* 栈:顺序存储、自动扩容
* Created by pwy on 2017/6/30.
*/
public class ResizingArrayStack<T> implements Iterable<T> {
/** 存储栈元素 */
private T[] a = (T[]) new Object[1];
/** 元素数量 */
private int N = 0;
/**
* 栈是否为空
* @return
*/
public boolean isEmpty(){
return N == 0;
}
/**
* 返回栈的长度
* @return
*/
public int size(){
return N;
}
/**
* 重新调整栈数组的大小
* @param max 调整的长度大小
*/
private void resize(int max){
T[] temp = (T[]) new Object[max];
for(int i = 0;i/**
* 入栈操作
* @param t
*/
public void push(T t){
if(N == a.length){
resize(2*a.length);
}
a[N++] = t;
}
/**
* 出栈操作
* @return
*/
public T pop(){
//出栈的元素
T t = a[--N];
//释放元素内存
a[N] = null;
//如果栈中元素数量等于数组容量1/4时,将数组容量缩减为原值的1/2
if(N > 0 && N == a.length/4){
resize(a.length/2);
}
return t;
}
/**
* 返回栈的迭代器
* @return
*/
@Override
public Iterator iterator() {
return new ReverseArrayIterator();
}
private class ReverseArrayIterator implements Iterator<T>{
/** 元素的个数 */
private int i = N;
@Override
public boolean hasNext() {
return i > 0;
}
@Override
public T next() {
return a[--i];
}
@Override
public void remove() {
}
}
}
/**
* Created by pwy on 2017/7/3.
*/
public class Stack<T> implements Iterable<T> {
private class Node{
T t;
Node next;
}
private Node first;
private int elementCount;
public void push(T t){
Node oldFirst = first;
first = new Node();
first.next = oldFirst;
first.t = t;
elementCount++;
}
public T pop(){
Node popElm = first;
first = first.next;
elementCount--;
return popElm.t;
}
public T peek(){
return first.t;
}
public boolean isEmpty(){
return first == null;
}
public int size(){
return elementCount;
}
@Override
public Iterator iterator() {
return new StackIterator();
}
private class StackIterator implements Iterator<T>{
private int index = elementCount;
private Node iterElm = first;
@Override
public boolean hasNext() {
return index > 0;
}
@Override
public T next() {
index--;
Node retElm = iterElm;
iterElm = iterElm.next;
return retElm.t;
}
}
}
3、队列
队列的性质是先进先出,设计的api如下:
public void enqueue(T t):入列
public T dequeue():出列
public boolean isEmpty():列是否为空
public int size():列大小
public Iterator iterator():可迭代,实现Iterable接口
同样使用自动扩容数组、单链表两种方式来实现:
/**
* Created by pwy on 2017/7/12.
*/
public class ResizingArrayQueue<T> implements Iterable<T> {
private T[] queueElements = (T[])new Object[1];
/** 队尾索引 */
private int tailIndex = -1;
private int size = 0;
public int size(){
return size;
}
public boolean isEmpty(){
return size == 0;
}
public void enqueue(T t){
if(queueElements.length <= tailIndex + 1){
resizeQueue(queueElements.length*2);
}
queueElements[++tailIndex] = t;
size++;
}
public T dequeue(){
if(queueElements.length/4 >= tailIndex + 1){
resizeQueue(queueElements.length/2);
}
size--;
tailIndex--;
T popElement = queueElements[0];
if(queueElements.length > 1){
for(int i = 1;i1] = queueElements[i];
}
}
return popElement;
}
private void resizeQueue(int directionSize){
T[] newQueueElements = (T[])new Object[directionSize];
for(int i = 0;i@Override
public Iterator iterator() {
return new QueueIterator();
}
private class QueueIterator implements Iterator<T>{
private int tailIndex = ResizingArrayQueue.this.tailIndex;
private int currIndex = -1;
@Override
public boolean hasNext() {
return this.currIndex + 1 <= tailIndex;
}
@Override
public T next() {
return queueElements[++this.currIndex];
}
}
}
/**
* Created by pwy on 2017/7/10.
*/
public class Queue<T> implements Iterable<T> {
private class Node{
T t;
Node next;
}
/** 队首 */
private Node first;
/** 队尾 */
private Node last;
private int size;
public boolean isEmpty(){
return size == 0;
}
public void enqueue(T t){
Node oldLast = last;
last = new Node();
last.t = t;
if(isEmpty()){
first = last;
}else{
oldLast.next = last;
}
size++;
}
public T dequeue(){
T t = first.t;
first = first.next;
if(isEmpty()){
last = null;
}
size--;
return t;
}
@Override
public Iterator iterator() {
return new QueueIterator();
}
private class QueueIterator implements Iterator<T>{
private int index = size;
private Node cursor = first;
@Override
public boolean hasNext() {
return size > 0;
}
@Override
public T next() {
T retVal = cursor.t;
cursor = cursor.next;
index--;
return retVal;
}
}
}
4、单向链表
这里对单向链表进行一些封装,使其支持一些操作:
public void addFirst(String s):在链表头部添加节点
public void addLast(String s):在链表结尾添加节点
public void removeFirst():删除头节点
public void removeLast():删除尾节点
public boolean isEmpty():链表是否为空
具体实现如下:
/**
* Created by pwy on 2017/7/13.
*/
public class SingleLinkTable {
private class Node{
String data;
Node next;
}
private Node first = null;
public void printNode(){
Node cur = first;
while(cur != null){
System.out.println(cur.data);
cur = cur.next;
}
}
public boolean isEmpty(){
return first == null;
}
public void removeFirst(){
if(isEmpty()){
// do nth
}else{
first = first.next;
}
}
public void removeLast(){
if(isEmpty()){
// do nth
}else if(first.next == null){
first = null;
}else{
Node cache = first;
while (true){
if(cache.next.next == null){
cache.next = null;
break;
}
cache = cache.next;
}
}
}
public void addFirst(String s){
Node n = new Node();
n.data = s;
if(isEmpty()){
first = n;
}else{
Node cache = first;
first = n;
n.next = cache;
}
}
public void addLast(String s){
Node n = new Node();
n.data = s;
if(isEmpty()){
first = n;
}else{
Node cache = first;
while(true){
if(cache.next == null){
cache.next = n;
break;
}
cache = cache.next;
}
}
}
}
本文给出的示例,有一定参考作用,但并非一定是最佳实现,双向链表和循环链表这里就不实现了,先写到这里吧。
另外同时文章也在我自己的博客进行更新,欢迎大家访问^_^:http://e-pingtai.com/html/35d936bc4dac47d4bc66e8af4a4504a7.html