链表简介

什么是链表?

链表就是链式存储的线性表。根据指针域的不同,链表分为单向链表、双向链表、循环链表等等

链表属性

  • 相邻元素之间通过指针链接
  • 最后一个元素的后继指针为NULL
  • 在程序执行过程中,链表的长度可以增加或缩小
  • 链表的空间能够按需分配
  • 没有内存空间的浪费

优点:

  • 插入和删除时不需移动其他元素, 只需改变指针。
  • 链表各个节点在内存中空间不要求连续!空间利用率高。

优点:

  • 访问数组元素效率低。

单向链表

结构

单向链表的类型声明

public class ListNode {
    private int data;
    private ListNode next;

    public ListNode(int data){
        this.data = data;
    }
    
    public void setData(int data){
        this.data = data;
    }
    public int getData(){
        return data;
    }
    public void setNext(ListNode next){
        this.next = next;
    }
    public ListNode getNext(){
        return this.next;
    }
}

遍历单向链表

int ListLength(ListNode headNode){
    int length = 0;
    ListNode current = headNode;
    while(current != null){
        length++;
        current = current.getNext();
    }
    return length;
}

时间复杂度为O(n), 空间复杂度O(1)

单向链表插入元素

单向链表插入操作有以下三种情况: 表头插入,表尾插入,中间插入

ListNode InsertInLinkedList(ListNode headNode, ListNode nodeToInsert, int position){
	if(headNode == null){  // 链表为空时插入
		return nodeToInsert;
	}
	int size = ListLength(headNode);
	if(position > size+1 || position <1){
	    System.out.println("Position of nodeToInsert is invalid");
		return headNode;
	}
	
	if(position == 1){  // 在链表开头插入
		nodeToInsert.setNext(headNode);
		return nodeToInsert;
	}else{  // 在链表中间或末尾插入
		ListNode previousNode = headNode;
		int count = 1;
		while(count < position-1){
			previousNode = previousNode.getNext();
			count++;
		}
		ListNode currentNode = previousNode.getNext();
		nodeToInsert.setNext(currentNode);
		previousNode.setNext(nodeToInsert);
	}
	return headNode;
}

时间复杂度为O(n), 空间复杂度O(1)

链表删除元素

单向链表插入操作有以下三种情况:删除表头元素,删除表尾元素, 删除中间元素

ListNode DeleteNodeFromLinkedList(ListNode headNode, int position){
	int size = ListLength(headNode);
	if(position > size || position < 1){
		return headNode;
	}
	
	if(position == 1){
		ListNode currentNode = headNode.getNext();
		headNode = null;
		return currentNode;
	}else{
		ListNode previousNode = headNode;
		int count = 1;
		while (count < position){
			previousNode = previousNode.getNext();
			count++;
		}
		ListNode currentNode = previousNode.getNext();
		previousNode.setNext(currentNode.getNext());
		currentNode = null;
	}
	return headNode;
}

时间复杂度为O(n), 空间复杂度O(1)

删除单向链表

void DeleteLinkedList(ListNode headNode){
	ListNode auxilaryNode, iterator = headNode;
	while (iterator != null){
		auxilaryNode = iterator.getNext();
		iterator = null;
		iterator = auxilaryNode;
	}
}

时间复杂度为O(n), 空间复杂度O(1)

双向链表

优点:

对于链表中一个给的的结点,可以从两个方向进行操作。

缺点:

  • 每个结点需要再添加一个额外的指针,因此需要更多的空间开销
  • 结点的插入或者删除更加费时

结构

双向链表类型声明

public class DLLNode {
	private int data;
	private DLLNode next;
	private DLLNode previous;
	public DLLNode(int data){
		this.data = data;
	}
	
	public void setData(int data){
		this.data = data;
	}
	public int getData(){
		return data;
	}
	public void setNext(DLLNode next){
		this.next = next;
	}
	public DLLNode getNext(){
		return this.next;
	}
	public void setPrevious(DLLNode previous){
		this.previous = previous;
	}
	public DLLNode getPrevious(){
		return this.previous;
	}
}

双向链表插入元素

单向链表插入操作有以下三种情况:表头插入,表尾插入, 中间插入

DLLNode DLLInsert(DLLNode headNode, DLLNode nodeToInsert, int position) {
	// 链表为空时插入
	if (headNode == null) {
		return nodeToInsert;
	}
	int size = ListLength(headNode);
	if (position > size + 1 || position < 1) {
		System.out.println("Position of nodeToInsert is invalid");
		return headNode;
	}

	if (position == 1) { // 在链表开头插入
		nodeToInsert.setNext(headNode);
		headNode.setPrevious(nodeToInsert);

	} else {// 在链表中间或末尾插入
		DLLNode previousNode = headNode;
		int count = 1;
		while (count < position - 1) {
			previousNode = previousNode.getNext();
			count++;
		}
		DLLNode currentNode = previousNode.getNext();
		nodeToInsert.setNext(currentNode);
		if (currentNode != null) {
			currentNode.setPrevious(nodeToInsert);
		}
		previousNode.setNext(nodeToInsert);
		nodeToInsert.setPrevious(previousNode);
	}
	return headNode;
}

时间复杂度为O(n), 空间复杂度O(1)

双向链表删除元素

单向链表插入操作有以下三种情况:删除表头元素,删除表尾元素,删除中间元素

DLLNode DDLDelete(DLLNode headNode, int position){
	int size = ListLength(headNode);
	// 如果被删除位置不在链表长度范围内,报错并返回
	if(position > size || position < 1){
		System.out.println("Position of node to delete is invalid.");
	}

	if(position == 1){ //删除链表的第一个结点
		DLLNode currentNode = headNode.getNext();
		headNode = null;
		currentNode.setPrevious(null);
		return currentNode;
	}else { //删除中间或表尾结点
		DLLNode previousNode = headNode;
		int count = 1;
		while (count < position - 1){
			previousNode = previousNode.getNext();
			count++;
		}
		DLLNode currentNode = previousNode.getNext();
		DLLNode laterNode = currentNode.getNext();
		previousNode.setNext(laterNode);
		if(laterNode != null){
			// 如果被删除结点的后继结点不是null结点,那么设置其前驱指针指向被删除结点的前驱结点
			laterNode.setPrevious(previousNode);
		}
		currentNode = null;
	}
	return headNode;
}

时间复杂度为O(n), 空间复杂度O(1)

循环链表

结构

类型声明(同单向链表

public class CLLNode {
	private int data;
	private CLLNode next;

	public CLLNode(int data) {
		this.data = data;
	}

	public void setData(int data) {
		this.data = data;
	}

	public int getData() {
		return data;
	}

	public void CLLNode(CLLNode next) {
		this.next = next;
	}

	public CLLNode getNext() {
		return this.next;
	}
}

循环链表的长度

int CircularListLength(CLLNode headNode) {
	int length = 0;
	CLLNode currentNode = headNode;
	while (currentNode != null){
		length++;
		currentNode = currentNode.getNext();
		if(currentNode == headNode){
			break;
		}
	}
	return length;
}

松散链表

松散链表是单向链表的简单变种。松散链表中的每个结点存储多个元素(简称为块)。而每个块中的所有结点由循环链表链接在一起。

结构

posted @ 2018-07-29 21:32  sane33  阅读(823)  评论(0编辑  收藏  举报