栈结构
-
栈的定义:
栈结构是一种只能从一端存取数据并且遵循“后进先出(LIFO)”原则的线性存储结构。
-
实现栈容器
package www.reptile.com;
import java.util.Arrays;
import java.util.EmptyStackException;
/**
* TODO
*
* @author dang
* @date 2022/1/25 16:59
*/
public class Stack<E> {
/**
* 初始化一个物理容器
*/
private Object[] arr;
/**
* 初始化栈容器的大小
*/
private int stackLength = 4;
/**
* 数组的长度
*/
private int size;
/**
* 初始化指针
*/
private int index = -1;
/**
* 判断栈容器是否为空
*
* @return
*/
public boolean empty() {
return this.size == 0;
}
/**
* 获取栈顶元素
*
* @return
*/
public E pop() {
if (this.index == -1) {
throw new EmptyStackException();
}
this.size--;
return (E) this.arr[this.index--];
}
/**
* 添加元素
*
* @param element
* @return
*/
public E push(E element) {
//初始化数组或者以1.5倍进行扩容
this.capacity();
//向容器中添加元素
this.arr[++this.index] = element;
//记录容器大小
this.size++;
return element;
}
/**
* 初始化数组或者以1.5倍进行扩容
*/
private void capacity() {
if (this.arr == null) {
this.arr = new Object[this.stackLength];
} else {
if (this.size - (this.stackLength - 1) >= 0) {
this.stackLength = this.stackLength + (this.stackLength >> 1);
this.arr = Arrays.copyOf(this.arr, this.stackLength);
}
}
}
public static void main(String[] args) {
Stack<String> stack = new Stack<String>();
stack.push("a");
stack.push("b");
stack.push("c");
stack.push("d");
stack.push("e");
System.out.println(stack.size);
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.empty());
}
}
链表结构
定义:
- 链表结构是由许多节点构成,每个节点包含两个部门:
- 数据部分:保存该节点的实际数据。
- 地址部分:保存的是上一个或下一个节点的地址。
分类:
- 单向链表结构
- 双向链表结构
- 双向循环链表结构
特点:
- 节点在存储容器中的位置是任意的,不像数组这个数据结构是连续存储,即逻辑上相邻的数据元素在物理上不一定相邻。
- 访问时只能通过头指针或者尾部指针进入链表,并通过每个节点的指针域向前或向后扫描其余节点,所以寻找第一个节点和最后一个节点所花费的时间不等,所以查询效率较为低下。
- 优点:数据元素的个数可以自由扩充、插入、删除等操作不需要移动数据,只需要修改前后节点的指针地址即可,修改效率较高。
- 缺点:必须采用顺序存储,即存取数据元素时,只能按照链表的顺序进行访问,访问节点效率低。
单向链表
定义:
单向链表是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过从头开始顺序读取。
示例:
package www.reptile.com;
/**
* TODO
*
* @author DangWangYi
* @date 2022/1/25 15:43
*/
public interface List<E> {
/**
* 获取元素的个数
*
* @return
*/
int size();
boolean isEmpty();
/**
* 添加元素
*
* @param element
*/
void add(E element);
/**
* 根据元素位置获取元素
*
* @param index
* @return
*/
E get(int index);
/**
* 根据元素位置删除元素
*
* @param index
* @return
*/
E remove(int index);
}
package www.reptile.com;
/**
* TODO 单向链表数据结构
*
* @author DangWangYi
* @date 2022/1/25 15:50
*/
public class SingleLinkedList<E> implements List<E> {
/**
* 头节点
*/
private Node<E> head;
/**
* 记录元素个数
*/
private int size;
/**
* 获取元素的个数
*
* @return
*/
@Override
public int size() {
return this.size;
}
@Override
public boolean isEmpty() {
return this.size() == 0;
}
/**
* 添加元素
*
* @param element
*/
@Override
public void add(E element) {
//创建节点
Node<E> node = new Node<E>(element, null);
//找到尾结点
Node tailNode = getTailNode();
//节点挂接
if (tailNode == null) {
this.head = node;
} else {
tailNode.next = node;
}
//记录元素个数
this.size++;
}
/**
* 找到尾结点
*
* @return
*/
private Node getTailNode() {
//判断头节点是否存在
if (this.head == null) {
return null;
}
Node<E> node = this.head;
while (true) {
//当当前节点的下一个节点为空退出循环
if (node.next == null) break;
node = node.next;//当前节点的下一个节点
}
return node;
}
/**
* 根据元素位置获取元素
*
* @param index
* @return
*/
@Override
public E get(int index) {
//校验指针的合法性
this.checkIndex(index);
//根据指针位置获取节点
Node<E> node = getNode(index);
//将该节点的元素返回
return node.item;
}
/**
* 根据指针获取节点
*
* @param index
* @return
*/
private Node<E> getNode(int index) {
Node<E> node = this.head;
for (int i = 0; i < index; i++) {
node = node.next;
}
return node;
}
/**
* 校验指针的合法性
*/
private void checkIndex(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size);
}
}
/**
* 根据元素位置删除元素
*
* @param index
* @return
*/
@Override
public E remove(int index) {
//校验指针的合法性
this.checkIndex(index);
//根据指针获取对应的节点
Node<E> node = this.getNode(index);
//获取该节点对象中的元素
E item = node.item;
//将该对象从单项列表中移除
if (this.head == node) {
this.head = node.next;
} else {
Node<E> temp = this.head;
for (int i = 0; i < index - 1; i++) {
temp = temp.next;
}
temp.next = node.next;
}
node.next = null;
//记录元素个数
this.size--;
return item;
}
/**
* 定义单项列表节点对象
*/
class Node<E> {
/**
* 当前节点元素
*/
private E item;
/**
* 当前节点的下一个节点
*/
private Node<E> next;
Node(E item, Node<E> next) {
this.item = item;
this.next = next;
}
}
public static void main(String[] args) {
List<Integer> list = new SingleLinkedList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list.remove(0));
System.out.println(list.size());
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
双向链表
定义:
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。
示例:
package www.reptile.com;
/**
* TODO 双向链表数据结构
*
* @author DangWangYi
* @date 2022/1/25 15:52
*/
public class DoubleLinkedList<E> implements List<E> {
private Node<E> head; //记录头节点
private Node<E> tail; //记录尾节点
private int size;//记录元素个数
@Override
public int size() {
return this.size;
}
@Override
public boolean isEmpty() {
return this.size() == 0;
}
@Override
public void add(E element) {
this.linkLast(element);
}
/**
* 将节点对象添加到双向列表的尾部
*/
public void linkLast(E element) {
//获取尾节点
Node<E> tail = this.tail;
//创建节点对象
Node<E> node = new Node<E>(tail, element, null);
//将新节点定义为尾部节点
this.tail = node;
if (tail == null) {
this.head = node;
} else {
tail.nextNode = node;
}
this.size++;
}
@Override
public E get(int index) {
//校验指针的合法性
this.checkIndex(index);
//根据指针查找节点对象
Node<E> node = getNode(index);
return node.item;
}
/**
* 根据指针查找节点对象
*
* @param index
* @return
*/
private Node<E> getNode(int index) {
if (index < this.size >> 1) {
Node<E> node = this.head;
for (int i = 0; i < index; i++) {
node = node.nextNode;
}
return node;
} else {
Node<E> node = this.tail;
for (int i = this.size - 1; i > index; i--) {
node = node.prevNode;
}
return node;
}
}
/**
* 校验指针的合法性
*
* @param index
*/
private void checkIndex(int index) {
if (index < 0 || index >= this.size) {
throw new IndexOutOfBoundsException("Index: " + index + ",Size: " + this.size);
}
}
@Override
public E remove(int index) {
//校验指针合法性
this.checkIndex(index);
//根据位置获取节点对象
Node<E> node = this.getNode(index);
//获取节点对象中的元素
E item = node.item;
//判断当前节点是否为头节点
if (node.prevNode == null) {
this.head = node.nextNode;
} else {
node.prevNode.nextNode = node.nextNode;
}
if (node.nextNode == null) {
this.tail = node.prevNode;
} else {
node.nextNode.prevNode = node.prevNode;
}
node.prevNode = null;
node.nextNode = null;
node.item = null;
this.size--;
return item;
}
/**
* 在链表的头部添加元素
*
* @param element
*/
public void addLinkFirst(E element) {
Node<E> head = this.head;
Node<E> node = new Node<E>(null, element, head);
this.head = node;
if (head == null) {
this.tail = node;
} else {
head.prevNode = node;
}
this.size++;
}
/**
* 在链表的尾部添加元素
*
* @param element
*/
public void addLinkLast(E element) {
this.linkLast(element);
}
/**
* 定义双向链表的节点的对象
*
* @param <E>
*/
class Node<E> {
/**
* 记录前一个节点对象
*/
private Node<E> prevNode;
/**
* 当前元素
*/
private E item;
/**
* 记录后一个节点对象
*/
private Node<E> nextNode;
Node(Node<E> prevNode, E item, Node<E> nextNode) {
this.prevNode = prevNode;
this.item = item;
this.nextNode = nextNode;
}
}
public static void main(String[] args) {
List<String> list = new DoubleLinkedList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
System.out.println(list.remove(2));
System.out.println(list.size());
((DoubleLinkedList<String>) list).addLinkFirst("A");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}