前言
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、链表介绍
1.链表在内存中的存储(真实存储)
链表:
1,链表以节点的方式来存储,是链式存储
2,每个节点包含一个date域,next域指向下一个节点
3,链表的各个节点不一定是连续存储
4,链表分带头节点的链表和不带头节点的链表,根据实际需求来确定
2.单链表(带头节点)逻辑结构
二、单向链表的实现
1.不考虑编号顺序时,添加英雄分析
2.不考虑编号顺序时,添加英雄代码
代码如下(示例):
//定义 SingleLinkedList管理HeroNode节点对象
class SingleLinkedList{
//先创建一个头节点,作用就是表示单链表的头,头节点不动,不存放具体的数据
private HeroNode head=new HeroNode(0,"","");
/**
* 添加节点到单向链表:(当不考虑编号顺序时)
* 1,找到当前链表的最后节点
* 2,将最后这个节点的next指向新的节点
*/
public void add(HeroNode heroNode){
//1,找到当前链表的最后节点
//因为head节点不能动,所以我们需要一个辅助遍历temp,从头开始遍历
HeroNode temp=head;
//遍历链表,找到最后
while(true){
//如果链表为空,表明已经找到了最后
if(temp.next==null){
break;
}
//如果没有找到最后,就将temp后移
temp=temp.next;
}
//当退出while循环时,temp就指向链表的最后,将最后这个节点的next执行那个新的节点heroNode
temp.next=heroNode;
}
//显示链表,遍历
public void list(){
//判断链表是否为空
if(head.next==null){
System.out.println("链表为空!");
return;
}
//因为头节点不能动,所以我们需要一个辅助变量来遍历
HeroNode temp=head.next;
while(true){
//判断链表是否到最后
if(temp==null){
return;
}
//输出节点的信息,并将辅助变量后移
System.out.println(temp);
temp=temp.next;
}
}
}
//定义HeroNode,每一个HeroNode的对象,就是一个节点
class HeroNode{
public int no;//英雄编号
public String name;//英雄名字
public String nickname;//英雄昵称
public HeroNode next;//指向下一个节点
@Override
public String toString() {
return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
}
//构造器,构造一个节点
public HeroNode(int no,String name,String nickname){
this.no=no;
this.name=name;
this.nickname=nickname;
//为了显示节点,重写toString方法
}
}
3.考虑编号顺序时,添加英雄分析
4.考虑编号顺序时,添加英雄代码
代码如下(示例):
public void addByOrder(HeroNode heroNode){
//1,首先找到新添加节点的位置,是通过辅助变量(指针),通过遍历来完成
//因为head节点不能动,所以我们需要一个辅助遍历temp,从头开始遍历
HeroNode temp=head;
//因为是单链表,所以我们找的temp是位于添加位置的前一个节点,否则插入不了
boolean flag=false;//flag标志添加的编号是否存在,默认为false
//while循环来进行遍历,找到新添加节点的位置
while(true){
if(temp.next==null){
//说明temp已经在链表的最后
break;
}
if(temp.next.no>heroNode.no){
//位置找到,就在temp的后面插入
//该值小于temp.next.no,说明该值就在temp和temp.next之间,直接插入到它们之间即可
break;
}else if(temp.next.no==heroNode.no){
//说明希望添加的heroNode的编号已经存在了
flag = true;//说明编号存在
break;
}
//将temp后移,遍历当前链表,继续找看是否有符合上述条件的
temp=temp.next;
}
//判断flag的值
if(flag){
//不能添加,说明编号存在
System.out.printf("准备插入的英雄的编号%d已经存在了,不能加入\n",heroNode.no);
}else{
//2.插入到链表中, temp 的后面,将该编号插入到temp和temp.next之间
heroNode.next=temp.next;
temp.next=heroNode;
}
}
5.单链表的修改
修改节点的信息,根据no编号来修改,即no编号不能改,no编号改相当于添加数据
1.找到需要修改的节点,根据no编号
2,根据newHeroNode的no来修改即可
代码如下(示例):
public void update(HeroNode newHeroNode){
//判断是否为空
if(head.next==null){
System.out.println("链表为空~");
}
//1.找到需要修改的节点,根据no编号
//定义一个辅助变量
HeroNode temp=head.next;
boolean flag=false;//表示是否找到该节点
while(true){
if(temp==null){
//表示已经遍历完链表
break;
}
if(temp.no==newHeroNode.no){
//找到了该节点
flag=true;
break;
}
//如果没有找到最后,就将temp后移,遍历
temp=temp.next;
}
//2,根据newHeroNode的no来修改即可
//根据flag判断是否找到要修改的节点
if(flag){
temp.name=newHeroNode.name;
temp.nickname=newHeroNode.nickname;
}else{
//没有找到
System.out.printf("没有找到编号%d的节点,不能修改\n",newHeroNode.no);
}
}
6.单链表的删除
删除节点思路
分析:head头节点不能动,因此我们需要一个temp辅助节点找到待删除节点的前一个节点
1,先找到需要删除的这个节点的前一个节点temp
说明我们在比较时,是temp.next.no和需要删除的节点的no进行比较
2,temp.next=temp.next.next;
说明:被删除的节点,将不会有其它引用指向,会被垃圾回收机制回收
代码如下(示例):
public void delete(int no){
//1,先找到需要删除的这个节点的前一个节点temp
//定义一个辅助变量
HeroNode temp=head.next;
boolean flag=false;//表示是否找到待删除节点
while(true){
if(temp==null){
//表示已经到了链表的最后
break;
}
if(temp.next.no==no){
//找到了待删除节点的前一个节点temp
flag=true;
break;
}
//如果没有找到最后,就将temp后移,遍历
temp=temp.next;
}
//2,根据newHeroNode的no来修改即可
//根据flag判断是否找到要修改的节点
if(flag){
//找到了,进行删除
temp.next=temp.next.next;
}else{
//没有找到
System.out.printf("要删除的节点%d不存在",no);
}
}
7.单链表小练-问题1:求单链表中有效节点的个数
分析:
方法:获取到单链表有效节点的个数(如果是带头节点的链表,需要不统计头节点)
代码如下(示例):
public static int getLength(HeroNode head){
//如果链表为空
if(head.next==null){
return 0;
}
//定义一个计数变量length来统计来年表有效节点的个数
int length=0;
//因为头节点不能动,所以我们需要一个辅助变量来遍历
//不统计头节点head.next
HeroNode temp=head.next;
while(temp!=null){
//没有到最后,遍历链表,得到每一个节点length++
length++;
//将temp后移
temp=temp.next;
}
return length;
}
8.单链表小练-问题2:查找单链表中的倒数第k个节点
(1)分析:倒数第k个节点即就是寻找第:(单链表有效结点的个数-k)个节点,返回这个节点即可
* 1,编写一个方法,来接收头节点和索引
* 2,遍历链表,找到(单链表有效结点的个数-k)个节点,并将其返回即可
(2)代码如下(示例):
public static HeroNode findLastIndexNode(HeroNode head,int index){
//先判断链表是否为空,为空的话,返回一个null
if(head.next==null){
return null;
}
//第一个遍历得到链表的长度
int size=getLength(head);
//先做一个index的校验
if(index<=0||index>size){
return null;
}
//2,遍历链表,找到(单链表有效结点的个数-k)个节点,并将其返回即可
//该节点的索引就为:getLength(head)-index
//因为头节点不能动,所以我们需要一个辅助变量来遍历
//不统计头节点head.next
HeroNode temp=head.next;
//遍历链表得到第getLength(head)-index个节点
for(int i=0;i<getLength(head)-index;i++){
//没有到该节点,将temp后移
temp=temp.next;
}
//返回该节点
return temp;
}
(3)力扣小练-快慢指针解决链表中倒数第k个节点
https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/
9.单链表小练-问题3:单链表的反转(头插法)
(1)#思路
1,遍历单链表拿出每一个节点
2,创建一个新的链表头reverseHead=new HeroNode();
3.将原单链表中拿出的节点,添加到新的链表头reverseHead中的最前端
接着让temp指向原头的下一个节点
4,再让原来的链表头指向新的链表头的下一个节点
(2)#代码
代码如下(示例):
public static void reverseList(HeroNode head){
//如果当前链表为空,或者只有一个节点,直接返回即可
if(head.next==null||head.next.next==null){
return;
}
// 2,创建一个新的链表头reverseHead
HeroNode reverseHead=new HeroNode(0,"","");
//1,遍历单链表拿出每一个节点
//因为头节点不能动,所以我们需要一个辅助变量来遍历原来的链表
HeroNode temp=head.next;
HeroNode next = null;// 指向当前节点[temp]的下一个节点
while(temp!=null){
//拿走了temp节点,而没有记录下一节点,单链表就断开了,无法进行下面拿原单链表中的节点
next = temp.next;//先暂时保存原头的下一个节点,因为后面需要使用
//将摘下来的节点放在新的链表和它下一个节点之间
//将temp的下一个节点指向新的链表头的最前端
temp.next=reverseHead.next;
//将temp连接到新的链表头上
reverseHead.next=temp;
//让next后移,继续遍历原头后面的节点
temp=next;
}
//4,再让原来的链表头指向新的链表头的下一个节点
//将head.Next指向新的链表头,实现单链表的反转
head.next=reverseHead.next;
}
(3)力扣小练
https://leetcode-cn.com/problems/reverse-linked-list/submissions/
代码如下(示例):
//采用头插法
public ListNode reverseList(ListNode head) {
//创建一个新的链表头
ListNode reverseHead=null;
ListNode temp=head;
ListNode next=null;//用来保存当前节点的下一个节点
while(temp!=null){
next=temp.next;//先保存当前节点的下一个节点
temp.next=reverseHead;
reverseHead=temp;
//让temp后移,继续遍历原链表的下一个节点
temp=next;
}
return reverseHead;
}
10.单链表小练-问题4:从尾到头打印单链表
(1)分析:
方式一:先将单链表进行反转操作,然后再遍历即可(会破坏原来单链表的结构)
方式二:利用栈(Stack)这个数据结构,将各个节点压入到栈中,然后利用栈的先进后出的特点,就实现了逆序打印的效果
1,创建一个栈stack
2,遍历单链表得到每一个节点,将每一个节点添加到栈中(入栈)
3,遍历栈,出栈
(2)代码如下(示例):
public static void reversePrint(HeroNode head){
//如果是空链表不能打印
if(head.next==null){
return;
}
//1,创建一个栈stack
Stack<HeroNode> stack=new Stack<HeroNode>();
// 2,遍历单链表得到每一个节点,将每一个节点添加到栈中(入栈)
//因为头节点不能动,所以需要一个辅助变量来遍历链表
HeroNode temp=head.next;
//入栈
while(temp!=null){
//2,遍历单链表得到每一个节点,将每一个节点添加到栈中(入栈)
stack.add(temp);
//将temp后移
temp=temp.next;
}
//出栈
while(stack.size()>0){
System.out.println(stack.pop());
}
}
(3)力扣小练
https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/submissions/
11.单链表小练-问题5:合并两个有序的单链表(力扣)
https://leetcode-cn.com/problems/merge-two-sorted-lists/submissions/
三、双向链表
1.双向链表相对于单向链表的优点
1,单向链表,查找的方向只能是一个方向,而双向链表可以向前或向后查找
2,单向链表不能自我删除,需要借助辅助节点,而双向链表,则可以自我删除,所以前面单链表删除节点,总是找到temp,temp是待删除节点的前一个节点
2.双向链表增删改除思路分析
2.双向链表增删改除代码
(1)双向链表的添加分析
双向链表的添加(默认添加到双向链表的最后)
1,先找到双向链表的最后这个节点
2,temp.next=newHeroNode
3,newHeroNode.pre=temp
(1)双向链表的添加代码如下(示例):
//添加一个节点到双向链表的最后
public void add(HeroNode2 heroNode) {
//因为head头节点不能动,因此我们需要一个辅助遍历temp
HeroNode2 temp = head;
//遍历链表,找到链表最后位置
while (true) {
//如果到链表最后,就退出
if (temp.next == null) {
break;
}
//如果没到最后,就将temp后移
temp = temp.next;
}
//当退出while循环时,temp就指向链表的最后
//形成一个双向来年表
temp.next = heroNode;
heroNode.pre = temp;
}
(2)双向链表的修改分析
修改一个节点的内容,和单链表一样
(2)双向链表的修改代码如下(示例):
public void update(HeroNode2 newHeroNode) {
//判断链表是否为空
if(newHeroNode==null){
System.out.println("链表为空~");
return;
}
//定义一个辅助变量
HeroNode2 temp=head.next;
//找到需要修改的节点,根据其no编号
//表示是否找到该节点
boolean flag=false;
while(true){
if(temp==null){
//已经遍历完该链表
break;
}
if(temp.no==newHeroNode.no){
//找到需要修改的节点
flag=true;
break;
}
//将temp后移
temp=temp.next;
}
//根据flag判断是否找到要修改的节点
if(flag){
temp.name=newHeroNode.name;
temp.nickname=newHeroNode.nickname;
}else{
//没有找到
System.out.printf("没有找到编号为%d的节点,不能修改\n",newHeroNode.no);
}
}
(3)双向链表的删除分析
双向链表的删除
1,因为是双向链表,因此我们可实现自我删除某个节点
2,直接找到要删除的这个节点,比如temp
3,temp.pre.next=temp.next
4,temp.next.pre=temp.pre
(3)双向链表的删除代码如下(示例):
public void delete(int no) {
//判断当前链表是否为空
if(head.next==null){
System.out.println("链表为空,不能删除~");
return;
}
//辅助变量
HeroNode2 temp=head.next;
boolean flag=false;//标记变量,标记是否找到待删除节点
while(true){
//已经到链表的最后
if(temp==null){
break;
}
if(temp.no==no){
//找到待删除节点
flag=true;
break;
}
//将temp后移,进行遍历
temp=temp.next;
}
//判断flag
if(flag){
temp.pre.next=temp.next;
/*
这里代码有问题:
如果是最后一个节点,就不需要执行下面这句话,否则就会出现空指针异常
是最后一个的话, temp.next==null
*/
if(temp.next!=null) {
temp.next.pre = temp.pre;
}
}else{
System.out.printf("要删除的节点%d不存在\n",no);
}
}
(4)添加节点到双向链表(当考虑编号顺序时)分析
1,首先找到新添加节点的位置,是通过辅助变量(指针),通过遍历来完成
2. 将该英雄编号插入到temp和temp.next之间
(4)添加节点到双向链表(当考虑编号顺序时)的代码如下(示例):
public void addByOrder(HeroNode2 heroNode){
//1,首先找到新添加节点的位置,是通过辅助变量(指针),通过遍历来完成
//因为head节点不能动,所以我们需要一个辅助遍历temp,从头开始遍历
HeroNode2 temp=head;
//因为是单链表,所以我们找的temp是位于添加位置的前一个节点,否则插入不了
boolean flag=false;//flag标志添加的编号是否存在,默认为false
//while循环来进行遍历,找到新添加节点的位置
while(true){
if(temp.next==null){
//说明temp已经在链表的最后
break;
}
if(temp.next.no>heroNode.no){
//位置找到,就在temp的后面插入
//该值小于temp.next.no,说明该值就在temp和temp.next之间,直接插入到它们之间即可
break;
}else if(temp.next.no==heroNode.no){
//说明希望添加的heroNode的编号已经存在了
flag = true;//说明编号存在
break;
}
//将temp后移,遍历当前链表,继续找看是否有符合上述条件的
temp=temp.next;
}
//判断flag的值
if(flag){
//不能添加,说明编号存在
System.out.printf("准备插入的英雄的编号%d已经存在了,不能加入\n",heroNode.no);
}else if(temp.next!=null){
//待添加的节点不存在,且temp不是指向双链表的最后一个节点,将该编号插入到temp和temp.next之间(画个图即可)
//使用一个变量来保存temp.next
HeroNode2 count=temp.next;
heroNode.next=count;
count.pre=heroNode;
temp.next=heroNode;
heroNode.pre=temp;
}else{
//待添加的节点不存在,且temp指向双链表的最后一个节点
temp.next=heroNode;
heroNode.next=null;
heroNode.pre=temp;
}
}
4.双链表小练习(也可使用单链表)
https://leetcode-cn.com/problems/design-linked-list/submissions/
四、单向环形链表(Josepfu约瑟夫环问题)
1.单向环形链表应用场景
2.约瑟夫问题示意图
3.构建一个单向环形链表思路
4.构建一个单向环形链表代码
代码如下(示例):
//创建一个单向的环形链表
class CircleSingleLinkedList{
//创建一个first节点,当前没有编号
private Boy first=null;
//添加小孩节点,构成一个环形的链表
/**
* 构建一个单向环形链表的思路
* 1,先创建第一个节点,让first指向该节点,并形成环形
* 2,后面当我们每创建一个新的节点,就把该节点加入到已有的环形链表中即可
* @param nums 代表环形链表有几个小孩
*/
public void addBoy(int nums){
//nums做一个数据校验
if(nums<1){
System.out.println("nums的值不正确~");
return;
}
//辅助指针,帮助构建环形链表,因为first头指针不能动
Boy curBoy=null;
//根据for,将nums个小孩添加到单向环形链表中,因为已经创建了一个null的first指针,如果for从0开始会出现空指针异常
for(int i=1;i<=nums;i++){
//根据编号创建小孩节点,,第几个小孩
Boy boy=new Boy(i);
if(i==1){
//如果是第一个小孩,单独考虑,形成环状
first=boy;//让first节点指向boy
first.setNext(first);//让first指向first构成一个环状
curBoy=first;//让curBoy指向first节点,first不能动
}else{
curBoy.setNext(boy);//先让curBoy指向boy节点
boy.setNext(first);//再让boy节点next指向first节点
curBoy=boy;//让curBoy指向boy节点,因为后面可能还有节点
}
}
}
/**
* 遍历环形链表
* 1,先让一个辅助指针(变量)curBoy,指向first节点
* 2,然后通过一个while循环遍历该环形链表即可,curBoy.next=first结束
*/
public void showBoy(){
//判断链表是否为空
if(first==null){
System.out.println("没有任何小孩~~");
return;
}
//因为first不能动,所以我们需要一个辅助指针来帮助我们进行遍历
Boy curBoy=first;
while(true){
System.out.printf("小孩的编号%d\n",curBoy.getNo());
if(curBoy.getNext()==first){
//说明已遍历完毕
break;
}
//将curBoy后移
curBoy=curBoy.getNext();
}
}
}
5.约瑟夫问题思路(小孩出圈顺序思路)
6.约瑟夫问题代码
代码如下(示例):
public void countBoy(int startNo,int countNum,int nums){
//先对数据进行校验
if(first==null||startNo<1 ||startNo>nums){
System.out.println("输入参数有误,请重新输入~");
return;
}
//1,需要创建一个辅助指针(变量)helper,事先应该指向环形链表的最后这个节点
Boy helper=first;
while(true){
if(helper.getNext()==first){
//说明helper已经指向最后小孩节点
break;
}
//helper没指向环形链表的最后那个节点,将helper后移
helper=helper.getNext();
}
//2,先让first和helper移动到开始数的的地方(当小孩报数前)
for(int i=0;i<startNo-1;i++){
first=first.getNext();
helper=helper.getNext();
}
//3,当小孩报数时,让first和helper指针同时移动(countNum-1)次, 然后出圈
//这是一个循环操作,直到圈中只剩下一个节点
while(true){
if(helper==first){
//圈中只剩下一个节点
break;
}
//当小孩报数时,让first和helper指针同时移动(countNum-1)次
for(int i=0;i<countNum-1;i++){
first=first.getNext();
helper=helper.getNext();
}
//这时first指向的节点就是要出圈的小孩节点
System.out.printf("编号为%d的小孩出圈\n",first.getNo());
//这时就可以将first指向的小孩节点出圈 *(1)first=first.getNext() (2)helper.setNext(first);
first=first.getNext();
helper.setNext(first);
}
System.out.printf("最后留在圈中的小孩编号为:%d\n",first.getNo());
}
7.单向环形链表(Josepfu约瑟夫环问题)小练力扣(数学方法-反推)
(1)数学方法-反推(详解)
https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/solution/javajie-jue-yue-se-fu-huan-wen-ti-gao-su-ni-wei-sh/
(2)反推力扣
https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/
(3)结论
//反推的过程,就是 (当前index + m) % 上一轮剩余数字的个数。
(4)代码
class Solution {
//数学方法-反推
//https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/solution/javajie-jue-yue-se-fu-huan-wen-ti-gao-su-ni-wei-sh/
public int lastRemaining(int n, int m) {
int result=0;
for(int i=2;i<=n;i++){
result=(result+m)%i;
}
return result;
}
}
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。