链表(Linked List)
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。
一,单向链表(Single Linked List)
单链表是链表中结构最简单的。一个单链表的节点(Node)分为两个部分,第一个部分(data)保存或者显示关于节点的信息,另一个部分存储下一个节点的地址。最后一个节点存储地址的部分指向空值。
单向链表只可向一个方向遍历,一般查找一个节点的时候需要从第一个节点开始每次访问下一个节点,一直访问到需要的位置。而插入一个节点,对于单向链表,我们只提供在链表头插入,只需要将当前插入的节点设置为头节点,next指向原头节点即可。删除一个节点,我们将该节点的上一个节点的next指向该节点的下一个节点。
SingleLinkedList.java
package LinkedList;
public class SingleLinkedList<E> {
private class Node<E>{
private E data;
private Node next;
public Node(E data) {
this.data = data;
}
}
private int size;
private Node head;
public SingleLinkedList(){
int size = 0;
head = null;
}
//在链表头添加元素
public Object add(E e){
Node node = new Node(e);
if(size == 0){
head = node;
}
else{
node.next = head;
head = node;
}
size++;
return e;
}
//在链表头删除元素
public E delete(){
E e = (E) head.data;
head = head.next;
size--;
return e;
}
//查找指定元素,返回,如果无,返回null
public E find(E e){
Node currentNode = head;
int temp = size;
while(temp>0){
if(e.equals(head.data)){
return (E) currentNode;
}
else{
currentNode = currentNode.next;
}
temp--;
}
return null;
}
//删除指定元素
public boolean delete(E e){
if(size == 0){
throw new RuntimeException("链表为空");
}
else{
Node current = head;
Node previous = head;
while (current.data != e){
if(current.next == null){
return false;
}
else{
previous = current;
current = current.next;
}
}
if(current == head){
head = current.next;
size--;
}
else{
previous.next=current.next;
size--;
}
return true;
}
}
//判空
public boolean empty(){
return size==0?true:false;
}
//显示节点信息
public void show(){
if(size>0){
Node current = head;
int temp =size;
while (temp!=0){
//System.out.println(temp);
if(temp==size){
System.out.print("["+current.data+">");
current = current.next;
}
else if(temp==1){
System.out.print(current.data+"]");
}
else{
System.out.print(current.data+">");
current = current.next;
}
temp--;
}
}
}
public static void main(String[] args) {
SingleLinkedList sll = new SingleLinkedList();
sll.add("a");
sll.add("b");
sll.add("c");
sll.add("d");
sll.show();
sll.delete();
sll.show();
sll.delete("b");
sll.show();
}
}
二,双端链表(Double Point Linked List)
对于单项链表,我们如果想在尾部添加一个节点,那么必须从头部一直遍历到尾部,找到尾节点,然后在尾节点后面插入一个节点。这样操作很麻烦,如果我们在设计链表的时候多个对尾节点的引用,那么会简单很多。
package LinkedList;
public class DoublePointLinkedList<E> {
private class Node<E>{
private E data;
private Node next;
public Node(E data) {
this.data = data;
}
}
private Node head;
private Node rear;
private int size;
public DoublePointLinkedList() {
size=0;
head = rear = null;
}
public E addHead(E e){
Node node = new Node(e);
if(size==0){
head = rear = node;
}
else{
node.next = head;
head = node;
}
size++;
return e;
}
//在尾部添加元素
public E addRear(E e){
Node node = new Node(e);
if(size==0){
head = rear = node;
}
else{
rear.next = node;
node.next=head;
rear = node;
}
size++;
return e;
}
//删除头节点
public void deleteHead(){
if(size==0){
throw new RuntimeException("链表为空");
}
else{
rear.next = head.next;
head=head.next;
}
size--;
}
//显示节点信息
public void show(){
if(size>0){
Node current = head;
int temp =size;
while (temp!=0){
//System.out.println(temp);
if(temp==size){
System.out.print("["+current.data+">");
current = current.next;
}
else if(temp==1){
System.out.print(current.data+"]");
}
else{
System.out.print(current.data+">");
current = current.next;
}
temp--;
}
}
}
public static void main(String[] args) {
DoublePointLinkedList dpll = new DoublePointLinkedList();
dpll.addHead("a");
dpll.addHead("b");
dpll.addHead("c");
dpll.show();
dpll.addRear("d");
dpll.deleteHead();
dpll.show();
}
}
三,有序链表(Order Linked List)
前面的链表实现插入数据都是无序的,在有些应用中需要链表中的数据有序,这称为有序链表。
在有序链表中,数据是按照关键值有序排列的。一般在大多数需要使用有序数组的场合也可以使用有序链表。有序链表优于有序数组的地方是插入的速度(因为元素不需要移动),另外链表可以扩展到全部有效的使用内存,而数组只能局限于一个固定的大小中。
package LinkedList;
public class OrderLinkedList {
private class Node{
private int data;
private Node next;
public Node(int data) {
this.data = data;
}
}
private Node head;
private int size;
public OrderLinkedList() {
size=0;
head=null;
}
public void add(int value) {//3281
Node node = new Node(value);
Node currentNode = head;
Node previousNode = null;
while (currentNode != null && value>currentNode.data) {
previousNode = currentNode;
currentNode = currentNode.next;
}
if(previousNode == null){
head = node;
head.next=currentNode;
}
else{
previousNode.next=node;
node.next=currentNode;
}
size++;
//System.out.println(value);
}
//删除头节点
public void delete(){
head = head.next;
}
//display
public void display(){
Node currentNode = head;
while (currentNode != null){
System.out.print(currentNode.data+" ");
currentNode = currentNode.next;
}
System.out.println();
}
//测试方法
public static void main(String[] args) {
OrderLinkedList oll = new OrderLinkedList();
oll.add(3);
oll.add(2);
oll.add(8);
oll.add(1);
oll.display();
oll.delete();
oll.display();
}
}
四,双向链表(Two Way Linked List)
单向链表只能从一个方向遍历,那么双向链表它可以从两个方向遍历。
package LinkedList;
public class TwoWayLinkedList<E> {
private class Node<E>{
private E data;
private Node previous;
private Node next;
public Node(E data) {
this.data = data;
}
}
private Node head;
private Node rear;
private int size;
public TwoWayLinkedList() {
size=0;
head = rear =null;
}
//表头添加
public void addHead(E e){
Node node = new Node(e);
if(size==0){
head = rear = node;
}
else{
node.next=head;
head.previous = node;
head = node;
}
size++;
}
//表尾添加
public void addRear(E e){
Node node = new Node(e);
if(size==0){
head = rear = node;
}
else{
rear.next = node;
node.previous = rear;
rear = node;
}
size++;
}
//删除头
public boolean deleteHead(){
if(size == 0){
throw new RuntimeException("链表为空");
}
else{
head = head.next;
head.previous = null;
size--;
return true;
}
}
public boolean deleteRear(){
if(size == 0){
throw new RuntimeException("链表为空");
}
else{
rear = rear.previous;
rear.next = null;
size--;
return true;
}
}
//显示节点
public void display(){
Node currentNode = head;
while (currentNode != null){
System.out.print(currentNode.data+" ");
currentNode = currentNode.next;
}
System.out.println();
}
//测试方法
public static void main(String[] args) {
TwoWayLinkedList twll = new TwoWayLinkedList();
twll.addHead("a");
twll.addHead("b");
twll.addRear("1");
twll.addHead("2");
twll.display();
twll.deleteHead();
twll.display();
twll.deleteRear();
twll.display();
}
}