链表
- 数据存储在节点中
- 另一部分存引用
定义链表的内部节点
public class Node {
private E e;
private Node next;
}
最后一个节点的next存储的为空,丧失了随机访问的能力,适应索引没有语义的情况。链表的添加元素
链表的增加节点(不带头节点)
public class LinkList<E> {
//存储链表的头节点
private Node head;
private int size;
public LinkList() {
head = null;
size = 0;
}
//链表首部添加元素,这时候是简单的
public void addFrist(E e) {
Node node = new Node(e);
node.next = head;
head = node;
size++;
}
//链表的中间添加元素
public void add(int index, E e) {
if (index < 0 || index > size) {
throw new RuntimeException("添加的索引范围不正确");
}
Node pre = head;
if (index == 0) {
addFrist(e);
return;
}
//找到插入的位置
for (int i = 1; i < index; i++) {
pre = pre.next;
}
//然后进行添加
Node node = new Node(e);
node.next = pre.next;
pre.next = node;
size++;
}
//末尾添加元素
public void addLast(E e){
add(size,e);
}
@Override
public String toString() {
Node temp = head;
StringBuilder builder = new StringBuilder();
builder.append("[");
while (temp != null) {
builder.append(" " + temp.e);
temp = temp.next;
}
return builder.toString();
}
}
对链表的改善,增加虚拟头节点
//存储链表的头节点
private Node dummyHead;
public LinkList() {
dummyHead = new Node();
size = 0;
}
注意此时对toString的循环中需要换成temp.next
。此时不需要对头节点进行额外处理了,addFrist还可以复用add方法。
返回链表中指定索引的元素
//返回链表的指定索引的元素
public E get(int index){
if (index<0||index>size-1){
throw new RuntimeException("索引不正确");
}
Node cur = dummyHead.next;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
return cur.e;
}
查找某个元素
//查找某个元素是否存在
public boolean contain(E e){
Node cur = dummyHead.next;
while (cur!=null){
if(cur.e.equals(e)){
return true;
}
}
return false;
}
删除指定索引元素
//删除指定索引
public E delete(int index){
if (index < 0 || index >= size) {
throw new RuntimeException("添加的索引范围不正确");
}
Node pre = dummyHead.next;
for (int i = 0; i < index; i++) {
pre = pre.next;
}
Node deleteNode = pre.next;
pre.next = deleteNode.next;
deleteNode.next = null;
return deleteNode.e;
size--;
}
链表对头的操作是O(1),其他操作都是O(n)
用链表实现栈和队列
思路和数组差不多,实现栈的话,在头部增加和取出,实现栈。队列的话可以使用在队首进行出列,队尾进行入列,head,tail。 因为在队首删除元素比较容易,在队尾进行删除不容易。
链表的入队和出队。
@Override
public E dequeue(){
if(isEmpty())
throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
Node retNode = head;
head = head.next;
retNode.next = null;
if(head == null)
tail = null;
size --;
return retNode.e;
}
入队
@Override
public void enqueue(E e){
if(tail == null){
tail = new Node(e);
head = tail;
}
else{
tail.next = new Node(e);
tail = tail.next;
}
size ++;
}
链表的天然递归结构特性
链表可以堪称该节点和其他节点的递归。
LeetCode中的一个算法题
删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
if(head==null){
return null;
}
head.next = removeElements(head.next,val);
if(head.val == val){
return head.next;
}else{
return head;
}
}
}
优点就是简洁,但是占用的内存较大。