数据结构与算法经典问题解析-Java语言描述(第三章链表(前半部分))

2020年2月25日
1.什么是链表?
链表是一种用于存储数据集合的数据结构。链表有以下属性:
相邻元素之间通过指针连接。
最后一个元素的后继指针值为NULL。
在程序执行过程中,链表的长度可以增加或缩小。
链表的空间能够按需分配(直到系统内存耗尽)。
没有内存空间的浪费(但是链表中的指针需要一些额外的内存开销)

2.链表抽象数据类型
链表抽象数据类型中的操作如下:
链表的主要操作:
插人:插人一个元素到链表中。
删除:移除并返回链表中指定位置的元素。
链表的辅助操作:
删除链表:移除链表中的所有元素(清空链表)。
计数:返回链表中元素的个数。
査找:寻找从链表表尾开始的第n个结点(node) 。

3.链表和数组的区别
3.1数组的优缺点:
优点:
简单且易用。
访问元素快(常数时间)。
缺点:
大小固定:数组的大小是静态的(在使用前指定数组的大小)。
分配一个连续空间块:数组初始分配空间时,有时无法分配能存储整个数组的内存空间(当数组规模太大时)。
基于位置的插入操作实现复杂:如果要在数组中的给定位置插入元素,可能需要移 动存储在数组中的其他元素,这样才能腾出指定的位置来放插人的新元素。如果在数组的开始位置插人元素,那么移动操作的开销将更大。
3.2链表的优缺点
优点:
可以在常数时间内扩展。当创建数组时, 必须分配能存储一定数董元素的内存。对于链表,初始时仅需要分配一个元素的存储空间,并且添加新的元素也很容易,不需要做任何内存复制和重新分配操作。
缺点:
尽管链表的动态分配存储空间有很大的优势,但在存储和检索数据的开销方面却有很大的不足。有时很难对链表操作。如果要删除最后一项,倒数第二项必须更改后继指针值为 NULL。这需要从头遍历链表,找到倒数第二个结点的链接,并设置其后继指针为NULL。
数组是随机存取的,即存取数组中任一元素的时间开销为O(1)。而链表在最差情况下访问一个元素 的开销为O(n)。数组在存取时间方面的另外一个优点是内存的空间局部性。由于数组被 定义为连续的内存块,所以任何数组元素与其邻居是物理相邻的。这极大得益于现代 CPU的缓存模式。
数组与链表的比较:
数据结构与算法经典问题解析-Java语言描述(第三章链表(前半部分))_第1张图片
4.单向链表
4.1单向链表定义
链表通常是指向单向链表,它包含多个结点,每个结点有一个指向后继元素的next(下一个)指针。表中最后一个结点的next指针值为NULL,表示该链表的结束。
4.2单向链表类型声明代码

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;
	}

}

4.3遍历链表代码

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

时间复杂度为O(n),用于扫描长度为n的链表。空间复杂度为O(1),仅用于创建临时变量。
4.4插入结点代码

ListNode InsertInLinkedList (ListNode headNode, ListNode /*新结点*/nodeTonlnsert, int position) {
	if(headNode == null)	//若链表为空,插入
		return nodeToInsert;
	int size = ListLength(headNode);
	if(position > size + 1 || position < 1){
	System.out.println("该位置不可插入,正确插入的位置是1至" +(size+1));
	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),仅用于创建一个临时变量。
4.5删除结点代码

ListNode DeleteNodeFromLinkedList(ListNode headNode, int position) {
		int size = getLinkedListLength(headNode){
			System.out.println("输入删除位置为无效位置,有效输入位置为1"+ size);
			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),仅用于创建一个临时变量。
4.6删除单向链表代码

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

时间复杂度为O(n),用于扫描大小为n的整个链表。
空间复杂度为O(1),用于创建临时变量。

你可能感兴趣的:(笔记)