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

本文深入探讨链表数据结构,包括链结点概念、链表类型如单链表、双端链表和双向链表的特性及优势,并提供详细的代码实现示例,对比数组,阐述链表的灵活性和高效插入操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、学习目标

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

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

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

 

二、什么是链表

           1、链结点

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

          2、链表

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

 

         3、对比数组优缺点

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

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

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

 

三、相关链表的代码实现     

     

         1、单链表

              

  

/**
 * 单链表
 * 
 */
public class DefLinkList<E> {

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

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

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

	/**
	 * 向链表头部位置插入元素
	 * 
	 * @param data
	 */
	public void insertFirst(E data) {
		Node<E> node = new Node<E>(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<E> previous = first;
		Node<E> 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<E> temp = first; temp != null; temp = temp.next) {
				if (temp.element == null) {
					return true;
				}
			}
		} else {
			for (Node<E> 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<E> 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> {

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

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

	}
}

 

2、双端链表

     

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

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

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

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

	/**
	 * 链表尾部插入
	 * 
	 * @param e
	 */
	public void insertLast(E e) {
		Node<E> current = new Node<E>(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<E> current = first;
			Node<E> 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<E> current = first; current != null; current = current.next) {
				if (current.data == e) {
					return true;
				}
			}
		} else {
			for (Node<E> 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<E> current; 
		Node<E> 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<E> temp = first;
		while (temp != null) {
			displayBuilder.append(temp.data);
			temp = temp.next;
			if (temp == null) {
				displayBuilder.append("]");
				break;
			}
			displayBuilder.append(", ");
		}
		return displayBuilder.toString();
	}

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

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

}

 

 3、双向链表

       

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

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

	/**
	 * 组成链表的结点
	 * 
	 * @param <E>
	 */
	private static class Node<E> {
		private E data;
		private Node<E> previous;
		private Node<E> 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<E> 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<E> 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<E> 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<E> 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<E> 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<E> newNode = new Node<E>(data);
		Node<E> 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<E> temp = first;
		while (temp != null) {
			displayBuilder.append(temp.data);
			temp = temp.next;
			if (temp == null) {
				displayBuilder.append("]");
				break;
			}
			displayBuilder.append(", ");
		}
		return displayBuilder.toString();
	}



}

                 

内容概要:本文围绕直流微电网中带有恒功率负载(CPL)的DC/DC升压转换器的稳定控制问题展开研究,提出了一种复合预设性能控制策略。首先,通过精确反馈线性化技术将非线性不确定的DC转换器系统转化为Brunovsky标准型,然后利用非线性扰动观测器评估负载功率的动态变化输出电压的调节精度。基于反步设计方法,设计了具有预设性能的复合非线性控制器,确保输出电压跟踪误差始终在预定义误差范围内。文章还对比了多种DC/DC转换器控制技术如脉冲调整技术、反馈线性化、滑模控制(SMC)、主动阻尼法基于无源性的控制,并分析了它们的优缺点。最后,通过数值仿真验证了所提控制器的有效性优越性。 适合人群:从事电力电子、自动控制领域研究的学者工程师,以及对先进控制算法感兴趣的研究生及以上学历人员。 使用场景及目标:①适用于需要精确控制输出电压并处理恒功率负载的应用场景;②旨在实现快速稳定的电压跟踪,同时保证系统的鲁棒性抗干扰能力;③为DC微电网中的功率转换系统提供兼顾瞬态性能稳态精度的解决方案。 其他说明:文中不仅提供了详细的理论推导算法实现,还通过Python代码演示了控制策略的具体实现过程,便于读者理解实践。此外,文章还讨论了不同控制方法的特点适用范围,为实际工程项目提供了有价值的参考。
内容概要:该论文介绍了一种名为偏振敏感强度衍射断层扫描(PS-IDT)的新型无参考三维偏振敏感计算成像技术。PS-IDT通过多角度圆偏振光照射样品,利用矢量多层光束传播模型(MSBP)梯度下降算法迭代重建样品的三维各向异性分布。该技术无需干涉参考光或机械扫描,能够处理多重散射样品,并通过强度测量实现3D成像。文中展示了对马铃薯淀粉颗粒缓步类动物等样品的成功成像实验,并提供了Python代码实现,包括系统初始化、前向传播、多层传播、重建算法以及数字体模验证等模块。 适用人群:具备一定光学成像编程基础的研究人员,尤其是从事生物医学成像、材料科学成像领域的科研工作者。 使用场景及目标:①研究复杂散射样品(如生物组织、复合材料)的三维各向异性结构;②开发新型偏振敏感成像系统,提高成像分辨率对比度;③验证优化计算成像算法,应用于实际样品的高精度成像。 其他说明:PS-IDT技术相比传统偏振成像方法具有明显优势,如无需干涉装置、无需机械扫描、可处理多重散射等。然而,该技术也面临计算复杂度高、需要多角度数据采集等挑战。文中还提出了改进方向,如采用更高数值孔径(NA)物镜、引入深度学习超分辨率技术等,以进一步提升成像质量效率。此外,文中提供的Python代码框架为研究人员提供了实用的工具,便于理解应用该技术。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值