数据结构学习----线性表的链式表示之升序排序的单链表(Java实现)
2014-10-15 20:14 雪夜&流星 阅读(653) 评论(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; /** * 建立按升序排序的单链表 按升序排序的单链表类,继承单链表类,T必须实现Comparable<T>接口,支持对象可比较大小 * * @author clarck * * @param <T> */ public class SortedSinglyLinkedList<T extends Comparable<T>> extends SinglyLinkedList<T> { public SortedSinglyLinkedList() { super(); } /** * 将elements数组中所有对象插入构造排序的单链表,直接插入排序 * * @param element */ public SortedSinglyLinkedList(T[] element) { // 创建空单链表,调用父类默认构造方法 super(); // 插入一个结点,根据值的大小决定插入位置 for (int i = 0; i < element.length; i++) { this.insert(element[i]); } } /** * 插入值为x结点,根据x值大小插入在合适位置 重载父类的insert(i,x)方法,因参数不同没有覆盖父类的insert(i,x)方法 * * @param x */ public void insert(T x) { if (x == null) return; // front是p的前驱结点 Node<T> front = this.head; Node<T> p = front.next; // 寻找插入位置 while (p != null && p.data.compareTo(x) < 0) { front = p; p = p.next; } // 创建结点插入在front之后,p之前 front.next = new Node<T>(x, p); } /** * 不支持父类的insert(int i, T x)方法,将其覆盖并抛出异常。 */ @Override public void insert(int i, T x) { throw new UnsupportedOperationException("insert(int i, T x)"); // 本类不支持该方法 } /** * 不支持父类的append(T x)方法,将其覆盖并抛出异常。 */ @Override public void append(T x) { throw new UnsupportedOperationException("append(T x)"); // 本类不支持该方法 } /** * 删除首次出现的值为x结点,若没找到指定结点则不删除。O(n) 重载父类的remove(T x)方法,因参数不同没有覆盖父类的remove(T * x)方法 * * @param x */ public void remove(T x) { if (x == null) return; // front是p的前驱结点 Node<T> front = this.head; Node<T> p = front.next; // 寻找待删除的结点 while (p != null && p.data.compareTo(x) < 0) { front = p; p = p.next; } // 删除p结点 if (p != null && p.data.compareTo(x) == 0) front.next = p.next; } /** * 顺序查找关键字为key元素,返回首次出现的元素,若查找不成功返回null * key可以只包含关键字数据项,由T类的compareTo()提供比较对象大小和相等的依据 覆盖父类SinglyLinkedList的同名方法 */ @Override public T search(T key) { if (key == null) return null; for (Node<T> p = this.head.next; p != null && p.data.compareTo(key) <= 0; p = p.next) // 由compareTo()提供比较对象大小和相等的依据 if (p.data.compareTo(key) == 0) return p.data; return null; } }
升序排序的单链表的测试类:
package com.clarck.datastructure.linked; /** * 升序排序的单链表类 测试类 * * @author clarck * */ public class SortedSinglyLinkedList_test { /** * 返回产生n个随机数的数组 * @param n * @return */ public static Integer[] random(int n) { Integer[] elements = new Integer[n]; for (int i = 0; i < n; i++) elements[i] = (int) (Math.random() * 100); // 产生随机数 return elements; } public static void main(String args[]) { SortedSinglyLinkedList<Integer> list1 = new SortedSinglyLinkedList<Integer>(random(9)); // 插入指定值结点,调用排序单链表类的方法 list1.insert(-2); System.out.println("list1: " + list1.toString()); // 删除最后结点,参数类型是int,调用单链表类的方法 list1.remove(list1.length() - 1); // 删除首个结点,参数类型是Integer,调用排序单链表类的方法 list1.remove(list1.get(0)); // 删除指定值结点,可能没删除 list1.remove(new Integer(50)); System.out.println("list1: " + list1.toString()); } }
测试结果:
list1: (-2,0,14,15,22,27,46,47,55,94)
list1: (0,14,15,22,27,46,47,55)
源码:https://github.com/clarck/DataStructure/tree/master/Linear/src/com/clarck/datastructure/linear
【推荐】国内首个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搭建本