链表
之前看过了动态数组,栈和队列,虽然我们把第一个叫做动态数组,但是,他们的底层实质上还是静态数组。靠
resize来实现动态数组。而链表是真正的数据结构
- 链表需要一个节点。
- 数据存储在链表中
相当于是一串火车,将数据放在车厢中,两个车厢之间还需要一个个节点来相互串联。
优点:实现了真正的动态。
缺点:无法进行随机访问
public class LinkedList<E> { private class Node { public E e; public Node next; public Node(E e) { this(e, null); } public Node(E e, Node next) { this.e = e; this.next = next; } public Node() { this(null, null); } @Override public String toString() { return e.toString(); } } private Node head; private int size; public LinkedList(Node head, int size) { head = null; this.size = 0; } //获取链表中的元素个数 public int getSize() { return size; } //返回链表是否为空 public boolean isEmpty() { return size == 0; } //链表添加新的元素 public void addFirst(E e) { // Node newNode = new Node((E) node); // newNode.next = head; // head = newNode; head = new Node(e, head); size++; } //在链表的index位置添加新的元素e //在链表中不是一个常用的操作 :) public void add(int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("Add failed,Illegal index"); } if (index == 0) { addFirst(e); } else { Node prev = new Node(e); for (int i = 0; i < index - 1; i++) { prev.next = prev; } // Node node = new Node(e); // prev.next = node.next; // prev.next = node; prev.next = new Node(e,prev.next); size++; } } //在末尾添加元素 public void addLast(E e){ add(size,e); }
这是自己建立的链表,在此处,一定要注意几点
- 在增加或删除元素时,不要忘了维护size
- 再添加链表时,一定不要忘了顺序的问题,一定是先prev.next = node.next.之后才是prev.next = node;
- 一开始的时候 head = prev
再添加操作时,由于头结点的上一个节点时null,所以我们需要特殊处理,为了解决这个问题,我们增加了一个虚拟
头结点。这个节点不存出任何数据。
public class LinkedList<E> { private class Node { public E e; public Node next; public Node(E e) { this(e, null); } public Node(E e, Node next) { this.e = e; this.next = next; } public 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 LinkedList(Node head, int size) { head = null; this.size = 0; } //获取链表中的元素个数 public int getSize() { return size; } //返回链表是否为空 public boolean isEmpty() { return size == 0; } //在链表的index位置添加新的元素e //在链表中不是一个常用的操作 :) public void add(int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("Add failed,Illegal index"); } Node prev = dummyHead; for (int i = 0; i < index; i++) { prev.next = prev; } // Node node = new Node(e); // prev.next = node.next; // prev.next = node; prev.next = new Node(e, prev.next); size++; } //链表添加新的元素 public void addFirst(E e) { // Node newNode = new Node((E) node); // newNode.next = head; // head = newNode; add(0,e); size++; } //在末尾添加元素 public void addLast(E e) { add(size, e); } }
链表的遍历,增 删 改 查。
public class LinkedList<E> { private class Node { public E e; public Node next; public Node(E e) { this(e, null); } public Node(E e, Node next) { this.e = e; this.next = next; } public Node() { this(null, null); } @Override public String toString() { return e.toString(); } } private Node dummyHead; private int size; public LinkedList() { dummyHead = new Node(); size = 0; } //获取链表中的元素个数 public int getSize() { return size; } //返回链表是否为空 public boolean isEmpty() { return size == 0; } //在链表的index位置添加新的元素e //在链表中不是一个常用的操作 :) public void add(int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("Add failed,Illegal index"); } Node prev = dummyHead; for (int i = 0; i < index; i++) { prev= prev.next ; } // Node node = new Node(e); // prev.next = node.next; // prev.next = node; prev.next = new Node(e, prev.next); size++; } //链表添加新的元素 public void addFirst(E e) { // Node newNode = new Node((E) node); // newNode.next = head; // head = newNode; add(0, e); } //在末尾添加元素 public void addLast(E e) { add(size, e); } /** * 遍历整个列表 * 实际过程中不会这么用 * 仅供联系 * * @param index * @return */ public E get(int index) { if (index < 0 || index > size) { throw new IllegalArgumentException("Get failed Illegal index"); } 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); } //修改链表的第index(0-based)个位置的元素为e //在链表中不是一个常用的操作,练习用 :) public void set(int index, E e) { if (index < 0 || index >= size) { throw new IllegalArgumentException("Set Failed,illegal index"); } Node cur = dummyHead.next; for (int i = 0; i < index; i++) { cur = cur.next; } cur.e = 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; } @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(); } }
之前这些代码我们写完了增 改 查,下面我们就要写删除操作。
//删除链表中的一个元素 public E remove(int index) { if (index > 0 || index < size) { throw new IllegalArgumentException("remove failed"); } 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); }
链表的时间复杂度
- addLast(e):O(n)
- addFirst(e):O(1)
- add(index,e):O(n)
- setIndex(e):O(n)
- get(index):O(n)
- contains(e):O(n)
如果只对链表头进行操作,那么他的时间复杂度是O(1),所以我们来尝试一下利用链表来做栈的可能性。
import java.util.List; /** * @author shkstart * @create 2019-11-29 13:03 */ public class LinkedListStack<E> implements Stack<E>{ private LinkedList<E> list; public LinkedListStack() { list = new LinkedList<E>(); } @Override public int getSize() { return getSize(); } @Override public void push(E e) { list.addFirst(e); } @Override public boolean isEmpty() { return isEmpty(); } @Override public E pop() { return list.removeFirst(); } @Override public E peek() { return list.getFirst(); } @Override public String toString(){ StringBuilder res = new StringBuilder(); res.append("stack:top"); res.append(list); return res.toString(); } public static void main(String[] args) { LinkedListStack<Integer> stack = new LinkedListStack<Integer>(); for (int i = 0; i < 5; i++) { stack.push(i); System.out.println(stack); } stack.pop(); System.out.println(stack); } }
做完了栈,同样的 我们试验一下利用链表实现队列。
private class Node { public E e; public Node next; public Node(E e, Node next) { this.e = e; this.next = next; } public Node(E e) { this(e, null); } public Node() { this(null, null); } @Override public String toString() { return e.toString(); } } private Node tail, head; private int size; public LinkedListQueue(Node tail, Node head, int size) { this.tail = null; this.head = null; this.size = 0; } public LinkedListQueue() { } @Override public int getSize() { return getSize(); } @Override public boolean isEmpty() { return isEmpty(); } @Override public void enqueue(E e) { if (tail.next == null) { Node node = new Node(e); tail = head; } else { tail.next = new Node(e); tail = tail.next; } size++; } @Override public E dequeue() { if (isEmpty()) { throw new IllegalArgumentException("dequeue出错!"); } Node ret = head; head = head.next; ret.next = null; if (head == null) { tail = head; } size--; return ret.e; } @Override public E getFront() { if (isEmpty()) { throw new IllegalArgumentException("Queue is empty"); } return head.e; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Queue:"); builder.append("front ["); Node cur = head; while (cur != null) { cur = cur.next; } builder.append("Null tail"); return builder.toString(); } public static void main(String[] args) { LinkedListQueue<Integer> LinkedListQueue = new LinkedListQueue<Integer>(); for (int i = 0; i < 10; i++) { LinkedListQueue.enqueue(i); System.out.println(LinkedListQueue); if (i % 3 == 2) { LinkedListQueue.dequeue(); System.out.println(LinkedListQueue); } } }
在这里的链表我们写了尾指针!
来源:https://www.cnblogs.com/xiaobaoa/p/11960642.html