链表

之前看过了动态数组,栈和队列,虽然我们把第一个叫做动态数组,但是,他们的底层实质上还是静态数组。靠

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);
}
}
}

在这里的链表我们写了尾指针!

学习数据结构Day4的更多相关文章

  1. Spark菜鸟学习营Day4 单元测试程序的编写

    Spark菜鸟学习营Day4 单元测试程序的编写 Spark相比于传统代码是比较难以调试的,单元测试的编写是非常必要的. Step0:需求分析 在测试案例编写前,需完成需求分析工作,明确程序所有的输入 ...

  2. SqList *L 和 SqList * &L的区别/学习数据结构突然发现不太懂 小祥我查找总结了一下

    小祥在学习李春葆的数据结构教程时发现一个小问题,建立顺序表和输出线性表,这两个函数的形参是不一样的. 代码在这里↓↓↓ //定义顺序表L的结构体 typedef struct { Elemtype d ...

  3. 在Object-C中学习数据结构与算法之排序算法

    笔者在学习数据结构与算法时,尝试着将排序算法以动画的形式呈现出来更加方便理解记忆,本文配合Demo 在Object-C中学习数据结构与算法之排序算法阅读更佳. 目录 选择排序 冒泡排序 插入排序 快速 ...

  4. OpenCV图像处理学习笔记-Day4(完结)

    OpenCV图像处理学习笔记-Day4(完结) 第41课:使用OpenCV统计直方图 第42课:绘制OpenCV统计直方图 pass 第43课:使用掩膜的直方图 第44课:掩膜原理及演示 第45课:直 ...

  5. 数据结构之Queue | 让我们一块来学习数据结构

    前面的两篇文章分别介绍了List和Stack,下面让我们一起来学习Queue 数据结构之List | 让我们一块来学习数据结构 数据结构之Stack | 让我们一块来学习数据结构 队列的概况 队列是一 ...

  6. 数据结构之LinkedList | 让我们一块来学习数据结构

    highlight: monokai theme: vue-pro 上一篇文章中使用列表(List)对数据排序,当时底层储存数据的数据结构是数组.本文将讨论另外一种列表:链表.我们会解释为什么有时链表 ...

  7. 数据结构之Set | 让我们一块来学习数据结构

    数组(列表).栈.队列和链表这些顺序数据结构对你来说应该不陌生了.现在我们要学习集合,这是一种不允许值重复的顺序数据结构.我们将要学到如何创建集合这种数据结构,如何添加和移除值,如何搜索值是否存在.你 ...

  8. MySQL 学习 --- 数据结构和索引

    本文参考了多篇文章集成的笔记,希望各位学习之前可以阅读以下参考资料先 概述 文章分几个部分 :第一部分介绍了B-Tree 和 B+Tree 这种数据结构作为索引:第二部分介绍索引的最左前缀原则和覆盖索 ...

  9. 省选算法学习-数据结构-splay

    于是乎,在丧心病狂的noip2017结束之后,我们很快就要迎来更加丧心病狂的省选了-_-|| 所以从写完上一篇博客开始到现在我一直深陷数据结构和网络流的漩涡不能自拔 今天终于想起来写博客(只是懒吧.. ...

随机推荐

  1. wordpress模板加载顺序汇总

    我们要创建一个新的wordpress模板需要先了解有哪些页面模板,这些页面模板的文件是什么?它们是怎么工作的?下面ytkah汇总了一些常用的wordpress模板结构方便大家查找 首页 首先WordP ...

  2. 如何使用keil5将stm32的hal库编译成lib文件——F1版本

    hal库中keil5中编译的速度是比较慢的,相同情况下,每次都要编译的时候,比标准库是要慢很多的,因此就hal库编译成lib文件是一种加快编译速度的方法,当然也有其自身的缺点.一.步骤1.使用cube ...

  3. L1434滑雪

    一,看题 1,这个长度怎么算的. 从它自己数,可以走下去的位置. 2,这个题的衣服怎么披上去呀. 3,搜索目标,状态. 肯定要用坐标,不然怎么搜索. 4,在前期还是多写把. 5,我靠这个点还是随机的& ...

  4. LeetCode 1027. Longest Arithmetic Sequence

    原题链接在这里:https://leetcode.com/problems/longest-arithmetic-sequence/ 题目: Given an array A of integers, ...

  5. 计蒜之道 百度AI小课堂-上升子序列

    计蒜之道 百度AI小课堂-上升子序列 题目描述 给一个长度为 \(n\) 的数组 \(a\) .试将其划分为两个严格上升子序列,并使其长度差最小. 输入格式 输入包含多组数据. 数据的第一行为一个正整 ...

  6. 洛谷 P1880 [NOI1995]石子合并 题解

    P1880 [NOI1995]石子合并 题目描述 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试 ...

  7. 60、Spark Streaming:缓存与持久化机制、Checkpoint机制

    一.缓存与持久化机制 与RDD类似,Spark Streaming也可以让开发人员手动控制,将数据流中的数据持久化到内存中.对DStream调用persist()方法,就可以让Spark Stream ...

  8. HTML之微信全屏播放视频

    不废话,上代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...

  9. 前端微信小程序仿菜谱精灵

    需求描述及交互分析 设计思路和相关知识点 底部标签导航设计 幻灯片轮播效果设计 菜谱专题列表显示设计 菜谱专题详情设计 菜谱分类设计 幻灯片轮播效果动态切换展示一些美食图片 若本号内容有做得不到位的地 ...

  10. (9)Go指针

    区别于C/C++中的指针,Go语言中的指针不能进行偏移和运算,是安全指针. 要搞明白Go语言中的指针需要先知道3个概念:指针地址.指针类型和指针取值. Go语言中的指针 任何程序数据载入内存后,在内存 ...