动态数据结构基础01:链表
动态数据结构
动态数组、栈和队列的底层仍依托静态数组来实现,通过定义一个resize()方法解决固定容量问题
而链接是真正的动态数据结构,学习的同时也可以更深入的理解引用(指针)和递归
单链表的实现
数据存储在“节点”(Node)中,每个节点拥有value和next两个属性,next为指向下一个节点的指针
优点:链表是真正的动态数据结构,不需要进行扩缩容
缺点:链表丧失了随机访问的能力,每次操作必须从头节点开始依次往后找,不适合索引有语意的情况
public class Algorithm {
public static void main(String[] args) {
LinkedList<Integer> linkedList = new LinkedList<>();
for (int i = 0; i < 5; i++) {
linkedList.addFirst(i);
System.out.println(linkedList);
}
linkedList.add(666, 3);
linkedList.remove(1);
System.out.println(linkedList);
System.out.println(linkedList.contains(0));
System.out.println(linkedList.contains(4));
linkedList.set(888, 4);
System.out.println(linkedList);
System.out.println(linkedList.get(4));
}
}
class LinkedList<E>{
/**
* 定义节点类Node,链表的底层采用Node类实现
*/
private class Node {
/**
* 属性设置为public,便于外部类调用链表
*/
public E e;
public Node next;
public Node(E e, Node next){
this.e = e;
this.next = next;
}
public Node(){
this.next = null;
}
}
/**
* 链表操作必须要知道前一个节点,为了方便实现,给头节点也定义一个虚拟前节点,称为虚拟头节点,这样所有的节点都可以使用同样的方法
* 虚拟头节点始终指向头节点前面的一位,链表为空时也会存在,其初始值为null,指向的也是null
*/
private Node dummyHead;
private int size;
public LinkedList(){
dummyHead = new Node(null, null);
size = 0;
}
public int getSize(){
return size;
}
public boolean isEmpty(){
return size == 0;
}
/**
* 链表所有操作都需要先找到前一个节点,因此可以定义一个返回任意索引前一个节点的方法
*/
public Node getPrevNode(int index){
if (index < 0 || index >= size){
throw new IllegalArgumentException("索引值非法");
}
/**
* 从头节点的前一个节点,也就是虚拟头节点开始
*/
Node prev = dummyHead;
/**
* 循环index - 1次,prev代表的就是第index - 1个节点,也就是index的前一个节点
*/
for (int i = 0; i < index; i++) {
prev = prev.next;
}
return prev;
}
/**
* 在链表中插入节点
*/
public void add(E e, int index) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("索引值非法");
}
/**
* add()方法不能复用getPrevNode()方法,因为add()方法可以取到size
*/
Node prev = dummyHead;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
/**
* 新建一个节点存放要添加的数据,让其指向prev节点的下一个节点,然后让prev节点指向新节点,完成节点的插入
*/
prev.next = new Node(e, prev.next);
size++;
}
/**
* 在链表头部添加节点
* 和数组相反,链表从头部添加元素是最方便的
*/
public void addFirst(E e){
add(e, 0);
}
public void addLast(E e){
add(e, size);
}
/**
* 删除链表元素
*/
public E remove(int index){
Node prev = getPrevNode(index);
Node tem = prev.next;
prev.next = tem.next;
tem.next = null;
size--;
return tem.e;
}
public E removeFirst(){
return remove(0);
}
public E removeLast(){
return remove(size - 1);
}
/**
* 修改链表的元素
*/
public void set(E e, int index){
Node prev = getPrevNode(index);
prev.next.e = e;
}
/**
* 查询链表的元素
*/
public E get(int index){
Node prev = getPrevNode(index);
return prev.next.e;
}
public E getFirst(){
return get(0);
}
public E getLast(){
return get(size - 1);
}
public boolean contains(E e){
for (Node prev = dummyHead; prev.next != null; prev = prev.next){
/**
* 只是比较是否相等用equals(),如果是比较大小排序,则要实现Comparable接口和重写compareTo()方法
*/
if (prev.next.e.equals(e)){
return true;
}
}
return false;
}
@Override
public String toString(){
StringBuilder str = new StringBuilder();
Node prev = dummyHead;
System.out.println("链表元素个数为:" + getSize());
/**
* 和contains()方法的for循环作用一样
*/
while (prev.next != null){
str.append(prev.next.e + "——>");
prev = prev.next;
}
str.append("null");
return str.toString();
}
}
复杂度分析
单链表增、删、改、查都需要遍历整个链表,时间复杂度是O(n)
但对单链表头进行操作时,复杂度仅为O(1),因此单链表可以实现只在一端进行操作的数据结构——栈
单链表实现栈
public class Algorithm {
public static void main(String[] args) {
LinkedListStack<Integer> stack = new LinkedListStack<>();
for (int i = 0; i < 5; i++) {
stack.push(i);
System.out.println(stack);
}
System.out.println(stack.getSize());
System.out.println(stack.peek());
stack.pop();
System.out.println(stack);
System.out.println(stack.isEmpty());
}
}
interface Stack<E> {
int getSize();
boolean isEmpty();
void push(E element);
E pop();
E peek();
}
class LinkedListStack<E> implements Stack<E> {
LinkedList<E> stack;
public LinkedListStack(){
stack = new LinkedList<>();
}
@Override
public void push(E element){
stack.addFirst(element);
}
@Override
public E pop(){
return stack.removeFirst();
}
@Override
public E peek(){
return stack.getFirst();
}
@Override
public int getSize(){
return stack.getSize();
}
@Override
public boolean isEmpty(){
return stack.isEmpty();
}
@Override
public String toString(){
StringBuilder str = new StringBuilder();
str.append("top [");
System.out.println("栈的元素个数为:" + getSize());
for (int i = 0; i < getSize(); i++) {
str.append(stack.get(i));
if (i != stack.getSize() - 1){
str.append(", ");
}
}
str.append("]");
return str.toString();
}
}
class LinkedList<E>{
private class Node {
public E e;
public Node next;
public Node(E e, Node next){
this.e = e;
this.next = next;
}
public Node(){
this.next = null;
}
}
private Node dummyHead;
private int size;
public LinkedList(){
dummyHead = new Node(null, null);
size = 0;
}
public int getSize(){
return size;
}
public boolean isEmpty(){
return size == 0;
}
public Node getPrevNode(int index){
if (index < 0 || index >= size){
throw new IllegalArgumentException("索引值非法");
}
Node prev = dummyHead;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
return prev;
}
public void add(E e, int index) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("索引值非法");
}
Node prev = dummyHead;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
prev.next = new Node(e, prev.next);
size++;
}
public void addFirst(E e){
add(e, 0);
}
public void addLast(E e){
add(e, size);
}
public E remove(int index){
Node prev = getPrevNode(index);
Node tem = prev.next;
prev.next = tem.next;
tem.next = null;
size--;
return tem.e;
}
public E removeFirst(){
return remove(0);
}
public E removeLast(){
return remove(size - 1);
}
public void set(E e, int index){
Node prev = getPrevNode(index);
prev.next.e = e;
}
public E get(int index){
Node prev = getPrevNode(index);
return prev.next.e;
}
public E getFirst(){
return get(0);
}
public E getLast(){
return get(size - 1);
}
public boolean contains(E e){
for (Node prev = dummyHead; prev.next != null; prev = prev.next){
if (prev.next.e.equals(e)){
return true;
}
}
return false;
}
}
数组栈和单链表栈性能比较
import java.util.Random;
public class Algorithm {
public static void main(String[] args) {
int count = 1000000;
ArrayStack<Integer> arrayStack = new ArrayStack<>();
System.out.println("数组栈用时:" + test(arrayStack, count) + "秒");
LinkedListStack<Integer> linkedListStack = new LinkedListStack<>();
System.out.println("链表栈用时:" + test(linkedListStack, count) + "秒");
}
public static double test(Stack<Integer> name, int count){
Random random = new Random();
long startTime = System.nanoTime();
for (int i = 0; i < count; i++) {
name.push(random.nextInt(count));
}
for (int i = 0; i < count; i++) {
name.pop();
}
long endTime = System.nanoTime();
return (endTime - startTime) / 1000000000.0;
}
}
interface Stack<E> {
int getSize();
boolean isEmpty();
void push(E element);
E pop();
E peek();
}
class LinkedListStack<E> implements Stack<E> {
LinkedList<E> stack;
public LinkedListStack(){
stack = new LinkedList<>();
}
@Override
public void push(E element){
stack.addFirst(element);
}
@Override
public E pop(){
return stack.removeFirst();
}
@Override
public E peek(){
return stack.getFirst();
}
@Override
public int getSize(){
return stack.getSize();
}
@Override
public boolean isEmpty(){
return stack.isEmpty();
}
@Override
public String toString(){
StringBuilder str = new StringBuilder();
str.append("top [");
System.out.println("栈的元素个数为:" + getSize());
for (int i = 0; i < getSize(); i++) {
str.append(stack.get(i));
if (i != stack.getSize() - 1){
str.append(", ");
}
}
str.append("]");
return str.toString();
}
}
class LinkedList<E>{
private class Node {
public E e;
public Node next;
public Node(E e, Node next){
this.e = e;
this.next = next;
}
public Node(){
this.next = null;
}
}
private Node dummyHead;
private int size;
public LinkedList(){
dummyHead = new Node(null, null);
size = 0;
}
public int getSize(){
return size;
}
public boolean isEmpty(){
return size == 0;
}
public Node getPrevNode(int index){
if (index < 0 || index >= size){
throw new IllegalArgumentException("索引值非法");
}
Node prev = dummyHead;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
return prev;
}
public void add(E e, int index) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("索引值非法");
}
Node prev = dummyHead;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
prev.next = new Node(e, prev.next);
size++;
}
public void addFirst(E e){
add(e, 0);
}
public void addLast(E e){
add(e, size);
}
public E remove(int index){
Node prev = getPrevNode(index);
Node tem = prev.next;
prev.next = tem.next;
tem.next = null;
size--;
return tem.e;
}
public E removeFirst(){
return remove(0);
}
public E removeLast(){
return remove(size - 1);
}
public void set(E e, int index){
Node prev = getPrevNode(index);
prev.next.e = e;
}
public E get(int index){
Node prev = getPrevNode(index);
return prev.next.e;
}
public E getFirst(){
return get(0);
}
public E getLast(){
return get(size - 1);
}
public boolean contains(E e){
for (Node prev = dummyHead; prev.next != null; prev = prev.next){
if (prev.next.e.equals(e)){
return true;
}
}
return false;
}
}
class ArrayStack<E> implements Stack<E>{
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();
}
@Override
public void push(E element) {
array.addLast(element);
}
@Override
public E pop() {
return array.removeLast();
}
@Override
public E peek() {
return array.getLast();
}
@Override
public String toString() {
StringBuilder str = new StringBuilder();
str.append("Stack: [");
for (int i = 0; i < array.getSize(); i++) {
str.append(array.get(i));
if (i != array.getSize() - 1){
str.append(", ");
}
}
str.append("] top");
return str.toString();
}
}
class Array<E> {
private E[] data;
private int size;
public Array(int capacity){
data = (E[]) new Object[capacity];
size = 0;
}
public Array(){
this(10);
}
public void add(int index, E element){
if (index < 0 || index > size){
throw new IllegalArgumentException("索引值非法");
}
if (size == data.length){
resize(2 * data.length);
}
for (int i = size - 1; i >= index; i--) {
data[i + 1] = data[i];
}
data[index] = element;
size++;
}
public void addLast(E element){
add(size, element);
}
public E remove(int index){
if (index < 0 || index >= size) {
throw new IllegalArgumentException("索引值非法");
}
E target = data[index];
for (int i = index; i < size - 1; i++) {
data[i] = data[i + 1];
}
size--;
data[size] = null;
if (size == data.length / 4 && data.length / 2 != 0){
resize(data.length / 2);
}
return target;
}
public E removeLast(){
return remove(size - 1);
}
public int getSize(){
return size;
}
public E get(int index){
if (index < 0 || index >= size) {
throw new IllegalArgumentException("索引值非法");
}
return data[index];
}
public E getLast(){
return get(size - 1);
}
private void resize(int newCapacity){
E[] newData = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newData[i] = data[i];
}
data = newData;
}
public boolean isEmpty(){
return size == 0;
}
}
链表中包含很多的new Node()操作,会不停的开辟新的空间,因此在大规模时耗时很长
但在时间复杂度上没有量级的差异,可能只是相差了几倍
用带尾指针单链表实现队列
单链表在尾部添加或删除元素的时间复杂度是O(n),因此不能直接用来实现队列
可以再设置一个尾节点tail,此时在尾部添加节点也很容易,但是删除节点还是很麻烦,因为尾节点无法向前寻找前一个节点的位置
所以用带尾指针单链表实现队列,要从tail端入队,从haed端出队
public class Algorithm {
public static void main(String[] args) {
LinkedListQueue<Integer> queue = new LinkedListQueue<>();
for (int i = 0; i < 5; i++) {
queue.enqueue(i);
System.out.println(queue);
}
for (int i = 0; i < 4; i++) {
queue.dequeue();
System.out.println(queue);
}
}
}
interface Queue<E> {
int getSize();
boolean isEmpty();
void enqueue(E element);
E dequeue();
E getFront();
}
class LinkedListQueue<E> implements Queue<E>{
private class Node{
public E element;
public Node next;
public Node(E element){
this.element = element;
this.next = null;
}
public Node(E element, Node next){
this.element = element;
this.next = next;
}
}
/**
* 入队出队都不需要遍历链表中间,因此可以不用虚拟头节点
*/
private Node head;
private Node tail;
private int size;
public LinkedListQueue(){
head = null;
tail = null;
size = 0;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public void enqueue(E element) {
/**
* 当添加第一个节点时,head和tail会指向同一个节点
*/
if (tail == null){
tail = new Node(element);
head = tail;
}
else {
tail.next = new Node(element);
tail = tail.next;
}
size++;
}
@Override
public E dequeue() {
if (size == 0){
throw new IllegalArgumentException("队列为空");
}
Node tem = head;
head = head.next;
tem.next = null;
size--;
/**
* 当最后一个节点出队的时候,tail也要指向null
*/
if (size == 0) {
tail = null;
}
return tem.element;
}
@Override
public E getFront() {
if (size == 0){
throw new IllegalArgumentException("队列为空");
}
return head.element;
}
@Override
public String toString(){
StringBuilder str = new StringBuilder();
System.out.print("队列的元素个数为:" + getSize() + "\nhead [");
for (Node cur = head; cur != null; cur = cur.next) {
str.append(cur.element);
if (cur.next != null){
str.append(", ");
}
}
str.append("] tail");
return str.toString();
}
}
数组队列、循环队列、单链表队列性能对比
import java.util.Random;
public class Algorithm {
public static void main(String[] args) {
int count = 100000;
ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
System.out.println("数组队列用时:" + test(arrayQueue, count) + "秒");
LoopQueue<Integer> loopQueue = new LoopQueue<>();
System.out.println("循环队列用时:" + test(loopQueue, count) + "秒");
LinkedListQueue<Integer> linkedListQueue = new LinkedListQueue<>();
System.out.println("链表队列用时:" + test(linkedListQueue, count) + "秒");
}
public static double test(Queue<Integer> name, int count){
Random random = new Random();
long startTime = System.nanoTime();
for (int i = 0; i < count; i++) {
name.enqueue(random.nextInt(count));
}
for (int i = 0; i < count; i++) {
name.dequeue();
}
long endTime = System.nanoTime();
return (endTime - startTime) / 1000000000.0;
}
}
interface Queue<E> {
int getSize();
boolean isEmpty();
void enqueue(E element);
E dequeue();
E getFront();
}
class LinkedListQueue<E> implements Queue<E>{
private class Node{
public E element;
public Node next;
public Node(E element){
this.element = element;
this.next = null;
}
public Node(E element, Node next){
this.element = element;
this.next = next;
}
}
private Node head;
private Node tail;
private int size;
public LinkedListQueue(){
head = null;
tail = null;
size = 0;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public void enqueue(E element) {
if (tail == null){
tail = new Node(element);
head = tail;
}
else {
tail.next = new Node(element);
tail = tail.next;
}
size++;
}
@Override
public E dequeue() {
if (size == 0){
throw new IllegalArgumentException("队列为空");
}
Node tem = head;
head = head.next;
tem.next = null;
size--;
if (size == 0) {
tail = null;
}
return tem.element;
}
@Override
public E getFront() {
if (size == 0){
throw new IllegalArgumentException("队列为空");
}
return head.element;
}
@Override
public String toString(){
StringBuilder str = new StringBuilder();
System.out.print("队列的元素个数为:" + getSize() + "\nhead [");
for (Node cur = head; cur != null; cur = cur.next) {
str.append(cur.element);
if (cur.next != null){
str.append(", ");
}
}
str.append("] tail");
return str.toString();
}
}
class LoopQueue<E> implements Queue<E>{
private E[] data;
private int front, tail;
public LoopQueue(int capacity){
data = (E[]) new Object[capacity + 1];
front = 0;
tail = 0;
}
public LoopQueue(){
this(5);
}
@Override
public int getSize() {
if (front <= tail){
return tail - front;
}
else {
return data.length - (front - tail);
}
}
@Override
public boolean isEmpty() {
return front == tail;
}
@Override
public void enqueue(E element) {
if (front == (tail + 1) % data.length){
resize(2 * (data.length - 1));
}
data[tail] = element;
tail = (tail + 1) % data.length;
}
@Override
public E dequeue() {
if (isEmpty()){
throw new IllegalArgumentException("队列空了");
}
E tem = data[front];
data[front] = null;
front = (front + 1) % data.length;
if (getSize() == data.length / 4 && data.length / 2 != 0) {
resize(data.length / 2);
}
return tem;
}
public void resize(int newCapacity){
E[] newData = (E[]) new Object[newCapacity + 1];
for (int i = 0; i < getSize(); i++) {
newData[i] = data[(front + i) % data.length];
}
data = newData;
front = 0;
tail = getSize();
}
@Override
public E getFront() {
if (isEmpty()){
throw new IllegalArgumentException("队列空了");
}
return data[front];
}
@Override
public String toString() {
StringBuilder str = new StringBuilder();
str.append(String.format("Queue: 队列的容量为%d,元素个数为%d\nfront [", data.length - 1, getSize()));
for (int i = 0; i < getSize(); i++) {
str.append(data[(front + i) % data.length]);
if (i != getSize()- 1){
str.append(", ");
}
}
str.append("] tail");
return str.toString();
}
}
class ArrayQueue<E> implements Queue<E>{
Array<E> array;
public ArrayQueue(int capacity){
array = new Array<>(capacity);
}
public ArrayQueue(){
array = new Array<>();
}
@Override
public int getSize() {
return array.getSize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
@Override
public void enqueue(E element) {
array.addLast(element);
}
@Override
public E dequeue() {
return array.removeFirst();
}
@Override
public E getFront() {
return array.getFirst();
}
@Override
public String toString() {
StringBuilder str = new StringBuilder();
str.append("Queue: front [");
for (int i = 0; i < array.getSize(); i++) {
str.append(array.get(i));
if (i != array.getSize() - 1){
str.append(", ");
}
}
str.append("] tail");
return str.toString();
}
}
class Array<E> {
private E[] data;
private int size;
public Array(int capacity){
data = (E[]) new Object[capacity];
size = 0;
}
public Array(){
this(10);
}
public void add(int index, E element){
if (index < 0 || index > size){
throw new IllegalArgumentException("索引值非法");
}
if (size == data.length){
resize(2 * data.length);
}
for (int i = size - 1; i >= index; i--) {
data[i + 1] = data[i];
}
data[index] = element;
size++;
}
public void addLast(E element){
add(size, element);
}
public void remove(int index){
if (index < 0 || index >= size) {
throw new IllegalArgumentException("索引值非法");
}
for (int i = index; i < size - 1; i++) {
data[i] = data[i + 1];
}
size--;
data[size] = null;
if (size == data.length / 4 && data.length / 2 != 0){
resize(data.length / 2);
}
}
public E removeFirst(){
if (size == 0) {
throw new IllegalArgumentException("索引值非法");
}
E tem = data[0];
remove(0);
return tem;
}
public int getSize(){
return size;
}
public E get(int index){
if (index < 0 || index >= size) {
throw new IllegalArgumentException("索引值非法");
}
return data[index];
}
public E getFirst(){
return get(0);
}
private void resize(int newCapacity){
E[] newData = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newData[i] = data[i];
}
data = newData;
}
public boolean isEmpty(){
return size == 0;
}
@Override
public String toString() {
StringBuilder str = new StringBuilder();
str.append(String.format("数组的容量为%d,元素个数为%d\n", data.length, size));
str.append("[");
for (int i = 0; i < size; i++) {
str.append(data[i]);
if (i != size - 1){
str.append(", ");
}
}
str.append("]");
return str.toString();
}
}