数据结构学习----线性表的链式表示(Java实现)
2014-10-15 19:03 雪夜&流星 阅读(1119) 评论(0) 编辑 收藏 举报线性表接口LList:
package com.clarck.datastructure.linked; /** * 线性表接口LList,描述线性表抽象数据类型,泛型参数T表示数据元素的数据类型 * * @author clarck * */ public interface LList<T> { /** * 判断线性表是否空 * @return */ boolean isEmpty(); /** * 返回线性表长度 * @return */ int length(); /** * 返回第i(i≥0)个元素 * @param i * @return */ T get(int i); /** * 设置第i个元素值为x * @param i * @param x */ void set(int i, T x); /** * 插入x作为第i个元素 * @param i * @param x */ void insert(int i, T x); /** * 在线性表最后插入x元素 * @param x */ void append(T x); /** * 删除第i个元素并返回被删除对象 * @param i * @return */ T remove(int i); /** * 删除线性表所有元素 */ void removeAll(); /** * 查找,返回首次出现的关键字为key元素 * @param key * @return */ T search(T key); }
单链表结点类:
package com.clarck.datastructure.linked; /** * 单链表结点类,T指定结点的元素类型 * * @author clarck * * @param <T> */ public class Node<T> { /** * 数据域,保存数据元素 */ public T data; /** * 地址域,引用后继结点 */ public Node<T> next; /** * 构造结点,data指定数据元素,next指定后继结点 * * @param data * @param next */ public Node(T data, Node<T> next) { this.data = data; this.next = next; } /** * 构造节点 */ public Node() { this(null, null); } /** * 返回结点元素值对应的字符串 */ @Override public String toString() { return this.data.toString(); } /** * 比较两个结点值是否相等,覆盖Object类的equals(obj)方法 */ @SuppressWarnings("unchecked") @Override public boolean equals(Object obj) { return obj == this || obj instanceof Node && this.data.equals(((Node<T>)obj).data); } }
线性表的链式表示和实现:
package com.clarck.datastructure.linked; /** * 线性表的链式表示和实现 带头结点的单链表类,实现线性表接口 * * @author clarck * * @param <T> */ public class SinglyLinkedList<T> implements LList<T> { /** * 头指针,指向单链表的头结点 */ public Node<T> head; /** * 默认构造方法,构造空单链表 */ public SinglyLinkedList() { // 创建头结点,data和next值均为null this.head = new Node<T>(); } /** * 由指定数组中的多个对象构造单链表。采用尾插入构造单链表 * 若element==null,Java将抛出空对象异常;若element.length==0,构造空链表 * * @param element */ public SinglyLinkedList(T[] element) { // 创建空单链表,只有头结点 this(); // rear指向单链表最后一个结点 Node<T> rear = this.head; for (int i = 0; i < element.length; i++) { rear.next = new Node<T>(element[i], null); rear = rear.next; } } /** * 判断单链表是否空,O(1) */ @Override public boolean isEmpty() { return this.head.next == null; } /** * 返回单链表长度,O(n), 基于单链表遍历算法 */ @Override public int length() { int i = 0; // p从单链表第一个结点开始 Node<T> p = this.head.next; // 若单链表未结束 while (p != null) { i++; // p到达后继结点 p = p.next; } return i; } /** * 返回第i(≥0)个元素,若i<0或大于表长则返回null,O(n) */ @Override public T get(int i) { if (i >= 0) { Node<T> p = this.head.next; for (int j = 0; p != null && j < i; j++) { p = p.next; } // p指向第i个结点 if (p != null) { return p.data; } } return null; } /** * 设置第i(≥0)个元素值为x。若i<0或大于表长则抛出序号越界异常;若x==null,不操作。O(n) */ @Override public void set(int i, T x) { if (x == null) return; Node<T> p = this.head.next; for (int j = 0; p != null && j < i; j++) { p = p.next; } if (i >= 0 && p != null) { p.data = x; } else { throw new IndexOutOfBoundsException(i + ""); } } /** * 插入第i(≥0)个元素值为x。若x==null,不插入。 若i<0,插入x作为第0个元素;若i大于表长,插入x作为最后一个元素。O(n) */ @Override public void insert(int i, T x) { // 不能插入空对象 if (x == null) { return; } // p指向头结点 Node<T> p = this.head; // 寻找插入位置 for (int j = 0; p.next != null && j < i; j++) { // 循环停止时,p指向第i-1结点或最后一个结点 p = p.next; } // 插入x作为p结点的后继结点,包括头插入(i<=0)、中间/尾插入(i>0) p.next = new Node<T>(x, p.next); } /** * 在单链表最后添加x对象,O(n) */ @Override public void append(T x) { insert(Integer.MAX_VALUE, x); } /** * 删除第i(≥0)个元素,返回被删除对象。若i<0或i大于表长,不删除,返回null。O(n) */ @Override public T remove(int i) { if (i >= 0) { Node<T> p = this.head; for (int j = 0; p.next != null && j < i; j++) { p = p.next; } if (p != null) { // 获得原对象 T old = p.next.data; // 删除p的后继结点 p.next = p.next.next; return old; } } return null; } /** * 删除单链表所有元素 Java将自动收回各结点所占用的内存空间 */ @Override public void removeAll() { this.head.next = null; } /** * 顺序查找关键字为key元素,返回首次出现的元素,若查找不成功返回null * key可以只包含关键字数据项,由T类的equals()方法提供比较对象相等的依据 */ @Override public T search(T key) { if (key == null) return null; for (Node<T> p = this.head.next; p != null; p = p.next) if (p.data.equals(key)) return p.data; return null; } /** * 返回单链表所有元素的描述字符串,形式为“(,)”,覆盖Object类的toString()方法,O(n) */ @Override public String toString() { String str = "("; for (Node<T> p = this.head.next; p != null; p = p.next) { str += p.data.toString(); if (p.next != null) str += ","; // 不是最后一个结点时后加分隔符 } return str + ")"; // 空表返回() } /** * 比较两条单链表是否相等 */ @SuppressWarnings("unchecked") @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj instanceof SinglyLinkedList) { SinglyLinkedList<T> list = (SinglyLinkedList<T>) obj; return equals(this.head.next, list.head.next); } return false; } /** * 比较两条单链表是否相等,递归方法 * * @param p * @param q * @return */ private boolean equals(Node<T> p, Node<T> q) { return p == null && q == null || p != null && q != null && p.data.equals(q.data) && equals(p.next, q.next); } }
测试类:
package com.clarck.datastructure.linked; /** * 单链表的测试 * * @author clarck * */ public class SinglyLinkedList_test { public static void main(String args[]) { SinglyLinkedList<String> lista = new SinglyLinkedList<String>(); for (int i = 0; i <= 5; i++) lista.insert(i, new String((char) ('A' + i) + "")); System.out.println("lista: " + lista.toString() + ",length()=" + lista.length()); lista.set(3, new String((char) (lista.get(0).charAt(0) + 32) + "")); lista.remove(0); lista.remove(3); System.out.println("lista: " + lista.toString()); } }
测试结果:
lista: (A,B,C,D,E,F),length()=6
lista: (B,C,a,F)
源码:https://github.com/clarck/DataStructure/tree/master/LinkedList/src/com/clarck/datastructure/linked
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本