单链表的代码实现
写在前面:
链表采用一组地址任意的存储单元存放线性表中的数据元素,链式结构的线性表不会按线性的逻辑顺序来保存数据元素,它需要在每一个元素里保存一个引用下一个数据元素的引用(或者叫指针)。它的每个节点都必须包含数据元素本身和一或两个用来引用上一个/下一个节点的引用。
优点:由于不必须按顺序存储,链表在插入、删除数据元素时比顺序线性表快得多。使用链表结构可以克服顺序线性表(基于数组)需要预先知道数据大小的缺点,链表可以充分利用计算机内存空间,实现灵活的内存动态管理。
缺点:链表在查找一个节点或者访问特点编号的节点则比顺序线性表慢得多。由于链表结构失去了数组随机存取的优点,同时链表由于增加了节点的指针域,空间开销比较大。
以下使用Java语言实现一个单链表:
1 package com.ietree.basic.datastructure.linklist; 2 3 /** 4 * 链式存储结构 5 * 6 * @param <T> 7 * @author Dylan 8 */ 9 public class LinkList<T> { 10 11 // 定义一个内部类Node,Node实例代表链表的节点 12 private class Node { 13 14 // 保存节点的数据 15 private T data; 16 // 指向下一个节点的引用 17 private Node next; 18 // 无参构造器 19 public Node() { 20 } 21 // 初始化全部属性的构造器 22 public Node(T data, Node next) { 23 this.data = data; 24 this.next = next; 25 } 26 } 27 28 // 保存该链表的头节点 29 private Node header; 30 // 保存该链表的尾节点 31 private Node tail; 32 // 保存该链表中已包含的节点数 33 private int size; 34 35 // 创建空链表 36 public LinkList() { 37 // 空链表,header和tail都是null 38 header = null; 39 tail = null; 40 } 41 42 // 以指定数据元素来创建链表,该链表只有一个元素 43 public LinkList(T element) { 44 45 header = new Node(element, null); 46 tail = header; 47 size++; 48 49 } 50 51 // 返回链表的长度 52 public int length() { 53 54 return size; 55 56 } 57 58 // 获取链式线性表中索引为index处的元素 59 public T get(int index) { 60 61 return getNodeByIndex(index).data; 62 63 } 64 65 // 根据索引index获取指定位置的节点 66 public Node getNodeByIndex(int index) { 67 68 if (index < 0 || index > size - 1) { 69 70 throw new IndexOutOfBoundsException("线性表索引越界"); 71 72 } 73 74 // 从header节点开始 75 Node current = header; 76 for (int i = 0; i < size && current != null; i++, current = current.next) { 77 if (i == index) { 78 return current; 79 } 80 } 81 82 return null; 83 } 84 85 // 查找链式线性表中指定元素的索引 86 public int locate(T element) { 87 88 // 从头节点开始搜索 89 Node current = header; 90 for (int i = 0; i < size && current != null; i++, current = current.next) { 91 if (current.data.equals(element)) { 92 return i; 93 } 94 } 95 return -1; 96 } 97 98 // 向线性表的指定位置插入一个元素 99 public void insert(T element, int index) { 100 101 if (index < 0 || index > size) { 102 103 throw new IndexOutOfBoundsException("线性表索引越界"); 104 105 } 106 107 // 如果还是空链表 108 if (header == null) { 109 110 add(element); 111 112 } else { 113 // 当index为0时,即在链表头处插入 114 if (index == 0) { 115 116 addAtHeader(element); 117 118 } else { 119 120 // 获取插入点的前一个节点 121 Node prev = getNodeByIndex(index - 1); 122 // 让prev的next指向新节点,让新节点的next引用指向原来prev的下一个节点 123 prev.next = new Node(element, prev.next); 124 size++; 125 126 } 127 } 128 } 129 130 // 采用尾插法为链表添加新节点 131 public void add(T element) { 132 133 // 如果该链表还是空链表 134 if (header == null) { 135 136 header = new Node(element, null); 137 // 只有一个节点,header、tail都指向该节点 138 tail = header; 139 140 } else { 141 142 // 创建新节点 143 Node newNode = new Node(element, null); 144 // 让尾节点的next指向新增的节点 145 tail.next = newNode; 146 // 以新增节点作为新的尾节点 147 tail = newNode; 148 149 } 150 size++; 151 } 152 153 // 采用头插法为链表添加新节点 154 public void addAtHeader(T element) { 155 156 // 创建新节点,让新节点的next指向原来的header 157 // 并以新节点作为新的header 158 header = new Node(element, header); 159 // 如果插入之前是空链表 160 if (tail == null) { 161 162 tail = header; 163 164 } 165 size++; 166 } 167 168 // 删除链式线性表中指定索引处的元素 169 public T delete(int index) { 170 171 if (index < 0 || index > size - 1) { 172 173 throw new IndexOutOfBoundsException("线性表索引越界"); 174 175 } 176 Node del = null; 177 // 如果被删除的是header节点 178 if (index == 0) { 179 180 del = header; 181 header = header.next; 182 183 } else { 184 185 // 获取删除点的前一个节点 186 Node prev = getNodeByIndex(index - 1); 187 // 获取将要被删除的节点 188 del = prev.next; 189 // 让被删除节点的next指向被删除节点的下一个节点 190 prev.next = del.next; 191 // 将被删除节点的next引用赋为null 192 del.next = null; 193 194 } 195 size--; 196 return del.data; 197 } 198 199 // 删除链式线性表中最后一个元素 200 public T remove() { 201 202 return delete(size - 1); 203 204 } 205 206 // 判断链式线性表是否为空表 207 public boolean empty() { 208 209 return size == 0; 210 211 } 212 213 // 清空线性表 214 public void clear() { 215 216 // 将header、tail赋为null 217 header = null; 218 tail = null; 219 size = 0; 220 221 } 222 223 public String toString() { 224 225 // 链表为空链表时 226 if (empty()) { 227 228 return "[]"; 229 230 } else { 231 232 StringBuilder sb = new StringBuilder("["); 233 for (Node current = header; current != null; current = current.next) { 234 sb.append(current.data.toString() + ", "); 235 } 236 int len = sb.length(); 237 return sb.delete(len - 2, len).append("]").toString(); 238 239 } 240 } 241 }
测试类:
1 package com.ietree.basic.datastructure.linklist; 2 3 public class LinkListTest { 4 public static void main(String[] args) { 5 6 LinkList<String> list = new LinkList<String>(); 7 list.insert("aaaa", 0); 8 list.add("bbbb"); 9 list.add("cccc"); 10 list.insert("dddd", 1); 11 System.out.println(list); 12 list.delete(2); 13 System.out.println(list); 14 System.out.println("cccc在链表中的位置:" + list.locate("cccc")); 15 System.out.println("链表中索引2处的元素:" + list.get(2)); 16 } 17 }
程序输出:
[aaaa, dddd, bbbb, cccc] [aaaa, dddd, cccc] cccc在链表中的位置:2 链表中索引2处的元素:cccc
如果文章对你有所帮助的话,请点个推荐或者关注吧。也希望你能够养成分享的习惯,从而去帮助其他人。