数据结构(2)-- 单向链表

目录

0.目录
1.线性表 – 数组
2.线性表 – 单向链表

链表

  链表是一种物理存储单元上非连续、非顺序的数据结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列节点组成,这些节点不必在内存中相连。每个节点由数据部分Data和链部分Next组成,Next指向下一个节点,这样当添加或者删除时,只需要修改相关节点的Next指向,效率很高)。

// 链表节点代码(使用内部类)
private class Node<E> {
	private E element;
	private Node<E> next;
	public Node(E e, Node<E> next) {
		element = e;
		this.next = next;
	}
	public Node(E e) {
		this(e, null);
	}
	public Node() {
		this(null);
	}

// 使用虚拟头结点保持逻辑连贯性、使用尾节点改进性能
private Node<E> dummyHead;
private Node<E> tail;
private int size = 0;

// 两种构造方法
public Linked(E e) {
	dummyHead = new Node<>();
	dummyHead.next = new Node<>(e);
	tail = dummyHead.next;
	size++;
}

public Linked() {
	dummyHead = new Node<>();
	tail = dummyHead;
}

// 在指定索引位置插入元素
public void add(int index, E e) {
	if (index < 0 || index > size) {
		throw new IllegalArgumentException("Wrong index: " + index);
	}
	Node<E> pre = dummyHead;
	for (int i = 0; i < index; i++) {
		pre = pre.next;
	}
	pre.next = new Node<>(e, pre.next);
	if (index == size) {
		tail = pre.next;
	}
	size++;
}

// 顺序遍历链表(重写toString方法)
@Override
public String toString() {
	StringBuilder sb = new StringBuilder("Linked head[");
	Node<E> cur = dummyHead;
	while (cur.next != null) {
		cur = cur.next;
		sb.append(cur.element).append("->");
	}
	sb.append("NULL").append("]tail");
	return sb.toString();
}

// 逆序遍历链表(递归思想)
public void printDesc() {
	System.out.print("Linked tail[Null");
	printDesc(dummyHead.next);
	System.out.println("]head");
}
private void printDesc(Node<E> head) {
	if (head != null) {
		printDesc(head.next);
		System.out.print("<-" + head.element);
	}
}

// 单链表反转
public void reverse() {
	// 非递归(效率高)
	dummyHead.next = reverse(dummyHead.next);
	// 递归
	// dummyHead.next = reverseRecursion(dummyHead.next);
}
// 非递归实现
private Node<E> reverse(Node<E> head) {
	Node<E> pre = null;
	Node<E> cur = head;
	Node<E> next = null;
	while (cur != null) {
		next = cur.next;
		cur.next = pre;
		pre = cur;
		cur = next;
	}
	return pre;
}
// 递归实现
private Node<E> reverseRecursion(Node<E> node) {
	if (node == null) {
		return null;
	}
	if (node.next == null) {
		return node;
	}
	Node<E> next = node.next;
	node.next = null;
	Node<E> result = reverseRecursion(next);
	next.next = node;
	return result;
}

  上述代码简单实现了基于单向链表实现的线性表的一些细节。单向链表实现的线性表,不需要扩容操作;引入了虚拟头结点,使各方法在实现逻辑上会更加统一;引入了尾节点tail,使得对单链表的部分尾部操作性能显著提升(O(n) -> O(1)),可显著提升基于单向链表实现的其他数据结构的性能(如队列、栈等,后续会介绍)。
  除了上述单向链表,还有其他的实现方式,常见的有循环单向链表、双向链表、循环双向链表。其中,LinkedList集合类的实现就是双向链表。

复杂度分析

  1. 增:add – O(n)、addFirst 和 addLast(tail实现) – O(1)
  2. 删:remove – O(n)、removeFirst – O(1)
  3. 改:set – O(n)、setFirst 和 setLast(tail实现) – O(1)
  4. 查:get – O(n)、getFirst 和 getLast(tail实现) – O(1)

  可以看出,基于单链表实现的线性表结构,特点是:中间操作慢、首尾操作快

源码

public class Linked<E> {

	private class Node<E> {
		private E element;
		private Node<E> next;

		public Node(E e, Node<E> next) {
			element = e;
			this.next = next;
		}

		public Node(E e) {
			this(e, null);
		}

		public Node() {
			this(null);
		}

		@Override
		public String toString() {
			return element == null ? "null" : element.toString();
		}
	}

	private Node<E> dummyHead;
	private Node<E> tail;
	private int size = 0;

	public Linked(Node<E> node) {
		dummyHead = new Node<>();
		dummyHead.next = node;
		if (node == null) {
			tail = dummyHead;
		} else {
			tail = dummyHead.next;
			size++;
		}
	}

	public Linked(E e) {
		dummyHead = new Node<>();
		dummyHead.next = new Node<>(e);
		tail = dummyHead.next;
		size++;
	}

	public Linked() {
		dummyHead = new Node<>();
		tail = dummyHead;
	}

	/**
	 * 在指定索引位置插入元素 O(n) 
	 * 特例:addFirst 和 addLast(tail实现) 为O(1)
	 * 
	 * @param index
	 * @param e
	 */
	public void add(int index, E e) {
		if (index < 0 || index > size) {
			throw new IllegalArgumentException("Wrong index: " + index);
		}
		Node<E> pre = dummyHead;
		for (int i = 0; i < index; i++) {
			pre = pre.next;
		}
		pre.next = new Node<>(e, pre.next);
		if (index == size) {
			tail = pre.next;
		}
		size++;
	}

	public void addFirst(E e) {
		add(0, e);
	}

	public void addLast(E e) {
		// 复用add方法,则时间复杂度为O(n)
		// add(size, e);

		// 使用tail节点实现,则时间复杂度为O(1)
		tail.next = new Node<>(e);
		tail = tail.next;
		size++;
	}

	/**
	 * 删除指定位置的元素 O(n) 
	 * 特例:removeFirst为O(1)
	 * 
	 * @param index
	 */
	public void remove(int index) {
		if (index < 0 || index >= size) {
			throw new IllegalArgumentException("Wrong index: " + index);
		}
		Node<E> pre = dummyHead;
		for (int i = 0; i < index; i++) {
			pre = pre.next;
		}
		if (index == size - 1) {
			tail = pre;
		}
		// 使del节点能被JVM正常GC
		Node<E> del = pre.next;
		pre.next = del.next;
		del.next = null;
		size--;
	}

	public void removeFirst() {
		remove(0);
	}

	public void removeLast() {
		remove(size - 1);
	}

	/**
	 * 更改指定索引位置的元素 O(n) 
	 * 特例:setFirst 和 setLast(tail实现) 为O(1)
	 * 
	 * @param index
	 * @param e
	 */
	public void set(int index, E e) {
		if (index < 0 || index >= size) {
			throw new IllegalArgumentException("Wrong index: " + index);
		}
		Node<E> cur = dummyHead.next;
		for (int i = 0; i < index; i++) {
			cur = cur.next;
		}
		cur.element = e;
	}

	public void setFirst(E e) {
		set(0, e);
	}

	public void setLast(E e) {
		// 复用add方法,则时间复杂度为O(n)
		// set(size - 1, e);

		// 使用tail节点实现,则时间复杂度为O(1)
		tail.element = e;
	}

	/**
	 * 查询指定索引位置的元素 O(n) 
	 * 特例:getFirst 和 getLast(tail实现) 为O(1)
	 * 
	 * @param index
	 * @return
	 */
	public E get(int index) {
		if (index < 0 || index >= size) {
			throw new IllegalArgumentException("Wrong index: " + index);
		}
		Node<E> cur = dummyHead.next;
		for (int i = 0; i < index; i++) {
			cur = cur.next;
		}
		return cur.element;
	}

	public E getFirst() {
		return get(0);
	}

	public E getLast() {
		return tail.element;
	}

	public int getSize() {
		return size;
	}

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

	/**
	 * 查询链表是否包含某个元素,如果包含返回索引,如果不包含返回-1 O(n)
	 * 
	 * @param e
	 * @return
	 */
	public int contains(E e) {
		if (e == null) {
			return -1;
		}
		Node<E> cur = dummyHead.next;
		for (int i = 0; i < size; i++) {
			if (e.equals(cur.element)) {
				return i;
			}
			cur = cur.next;
		}
		return -1;
	}

	/**
	 * 反向打印链表,递归思想
	 */
	public void printDesc() {
		System.out.print("Linked tail[Null");
		printDesc(dummyHead.next);
		System.out.println("]head");
	}

	private void printDesc(Node<E> head) {
		if (head != null) {
			printDesc(head.next);
			System.out.print("<-" + head.element);
		}
	}

	/**
	 * 反转链表
	 */
	public void reverse() {
		// 非递归(效率高)
		dummyHead.next = reverse(dummyHead.next);

		// 递归
		// dummyHead.next = reverseRecursion(dummyHead.next);
	}

	private Node<E> reverse(Node<E> head) {
		Node<E> pre = null;
		Node<E> cur = head;
		Node<E> next = null;
		while (cur != null) {
			next = cur.next;
			cur.next = pre;
			pre = cur;
			cur = next;
		}
		return pre;
	}

	// 递归实现
	private Node<E> reverseRecursion(Node<E> node) {
		if (node == null) {
			return null;
		}
		if (node.next == null) {
			return node;
		}
		Node<E> next = node.next;
		node.next = null;
		Node<E> result = reverseRecursion(next);
		next.next = node;
		return result;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder("Linked head[");
		Node<E> cur = dummyHead;
		while (cur.next != null) {
			cur = cur.next;
			sb.append(cur.element).append("->");
		}
		sb.append("NULL").append("]tail");
		// sb.append(size);
		return sb.toString();
	}
}

猜你喜欢

转载自blog.csdn.net/wqxsw0/article/details/84973988