单链表的代码实现

写在前面:

  链表采用一组地址任意的存储单元存放线性表中的数据元素,链式结构的线性表不会按线性的逻辑顺序来保存数据元素,它需要在每一个元素里保存一个引用下一个数据元素的引用(或者叫指针)。它的每个节点都必须包含数据元素本身和一或两个用来引用上一个/下一个节点的引用。

优点:由于不必须按顺序存储,链表在插入、删除数据元素时比顺序线性表快得多。使用链表结构可以克服顺序线性表(基于数组)需要预先知道数据大小的缺点,链表可以充分利用计算机内存空间,实现灵活的内存动态管理。

缺点:链表在查找一个节点或者访问特点编号的节点则比顺序线性表慢得多。由于链表结构失去了数组随机存取的优点,同时链表由于增加了节点的指针域,空间开销比较大。

以下使用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
原文地址:https://www.cnblogs.com/Dylansuns/p/6783682.html