1.链表的实现
链表是一种很重要的数据结构,它是一些其他数据结构的基础如二叉树,平衡二叉树和AVL树。链表对于根据索引随机访问,修改元素这些操作复杂度很高,但是对于表头元素的操作就效率高多了。首先这个链表类里有一个内部类Node,记录结点信息。再看一下要实现链表所需要的方法。LinkedList()
getSize()
获取链表元素个数isEmpty()
判断链表是否为空addFirst(E e)
从链表头添加新的元素add(int index,E e)
添加到第index位置一个元素eaddLast(E e)
添加到链表末尾一个元素get(int index)
获取指定位置的元素getFirst()
获取第一个元素getLast()
获取最后一个元素set(int index,E e)
修改第index位置上的元素为econtains(E e)
判断链表是否包含元素eremove(int index)
移除指定位置的元素removeFirst()
移除第一个元素removeLast()
移除最后一个元素toString()
这里贴出链表的代码
/**
* @Author: Cui
* @Date: 2020/7/9
* @Description:
*/
public class LinkedList<E> {
private class Node{
E e;
Node next;
Node(E e,Node next){
this.e = e;
this.next = next;
}
Node(E e){
this(e,null);
}
Node(){
this(null,null);
}
@Override
public String toString(){
return e.toString();
}
}
private Node dummyHead;
private int size;
public LinkedList(){
dummyHead = new Node(null,null);
size = 0;
}
public int getSize() {
return size;
}
public boolean isEmpty(){
return size == 0;
}
//在链表头添加新的元素e
public void addFirst(E e){
add(0,e);
}
/**
* 在链表的index位置添加新的元素e,在链表中不是一个常用的操作
* @param index
* @param e
*/
public void add(int index,E e){
if(index < 0 || index > size){
throw new IllegalArgumentException("索引错误");
}
Node prev = dummyHead;
for(int i = 0; i < index ; i++){
prev = prev.next;
}
prev.next = new Node(e,prev.next);
size++;
}
/**
* 向链表末尾添加元素
* @param e
*/
public void addLast(E e){
add(size,e);
}
/**
* 获取链表的第index个元素
* @param index
* @return
*/
public E get(int index){
if(index < 0 || index > size){
throw new IllegalArgumentException("索引错误");
}
Node cur = dummyHead.next;
for(int i = 0; i < index; i++){
cur = cur.next;
}
return cur.e;
}
//获取链表的第一个元素
public E getFirst(){
return get(0);
}
//获取链表的最后一个元素
public E getLast(){
return get(size-1);
}
public void set(int index,E e){
if(index < 0 || index > size){
throw new IllegalArgumentException("索引错误");
}
Node cur = dummyHead.next;
for(int i = 0; i < index; i++){
cur = cur.next;
}
cur.e = e;
}
public boolean contains(E e){
Node cur = dummyHead.next;
while (cur != null){
if(cur.e.equals(e)){
return true;
}
cur = cur.next;
}
return false;
}
public E remove(int index){
if(index < 0 || index > size){
throw new IllegalArgumentException("索引错误");
}
Node prev = dummyHead;
for(int i = 0; i < index; i++){
prev = prev.next;
}
Node retNode = prev.next;
prev.next = retNode.next;
retNode.next = null;
size--;
return retNode.e;
}
//删除第一个元素
public E removeFirst(){
return remove(0);
}
//删除链表最后一个元素
public E removeLast(){
return remove(size-1);
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
Node cur = dummyHead.next;
while (cur != null){
res.append(cur+"->");
cur = cur.next;
}
res.append("null");
return res.toString();
}
}
测试代码
/**
* @Author: Cui
* @Date: 2020/7/4
* @Description:
*/
public class Main {
public static void main(String[] args) {
LinkedList<Integer> linkedList = new LinkedList<>();
for(int i = 0; i < 5; i++){
linkedList.addFirst(i);
System.out.println("linkedList = " + linkedList);
}
linkedList.add(2,666);
System.out.println("linkedList = " + linkedList);
linkedList.remove(2);
System.out.println("linkedList = " + linkedList);
linkedList.removeFirst();
System.out.println("linkedList = " + linkedList);
linkedList.removeLast();
System.out.println("linkedList = " + linkedList);
}
}
结果
2.链表的分析
复杂度分析:
添加操作
向链表末尾添加元素时需要一个一个的访问,从第一个向最后一个移动,所以访问最后一个元素要遍历前面所有的元素,所以复杂度为O(n)。
向链表首位添加时不需要访问其他元素,所以是一个O(1)级别的复杂度。
当向指定位置插入元素时,复杂度是O(n/2)=O(n)。
删除操作
删除最后一个元素时也需要从第一个元素向最后一个遍历,所以复杂度O(n)。
同添加操作。
修改操作
修改指定位置的元素,平均访问每一个元素复杂度为O(n/2)=O(n)。
查找操作
查找中的get方法和contains方法也是O(n/2)=O(n)。
链表综合的复杂度