链表

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

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. SQL操作Spark SQL--CatalogApiTest

    object CatalogApiTest { def main(args: Array[String]): Unit = { val spark = SparkSession .builder() ...

  2. js 数组 : 差集、并集、交集、去重、多维转一维

    //input:[{name:'liujinyu'},{name:'noah'}] //output:['liujinyu','noah'] Array.prototype.choose = func ...

  3. 对于模块加载:ES6、CommonJS、AMD、CMD的区别

    运行和编译的概念 编译包括编译和链接两步. 编译,把源代码翻译成机器能识别的代码或者某个中间状态的语言. 比如java只有JVM识别的字节码,C#中只有CLR能识别的MSIL.还简单的作一些比如检查有 ...

  4. Combined beamformers for robust broadband regularized superdirective beamforming

    [未完待续]结合波束形成器的鲁棒性宽带正则化超指向波束形成方法[1].用于宽带信号的波束形成方法.结合延时求和波束形成DSB以及超指向波束形成SDB方法,给定用户自定义的正则化因子,采用一个简单的参数 ...

  5. 笨办法学Python

    打印:%r%r 与 %s 的区别就好比 repr() 函数处理对象与 str() 函数处理对象的差别.%s => str(),比较智能%r => repr(),处理较为简单和直接 from ...

  6. (尚027)Vue_案例_交互添加

    TodoHeader.vue组件 写交互: 第一步:跟目标元素绑定监听 (1).按回车键确认@keyup.enter="add" (2). 注意:数据在哪个组件,更新数据的行为就应 ...

  7. 如何调试 Windows 服务

    概要 本文分步介绍了如何使用 WinDbg 调试程序 (windbg.exe) 调试 Windows 服务. 要调试 Windows 服务,可以在服务启动后将 WinDbg 调试程序附加到托管该服务的 ...

  8. comlink 是来自google chrome 团队的简化webwokers 开发的类库

    comlink 可以帮助我们简单webworkers 的开发,同时很小(1.1kb),具体使用我们可以看下面 一张图  说明 comlink 使用起来也比较方便,官方也提供了完整的api 文档 参考资 ...

  9. 网络开发Socket和ServerSocket

    已经发表个人公众号 Socket和ServerSocket Socket为"孔"或"插座",创建Socket,打开连接Socket的输入或输出流,对Socket ...

  10. shell脚本编程基础知识点

    整数比较: -eq:测试两个整数是否相等:相等为真,不等为假 -ne:测试两个整数是否不等:不等为真,相等为假 -gt:测试一个数是否大于另一个数:大于为真,否则为假 -lt:测试一个数是否小于另一个 ...