算法通关村第一关-链表青铜挑战笔记

2023-10-16 02:30

本文主要是介绍算法通关村第一关-链表青铜挑战笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

欢迎来到 : 第一关青铜关

  1. java如何创建链表
  2. 链表怎么增删改查

我们先了解链表

单链表的概念

我们从简单的创建和增删改查开始.

链表的概念

线性表分为顺序表(数组组成)和链表(节点组成) . 

链表又分: 

  • 单向 双向
  • 有哨兵节点 无哨兵节点
  • 循环 不循环

链表是一种物理存储单元上非连续、非顺序的存储结构,单链表就像铁链一样,元素之间互相连接。链表由一系列的结点(链表中的每一个元素称为结点也叫节点)组成, 结点可以在运行时动态生成。

 在链表中每个节点都有数据域和指针域两部分:

  数据域用来存值 , 指针域用来存放地址(下一节点的地址) . 

 举个简单的例子{1,2,3}用链表存储:

思考一下

思考一下面两个图 , 是否满足单链表的要求 , 为什么 ?

图一:

图二:

解析:

第一图是满足单链表的要求 , 因为我们说链表要求环环相扣,核心是一个结点只能有一个后继,但不代表个结点只能有一个被指向。第一个图中,c1被a2和b3同时指向,这是没关系的。这就好比法律倡导一夫一妻,你只能爱一个人,但是可以都多个人爱你。
第二图就不满足要求了,因为c1有两个后继a5和b4.另外在做题的时候要注意比较的是值还是结点,有时可能两个结点的值相等,但并不是同一个结点,例如下图中有两个结点的值都是1,但并不是同一个结点。

链表的相关概念

节点和头节点

每个点都由值和指向下一个结点的地址组成的独立的单元,称为一个结点,有时也称为节点,含义都在链表中,是一样的。
对于单链表,如果知道了第一个元素,就可以通过遍历访问整个链表,因此第一个结点最重要一般称为头结点

虚拟节点(哨兵节点)

在做题以及在工程里经常会看到虚拟结点的概念,其实就是一个结点dummyNode,其next指针指向head,也就是dummyNode.next=head.
因此,如果我们在算法里使用了虚拟结点,则要注意如果要获得head结点,或者从方法(函数)里返回的时候,则应使用dummyNode.next.
另外注意,dummyNode的val不会被使用,初始化为0或者-1等都是可以的。既然值不会使用,那虚拟结点有啥用呢?简单来说,就是为了方便我们处理首部结点,否则我们需要在代码里单独处理首部结点的问题。在链表反转里,我们会看到该方式可以大大降低解题难度

创建链表

那我们如何使用链表呢?按照面向对象的思想,我们可以设计一个类,来描述结点这个事物,用一个属性描述这个结点存储的元素,用来另外一个属性描述这个结点的下一个结点。

类名Node
构造方法Node(T t,Node next):创建Node对象
成员变量T value:存储数据 Node next:指向下一个结点

 举例 : 存储值为int类型

/*** 节点类*/
public class Node {//值int value;//地址Node next;public Node(int value, Node next) {this.value = value;this.next = next;}
}

生成链表:

    public static void main(String[] args) throws Exception {//构建结点Node<Integer> first = new Node<Integer>(11, null);Node<Integer> second = new Node<Integer>(13, null);Node<Integer> third = new Node<Integer>(12, null);Node<Integer> fourth = new Node<Integer>(8, null);Node<Integer> fifth = new Node<Integer>(9, null);//生成链表first.next = second;second.next = third;third.next = fourth;fourth.next = fifth;}

添加数据 :

    public static void main(String[] args) throws Exception {//构建结点Node<Integer> first = new Node<Integer>(11, null);Node<Integer> second = new Node<Integer>(13, null);Node<Integer> third = new Node<Integer>(12, null);Node<Integer> fourth = new Node<Integer>(8, null);Node<Integer> fifth = new Node<Integer>(9, null);//生成链表first.next = second;second.next = third;third.next = fourth;fourth.next = fifth;//添加数据Node<Integer> six= new Node<Integer>(22, null);six.next = second;first.next = six;}

删除数据: 

    public static void main(String[] args) throws Exception {//构建结点Node<Integer> first = new Node<Integer>(11, null);Node<Integer> second = new Node<Integer>(13, null);Node<Integer> third = new Node<Integer>(12, null);Node<Integer> fourth = new Node<Integer>(8, null);Node<Integer> fifth = new Node<Integer>(9, null);//生成链表first.next = second;second.next = third;third.next = fourth;fourth.next = fifth;//添加数据Node<Integer> six= new Node<Integer>(22, null);six.next = second;first.next = six;//删除数据first.next = second;}

修改数据:

修改值就很简单了找到节点直接修改就可以了:

 first.value = 100;

单向链表

单向链表是链表的一种,它由多个结点组成,每个结点都由一个数据域和一个指针域组成,数据域用来存储数据, 指针域用来指向其后继结点。链表的头结点的数据域不存储数据,指针域指向第一个真正存储数据的结点。

单向链表设计 :

类名LinkList
构造方法LinkList():创建LinkList对象
成员方法

1.public void clear():空置线性表

2.publicboolean isEmpty():判断线性表是否为空,是返回true,否返回false

3.public int length():获取线性表中元素的个数

4.public T get(int i):读取并返回线性表中的第i个元素的值

5.public void insert(T t):往线性表中添加一个元素;

6.public void insert(int i,T t):在线性表的第i个元素之前插入一个值为t的数据元素。

7.public T remove(int i):删除并返回线性表中第i个数据元素。

8.public int indexOf(T t):返回线性表中首次出现的指定的数据元素的位序号,若不存在,则

返回-11。

成员内部 类private class Node:结点类
成员变量

1.private Node head:记录首结点

2.private int N:记录链表的长度

/*** 单链表 (虚拟节点)* @param <T>*/
public class LinkList<T> {//记录头结点private Node head;//记录链表的长度private int N;public LinkList() {//初始化头结点head = new Node(null, null);N = 0;}//清空链表public void clear() {head.next = null;head.item = null;N = 0;}//获取链表的长度public int length() {return N;}//判断链表是否为空public boolean isEmpty() {return N == 0;}//获取指定位置i出的元素public T get(int i) {if (i < 0 || i >= N) {throw new RuntimeException("位置不合法!");}Node n = head.next;for (int index = 0; index < i; index++) {n = n.next;}return n.item;}//向链表中添加元素tpublic void insert(T t) {//找到最后一个节点Node n = head;while (n.next != null) {n = n.next;}Node newNode = new Node(t, null);n.next = newNode;//链表长度+1N++;}//向指定位置i处,添加元素tpublic void insert(int i, T t) {if (i < 0 || i >= N) {throw new RuntimeException("位置不合法!");}//寻找位置i之前的结点Node pre = head;for (int index = 0; index <= i - 1; index++) {pre = pre.next;}//位置i的结点Node curr = pre.next;Node newNode = new Node(t, curr);//让之前的结点指向新结点pre.next = newNode;//长度+1N++;}//删除指定位置i处的元素,并返回被删除的元素public T remove(int i) {if (i < 0 || i >= N) {throw new RuntimeException("位置不合法");}//寻找i之前的元素Node pre = head;for (int index = 0; index <= i - 1; index++) {pre = pre.next;}//当前i位置的结点Node curr = pre.next;//前一个结点指向下一个结点,删除当前结点pre.next = curr.next;//长度-1N--;return curr.item;}//查找元素t在链表中第一次出现的位置public int indexOf(T t) {Node n = head;for (int i = 0; n.next != null; i++) {n = n.next;if (n.item.equals(t)) {return i;}}return -1;}//结点类private class Node {//存储数据T item;//下一个结点Node next;public Node(T item, Node next) {this.item = item;this.next = next;}}
}

测试 : 

public class LinkTest {public static void main(String[] args) {LinkList<String> list = new LinkList<>();list.insert("aa");list.insert("bb");list.insert(1,"cc");list.insert("dd");for (int i = 0; i < list.length(); i++) {System.out.println(list.get(i));}list.remove(1);System.out.println(list.length());}
}

只要设计合理 , 都可以!

简化一点的版本 : 


/*** 单向链表*/
public class SinglyLinkedList {//哨兵(头指针)private Node head = null;//节点类private static class Node {int data;Node next;public Node(int data, Node next) {this.data = data;this.next = next;}}/*** 向链表头部插入** @param value*/public void addFirst(int value) {//1.链表为空的情况//head = new Node(value, null);//2.链表非空head = new Node(value, head);}/*** 遍历*/public void foreach(Consumer<Integer> consumer) {Node p = head;while (p != null) {consumer.accept(p.data);p = p.next;}}/*** 找到最后一个节点** @return*/private Node findLast() {if (head == null) {return null;}Node p = head;while (p.next != null) {p = p.next;}return p;}/*** 在链表尾部添加节点** @param value*/public void addLast(int value) {Node last = findLast();if (last == null) {addFirst(value);return;}last.next = new Node(value, null);}/*** 根据索引查找** @param index* @return*/private Node findNode(int index) {int i = 0;for (Node p = head; p != null; p = p.next, i++) {if (i == index) {return p;}}return null;}/*** 根据索引获取值** @param index* @return*/public int get(int index) {Node node = findNode(index);if (node == null) {throw new IllegalArgumentException(String.format("index is error"));}return node.data;}/*** 向索引位置插入数据** @param index* @param value*/public void insert(int index, int value) {if (index == 0) {addFirst(value);return;}Node node = findNode(index - 1);if (node == null) {throw new IllegalArgumentException(String.format("index is error"));}node.next = new Node(value, node.next);}/*** 删除头*/public void removeFirst() {if (head == null) {throw new IllegalArgumentException("Null");} else {head = head.next;}}/*** 按索引删除** @param index*/public void removeIndex(int index) {if (index == 0) {removeFirst();} else {Node node = findNode(index - 1);if (node == null) {throw new IllegalArgumentException("error");}if (node.next == null) {throw new IllegalArgumentException("error");}node.next = node.next.next;}}}

测试大家自己练习一下......

双向链表

双向链表也叫双向表,是链表的一种,它由多个结点组成,每个结点都由一个数据域和两个指针域组成,数据域用 来存储数据,其中一个指针域用来指向其后继结点,另一个指针域用来指向前驱结点。链表的头结点的数据域不存 储数据,指向前驱结点的指针域值为null,指向后继结点的指针域指向第一个真正存储数据的结点。

 简单写了一下 , 伙伴们自己完善和修改吧 : 

/*** 双链表*/public class TwoLinkList {//哨兵节点private Node head = new Node(null, -1, null);//节点private static class Node {Node pre;int value;Node next;public Node(Node pre, int value, Node next) {this.pre = pre;this.value = value;this.next = next;}}/*** 查找尾节点** @return*/private Node findLastNode() {Node node = head;while (node.next != null ) {node = node.next;}return node;}/*** 尾插入** @param value*/public void insert(int value) {Node lastNode = findLastNode();lastNode.next = new Node(lastNode,value,null);}/*** 头插入** @param value*/public void addFist(int value) {//插入Node node = head;node.next=new Node(head,value,head.next);}/*** 遍历*/public void forEach() {if (head.next == null) {System.out.println("null!");}else {Node p = head.next;while (p != null) {System.out.println(p.value);p = p.next;}}}
}

测试 : 

public class TwoLinkListTest {public static void main(String[] args) {TwoLinkList twoLinkList = new TwoLinkList();twoLinkList.addFist(1);twoLinkList.addFist(2);twoLinkList.addFist(3);twoLinkList.addFist(4);twoLinkList.insert(4);twoLinkList.forEach();}
}

这关就到这里了, 朋友们下一关见!

这篇关于算法通关村第一关-链表青铜挑战笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/218564

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

跨国公司撤出在华研发中心的启示:中国IT产业的挑战与机遇

近日,IBM中国宣布撤出在华的两大研发中心,这一决定在IT行业引发了广泛的讨论和关注。跨国公司在华研发中心的撤出,不仅对众多IT从业者的职业发展带来了直接的冲击,也引发了人们对全球化背景下中国IT产业竞争力和未来发展方向的深思。面对这一突如其来的变化,我们应如何看待跨国公司的决策?中国IT人才又该如何应对?中国IT产业将何去何从?本文将围绕这些问题展开探讨。 跨国公司撤出的背景与

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

csu1329(双向链表)

题意:给n个盒子,编号为1到n,四个操作:1、将x盒子移到y的左边;2、将x盒子移到y的右边;3、交换x和y盒子的位置;4、将所有的盒子反过来放。 思路分析:用双向链表解决。每个操作的时间复杂度为O(1),用数组来模拟链表,下面的代码是参考刘老师的标程写的。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学