单链表——基本操作
1.获取链表第i个数据的算法思路
- 声明一个结点p指向链表第一个结点,初始化j从1开始
- 当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1
- 若到链表末尾p为空,则说明第i个元素不存在
- 否则查找成功,返回结点p的数据
2.单链表第i个数据插入结点的算法思路
- 声明一结点p指向链表第一个结点,初始化j从1开始
- 当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1
- 若到链表末尾p为空,则说明第i个元素不存在
- 否则查找成功,在系统中生成一个空结点s
- 将数据元素e赋值给s->data
- 单链表的插入标准语句s->next=p->next;p->next=s
- 返回成功
3.单链表第i个数据删除结点的算法思路
- 声明一结点p指向链表第一个结点,初始化j从1开始
- 当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1
- 若到链表末尾p为空,则说明第i个元素不存在
- 否则查找成功,将欲删除的结点p->next赋值给q
- 单链表的删除标准语句p->next=q->next
- 将q结点中的数据赋值给e,作为返回
- 释放q结点
- 返回成功
分析一下刚才我们讲解的单链表插入和删除算法,我们发现,它们其实都是由两部分组成:第一部分就是遍历查找第i个结点;第二部分就是插入和删除结点。
4.单链表整表创建的算法思路
- 声明一结点p和计数器变量i
- 初始化一空链表L
- 让L的头结点的指针指向NULL,即建立一个带头结点的单链表
- 循环:
- 生成一新结点赋值给p
- 随机生成一数字赋值给p的数据域p->data
- 将p插入到头结点与前一新节点之间
头插法,尾插法
5.单链表的整表删除
- 声明一结点p和q
- 将第一个结点赋值给p
- 循环:
- 将下一结点赋值给q
- 释放p
- 将q赋值给p
Java代码实现,以下代码实现了单链表的各种操作
/** * 结点类的描述 * * 单链表是由若干个结点连接而成,要实现单链表,首先需要设计结点类 * * 结点类由data和next组成 * * data是数据域,用来存放数据元素的值 * next是指针域,用来存放后继结点的地址 * * @author acer * */ public class Node { private Object data; //存放结点值 private Node next; //后继结点的引用 //无参数时的构造方法 public Node() { this(null, null); } //带一个参数的构造方法 public Node(Object data) { this(data, null); } //带两个参数的构造方法 public Node(Object data, Node next) { this.data = data; this.next = next; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } }
创建一个接口
public interface IList { public void clear(); public boolean isEmpty(); public int length(); public Object get(int i) throws Exception; public void insert(int i, Object x) throws Exception; public void remove(int i) throws Exception; public int indexOf(Object x); public void display(); }
/** * 单链表类的描述 * * 由于单链表只需一个头指针就能唯一的标示它,所以单链表类的成员变量只需设置一个头指针即可 * * @author acer * */ public class LinkList implements IList { //单链表的头指针 private Node head; public LinkList() { //初始化头结点 head = new Node(); } public LinkList(int n, boolean Order) throws Exception { this(); if (Order) { create1(n); } else { create2(n); } } // 用尾插法顺序建立单链表,其中n为单链表的结点个数 public void create1(int n) throws Exception { //构造用于输入对象 Scanner sc = new Scanner(System.in); for(int j = 0; j < n; j++) { //生成新结点,插入到表尾 insert(length(), sc.next()); } } // 用头插法逆位序建立单链表,其中n为单链表的结点个数 public void create2(int n) throws Exception { /* * 构造用于输入对象 * * Scanner 使用分隔符模式将其输入分解为标记,默认情况下该分隔符模式与空白匹配 * 然后可以使用不同的 next 方法将得到的标记转换为不同类型的值。 */ Scanner sc = new Scanner(System.in); for (int j = 0; j < n; j++) { //生成新结点,插入到表头 insert(0, sc.next()); } } // 将一个已经存在的带头结点单链表置为空表 @Override public void clear() { head.setData(null); head.setNext(null); } // 判断带头结点的单链表是否为空 @Override public boolean isEmpty() { return head.getNext() == null; } // 求带头结点的单链表的长度 @Override public int length() { Node p = head.getNext(); int lenth = 0; while(p != null) { p = p.getNext(); ++lenth; } return lenth; } /* * 读取带头结点的单链表中的第i个结点 * * 时间复杂度为O(n) */ @Override public Object get(int i) throws Exception { // 初始化,p指向首结点,j为计数器 Node p = head.getNext(); int j = 0; // 从首结点开始向后查找,直到p指向第i个结点或p为空 while (p != null && j < i) { // 指向后继结点 p = p.getNext(); // 极速器加1 ++j; } // i小于0或者大于表长减1 if (j > i || p == null) { // 抛出异常 throw new Exception("第" + i + "个元素不存在"); } // 返回结点p的数据域的值 return p.getData(); } /* * 在带头结点的单链表中的第i个结点之前插入一个值为x的新结点 * * 时间复杂度为O(n) */ @Override public void insert(int i, Object x) throws Exception { // 初始化,p指向首结点,j为计数器 Node p = head; int j = -1; // 寻找第i个结点的前驱 while (p != null && j < i - 1) { p = p.getNext(); // 计数器加1 ++j; } // i不合法 if (j > i - 1 || p == null) { // 抛出异常 throw new Exception("插入位置不合法"); } // 生成新结点 Node s = new Node(x); // 修改链,使新结点插入到单链表中 s.setNext(p.getNext()); p.setNext(s); } /* * 在不带头结点的单链表的第i个结点之前插入一个数据域值为x的新结点 * * 时间复杂度为O(n) */ public void insert2(int i, Object x) throws Exception { // 初始化,p指向首结点,j为计数器 Node p = head; int j = 0; // 用i = -1\0\1测试 while (p != null && j < i - 1) { p = p.getNext(); ++j; } if (j > i || p == null) { throw new Exception("插入位置不合法"); } Node s = new Node(x); // 插入位置为表头时 if (i == 0) { s.setNext(head); head = s; } // 插入位置为表的中间或表尾时 else { s.setNext(p.getNext()); p.setNext(s); } } /* * 删除单链表中的第i个结点 * * 时间复杂度为O(n) */ @Override public void remove(int i) throws Exception { // 初始化,p指向首结点,j为计数器 Node p = head; int j = -1; // 寻找第i个结点的前驱 while (p.getNext() != null && j < i - 1) { p = p.getNext(); ++j; } if (j > i - 1 || p.getNext() == null) { throw new Exception("删除位置不合法"); } // 修改链指针,使待删除结点从单链表中脱离 p.setNext(p.getNext().getNext()); } /* * 在单链表中查找值为x的结点 * * 时间复杂度为O(n) */ @Override public int indexOf(Object x) { // 初始化,p指向首结点,j为计数器 Node p = head.getNext(); int j = 0; // 下面从单链表中的首结点开始查找,直到p.getData()为x或到达单链表的表尾 while (p != null && !p.getData().equals(x)) { // 指向下一个结点 p = p.getNext(); // 计数器加1 ++j; } // 若p指向单链表中的某个结点,返回值为x的结点在单链表中的位置 if (p != null) { return j; } else { // 值为x的结点不在链表中 return -1; } } // 输出单链表中的所有结点 @Override public void display() { //取出带头结点的单链表中的首结点 Node p = head.getNext(); while(p != null) { //输出结点的值 System.out.print(p.getData() + " "); //取下一个结点 p = p.getNext(); } //换行 System.out.println(); } }