1.什么是单链表
链表里的数据是以节点的方式表示的,每一个结点的组成是由:元素+指针来组成的,元素就是存储数据里的存储单元,指针就是用来连接每一个结点的地址数据。这个以结点的序列来表示线性表被称作为单链表。
单链表是一种链式储存结构。在物理储存单元不连续,非顺序。
- 结点里的数据域是用来存储数据元素的
- 指针是用于指向下一个具有相同结构的结点
- 因为只有一个指针结点,故而被称为单链表
2.单链表的实现
末尾增加元素 | void add(T data) |
指定下标增加元素 | void add(int index,T data) |
删除指定下标元素 | void delete(int index) |
获取指定下标的元素 | T get(int index) |
修改指定下标的元素 | void update(int index,T data) |
遍历输出所有元素 | void show() |
获取链表的长度 | int length() |
(1)定义数据节点实体
(2)向链表末尾增加元素
(3)指定下标向链表增加元素
(4)删除指定下标元素的数据
(5)修改指定下标的元素
(6)获取指定下表的元素
(7)循环遍历输出
(8)获取当前有效的元素个数
3.单链表测试
(1)整体代码实现
public class SingleLinkedList<T> { /** * 定义头节点 */ public Node<T> head = new Node<>(null); /** * 链表有效元素个数 */ private int length=0; /** * 向单链表中添加一个元素 * @param data */ public void add(T data){ //定义当前数据 Node<T> nodeData = new Node<>(data); //定义辅助指针 Node<T> cur = head; //while 循环不断遍历找到最后一个节点 while (cur.next != null) { //只要有下一个节点,辅助指针就向下移动一位 cur = cur.next; } //while循环之后,cur已经是最后一个节点,将data赋值到cur的下一个节点上 cur.next = nodeData; //整体链表长度++ length++; } /** * 向单链表中添加一个元素 * @param data */ public void add(int index,T data){ //判断index是否合法 if(index <0 || index >length){ System.out.println("index不合法"); return; } //定义当前数据 Node<T> nodeData = new Node<>(data); //定义辅助指针 Node<T> cur = head; //找到当前index的元素 for (int i = 0; i < index; i++) { cur = cur.next; } //将当前节点的next设置成cur的next nodeData.next = cur.next; //将cur.next设置成nodeData(当前节点) cur.next = nodeData; //整体链表长度++ length++; } /** * 遍历输出链表中全部节点 */ public void show(){ //定义辅助指针 Node<T> cur = head; //判断链表中是否存在元素 if (cur.next == null){ System.out.println("当前单向链表中元素为空"); return; } while (cur.next != null){ System.out.print(cur.next.data+" "); cur = cur.next; } System.out.print("\n"); } /** * 修改指定下标的数据 * @param index * @param data */ public void update(int index,T data){ Node<T> cur = head; if(index > length){ System.out.println("当前index查过边界"); return; } //遍历寻找指定index的节点 for (int i = 0; i <= index; i++) { cur = cur.next; } //找到之后将新的data赋值上去 cur.data = data; } /** * 删除指定下标元素 * @param data */ public void delete(int index){ Node<T> cur = head; if(index > length){ System.out.println("当前index超过边界"); return; } //遍历寻找指定index的节点 for (int i = 0; i < index; i++) { cur = cur.next; } //找到之后将新的data赋值上去,将后一个data赋值到当前节点上 cur.data = cur.next.data; //将当前节点的next变成当前节点的next.next相当于将当前元素的next节点删掉 cur.next = cur.next.next; //单链表整体有效元素减一 length--; } /** * 获取指定下标元素的数据 * @param index * @return */ public T get(int index){ //校验参数是否合法 if(index<0 || index>length){ System.out.println("当前index超过边界"); } //定义辅助节点 Node<T> cur = head; //找到对应下标的node for (int i = 0; i <=index; i++) { cur = cur.next; } //返回node.data return cur.data; } /** * 获取单链表的长度 * @return */ public int length(){ return this.length; } /** * 内部节点类 * @param <T> */ class Node<T>{ /** * 定义数据 */ public T data; /** * 定义下一个节点的 */ public Node<T> next; public Node(T data) { this.data = data; } } }
(2)测试代码
public class Main { public static void main(String[] args) { SingleLinkedList<Integer> singleLink = new SingleLinkedList<>(); //增加元素 singleLink.add(1); singleLink.add(2); singleLink.add(3); System.out.print("新增之后:"); singleLink.show(); //删除元素 singleLink.delete(0); System.out.print("删除index为0之后:"); singleLink.show(); //修改元素 singleLink.update(0,9); System.out.print("修改index为0之后:"); singleLink.show(); //获取单链表的长度 int length = singleLink.length(); System.out.println("单链表的长度:"+length); //获取指定下标的数据 System.out.println("获取指定下标的数据:"+singleLink.get(1)); //指定下标插入数据 singleLink.add(1,6); System.out.print("在index为1位置插入数据之后:"); singleLink.show(); } }
4.链表的优缺点
- 优点:
- 链表的内存空间不连续。
- 如果知道要处理节点的前一个位置,则进行插入和删除的复杂度为O(1);
- 如果不知道要处理节点的前一个位置,则进行插入和删除的复杂度为O(N)。
- 头插、头删的效率高,时间复杂度是O(1)。
- 没有空间限制,不会溢出,可以存储很多元素。
- 缺点:
- 链表不支持随机访问,查找元素效率低,需要遍历节点,时间复杂度是O(n)。