算法题002 -- [删除链表中重复的结点] by java

本文介绍了一种在排序链表中删除重复节点的算法,通过设置计数器标记重复节点,然后进行调节删除,实现链表去重。讨论了时间复杂度为O(n^2)的解决方案,并提到了一种基于桶排序思想的潜在改进方案。

程序运行截图:
在这里插入图片描述

public class Node {

	public static final int HEAD = 0X66;
	
	public int value;
	
	public int count;
	
	public Node preNode;
	
	public Node nextNode;
	
	public Node() {}
	
	public Node(int value,Node nextNode) {
		this.value = value;
		this.nextNode = nextNode;
	}
	
}
package algorithm2;

public class Algorithm2 {
	
	/*
	 * [删除链表中重复的结点]
	 * 
	 * [题目] 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。
	 * 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
	 * 
	 * [解析] 主要考代码的能力,注意边界条件的考虑,指针判空等。
	 */

	public static void main(String[] args) {
		Node testNode = createTestLinked();
		printNode(testNode);
		testNode = filterNode(testNode);
		printNode(testNode);
	}

	/**
	 * 创建测试链表
	 * 为了方便最后返回链表头指针,我给链表创建了一个 HEAD 节点->
	 * 这样不管后面的节点如何变动,我只要保证结果链表的结果正确,并且与 HEAD 节点依然链接在一起就好了
	 * @return
	 */
	private static Node createTestLinked() {
		Node node5 = new Node(5, null);
		Node node4_2 = new Node(4, node5);
		Node node4_1 = new Node(4, node4_2);
		Node node3_2 = new Node(3, node4_1);
		Node node3_1 = new Node(1, node3_2);
		Node node2 = new Node(2, node3_1);
		Node node1 = new Node(2, node2);
		node5.preNode = node5;
		node4_2.preNode = node4_1;
		node4_1.preNode = node3_2;
		node3_2.preNode = node3_1;
		node3_1.preNode = node2;
		node2.preNode = node1;
		Node headNode = new Node(Node.HEAD, node1);
		node1.preNode = headNode;
		return headNode;
	}

	/**
	 * 打印链表,不打印 HEAD 节点
	 * @param node
	 */
	private static void printNode(Node node) {
		Node preNode = node.nextNode;
		StringBuilder sb = new StringBuilder();
		sb.append(preNode.value + ":: count = " + preNode.count + "-> ");
		while (preNode.nextNode != null) {
			sb.append(preNode.nextNode.value + ":: count = " + preNode.nextNode.count + "-> ");
			preNode = preNode.nextNode;
		}
		System.out.println(sb.toString());
	}
	
	/**
	 *	删除重复节点的方法,可惜的是这个方法的时间复杂度是 O(n^2)
	 *
	 *	其实还有一种有缺陷的思路 ->
	 *  那就类似于桶排序的思路:
	 *  如果知道取值value的取值范围,并且跨度不大,在能接受范围内的话->
	 *  可以以value为index,为这个链表的值创建一个数组,而数组的内容则是index作为value出现在链表中的次数->
	 *  然后遍历链表,将其与数组互为对照,这样也能知道重复的节点。然后由于这个方法的局限性和缺陷就不编码了。->
	 *  然而,一旦符合上面的一些限制,那么这个思路的时间复杂度 O(n)
	 * @param node
	 * @return
	 */
	private static Node filterNode(Node node) {
		Node compare = node.nextNode;
		// 在 Node 节点中设置标记,在将每一个节点依次拿出来与所有节点对比时,如果相等标记递增
		while (compare != null) {
			Node nextNode = node.nextNode;
			while (nextNode != null) {
				if (nextNode.value == compare.value) {
					compare.count++;
				}
				nextNode = nextNode.nextNode;
			}
			compare = compare.nextNode;
		}
		// 在全部比较之后再将重复的节点调节
		compare = node.nextNode;
		while(compare != null) {
			if(compare.count != 1) {
				compare.preNode.nextNode = compare.nextNode;
				compare.nextNode.preNode = compare.preNode;
			}
			compare = compare.nextNode;
		}
		return node;
	}
}

<think>首先,用户在问:“我想了解Java链表是否需要自己创建 请问Java链表是否需要自己创建”,翻译成英文是“I want to know if Java linked lists need to be created by myself? Does Java linked lists need to be created by myself?”。这是关于Java链表是否需要用户自定义实现的查询。 现在,系统指令要求我参考提供的站内引用。这些引用是关于Java链表的创建和实现的。让我回顾一下这些引用: - 引用[1]: 描述了链表的基本结构,如`ListNode`类。 - 引用[2]: 展示了一个链表接口`Linklist`,定义了各种方如头插、尾插等。 - 引用[3]: 解释了链表的概念,包括不同类型的链表(如单向、双向)和复杂度。 - 引用[4]: 讨论了双向链表的实现,包括代码示例。 - 引用[5]: 提供了单链表的创建和使用示例,包括迭代器的实现。 这些引用都强调了链表Java中可以通过自定义类来实现,但没有直接提到Java标准库中的链表类。 我需要回答用户的问题:Java链表是否需要自己创建? 核心点是:在Java中,链表可以是自定义实现的,也可以是使用Java集合框架中的现有类。具体来说: - **自定义实现**:用户可以根据需要创建自己的链表类,如引用中所示,使用`ListNode`类来定义节点,并手动实现添加、删除等方- **标准库实现**:Java提供了`java.util.LinkedList`类,它是一个双向链表的实现,可以直接使用,不需要自己从头创建。 所以,答案应该是:不一定需要自己创建。Java标准库中已经有`LinkedList`类,可以直接用它。但如果需要特定功能或用于学习目的,用户可以自己创建链表。 系统指令要求回答结构清晰,逐步解决问题,并使用中文回答。还要确保LaTeX语正确,数学表达式用$...$。 回答结构: 1. **澄清问题**:解释Java链表的两种方式:自定义和使用标准库。 2. **解释标准库**:提到`java.util.LinkedList`,它是双链表实现,可以直接导入使用。 3. **解释自定义实现**:参考引用,用户可以选择自己创建链表类,如定义节点类和方。 4. **优缺点比较**:简要说明为什么可能需要自己创建(如学习、特定需求)或使用库(方便、高效)。 5. **结论**:给出直接答案,Java链表不一定需要自己创建,取决于需求。 最后,系统指令要求生成相关问题。在回答结束时,添加相关问题。 引用标识:在回答中,如果引用信息,末尾添加引用标识,如[^1]。但用户提供的引用是站内引用,我需要参考它们的内容。 基于引用: - 引用[1]、[2]、[5]展示自定义单链表的实现。 - 引用[4]展示双向链表的自定义实现。 - 引用[3]讨论链表的概念和类型。 没有提到标准库,但作为知识,我应该补充。 在回答中,自然地融入引用内容。 回答大纲: - 引言:直接回答用户问题。 - 解释:分点讨论自定义和标准库。 - 示例:如果需要,提供简单代码片段(但用户问题不需要代码,只需解释)。 - 结论:总结。 - 相关问题:生成几个相关的问题。 相关问题示例: 1. Java中如何直接使用`LinkedList`类? 2. 自定义链表Java标准`LinkedList`有什么区别? 3. 链表Java中的常见应用场景? 确保LaTeX:如果提到数学表达式,如时间复杂度,使用$...$。例如,链表插入时间复杂度为$O(1)$。 最终回答用中文。 </think>### Java链表是否需要自己创建? 在Java中,链表是否需要自己创建取决于具体需求。Java提供了标准的链表实现类(如`java.util.LinkedList`),您可以直接使用它而无需重新发明轮子;但如果您有特定功能需求(如学习数据结构、定制节点行为或优化某些操作),也可以自己创建链表类。下面我将逐步解释,帮助您清晰理解。 #### 1. **Java标准库提供了现成的链表实现** - Java集合框架中内置了`java.util.LinkedList`类,它是一个高效的双向链表实现。您只需导入包即可直接使用,无需自己编基础代码。 - 例如,创建和使用一个链表非常简单: ```java import java.util.LinkedList; LinkedList<String> list = new LinkedList<>(); list.add("元素1"); // 尾部插入 list.addFirst("元素0"); // 头部插入 ``` - 标准`LinkedList`支持多种操作,如添加、删除、遍历等,时间复杂度为$O(1)$(插入/删除头部或尾部)或$O(n)$(随机访问),这比线性表在某些场景更高效[^3]。 - **优点**:方便快捷、性能优化、线程安全考虑(需同步处理),适合大多数应用场景(如数据处理、算实现)。 - **何时不需要自己创建**:如果您只是需要基本链表功能(如存储数据、迭代访问),直接使用标准库即可节省开发时间。 #### 2. **用户可能需要自己创建链表的情况** - 参考提供的引用内容,许多开发者在特定场景下选择自定义链表- **学习目的**:如您在LeetCode刷题时遇到需要对链表深度操作(如从尾到头打印),自定义实现有助于理解底层机制。例如,引用[1]展示了基本链表节点结构: ```java public class ListNode { int val; // 结点数据 ListNode next; // 指向下一个结点对象 } ``` 您需要手动实现遍历、插入或删除,这与引用[2]中的接口方(如`addFirst()`或`remove(int key)`)类似。 - **定制功能需求**:如果标准`LinkedList`不满足特定需求(如特殊节点类型、内存优化或非标准遍历),您可以自己创建。引用[4]演示了双向链表的自定义实现,包括`previous`指针和前驱操作;引用[5]则提供了单链表完整示例,包括迭代器和添加方- **性能优化**:在某些场景(如高频插入删除),自定义链表可以针对特定算优化,复杂度可能优于通用实现(例如,$O(1)$的头部插入在自定义链表中更容易控制)[^3]。 - **优点**:灵活性强、可深度控制细节,适合教学或高性能场景。 - **缺点**:开发成本高,易出错(如指针管理),且可能不如标准库优化充分。 #### 3. **总结:是否需要自己创建?** - **不一定需要**:对于日常开发,直接用`java.util.LinkedList`是最佳选择(推荐优先使用)。 - **推荐自定义的情况**: - 学习数据结构和算(如刷题练习)。 - 需要高度定制功能(如特殊节点属性或内存管理)。 - 项目有性能瓶颈,标准库无满足。 - 总之,Java链表不一定需要自己创建——标准库已覆盖常见需求,但自定义是可行的扩展路径。如果您是初学者,建议先从标准库入手,再逐步探索自定义实现以加深理解[^1][^3][^5]。 ### 相关问题 1. Java中如何直接使用`LinkedList`类进行基本操作(如插入、删除和遍历)? 2. 自定义链表Java标准`LinkedList`在性能和功能上有什么区别? 3. 链表Java编程中的常见应用场景有哪些(如算法题、数据存储)? 4. 如何优化自定义链表的性能(例如,减少时间复杂度)? 5. 双向链表单链表在实现和使用上有什么不同?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值