演算法系列(六)資料結構之表佇列和棧

robert_chao發表於2016-06-04

在http://blog.csdn.net/robertcpp/article/details/51559333一文中,我們講了排序,這一章來介紹一下基本資料結構:表、佇列、棧和它們的簡單實現

一、表ADT

1、陣列實現順序表

通過對陣列操作,來直接對錶進行增刪查改操作,這種線性表查詢某個位置的元素花費的時間為O(1),但是插入刪除元素花費的時間為O(n),如果對錶的操作更多的是訪問操作,那麼選擇這種實現更為合適。
下面是一個簡單實現
package com.algorithm.list;

import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * 線性陣列
 * 
 * @author chao
 *
 * @param <E>
 */
public class MyArrayList<E> implements Iterable<E> {
	private static final int DEFAULT_CAPACITY = 10;
	private int theSize;
	private E[] theItems;

	public MyArrayList() {
		clear();
	}

	public void clear() {
		theSize = 0;
		ensureCapacity(DEFAULT_CAPACITY);
	}

	public void ensureCapacity(int newCapacity) {
		if (newCapacity < theSize) {
			return;
		}
		E[] oldEs = theItems;
		theItems = (E[]) new Object[newCapacity];
		for (int i = 0; i < theSize; i++) {
			theItems[i] = oldEs[i];
		}

	}

	public int size() {
		return theSize;
	}

	public void trimToSize() {
		ensureCapacity(size());
	}

	public boolean isEmpty() {
		return theSize == 0;
	}

	public E get(int index) {
		if (index < 0 || index > theSize) {
			throw new ArrayIndexOutOfBoundsException();
		}
		return theItems[index];

	}

	public E set(int index, E newval) {
		if (index < 0 || index >= theSize) {
			throw new ArrayIndexOutOfBoundsException();
		}
		E oldvallue = theItems[index];
		theItems[index] = newval;
		return oldvallue;
	}

	@Override
	public Iterator<E> iterator() {
		return new ArraylistIterator();
	}

	public boolean add(E value) {
		add(size(), value);
		return true;
	}

	public void add(int index, E value) {
		if (theItems.length == theSize) {
			ensureCapacity(theSize * 2 + 1);
		}
		for (int i = theSize; i > index; i--) {
			theItems[i] = theItems[i - 1];
		}
		theItems[index] = value;
		theSize++;
	}

	public E remove(int index) {
		E oldvalue = theItems[index];
		for (int i = index; i < theSize; i++) {
			theItems[i] = theItems[i + 1];
		}
		theSize--;
		return oldvalue;
	}

	class ArraylistIterator<E> implements Iterator<E> {
		private int current = 0;

		@Override
		public boolean hasNext() {
			return current < theSize;
		}

		@Override
		public E next() {
			if (!hasNext()) {
				throw new NoSuchElementException();
			}
			return (E) theItems[current++];
		}

		@Override
		public void remove() {
			MyArrayList.this.remove(--current);
		}

	}
}
2、連結串列
連結串列是一種插入刪除操作時間複雜度為O(1)的資料結構,但是訪問操作的時間複雜度為O(n)
下面是我們的簡單實現
package com.algorithm.list;

import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * 連結串列資料結構
 * 
 * @author chao
 *
 * @param <E>
 */
public class MyLinkedList<E> implements Iterable<E> {
	private class Node<T> {
		public Node() {

		}

		public Node(T ele, Node next, Node pre) {
			this.ele = ele;
			this.next = next;
			this.pre = pre;
		}

		T ele;
		Node next;
		Node pre;
	}

	private Node<E> head;
	private Node<E> tail;
	private int size;
	private int modcount = 0;

	public MyLinkedList() {
		clear();
	}

	public void clear() {
		head = new Node<E>(null, null, null);
		tail = new Node<E>(null, head, null);
		head.next = tail;
		size = 0;
		modcount++;

	}

	@Override
	public Iterator<E> iterator() {
		return new LinkedListIterator();
	}

	private class LinkedListIterator implements Iterator<E> {
		private Node<E> current = head.next;
		private boolean okToremove = false;
		private int expectedModcount = modcount;

		@Override
		public boolean hasNext() {
			return current != tail;
		}

		@Override
		public E next() {
			if (modcount != expectedModcount) {
				throw new ConcurrentModificationException();
			} else if (!hasNext()) {
				throw new NoSuchElementException();
			}
			current = current.next;
			okToremove = true;
			return (E) current.pre.ele;
		}

		@Override
		public void remove() {
			if (!okToremove) {
				throw new IllegalStateException();
			} else if (modcount != expectedModcount) {
				throw new ConcurrentModificationException();
			}
			MyLinkedList.this.remove(current.pre);
			okToremove = false;
			expectedModcount++;
		}
	}

	public boolean isempty() {
		return size == 0;
	}

	public int size() {
		return size;
	}

	private Node getNode(int index) {
		if (index < 0 || index >= size) {
			throw new IndexOutOfBoundsException();
		}
		Node<E> p = null;
		if (index < size / 2) {
			p = head.next;
			for (int i = 0; i < index; i++)
				p = p.next;
		} else {
			p = tail;
			for (int i = size; i > index; i--)
				p = p.pre;
		}
		return p;
	}

	public E get(int index) {
		return (E) getNode(index).ele;
	}

	private E remove(Node node) {
		node.next.pre = node.pre;
		node.pre.next = node.next;
		size--;
		modcount++;
		return (E) node.ele;
	}

	public E remove(int index) {
		return remove(getNode(index));
	}

	public void add(int index, E ele) {
		addBefore(getNode(index), ele);
	}

	private void addBefore(Node node, E ele) {
		Node newnode = new Node<E>(ele, node.pre, node);
		node.pre.next = newnode;
		node.pre = newnode;
		size++;
		modcount++;
	}

	public void add(E ele) {
		add(size, ele);
	}
}

三、佇列資料結構

佇列是一種先進先出的資料結構FIFO
有兩種實現方式,一種是連結串列實現,一種是陣列實現。跟剛才講的表的實現一樣的,只是在表的基礎上加了限制,
每次只能在表的末尾增加新元素,訪問元素只能從表頭訪問。

執行緒池中有執行緒佇列,訊息池中有訊息佇列,實際應用中通常會對佇列中的元素新增優先順序,那種佇列叫做優先佇列,後期會做單獨分析。

四、棧的資料結構

棧是一種後進先出的資料結構LIFO
跟佇列一樣,棧也有兩種實現方式,陣列實現跟連結串列實現。在表的基礎上加了限制,
每次只能在表的末尾新增元素,訪問元素也只能從表的末尾


棧是計算機術語中比較重要的概念,實質上棧就是一段記憶體區域,但是棧滿足一定的特性,那就是隻有一個口,具有先入後出的特性,這種特性在計算機中有很廣泛的運用。其實在程式設計師無時無刻不在運用棧,函式的呼叫是我們間接使用棧的最好例子,因此可以說棧的一個最重要的運用就是函式的呼叫。但是棧的運用還不止這些,還有很多,其中幾個典型的執行如下:判斷平衡符號,實現表示式的求值(也就是中綴表示式轉字尾表示式的問題以及字尾表示式求值問題),在路勁探索中實現路勁的儲存也可以說是棧的經典運用之一。
佇列和和棧的兩種實現請參考我的github地址https://github.com/robertjc/simplealgorithm


github程式碼也在不斷完善中,有些地方可能有問題,還請多指教

歡迎掃描二維碼,關注公眾賬號



相關文章