《Java数据结构和算法》之 链表(学习笔记)

一、学习目标

            1、了解链表的结构和优点;

            2、知道链表的分类及其区别;   

            3、各种链表的代码实现。

 

二、什么是链表

           1、链结点

                        是链表最基本的组成单元。在链表中我们的数据项是存储在链结点中的,每个链结点持有指向其他节点的指针,有了该指针,我们可以以某种顺序搜索到目标数据项的结点,进而获取到数据项。

          2、链表

                         是由链结点构成的存储结构,链表的物理存储可以是非顺序和非连续的,这点和数组不同,但是我们链表是可以实现逻辑顺序的数据结构,因为链结点中可以包含相邻链结点的指针。链表的类型有:单链表、双端链表、双向链表以及有序链表等等。

 

         3、对比数组优缺点

                           除非频繁需要通过下标随机访问数据(没有数组的随机访问性强),我们基本可以用链表代替数组。

                         (1)链表的灵活性高。数组的存储大小受限创建创建时的大小,而链表的存储是无限制的(局限于内存)。

                         (2)链表的插入元素方便。数组中间插入一个元素,该位置的后续元素需要向后移动,而链表只需要改变相邻结点的指针即可。

 

三、相关链表的代码实现     

     

         1、单链表

              《Java数据结构和算法》之 链表(学习笔记)_第1张图片

  

/**
 * 单链表
 * 
 */
public class DefLinkList {

	private Node first; // 链表的第一个节点

	/**
	 * 初始化链表
	 */
	public DefLinkList() {
		first = null;
	}

	/**
	 * 判断链表是否为空
	 * 
	 * @return 空 true 非空 false
	 */
	public boolean isEmpty() {
		return first == null;
	}

	/**
	 * 向链表头部位置插入元素
	 * 
	 * @param data
	 */
	public void insertFirst(E data) {
		Node node = new Node(data);
		node.next = first;
		first = node;
	}

	/**
	 * 删除链表的头部元素
	 * 
	 * @param data
	 */
	public E deleteFirst() {
		if (!isEmpty()) {
			E temp = first.element;
			first = first.next;
			return temp;
		}
		return null;
	}

	/**
	 * 删除指定元素
	 * 
	 * @param data
	 */
	public boolean delete(E e) {
		if (isEmpty()) {
			return false;
		}
		Node previous = first;
		Node current = first;
		// 为了防止空指针异常 ,元素为 null和非null的逻辑分开写
		if (e == null) {
			for ( ; current != null; previous = current, current = current.next) {
				if (current.element == null) {
					// 被删除的元素是头节点
					if (current == first) {
						first = current.next;
					} else {
						// 被删的元素非节点
						previous.next = current.next;
					}
					return true;
				}
			}
		} else {
			for ( ; current != null; previous = current, current = current.next) {
				// 注意判断的顺序,current.element有可能是存放的null元素
				if (e.equals(current.element)) {
					// 被删的元素是头节点
					if (current == first) {
						first = current.next;
					} else {
						// 被删的元素非头节点
						previous.next = current.next;
					}
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 是否包含某元素
	 * 
	 * @param e
	 * @return
	 */
	public boolean contains(E e) {
		if (isEmpty()) {
			return false;
		}
		if (e == null) {
			for (Node temp = first; temp != null; temp = temp.next) {
				if (temp.element == null) {
					return true;
				}
			}
		} else {
			for (Node temp = first; temp != null; temp = temp.next) {
				if (e.equals(temp.element)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 复写toString方法
	 */
	public String toString() {
		if (isEmpty())
			return "[]";
		StringBuilder displayBuilder = new StringBuilder("[");
		Node temp = first;
		while (temp != null) {
			displayBuilder.append(temp.element);
			temp = temp.next;
			if (temp == null) {
				displayBuilder.append("]");
				break;
			}
			displayBuilder.append(", ");
		}
		return displayBuilder.toString();
	}

	/**
	 * 结点 链表的组成元素
	 */
	private static class Node {

		E element; // 节点里面存放的数据项
		Node next; // 指向下一个节点的指针

		public Node(E element) {
			this.element = element;
		}

	}
}

 

2、双端链表

《Java数据结构和算法》之 链表(学习笔记)_第2张图片

     

/**
 *双端队列
 * @param 
 */
public class DefDeque {

	private Node first; // 链表的第一个元素
	private Node last; // 链表的最后一个元素

	/**
	 * 初始化链表
	 */
	public DefDeque() {
		first = null;
		last = null;
	}

	/**
	 * 链表头部插入
	 * 
	 * @param e
	 */
	public void insertFirst(E e) {
		Node current = new Node(e);
		if (first == null) {
			first = current;
			last = current;
		} else {
			current.next = first;
			first = current;
		}
	}

	/**
	 * 链表尾部插入
	 * 
	 * @param e
	 */
	public void insertLast(E e) {
		Node current = new Node(e);
		if (first == null) {
			first = current;
			last = current;
		} else {
			last.next = current;
			last = current;
		}
	}

	/**
	 * 判断链表是否为空
	 * 
	 * @return
	 */
	public boolean isEmpty() {
		return first == null;
	}

	/**
	 * 移除链表头部元素
	 * 
	 * @return
	 */
	public E removeFirst() {
		// 链表为空,返回null
		if (isEmpty())
			return null;
		E data = first.data;
		// 链表只有一个元素
		if (first == last) {
			first = last = null;
		} else {
			first = first.next;
		}
		return data;
	}

	/**
	 * 移除链表尾部元素
	 * 
	 * @return
	 */
	public E removeLast() {
		// 链表为空,返回null
		if (isEmpty())
			return null;
		E data = last.data;
		// 链表只有一个元素
		if (first == last) {
			first = last = null;
		} else {
			Node current = first;
			Node previous = current;
			// 找到链表的倒数第二个元素,双端链表就不用这么麻烦
			while(current != last) {
				previous = current;
				current = current.next;
			}
			previous.next = null;
			last = previous;
		}
		return data;
	}

	public boolean contains(E e) {
		if (isEmpty()) {
			return false;
		}
		// e是空和非空逻辑分开
		if (e == null) {
			for (Node current = first; current != null; current = current.next) {
				if (current.data == e) {
					return true;
				}
			}
		} else {
			for (Node current = first; current != null; current = current.next) {
				if (e.equals(current.data)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 删除链表中包含数据项e的第一个结点(不是所有)
	 * 
	 * @param e
	 * @return
	 */
	public boolean remove(E e) {
		if(isEmpty()) {
			return false;
		}
		Node current; 
		Node previous;
		// e是空和非空逻辑分开
		if (e == null) {
			
			for(previous = current = first;current != null;previous = current, current = current.next) {
				if(current.data == e) {
					if(current == first) {
						first = first.next;
					}else {
						previous.next = current.next;
						if(current == last) {
							last = previous;
						}
					}
					
					return true;
				}
			}
			
		} else {
			for(previous = current = first;current != null;previous = current, current = current.next) {
				if(e.equals(current.data)) {
					if(current == first) {
						first = first.next;
					}else {
						previous.next = current.next;
						if(current == last) {
							last = previous;
						}
					}
					return true;
				}
			}
		}
		
		return false;
	}

	public String toString() {
		if (isEmpty())
			return "[]";
		StringBuilder displayBuilder = new StringBuilder("[");
		Node temp = first;
		while (temp != null) {
			displayBuilder.append(temp.data);
			temp = temp.next;
			if (temp == null) {
				displayBuilder.append("]");
				break;
			}
			displayBuilder.append(", ");
		}
		return displayBuilder.toString();
	}

	/**
	 * 结点 链表的组成元素
	 * 
	 * @param 
	 *            结点存放的数据项
	 */
	private static class Node {
		private E data; // 节点存放的数据项
		private Node next; // 指向下一个节点的指针

		public Node(E e) {
			data = e;
			next = null;
		}
	}

}

 

 3、双向链表

        《Java数据结构和算法》之 链表(学习笔记)_第3张图片

/**
 * 双向队列 
 * 特点:反向遍历链表很容易,可以很轻松地实现双端队列。
 */
public class DublyLinkList {

	private Node first; // 链表头部指针
	private Node last; // 链表尾部指针

	/**
	 * 组成链表的结点
	 * 
	 * @param 
	 */
	private static class Node {
		private E data;
		private Node previous;
		private Node next;

		public Node(E e) {
			this.data = e;
			previous = null;
			next = null;
		}
	}

	/**
	 * 头部元素为空
	 * 
	 * @return
	 */
	public boolean isEmpty() {
		return first == null;
	}

	/**
	 * 头部插入元素
	 * 
	 * @param data
	 */
	public void insertFirst(E data) {
		Node newNode = new Node<>(data);
		newNode.next = first;
		// 如果是空链表
		if (isEmpty()) {
			last = newNode;
		} else {
			first.previous = newNode;
		}
		first = newNode;

	}

	/**
	 * 尾部插入元素
	 * 
	 * @param data
	 */
	public void insertLast(E data) {
		Node newNode = new Node<>(data);
		// 如果是空链表
		if (isEmpty()) {
			first = newNode;
		} else {
			last.next = newNode;
			newNode.previous = last;
		}
		last = newNode;
	}

	/**
	 * 删除头部元素
	 * 
	 * @return
	 */
	public E removeFirst() {
		// 空链表,返回null。正确的方法应该抛出异常
		if (isEmpty()) {
			return null;
		}
		E data = first.data;
		Node next = first.next;
		first.next = null;
		first.data = null; // 帮助GC
		first = next;

		// 链表只有一个元素,需要修改尾部指针
		if (next == null) {
			last = null;
		} else {
			next.previous = null;
		}
		return data;
	}

	/**
	 * 删除尾部结点
	 * 
	 * @return
	 */
	public E removeLast() {
		// 空链表,返回null。正确的方法应该抛出异常
		if (isEmpty()) {
			return null;
		}

		E data = last.data;
		Node previous = last.previous;
		last.data = null; // GC
		last.previous = null;
		last = previous;

		// 链表只有一个元素,需要修改头部指针
		if (last == first) {
			first = null;
		} else {
			previous.next = null;
		}
		return data;
	}

	/**
	 * 删除特定的元素
	 * PS:写完,看见linkedList的源码瞬间知道差距
	 * @param e
	 * @return
	 */
	public boolean remove(E e) {
		if (isEmpty()) {
			return false;
		}
		Node current = first;
		if (e == null) {
			while (current != null) {
				if (current.data == e) {
					// 删除的是链表头结点
					if (current == first) {
						first = current.next;
						last = first;
					//刪除的是链表尾部结点	
					}else if(current == last) {
						last = current.previous;
						last.next = null;
					//刪除的是链表中间结点	
					} else {
						current.previous.next = current.next;
						current.next.previous = current.previous;
					}
					current.previous = null;
					current.next = null;
					current.data = null;
					return true;
				}
				current = current.next;
			}
		} else {
			while (current != null) {
				if (e.equals(current.data)) {
					// 删除的是链表头结点
					if (current == first) {
						first = current.next;
						last = null;
					//刪除的是链表尾部结点	
					} else if (current == last) {
						last = current.previous;
						last.next = null;
					//刪除的是链表中间结点
					} else {
						current.previous.next = current.next;
						current.next.previous = current.previous;
					}
					current.previous = null;
					current.next = null;
					current.data = null;
					return true;
				}
				current = current.next;
			}
		}
		return false;
	}

	/**
	 * 插入指定元素后面 插入的位置():头部、尾部或中间。
	 * 
	 * @param data
	 */
	public boolean insertAfter(E key, E data) {
		// 空链表,直接返回fasle
		if (isEmpty()) {
			return false;
		}
		Node newNode = new Node(data);
		Node current = first;
		// 防止空指针异常,代码逻辑分开
		if (key == null) {
			while (current != null && current.data != key) {
				current = current.next;
			}
			// 如果插入的位置是链表尾部
			if (current == last) {
				last = newNode; // 修改尾部指针
				newNode.next = null;
			} else {
				newNode.next = current.next; // 新插入的结点的next指向current.next
				current.next.previous = newNode;// current.next结点的previous指向新结点
			}
			newNode.previous = current;
			current.next = newNode;
		} else {
			while (current != null && !key.equals(current.data)) {
				current = current.next;
			}
			if (current == last) {
				last = newNode;
				newNode.next = null;
			} else {
				newNode.next = current.next;
				current.next.previous = newNode;
			}
			newNode.previous = current;
			current.next = newNode;
		}
		return true;
	}

	/**
	 * 复写toString方法
	 */
	public String toString() {
		if (isEmpty())
			return "[]";
		StringBuilder displayBuilder = new StringBuilder("[");
		Node temp = first;
		while (temp != null) {
			displayBuilder.append(temp.data);
			temp = temp.next;
			if (temp == null) {
				displayBuilder.append("]");
				break;
			}
			displayBuilder.append(", ");
		}
		return displayBuilder.toString();
	}



}

                 

你可能感兴趣的:(数据结构和算法)