手动实现 数组和链表
创始人
2024-05-30 15:09:32
0

数组和链表示意图

在这里插入图片描述

目的

不用java标准库
自己时间 一个 arraylist 和 linked list

区别

数组

数组 支持 随机访问 可以立即访问
根据 第一个元素的位置, 推算出 第三个地方的位置

如下图所示,假设我们知道 头的位置是X
数组是 int类型
每个位置就是 占4个字节
所以第三个元素的位置应该是 X + 3 x 4
即为 X + 12
就可以直接访问 第三个位置,直接进行赋值操作。

C语言有句话, 数组是指针, 指针不是数组。
我们 可以 把 数组就当做指针
我们知道 指针,即为 第一个元素位置
我们又可以根据 数组的类型 推断出 每个位置的大小
然后 通过 arr + X x 元素大小 直接推算出我们想修改的位置。
在这里插入图片描述

链表

链表存储的元素,它们不是连在一起放在内存的。
而是 分散位于 不同的位置。

通过 next指针记录下一个元素位置是多少,然后找到下一个元素。

只能通过 next指针 一个一个访问。
在这里插入图片描述

但是对于数组来说,每删除一个元素,需要将后面的所有元素都 往前移动1个位置。
在这里插入图片描述
对数组的增加操作,也类似,需要将后面的所有元素都 往后移动1个位置。

对于链表来说,增删都比较方便。

另外,对于数组来说,一开始就设置固定大小空间,当数组满了之后,需要重新申请一块新的空间,更大的
然后 将所有元素 都 移动过去。

数据结构的本质

遍历 和 访问

都是增删查改

postion 和 index的区别

在这里插入图片描述

数组实现

import java.util.NoSuchElementException;public class MyArrayList{// 真正存储的数据private E[] data;private int size;private static final int INIT_CAP = 1;public MyArrayList() {this(INIT_CAP);}public MyArrayList(int initCapacity) {data = (E[])new Object[initCapacity];}/** 增 **/// 在数组尾部添加一个元素 epublic void addLast(E e) {// 边界条件 Boundary conditionsif (data.length == size) {resize(data.length * 2); // 扩容 Expansion}data[size]= e;size++;}// 在index 索引位置添加一个元素 elementprivate void add(int index, E element) {checkPositionIndex(index); // 注意这里检查positionif (data.length == size) {resize(data.length * 2); // 扩容 Expansion}// data[index...] -> data[index+1 ....]// 数据迁移 Data migrationSystem.arraycopy(data, index, data, index + 1, size - 1);// 数据赋值 Data assignmentdata[index] = element;size++;}/** 删 **/// 删除数组的最后一个元素 并 返回public E removeLast() {if (isEmpty()) { // 数组为空throw new NoSuchElementException();}if (size < data.length / 4) { // 缩容 Shrinkageresize(data.length / 2);}E last = data [size - 1];// 删除最后一个元素 Delete the last elementdata[size - 1] = null;size--;return last;}// 删除index 对应的元素 并返回public E remove(int index) {checkElementIndex(index);if (size < data.length / 4) {resize (data.length / 2);}// 数据迁移// data[index + 1....] -> data[index...]E delVal = data[index];System.arraycopy(data, index + 1, data,  index,size - index - 1);// 删除最后一个元素data[size - 1] = null;size--;return delVal;}/** 查 **/// 返回索引 index 对应的元素public E get(int index) {checkElementIndex(index);return data[index];}/** 改 **/// 将索引 index的元素 修改为 element 并返回之前的值public E set(int index, E element) {checkElementIndex(index);E oldVal = data[index];data[index] = element;return oldVal;}/** 工具函数 */public int size() {return size;}public boolean isEmpty() {return size == 0;}/*** 私有函数*/// 明确index 和 postion区别private boolean isElementIndex(int index) {return index >= 0 && index < size;}private boolean isPositionIndex(int index) {return index >= 0 && index <= size;}// 检查 index 索引位置是否可以存在元素private void checkElementIndex(int index) {if (!isElementIndex(index)) {throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);}}private void checkPositionIndex(int index) {if (!isPositionIndex(index)) {throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);}}private void resize(int newCap) {E[] temp = (E[]) new Object[newCap];System.arraycopy(data, 0, temp, 0, size);data = temp;}
}

数组增加元素(add)的图解

再写一遍。逻辑同上面


import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;public class MyArrayList implements Iterable {// 真正存储数据的底层数组 The underlying array that really stores dataprivate E[] data;// 记录当前元素个数 Record the current number of elementsprivate int size;// 默认初始容量 Default initial capacityprivate static final int INIT_CAP = 1;public MyArrayList(){this(INIT_CAP);}public MyArrayList(int initCapability) {data = (E[])new Object[initCapability];size = 0;}/** 增**/public void addLast(E e){int cap = data.length;if (size == cap) {resize(2 * cap);}data[size] = e;size++;}public void add(int index, E e){checkPositionIndex(index);int cap = data.length;if (size == cap) {resize(2 * cap);}// move dat data[index...] -> data[index + 1..]System.arraycopy(data, index, data, index + 1, size - index);data[index] = e;size++;}public void addFirst(E e){add(0, e);}/** 删*/public E removeLast(){if (size == 0) {throw new NoSuchElementException();}int cap = data.length;if (size == cap / 4) {resize(cap / 2);}E deletedE = data[size - 1];data[size - 1] = null;size--;return deletedE;}public E remove(int index){checkElementIndex(index);int cap = data.length;if (size < cap / 4) {resize(cap / 2);}E deletedE = data[index];// move data data[index + 1....] -> data[index....]System.arraycopy(data, index + 1, data, index, size - index - 1);data[size - 1] = null;size--;return deletedE;}public E removeFirst(){return remove(0);}/** 查*/public E get(int index){checkElementIndex(index);return data[index];}/** 改*/public E set(int index, E element){checkElementIndex(index);E old = data[index];data[index] = element;return old;}/** tool method functions */public int size(){return size;}public boolean isEmpty(){return size == 0;}// 修改容量大小 Modify capacity sizepublic void resize(int newCap) {if (size > newCap) return;E[] temp = (E[])new Object[newCap];/** System.arraycopy* 四个参数:*      param1: the array to copied from*      param2: the start index in original*      param3: the array to be copied in*      param4: the total no of coponments to be copied*/System.arraycopy(data, 0, temp,0 ,size);data = temp;}// Check whether there are elements in the index positionprivate boolean isElementIndex(int index) {return index >= 0 && index < size;}// Check whether elements can be added to the index position.private boolean isPositionIndex(int index) {return index >=0 && index <= size;}private void checkElementIndex(int index) {if (!isElementIndex(index)){throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);}}private void checkPositionIndex(int index) {if (!isPositionIndex(index)) {throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);}}@Overridepublic Iterator iterator(){return new Iterator() {private  int p = 0;@Overridepublic boolean hasNext() {return p != size;}@Overridepublic E next() {return data[p++];}};}private void display() {System.out.println("size = " + size + "cap = " + data.length);System.out.println(Arrays.toString(data));}public static void main(String[] args) {// 初始容量设置为 3MyArrayList arr = new MyArrayList<>(3);// 添加 5 个元素for (int i = 1; i <= 5; i++) {arr.addLast(i);}arr.remove(3);arr.add(1, 9);arr.addFirst(100);int val = (int)arr.removeLast();for (int i = 0; i < arr.size(); i++) {System.out.println(arr.get(i));}}}

链表

图解
头部添加元素
在这里插入图片描述

尾部插入
在这里插入图片描述
删除元素
在这里插入图片描述

在index对应位置插入元素
在这里插入图片描述
如果要在1的位置插入元素,首先要找到 index为 0 和 index为1的位置,然后 进行操作。
在这里插入图片描述
在这里插入图片描述
双链表实现代码

import java.util.NoSuchElementException;public class MyLinkedList {// 双链表的节点private static class Node{E val;Node next;Node prev;Node(E val){this.val = val;}}private final Node head, tail;private int size;public MyLinkedList(){this.head = new Node<>(null);this.tail = new Node<>(null);head.next = tail;tail.prev = head;this.size = 0;}/**增*/public void addFirst(E e){Node node = new Node<>(e);Node temp = head.next;// head 和 tempnode.prev = head;node.next = temp;head.next = node;temp.prev = node;size++;}public void addLast(E e){Node node = new Node<>(e);Node temp = tail.prev;// temp headnode.next = tail;node.prev = temp;tail.prev = node;temp.next = node;size++;}public void add(int index, E e){checkPositionindex(index);Node node = new Node<>(e);Node p = getNode(index);Node temp = p.prev;node.next = p;node.prev = temp;p.prev = node;temp.next = node;size++;}/**删*/public E removeFirst(){if (isEmpty()) {throw new NoSuchElementException();}Node x = head.next;Node temp = x.next;// head -> x -> temphead.next = temp;temp.prev = head;x.next = x.prev = null;size--;return x.val;}public E removeLast(){if (isEmpty()) {throw new NoSuchElementException();}Node x = tail.prev;Node temp = x.prev;// temp <-> x <-> tailtemp.next = tail;tail.prev = temp;x.prev = x.next = null;size--;return x.val;}public E remove(int index){checkElementIndex(index);Node p = getNode(index);Node prev = p.prev;Node next = p.next;// prev <-> p ;prev.next = next;next.prev = prev;p.next = p.prev = null;size--;return p.val;}/**查*/public E getFirst(){if (isEmpty()) {throw new NoSuchElementException();}return head.next.val;}public E getLast(){if (isEmpty()) {throw new NoSuchElementException();}return tail.prev.val;}public E get(int index){checkElementIndex(index);Node p = getNode(index);return p.val;}/**改*/public E set(int index, E element){checkElementIndex(index);Node p = getNode(index);E old = p.val;p.val = element;return old;}/** other utility funcations 工具函数*/public int size(){return this.size;}public boolean isEmpty(){return 0 == size;}private boolean isElementIndex(int index) {return index >= 0 && index < size;}private boolean isPositionIndex(int index) {return index >=0 && index <= size;}// 检查index索引位置是否可以存在元素// Check whether the element can exist at the index positionprivate void checkElementIndex(int index) {if (isElementIndex(index)) {throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);}}private void checkPositionindex(int index) {if (isPositionIndex(index)) {throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);}}private Node getNode(int index) {checkElementIndex(index);Node p = head.next;for (int i = 0; i < index; i++) {p = p.next;}return p;}
}

单链表实现

import java.util.NoSuchElementException;public class MyLinkedList {private static class Node {E val;Node next;Node(E val) {this.val = val;}}private final Node head, tail;private int size;public MyLinkedList(){this.head = new Node(null);this.tail = new Node(null);head.next = tail;this.size = 0;}/**增*/public void addFirst(E e){Node x = new Node<>(e);Node temp = head.next;// head -> temp// x-> tempx.next = temp;// head-> x -> temphead.next = x;size++;}public void addLast(E e){Node x = new Node<>(e);Node temp;// find the node before last nodeif (size > 0) {temp = getNode(size - 1);} else {temp = head;}// x-> tailx.next = tail;// temp -> x -> tailtemp.next = x;size++;}public void add(int index, E e){checkPositionIndex(index);if (index == size) {addLast(e);return;}Node x = new Node<>(e);Node temp;if (index >= 0) {temp = getNode(index - 1);} else {temp = head;}Node p = getNode(index);// x->px.next = p;// temp -> x -> ptemp.next = x;size++;}/**删*/public E removeFirst(){if (isEmpty()){throw new NoSuchElementException();}Node x = head.next;head.next = head.next.next;x.next = null;size--;return x.val;}public E removeLast(){if (isEmpty()){throw new NoSuchElementException();}Node p = getNode(size - 1);Node temp;if (size >= 2) {temp = getNode(size - 2);} else {temp = head;}// temp -> x -> tailtemp.next = tail;p.next = null;size--;return p.val;}public E remove(int index){checkElementIndex(index);Node p = getNode(index);Node prev;if (index >= 0) {prev = getNode(index - 1);} else {prev = head;}// prev -> p -> nextNode next = p.next;prev.next = next;p.next = null;size--;return next.val;}/**查*/public E getFirst(){if (isEmpty()) {throw new NoSuchElementException();}return head.next.val;}public E getLast(){if (isEmpty()) {throw new NoSuchElementException();}return getNode(size - 1).val;}public E get(int index){checkElementIndex(index);Node p = getNode(index);return p.val;}/**改*/public E set(int index, E element){checkElementIndex(index);Node p = getNode(index);E old = p.val;p.val = element;return old;}/**其他工具函数Other tool functions*/public int size(){return this.size;}public boolean isEmpty(){return this.size == 0;}public  boolean isElementIndex(int index){return index >= 0 && index < size;}public boolean isPositionIndex(int index){return index >= 0 && index <= size;}private void checkElementIndex(int index){if (isElementIndex(index)) {throw new IndexOutOfBoundsException("index: "+ index + ", size:" + size);}}private void checkPositionIndex(int index){if (isPositionIndex(index)) {throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);}}private Node getNode(int index){checkElementIndex(index);Node p = head.next;for (int i = 0; i < index; i++) {p = p.next;}return p;}}

Leetcode练习题

易错点 注意 add(index) 和 delete(index)的两者不同
前者检查的是position
后者检查的是index

https://leetcode.com/problems/design-linked-list

class ListNode {int val;ListNode prev, next;public ListNode(int val){this.val = val;}
}
class MyLinkedList {private final ListNode head, tail;int size;public MyLinkedList(){this.head = new ListNode(-1);this.tail = new ListNode(-1);head.next = tail;tail.prev = head;this.size = 0;}public int get(int index) {if (!checkIndex(index)) return -1;ListNode p = head.next;for (int i = 0; i < index; i++) {p = p.next;}return p.val;}public void addAtHead(int val) {ListNode x = new ListNode(val);ListNode temp = head.next;x.prev = head;x.next = temp;head.next = x;temp.prev = x;this.size++;}   public void addAtTail(int val) {ListNode x = new ListNode(val);ListNode temp = tail.prev;x.next = tail;x.prev = temp;temp.next = x;tail.prev = x;this.size++;}public void addAtIndex(int index, int val) {if (!checkPostion(index)) return;ListNode x = new ListNode(val);ListNode p = getNode(index);ListNode temp = p.prev;x.prev = temp;x.next = p;temp.next = x;p.prev = x;this.size++;}public void deleteAtIndex(int index) {if (!checkIndex(index)) return;ListNode p = getNode(index);ListNode prev = p.prev;ListNode next = p.next;prev.next = next;next.prev = prev;p.next = p.prev = null;this.size--;}private boolean checkIndex(int index) {return index >= 0 && index < size;}private boolean checkPostion(int index) {return index >= 0 && index <= size;}private ListNode getNode(int index) {ListNode p = head.next;for (int i = 0; i < index; i++) {p = p.next;}        return p;}
}

Reference

https://appktavsiei5995.pc.xiaoe-tech.com/p/t_pc/course_pc_detail/video/v_62655468e4b09dda125fa007?product_id=p_626541bbe4b0812e1790e3f8&content_app_id=&type=6

相关内容

热门资讯

122.(leaflet篇)l... 听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行...
育碧GDC2018程序化大世界... 1.传统手动绘制森林的问题 采用手动绘制的方法的话,每次迭代地形都要手动再绘制森林。这...
育碧GDC2018程序化大世界... 1.传统手动绘制森林的问题 采用手动绘制的方法的话,每次迭代地形都要手动再绘制森林。这...
Vue使用pdf-lib为文件... 之前也写过两篇预览pdf的,但是没有加水印,这是链接:Vu...
PyQt5数据库开发1 4.1... 文章目录 前言 步骤/方法 1 使用windows身份登录 2 启用混合登录模式 3 允许远程连接服...
Android studio ... 解决 Android studio 出现“The emulator process for AVD ...
Linux基础命令大全(上) ♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维...
再谈解决“因为文件包含病毒或潜... 前面出了一篇博文专门来解决“因为文件包含病毒或潜在的垃圾软件”的问题,其中第二种方法有...
南京邮电大学通达学院2023c... 题目展示 一.问题描述 实验题目1 定义一个学生类,其中包括如下内容: (1)私有数据成员 ①年龄 ...
PageObject 六大原则 PageObject六大原则: 1.封装服务的方法 2.不要暴露页面的细节 3.通过r...
【Linux网络编程】01:S... Socket多进程 OVERVIEWSocket多进程1.Server2.Client3.bug&...
数据结构刷题(二十五):122... 1.122. 买卖股票的最佳时机 II思路:贪心。把利润分解为每天为单位的维度,然后收...
浏览器事件循环 事件循环 浏览器的进程模型 何为进程? 程序运行需要有它自己专属的内存空间࿰...
8个免费图片/照片压缩工具帮您... 继续查看一些最好的图像压缩工具,以提升用户体验和存储空间以及网站使用支持。 无数图像压...
计算机二级Python备考(2... 目录  一、选择题 1.在Python语言中: 2.知识点 二、基本操作题 1. j...
端电压 相电压 线电压 记得刚接触矢量控制的时候,拿到板子,就赶紧去测各种波形,结...
如何使用Python检测和识别... 车牌检测与识别技术用途广泛,可以用于道路系统、无票停车场、车辆门禁等。这项技术结合了计...
带环链表详解 目录 一、什么是环形链表 二、判断是否为环形链表 2.1 具体题目 2.2 具体思路 2.3 思路的...
【C语言进阶:刨根究底字符串函... 本节重点内容: 深入理解strcpy函数的使用学会strcpy函数的模拟实现⚡strc...
Django web开发(一)... 文章目录前端开发1.快速开发网站2.标签2.1 编码2.2 title2.3 标题2.4 div和s...